mirror of
https://github.com/pi-hole/web.git
synced 2026-02-14 23:19:00 +00:00
First steps into the v6.0 world. Login and logout is now possible and all pages load without PHP errors (even when they are mostly empty until we added all the new API methods)
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
@@ -133,12 +133,6 @@ While we are primarily reachable on our <a href="https://discourse.pi-hole.net/"
|
||||
<img src="https://pi-hole.github.io/graphics/Screenshots/query-log-sorted.png" alt="Query log">
|
||||
</p>
|
||||
|
||||
## An audit log
|
||||
|
||||
<p align="center">
|
||||
<img src="https://pi-hole.github.io/graphics/Screenshots/audit-log.png" alt="Pi-hole Web interface">
|
||||
</p>
|
||||
|
||||
## Long-term statistics to view data over user defined time ranges
|
||||
|
||||
<p align="center">
|
||||
|
||||
197
api.php
197
api.php
@@ -1,197 +0,0 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
$api = true;
|
||||
require_once 'scripts/pi-hole/php/password.php';
|
||||
require_once 'scripts/pi-hole/php/FTL.php';
|
||||
require_once 'scripts/pi-hole/php/database.php';
|
||||
require_once 'scripts/pi-hole/php/auth.php';
|
||||
check_cors();
|
||||
|
||||
$data = array();
|
||||
|
||||
// Common API functions
|
||||
if (isset($_GET['enable']) && $auth) {
|
||||
if (isset($_GET['auth'])) {
|
||||
if ($_GET['auth'] !== $pwhash) {
|
||||
exit('Not authorized!');
|
||||
}
|
||||
} else {
|
||||
// Skip token validation if explicit auth string is given
|
||||
check_csrf($_GET['token']);
|
||||
}
|
||||
pihole_execute('enable');
|
||||
$data = array_merge($data, array('status' => 'enabled'));
|
||||
if (file_exists('../custom_disable_timer')) {
|
||||
unlink('../custom_disable_timer');
|
||||
}
|
||||
} elseif (isset($_GET['disable']) && $auth) {
|
||||
if (isset($_GET['auth'])) {
|
||||
if ($_GET['auth'] !== $pwhash) {
|
||||
exit('Not authorized!');
|
||||
}
|
||||
} else {
|
||||
// Skip token validation if explicit auth string is given
|
||||
check_csrf($_GET['token']);
|
||||
}
|
||||
$disable = intval($_GET['disable']);
|
||||
// intval returns the integer value on success, or 0 on failure
|
||||
if ($disable > 0) {
|
||||
$timestamp = time();
|
||||
pihole_execute('disable '.$disable.'s');
|
||||
file_put_contents('../custom_disable_timer', ($timestamp + $disable) * 1000);
|
||||
} else {
|
||||
pihole_execute('disable');
|
||||
if (file_exists('../custom_disable_timer')) {
|
||||
unlink('../custom_disable_timer');
|
||||
}
|
||||
}
|
||||
$data = array_merge($data, array('status' => 'disabled'));
|
||||
} elseif (isset($_GET['versions'])) {
|
||||
// Determine if updates are available for Pi-hole
|
||||
// using the same script that we use for the footer
|
||||
// on the dashboard (update notifications are
|
||||
// suppressed if on development branches)
|
||||
require 'scripts/pi-hole/php/update_checker.php';
|
||||
$updates = array('core_update' => $core_update,
|
||||
'web_update' => $web_update,
|
||||
'FTL_update' => $FTL_update, );
|
||||
$current = array('core_current' => $core_current,
|
||||
'web_current' => $web_current,
|
||||
'FTL_current' => $FTL_current, );
|
||||
$latest = array('core_latest' => $core_latest,
|
||||
'web_latest' => $web_latest,
|
||||
'FTL_latest' => $FTL_latest, );
|
||||
$branches = array('core_branch' => $core_branch,
|
||||
'web_branch' => $web_branch,
|
||||
'FTL_branch' => $FTL_branch, );
|
||||
$data = array_merge($data, $updates);
|
||||
$data = array_merge($data, $current);
|
||||
$data = array_merge($data, $latest);
|
||||
$data = array_merge($data, $branches);
|
||||
} elseif (isset($_GET['list'])) {
|
||||
if (!$auth) {
|
||||
exit('Not authorized!');
|
||||
}
|
||||
|
||||
if (!isset($_GET['list'])) {
|
||||
exit('List has not been specified.');
|
||||
}
|
||||
|
||||
switch ($_GET['list']) {
|
||||
case 'black':
|
||||
$_POST['type'] = LISTTYPE_BLACKLIST;
|
||||
|
||||
break;
|
||||
|
||||
case 'regex_black':
|
||||
$_POST['type'] = LISTTYPE_REGEX_BLACKLIST;
|
||||
|
||||
break;
|
||||
|
||||
case 'white':
|
||||
$_POST['type'] = LISTTYPE_WHITELIST;
|
||||
|
||||
break;
|
||||
|
||||
case 'regex_white':
|
||||
$_POST['type'] = LISTTYPE_REGEX_WHITELIST;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
exit('Invalid list [supported: black, regex_black, white, regex_white]');
|
||||
}
|
||||
|
||||
if (isset($_GET['add'])) {
|
||||
// Set POST parameters and invoke script to add domain to list
|
||||
$_POST['domain'] = $_GET['add'];
|
||||
$_POST['action'] = 'add_domain';
|
||||
require 'scripts/pi-hole/php/groups.php';
|
||||
} elseif (isset($_GET['sub'])) {
|
||||
// Set POST parameters and invoke script to remove domain from list
|
||||
$_POST['domain'] = $_GET['sub'];
|
||||
$_POST['action'] = 'delete_domain_string';
|
||||
require 'scripts/pi-hole/php/groups.php';
|
||||
} else {
|
||||
// Set POST parameters and invoke script to get all domains
|
||||
$_POST['action'] = 'get_domains';
|
||||
require 'scripts/pi-hole/php/groups.php';
|
||||
}
|
||||
|
||||
return;
|
||||
} elseif (isset($_GET['customdns']) && $auth) {
|
||||
if (isset($_GET['auth'])) {
|
||||
if ($_GET['auth'] !== $pwhash) {
|
||||
exit('Not authorized!');
|
||||
}
|
||||
} else {
|
||||
// Skip token validation if explicit auth string is given
|
||||
check_csrf($_GET['token']);
|
||||
}
|
||||
|
||||
switch ($_GET['action']) {
|
||||
case 'get':
|
||||
$data = echoCustomDNSEntries();
|
||||
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
$data = addCustomDNSEntry();
|
||||
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$data = deleteCustomDNSEntry();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
exit('Wrong action');
|
||||
}
|
||||
} elseif (isset($_GET['customcname']) && $auth) {
|
||||
if (isset($_GET['auth'])) {
|
||||
if ($_GET['auth'] !== $pwhash) {
|
||||
exit('Not authorized!');
|
||||
}
|
||||
} else {
|
||||
// Skip token validation if explicit auth string is given
|
||||
check_csrf($_GET['token']);
|
||||
}
|
||||
|
||||
switch ($_GET['action']) {
|
||||
case 'get':
|
||||
$data = echoCustomCNAMEEntries();
|
||||
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
$data = addCustomCNAMEEntry();
|
||||
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$data = deleteCustomCNAMEEntry();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
exit('Wrong action');
|
||||
}
|
||||
}
|
||||
|
||||
// Other API functions
|
||||
require 'api_FTL.php';
|
||||
|
||||
header('Content-type: application/json');
|
||||
if (isset($_GET['jsonForceObject'])) {
|
||||
echo json_encode($data, JSON_FORCE_OBJECT);
|
||||
} else {
|
||||
echo json_encode($data);
|
||||
}
|
||||
437
api_FTL.php
437
api_FTL.php
@@ -1,437 +0,0 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
if (!isset($api)) {
|
||||
exit('Direct call to api_FTL.php is not allowed!');
|
||||
}
|
||||
|
||||
if (isset($_GET['type'])) {
|
||||
$data['type'] = 'FTL';
|
||||
}
|
||||
|
||||
if (isset($_GET['version'])) {
|
||||
$data['version'] = 3;
|
||||
}
|
||||
|
||||
if (isset($_GET['status']) && $auth) {
|
||||
$return = callFTLAPI('stats');
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
if (in_array('status enabled', $return)) {
|
||||
$data = array_merge($data, array('status' => 'enabled'));
|
||||
} else {
|
||||
$data = array_merge($data, array('status' => 'disabled'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) && $auth) {
|
||||
require_once 'scripts/pi-hole/php/gravity.php';
|
||||
|
||||
$return = callFTLAPI('stats');
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$stats = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
|
||||
if ($tmp[0] === 'domains_being_blocked' && !is_numeric($tmp[1]) || $tmp[0] === 'status') {
|
||||
// Expect string response
|
||||
$stats[$tmp[0]] = $tmp[1];
|
||||
} elseif (isset($_GET['summary'])) {
|
||||
// "summary" expects a formmated string response
|
||||
if ($tmp[0] !== 'ads_percentage_today') {
|
||||
$stats[$tmp[0]] = number_format($tmp[1]);
|
||||
} else {
|
||||
$stats[$tmp[0]] = number_format($tmp[1], 1, '.', '');
|
||||
}
|
||||
} else {
|
||||
// Expect float response
|
||||
$stats[$tmp[0]] = floatval($tmp[1]);
|
||||
}
|
||||
}
|
||||
$stats['gravity_last_updated'] = gravity_last_update(true);
|
||||
$data = array_merge($data, $stats);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['getMaxlogage']) && $auth) {
|
||||
$return = callFTLAPI('maxlogage');
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
// Convert seconds to hours and rounds to one decimal place.
|
||||
$ret = round(intval($return[0]) / 3600, 1);
|
||||
// Return 24h if value is 0, empty, null or non numeric.
|
||||
$ret = $ret ?: 24;
|
||||
|
||||
$data = array_merge($data, array('maxlogage' => $ret));
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['overTimeData10mins']) && $auth) {
|
||||
$return = callFTLAPI('overTime');
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$domains_over_time = array();
|
||||
$ads_over_time = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
$domains_over_time[intval($tmp[0])] = intval($tmp[1]);
|
||||
$ads_over_time[intval($tmp[0])] = intval($tmp[2]);
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'domains_over_time' => $domains_over_time,
|
||||
'ads_over_time' => $ads_over_time,
|
||||
);
|
||||
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['topItems']) && $auth) {
|
||||
if ($_GET['topItems'] === 'audit') {
|
||||
$return = callFTLAPI('top-domains for audit');
|
||||
} elseif (is_numeric($_GET['topItems'])) {
|
||||
$return = callFTLAPI('top-domains ('.$_GET['topItems'].')');
|
||||
} else {
|
||||
$return = callFTLAPI('top-domains');
|
||||
}
|
||||
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$top_queries = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
if (count($tmp) == 2) {
|
||||
$tmp[2] = '';
|
||||
}
|
||||
$domain = utf8_encode($tmp[2]);
|
||||
$top_queries[$domain] = intval($tmp[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($_GET['topItems'] === 'audit') {
|
||||
$return = callFTLAPI('top-ads for audit');
|
||||
} elseif (is_numeric($_GET['topItems'])) {
|
||||
$return = callFTLAPI('top-ads ('.$_GET['topItems'].')');
|
||||
} else {
|
||||
$return = callFTLAPI('top-ads');
|
||||
}
|
||||
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$top_ads = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
$domain = utf8_encode($tmp[2]);
|
||||
if (count($tmp) > 3) {
|
||||
$top_ads[$domain.' ('.$tmp[3].')'] = intval($tmp[1]);
|
||||
} else {
|
||||
$top_ads[$domain] = intval($tmp[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'top_queries' => $top_queries,
|
||||
'top_ads' => $top_ads,
|
||||
);
|
||||
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth) {
|
||||
if (isset($_GET['topClients'])) {
|
||||
$number = $_GET['topClients'];
|
||||
} elseif (isset($_GET['getQuerySources'])) {
|
||||
$number = $_GET['getQuerySources'];
|
||||
}
|
||||
|
||||
if (is_numeric($number)) {
|
||||
$return = callFTLAPI('top-clients ('.$number.')');
|
||||
} else {
|
||||
$return = callFTLAPI('top-clients');
|
||||
}
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$top_clients = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
$clientip = utf8_encode($tmp[2]);
|
||||
if (count($tmp) > 3 && strlen($tmp[3]) > 0) {
|
||||
$clientname = utf8_encode($tmp[3]);
|
||||
$top_clients[$clientname.'|'.$clientip] = intval($tmp[1]);
|
||||
} else {
|
||||
$top_clients[$clientip] = intval($tmp[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$result = array('top_sources' => $top_clients);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['topClientsBlocked']) && $auth) {
|
||||
if (isset($_GET['topClientsBlocked'])) {
|
||||
$number = $_GET['topClientsBlocked'];
|
||||
}
|
||||
|
||||
if (is_numeric($number)) {
|
||||
$return = callFTLAPI('top-clients blocked ('.$number.')');
|
||||
} else {
|
||||
$return = callFTLAPI('top-clients blocked');
|
||||
}
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$top_clients = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
$clientip = utf8_encode($tmp[2]);
|
||||
if (count($tmp) > 3 && strlen($tmp[3]) > 0) {
|
||||
$clientname = utf8_encode($tmp[3]);
|
||||
$top_clients[$clientname.'|'.$clientip] = intval($tmp[1]);
|
||||
} else {
|
||||
$top_clients[$clientip] = intval($tmp[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$result = array('top_sources_blocked' => $top_clients);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['getForwardDestinations']) && $auth) {
|
||||
if ($_GET['getForwardDestinations'] === 'unsorted') {
|
||||
$return = callFTLAPI('forward-dest unsorted');
|
||||
} else {
|
||||
$return = callFTLAPI('forward-dest');
|
||||
}
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$forward_dest = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
$forwardip = utf8_encode($tmp[2]);
|
||||
if (count($tmp) > 3 && strlen($tmp[3]) > 0) {
|
||||
$forwardname = utf8_encode($tmp[3]);
|
||||
$forward_dest[$forwardname.'|'.$forwardip] = floatval($tmp[1]);
|
||||
} else {
|
||||
$forward_dest[$forwardip] = floatval($tmp[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$result = array('forward_destinations' => $forward_dest);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['getQueryTypes']) && $auth) {
|
||||
$return = callFTLAPI('querytypes');
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$querytypes = array();
|
||||
foreach ($return as $ret) {
|
||||
$tmp = explode(': ', $ret);
|
||||
// Reply cannot contain non-ASCII characters
|
||||
$querytypes[$tmp[0]] = floatval($tmp[1]);
|
||||
}
|
||||
|
||||
$result = array('querytypes' => $querytypes);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['getCacheInfo']) && $auth) {
|
||||
$return = callFTLAPI('cacheinfo');
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$cacheinfo = array();
|
||||
foreach ($return as $ret) {
|
||||
$tmp = explode(': ', $ret);
|
||||
// Reply cannot contain non-ASCII characters
|
||||
$cacheinfo[$tmp[0]] = floatval($tmp[1]);
|
||||
}
|
||||
|
||||
$result = array('cacheinfo' => $cacheinfo);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['getAllQueries']) && $auth) {
|
||||
if (isset($_GET['from'], $_GET['until'])) {
|
||||
// Get limited time interval
|
||||
$return = callFTLAPI('getallqueries-time '.$_GET['from'].' '.$_GET['until']);
|
||||
} elseif (isset($_GET['domain'])) {
|
||||
// Get specific domain only
|
||||
$return = callFTLAPI('getallqueries-domain '.$_GET['domain']);
|
||||
} elseif (isset($_GET['client']) && (isset($_GET['type']) && $_GET['type'] === 'blocked')) {
|
||||
// Get specific client only
|
||||
$return = callFTLAPI('getallqueries-client-blocked '.$_GET['client']);
|
||||
} elseif (isset($_GET['client'])) {
|
||||
// Get specific client only
|
||||
$return = callFTLAPI('getallqueries-client '.$_GET['client']);
|
||||
} elseif (isset($_GET['querytype'])) {
|
||||
// Get specific query type only
|
||||
$return = callFTLAPI('getallqueries-qtype '.$_GET['querytype']);
|
||||
} elseif (isset($_GET['forwarddest'])) {
|
||||
// Get specific forward destination only
|
||||
$return = callFTLAPI('getallqueries-forward '.$_GET['forwarddest']);
|
||||
} elseif (is_numeric($_GET['getAllQueries'])) {
|
||||
$return = callFTLAPI('getallqueries ('.$_GET['getAllQueries'].')');
|
||||
} else {
|
||||
// Get all queries
|
||||
$return = callFTLAPI('getallqueries');
|
||||
}
|
||||
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
// Set the header
|
||||
header('Content-type: application/json');
|
||||
|
||||
// Start the JSON string
|
||||
echo '{"data":[';
|
||||
$first = true;
|
||||
|
||||
foreach ($return as $line) {
|
||||
// Insert a comma before the next record (except on the first one)
|
||||
if (!$first) {
|
||||
echo ',';
|
||||
} else {
|
||||
$first = false;
|
||||
}
|
||||
|
||||
$row = str_getcsv($line, ' ');
|
||||
// UTF-8 encode domain
|
||||
$domain = utf8_encode(str_replace('~', ' ', $row[2]));
|
||||
// UTF-8 encode client host name
|
||||
$client = utf8_encode($row[3]);
|
||||
|
||||
// Insert into array and output it in JSON format
|
||||
// array: time type domain client status dnssecStatus reply response_time CNAMEDomain regexID upstream destination EDE
|
||||
echo json_encode(array($row[0], $row[1], $domain, $client, $row[4], $row[5], $row[6], $row[7], $row[8], $row[9], $row[10], $row[11]));
|
||||
}
|
||||
// Finish the JSON string
|
||||
echo ']}';
|
||||
// exit at the end
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['recentBlocked']) && $auth) {
|
||||
exit(utf8_encode(callFTLAPI('recentBlocked')[0]));
|
||||
unset($data);
|
||||
}
|
||||
|
||||
if (isset($_GET['getForwardDestinationNames']) && $auth) {
|
||||
$return = callFTLAPI('forward-names');
|
||||
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$forward_dest = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
$forwardip = utf8_encode($tmp[2]);
|
||||
if (count($tmp) > 3) {
|
||||
$forwardname = utf8_encode($tmp[3]);
|
||||
$forward_dest[$forwardname.'|'.$forwardip] = floatval($tmp[1]);
|
||||
} else {
|
||||
$forward_dest[$forwardip] = floatval($tmp[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$result = array('forward_destinations' => $forward_dest);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['overTimeDataQueryTypes']) && $auth) {
|
||||
$return = callFTLAPI('QueryTypesoverTime');
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$over_time = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
for ($i = 0; $i < count($tmp) - 1; ++$i) {
|
||||
$over_time[intval($tmp[0])][$i] = floatval($tmp[$i + 1]);
|
||||
}
|
||||
}
|
||||
$result = array('over_time' => $over_time);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['getClientNames']) && $auth) {
|
||||
$return = callFTLAPI('client-names');
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$client_names = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
$client_names[] = array(
|
||||
'name' => utf8_encode($tmp[0]),
|
||||
'ip' => utf8_encode($tmp[1]),
|
||||
);
|
||||
}
|
||||
|
||||
$result = array('clients' => $client_names);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['overTimeDataClients']) && $auth) {
|
||||
$return = callFTLAPI('ClientsoverTime');
|
||||
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$over_time = array();
|
||||
foreach ($return as $line) {
|
||||
$tmp = explode(' ', $line);
|
||||
for ($i = 0; $i < count($tmp) - 1; ++$i) {
|
||||
$over_time[intval($tmp[0])][$i] = floatval($tmp[$i + 1]);
|
||||
}
|
||||
}
|
||||
$result = array('over_time' => $over_time);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_lease']) && $auth) {
|
||||
$return = callFTLAPI('delete-lease '.$_GET['delete_lease']);
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$data['delete_lease'] = $return[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['dns-port']) && $auth) {
|
||||
$return = callFTLAPI('dns-port');
|
||||
if (array_key_exists('FTLnotrunning', $return)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
$data['dns-port'] = $return[0];
|
||||
}
|
||||
}
|
||||
423
api_db.php
423
api_db.php
@@ -1,423 +0,0 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
$api = true;
|
||||
require 'scripts/pi-hole/php/password.php';
|
||||
|
||||
header('Content-type: application/json');
|
||||
require 'scripts/pi-hole/php/database.php';
|
||||
require 'scripts/pi-hole/php/auth.php';
|
||||
require_once 'scripts/pi-hole/php/func.php';
|
||||
check_cors();
|
||||
|
||||
// Set maximum execution time to 10 minutes
|
||||
ini_set('max_execution_time', '600');
|
||||
|
||||
$data = array();
|
||||
|
||||
// Needs package php5-sqlite, e.g.
|
||||
// sudo apt-get install php5-sqlite
|
||||
|
||||
$QUERYDB = getQueriesDBFilename();
|
||||
$db = SQLite3_connect($QUERYDB);
|
||||
|
||||
if (isset($_GET['network']) && $auth) {
|
||||
$network = array();
|
||||
$results = $db->query('SELECT * FROM network');
|
||||
|
||||
while ($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) {
|
||||
$id = intval($res['id']);
|
||||
|
||||
// Get IP addresses and host names for this device
|
||||
$res['ip'] = array();
|
||||
$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));
|
||||
}
|
||||
|
||||
if (isset($_GET['getAllQueries']) && $auth) {
|
||||
$allQueries = array();
|
||||
if ($_GET['getAllQueries'] !== 'empty') {
|
||||
$from = intval($_GET['from']);
|
||||
$until = intval($_GET['until']);
|
||||
|
||||
// Use table "query_storage"
|
||||
// - replace domain ID with domain
|
||||
// - replace client ID with client name
|
||||
// - replace forward ID with forward destination
|
||||
$dbquery = 'SELECT timestamp, type,';
|
||||
$dbquery .= " CASE typeof(domain) WHEN 'integer' THEN (SELECT domain FROM domain_by_id d WHERE d.id = q.domain) ELSE domain END domain,";
|
||||
$dbquery .= " CASE typeof(client) WHEN 'integer' THEN (";
|
||||
$dbquery .= " SELECT CASE TRIM(name) WHEN '' THEN c.ip ELSE c.name END name FROM client_by_id c WHERE c.id = q.client";
|
||||
$dbquery .= ' ) ELSE client END client,';
|
||||
$dbquery .= " CASE typeof(forward) WHEN 'integer' THEN (SELECT forward FROM forward_by_id f WHERE f.id = q.forward) ELSE forward END forward,";
|
||||
$dbquery .= ' status, reply_type, reply_time, dnssec';
|
||||
$dbquery .= ' FROM query_storage q';
|
||||
$dbquery .= ' WHERE timestamp >= :from AND timestamp <= :until ';
|
||||
if (isset($_GET['status'])) {
|
||||
// if some query status should be excluded
|
||||
$excludedStatus = $_GET['status'];
|
||||
if (preg_match('/^[0-9]+(?:,[0-9]+)*$/', $excludedStatus) === 1) {
|
||||
// Append selector to DB query. The used regex ensures
|
||||
// that only numbers, separated by commas are accepted
|
||||
// to avoid code injection and other malicious things
|
||||
// We accept only valid lists like "1,2,3"
|
||||
// We reject ",2,3", "1,2," and similar arguments
|
||||
$dbquery .= 'AND status NOT IN ('.$excludedStatus.') ';
|
||||
} else {
|
||||
exit('Error. Selector status specified using an invalid format.');
|
||||
}
|
||||
}
|
||||
$dbquery .= 'ORDER BY timestamp ASC';
|
||||
$stmt = $db->prepare($dbquery);
|
||||
$stmt->bindValue(':from', intval($from), SQLITE3_INTEGER);
|
||||
$stmt->bindValue(':until', intval($until), SQLITE3_INTEGER);
|
||||
$results = $stmt->execute();
|
||||
|
||||
// Start the JSON string
|
||||
echo '{"data":[';
|
||||
|
||||
if (!is_bool($results)) {
|
||||
$first = true;
|
||||
while ($row = $results->fetchArray(SQLITE3_ASSOC)) {
|
||||
// Insert a comma before the next record (except on the first one)
|
||||
if (!$first) {
|
||||
echo ',';
|
||||
} else {
|
||||
$first = false;
|
||||
}
|
||||
|
||||
// Format, encode, transform each field (if necessary).
|
||||
$time = $row['timestamp'];
|
||||
$query_type = getQueryTypeStr($row['type']); // Convert query type ID to name
|
||||
$domain = utf8_encode(str_replace('~', ' ', $row['domain']));
|
||||
$client = $row['client'];
|
||||
$status = $row['status'];
|
||||
$destination = utf8_encode($row['forward']);
|
||||
$reply_type = $row['reply_type'];
|
||||
$reply_time = $row['reply_time'];
|
||||
$dnssec = $row['dnssec'];
|
||||
|
||||
// Insert into array and output it in JSON format
|
||||
echo json_encode(array($time, $query_type, $domain, $client, $status, $destination, $reply_type, $reply_time, $dnssec));
|
||||
}
|
||||
}
|
||||
|
||||
// Finish the JSON string
|
||||
echo ']}';
|
||||
|
||||
// exit at the end
|
||||
exit;
|
||||
}
|
||||
// only used if getAllQueries==empty
|
||||
$result = array('data' => $allQueries);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['topClients']) && $auth) {
|
||||
// $from = intval($_GET["from"]);
|
||||
$limit = '';
|
||||
if (isset($_GET['from'], $_GET['until'])) {
|
||||
$limit = 'WHERE timestamp >= :from AND timestamp <= :until';
|
||||
} elseif (isset($_GET['from']) && !isset($_GET['until'])) {
|
||||
$limit = 'WHERE timestamp >= :from';
|
||||
} elseif (!isset($_GET['from']) && isset($_GET['until'])) {
|
||||
$limit = 'WHERE timestamp <= :until';
|
||||
}
|
||||
$dbquery = "SELECT CASE typeof(client) WHEN 'integer' THEN (";
|
||||
$dbquery .= " SELECT CASE TRIM(name) WHEN '' THEN c.ip ELSE c.name END name FROM client_by_id c WHERE c.id = q.client)";
|
||||
$dbquery .= ' ELSE client END client, count(client) FROM query_storage q '.$limit.' GROUP BY client ORDER BY count(client) DESC LIMIT 20';
|
||||
|
||||
$stmt = $db->prepare($dbquery);
|
||||
$stmt->bindValue(':from', intval($_GET['from']), SQLITE3_INTEGER);
|
||||
$stmt->bindValue(':until', intval($_GET['until']), SQLITE3_INTEGER);
|
||||
$results = $stmt->execute();
|
||||
|
||||
$clientnums = array();
|
||||
|
||||
if (!is_bool($results)) {
|
||||
while ($row = $results->fetchArray()) {
|
||||
// $row[0] is the client IP
|
||||
|
||||
if (array_key_exists($row[0], $clientnums)) {
|
||||
// Entry already exists, add to it (might appear multiple times due to mixed capitalization in the database)
|
||||
$clientnums[$row[0]] += intval($row[1]);
|
||||
} else {
|
||||
// Entry does not yet exist
|
||||
$clientnums[$row[0]] = intval($row[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by number of hits
|
||||
arsort($clientnums);
|
||||
|
||||
// Extract only the first ten entries
|
||||
$clientnums = array_slice($clientnums, 0, 10);
|
||||
|
||||
$result = array('top_sources' => $clientnums);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['topDomains']) && $auth) {
|
||||
$limit = '';
|
||||
|
||||
if (isset($_GET['from'], $_GET['until'])) {
|
||||
$limit = ' AND timestamp >= :from AND timestamp <= :until';
|
||||
} elseif (isset($_GET['from']) && !isset($_GET['until'])) {
|
||||
$limit = ' AND timestamp >= :from';
|
||||
} elseif (!isset($_GET['from']) && isset($_GET['until'])) {
|
||||
$limit = ' AND timestamp <= :until';
|
||||
}
|
||||
// Select top permitted domains only
|
||||
$stmt = $db->prepare('SELECT domain,count(domain) FROM queries WHERE status IN (2,3,12,13,14,17)'.$limit.' GROUP by domain order by count(domain) desc limit 20');
|
||||
$stmt->bindValue(':from', intval($_GET['from']), SQLITE3_INTEGER);
|
||||
$stmt->bindValue(':until', intval($_GET['until']), SQLITE3_INTEGER);
|
||||
$results = $stmt->execute();
|
||||
|
||||
$domains = array();
|
||||
|
||||
if (!is_bool($results)) {
|
||||
while ($row = $results->fetchArray()) {
|
||||
// Convert domain to lower case UTF-8
|
||||
$c = utf8_encode(strtolower($row[0]));
|
||||
if (array_key_exists($c, $domains)) {
|
||||
// Entry already exists, add to it (might appear multiple times due to mixed capitalization in the database)
|
||||
$domains[$c] += intval($row[1]);
|
||||
} else {
|
||||
// Entry does not yet exist
|
||||
$domains[$c] = intval($row[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by number of hits
|
||||
arsort($domains);
|
||||
|
||||
// Extract only the first ten entries
|
||||
$domains = array_slice($domains, 0, 10);
|
||||
|
||||
$result = array('top_domains' => $domains);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['topAds']) && $auth) {
|
||||
$limit = '';
|
||||
|
||||
if (isset($_GET['from'], $_GET['until'])) {
|
||||
$limit = ' AND timestamp >= :from AND timestamp <= :until';
|
||||
} elseif (isset($_GET['from']) && !isset($_GET['until'])) {
|
||||
$limit = ' AND timestamp >= :from';
|
||||
} elseif (!isset($_GET['from']) && isset($_GET['until'])) {
|
||||
$limit = ' AND timestamp <= :until';
|
||||
}
|
||||
$stmt = $db->prepare('SELECT domain,count(domain) FROM queries WHERE status IN (1,4,5,6,7,8,9,10,11)'.$limit.' GROUP by domain order by count(domain) desc limit 10');
|
||||
$stmt->bindValue(':from', intval($_GET['from']), SQLITE3_INTEGER);
|
||||
$stmt->bindValue(':until', intval($_GET['until']), SQLITE3_INTEGER);
|
||||
$results = $stmt->execute();
|
||||
|
||||
$addomains = array();
|
||||
|
||||
if (!is_bool($results)) {
|
||||
while ($row = $results->fetchArray()) {
|
||||
$addomains[utf8_encode($row[0])] = intval($row[1]);
|
||||
}
|
||||
}
|
||||
$result = array('top_ads' => $addomains);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getMinTimestamp']) && $auth) {
|
||||
$results = $db->query('SELECT MIN(timestamp) FROM queries');
|
||||
|
||||
if (!is_bool($results)) {
|
||||
$result = array('mintimestamp' => $results->fetchArray()[0]);
|
||||
} else {
|
||||
$result = array();
|
||||
}
|
||||
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getMaxTimestamp']) && $auth) {
|
||||
$results = $db->query('SELECT MAX(timestamp) FROM queries');
|
||||
|
||||
if (!is_bool($results)) {
|
||||
$result = array('maxtimestamp' => $results->fetchArray()[0]);
|
||||
} else {
|
||||
$result = array();
|
||||
}
|
||||
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getQueriesCount']) && $auth) {
|
||||
$results = $db->query('SELECT COUNT(timestamp) FROM queries');
|
||||
|
||||
if (!is_bool($results)) {
|
||||
$result = array('count' => $results->fetchArray()[0]);
|
||||
} else {
|
||||
$result = array();
|
||||
}
|
||||
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getDBfilesize']) && $auth) {
|
||||
$filesize = filesize('/etc/pihole/pihole-FTL.db');
|
||||
$result = array('filesize' => $filesize);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getGraphData']) && $auth) {
|
||||
$limit = '';
|
||||
|
||||
if (isset($_GET['from'], $_GET['until'])) {
|
||||
$limit = 'timestamp >= :from AND timestamp <= :until';
|
||||
} elseif (isset($_GET['from']) && !isset($_GET['until'])) {
|
||||
$limit = 'timestamp >= :from';
|
||||
} elseif (!isset($_GET['from']) && isset($_GET['until'])) {
|
||||
$limit = 'timestamp <= :until';
|
||||
}
|
||||
|
||||
$interval = 600;
|
||||
|
||||
if (isset($_GET['interval'])) {
|
||||
$q = intval($_GET['interval']);
|
||||
if ($q >= 10) {
|
||||
$interval = $q;
|
||||
}
|
||||
}
|
||||
|
||||
// Round $from and $until to match the requested $interval
|
||||
$from = intval((intval($_GET['from']) / $interval) * $interval);
|
||||
$until = intval((intval($_GET['until']) / $interval) * $interval);
|
||||
|
||||
// Count domains and blocked queries using the same intervals
|
||||
$sqlcommand = "
|
||||
SELECT
|
||||
(timestamp / :interval) * :interval AS interval,
|
||||
SUM(CASE
|
||||
WHEN status !=0 THEN 1
|
||||
ELSE 0
|
||||
END) AS domains,
|
||||
SUM(CASE
|
||||
WHEN status IN (1,4,5,6,7,8,9,10,11,15,16) THEN 1
|
||||
ELSE 0
|
||||
END) AS blocked
|
||||
FROM queries
|
||||
WHERE $limit
|
||||
GROUP BY interval
|
||||
ORDER BY interval";
|
||||
|
||||
$stmt = $db->prepare($sqlcommand);
|
||||
$stmt->bindValue(':from', $from, SQLITE3_INTEGER);
|
||||
$stmt->bindValue(':until', $until, SQLITE3_INTEGER);
|
||||
$stmt->bindValue(':interval', $interval, SQLITE3_INTEGER);
|
||||
$results = $stmt->execute();
|
||||
|
||||
// Parse the DB result into graph data, filling in missing interval sections with zero
|
||||
function parseDBData($results, $interval, $from, $until)
|
||||
{
|
||||
$domains = array();
|
||||
$blocked = array();
|
||||
$first_db_timestamp = -1;
|
||||
|
||||
if (!is_bool($results)) {
|
||||
// Read in the data
|
||||
while ($row = $results->fetchArray()) {
|
||||
$domains[$row['interval']] = intval($row['domains']);
|
||||
$blocked[$row['interval']] = intval($row['blocked']);
|
||||
if ($first_db_timestamp === -1) {
|
||||
$first_db_timestamp = intval($row[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It is unpredictable what the first timestamp returned by the database will be.
|
||||
// This depends on live data. The bar graph can handle "gaps", but the Area graph can't.
|
||||
// Hence, we filling the "missing" timeslots with 0 to avoid wrong graphic render.
|
||||
// (https://github.com/pi-hole/AdminLTE/pull/2374#issuecomment-1261865428)
|
||||
$aligned_from = $from + (($first_db_timestamp - $from) % $interval);
|
||||
|
||||
// Fill gaps in returned data
|
||||
for ($i = $aligned_from; $i < $until; $i += $interval) {
|
||||
if (!array_key_exists($i, $domains)) {
|
||||
$domains[$i] = 0;
|
||||
$blocked[$i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return array('domains_over_time' => $domains, 'ads_over_time' => $blocked);
|
||||
}
|
||||
|
||||
$over_time = parseDBData($results, $interval, $from, $until);
|
||||
$data = array_merge($data, $over_time);
|
||||
}
|
||||
|
||||
if (isset($_GET['status']) && $auth) {
|
||||
$extra = ';';
|
||||
if (isset($_GET['ignore']) && $_GET['ignore'] === 'DNSMASQ_WARN') {
|
||||
$extra = "WHERE type != 'DNSMASQ_WARN';";
|
||||
}
|
||||
$results = $db->query('SELECT COUNT(*) FROM message '.$extra);
|
||||
|
||||
if (!is_bool($results)) {
|
||||
$result = array('message_count' => $results->fetchArray()[0]);
|
||||
} else {
|
||||
$result = array();
|
||||
}
|
||||
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['messages']) && $auth) {
|
||||
$extra = ';';
|
||||
if (isset($_GET['ignore']) && $_GET['ignore'] === 'DNSMASQ_WARN') {
|
||||
$extra = "WHERE type != 'DNSMASQ_WARN';";
|
||||
}
|
||||
|
||||
$messages = array();
|
||||
$results = $db->query('SELECT * FROM message '.$extra);
|
||||
|
||||
while ($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) {
|
||||
// Convert string to to UTF-8 encoding to ensure php-json can handle it.
|
||||
// Furthermore, convert special characters to HTML entities to prevent XSS attacks.
|
||||
foreach ($res as $key => $value) {
|
||||
if (is_string($value)) {
|
||||
$res[$key] = htmlspecialchars(utf8_encode($value));
|
||||
}
|
||||
}
|
||||
array_push($messages, $res);
|
||||
}
|
||||
|
||||
$data = array_merge($data, array('messages' => $messages));
|
||||
}
|
||||
|
||||
if (isset($_GET['jsonForceObject'])) {
|
||||
echo json_encode($data, JSON_FORCE_OBJECT);
|
||||
} else {
|
||||
echo json_encode($data);
|
||||
}
|
||||
86
auditlog.php
86
auditlog.php
@@ -1,86 +0,0 @@
|
||||
<?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_authenticated.php';
|
||||
?>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="page-header">
|
||||
<h1>Audit log (showing live data)</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-lg-6">
|
||||
<div class="box" id="domain-frequency">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Allowed queries</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Hits</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-sync fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
|
||||
<div class="col-xs-12 col-lg-6">
|
||||
<div class="box" id="ad-frequency">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Blocked queries</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Hits</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-sync fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<script src="<?php echo fileversion('scripts/pi-hole/js/auditlog.js'); ?>"></script>
|
||||
|
||||
<?php
|
||||
require 'scripts/pi-hole/php/footer.php';
|
||||
?>
|
||||
17
login.php
17
login.php
@@ -8,16 +8,11 @@
|
||||
* Please see LICENSE file for your rights under this license.
|
||||
*/
|
||||
|
||||
require 'scripts/pi-hole/php/password.php';
|
||||
|
||||
// Go directly to index, if authenticated.
|
||||
if ($_SESSION['auth']) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$wrongpassword = false;
|
||||
require_once 'func.php';
|
||||
require 'scripts/pi-hole/php/theme.php';
|
||||
require 'scripts/pi-hole/php/header.php';
|
||||
|
||||
?>
|
||||
<body class="hold-transition layout-boxed login-page">
|
||||
<div class="box login-box">
|
||||
@@ -39,7 +34,7 @@ require 'scripts/pi-hole/php/header.php';
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<form action="" id="loginform" method="post">
|
||||
<form id="loginform">
|
||||
<div class="form-group login-options has-feedback<?php if ($wrongpassword) { ?> has-error<?php } ?>">
|
||||
<div class="pwd-field">
|
||||
<!-- hidden username input field to help password managers to autfill the password -->
|
||||
@@ -48,7 +43,7 @@ require 'scripts/pi-hole/php/header.php';
|
||||
<span class="fa fa-key form-control-feedback"></span>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="logincookie" name="persistentlogin">
|
||||
<input type="checkbox" id="logincookie" name="persistentlogin" disabled="true">
|
||||
<label for="logincookie">Remember me for 7 days</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,6 +90,8 @@ require 'scripts/pi-hole/php/header.php';
|
||||
<strong><a href="https://pi-hole.net/donate/" rel="noopener" target="_blank"><i class="fa fa-heart text-red"></i> Donate</a></strong> if you found this useful.
|
||||
</div>
|
||||
</div>
|
||||
<script src="<?php echo fileversion('scripts/pi-hole/js/login.js'); ?>"></script>
|
||||
<script src="<?php echo fileversion('scripts/vendor/geraintluff-sha256.min.js'); ?>"></script>
|
||||
<script src="<?php echo fileversion('scripts/pi-hole/js/footer.js'); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
15
logout.php
15
logout.php
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once 'scripts/pi-hole/php/persistentlogin_token.php';
|
||||
|
||||
// If the user wants to log out, we free all session variables currently registered
|
||||
// and delete any persistent cookie.
|
||||
session_start();
|
||||
session_unset();
|
||||
|
||||
if (isset($_COOKIE['persistentlogin'])) {
|
||||
logoutPersistentLoginToken($_COOKIE['persistentlogin']);
|
||||
}
|
||||
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
@@ -1,153 +0,0 @@
|
||||
/* 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 */
|
||||
|
||||
// Define global variables
|
||||
var auditTimeout = null;
|
||||
|
||||
function updateTopLists() {
|
||||
$.getJSON("api.php?topItems=audit", function (data) {
|
||||
if ("FTLnotrunning" in data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear tables before filling them with data
|
||||
$("#domain-frequency td").parent().remove();
|
||||
$("#ad-frequency td").parent().remove();
|
||||
var domaintable = $("#domain-frequency").find("tbody:last");
|
||||
var adtable = $("#ad-frequency").find("tbody:last");
|
||||
var url, domain;
|
||||
for (domain in data.top_queries) {
|
||||
if (Object.prototype.hasOwnProperty.call(data.top_queries, domain)) {
|
||||
// Sanitize domain
|
||||
domain = utils.escapeHtml(domain);
|
||||
url = '<a href="queries.php?domain=' + domain + '">' + domain + "</a>";
|
||||
domaintable.append(
|
||||
"<tr><td>" +
|
||||
url +
|
||||
"</td> <td>" +
|
||||
data.top_queries[domain] +
|
||||
"</td> <td>" +
|
||||
'<button type="button" class="btn btn-default btn-xs text-red"><i class="fa fa-ban"></i> Blacklist</button>' +
|
||||
'<button type="button" class="btn btn-default btn-xs text-orange"><i class="fa fa-balance-scale"></i> Audit</button>' +
|
||||
"</td> </tr> "
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (domain in data.top_ads) {
|
||||
if (Object.prototype.hasOwnProperty.call(data.top_ads, domain)) {
|
||||
var input = domain.split(" ");
|
||||
// Sanitize domain
|
||||
var printdomain = utils.escapeHtml(input[0]);
|
||||
if (input.length > 1) {
|
||||
url =
|
||||
'<a href="queries.php?domain=' +
|
||||
printdomain +
|
||||
'">' +
|
||||
printdomain +
|
||||
"</a> (wildcard blocked)";
|
||||
adtable.append(
|
||||
"<tr><td>" +
|
||||
url +
|
||||
"</td> <td>" +
|
||||
data.top_ads[domain] +
|
||||
"</td> <td>" +
|
||||
'<button type="button" class="btn btn-default btn-sm text-orange"><i class="fa fa-balance-scale"></i> Audit</button>' +
|
||||
"</td> </tr> "
|
||||
);
|
||||
} else {
|
||||
url = '<a href="queries.php?domain=' + printdomain + '">' + printdomain + "</a>";
|
||||
adtable.append(
|
||||
"<tr><td>" +
|
||||
url +
|
||||
"</td> <td>" +
|
||||
data.top_ads[domain] +
|
||||
"</td> <td>" +
|
||||
'<button type="button" class="btn btn-default btn-xs text-green"><i class="fas fa-check"></i> Whitelist</button>' +
|
||||
'<button type="button" class="btn btn-default btn-xs text-orange"><i class="fa fa-balance-scale"></i> Audit</button>' +
|
||||
"</td> </tr> "
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$("#domain-frequency .overlay").hide();
|
||||
$("#ad-frequency .overlay").hide();
|
||||
// Update top lists data every ten seconds
|
||||
// Updates are also triggered by button actions
|
||||
// and reset the running timer
|
||||
if (auditTimeout !== null) {
|
||||
window.clearTimeout(auditTimeout);
|
||||
}
|
||||
|
||||
auditTimeout = setTimeout(updateTopLists, 10000);
|
||||
});
|
||||
}
|
||||
|
||||
function add(domain, list) {
|
||||
var token = $("#token").text();
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
data: {
|
||||
domain: domain,
|
||||
list: list,
|
||||
token: token,
|
||||
action: list === "audit" ? "add_audit" : "add_domain",
|
||||
comment: "Added from Audit Log",
|
||||
},
|
||||
success: function () {
|
||||
updateTopLists();
|
||||
},
|
||||
error: function (jqXHR, exception) {
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function blacklistUrl(url) {
|
||||
// We add to audit last as it will reload the table on success
|
||||
add(url, "black");
|
||||
add(url, "audit");
|
||||
}
|
||||
|
||||
function whitelistUrl(url) {
|
||||
// We add to audit last as it will reload the table on success
|
||||
add(url, "white");
|
||||
add(url, "audit");
|
||||
}
|
||||
|
||||
function auditUrl(url) {
|
||||
add(url, "audit");
|
||||
}
|
||||
|
||||
$(function () {
|
||||
// Pull in data via AJAX
|
||||
updateTopLists();
|
||||
|
||||
$("#domain-frequency tbody").on("click", "button", function (event) {
|
||||
var url = $(this).parents("tr")[0].textContent.split(" ")[0];
|
||||
|
||||
if (event.target.textContent.trim() === "Blacklist") {
|
||||
blacklistUrl(url);
|
||||
} else {
|
||||
auditUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
$("#ad-frequency tbody").on("click", "button", function (event) {
|
||||
var url = $(this).parents("tr")[0].textContent.split(" ")[0];
|
||||
|
||||
if (event.target.textContent.trim() === "Whitelist") {
|
||||
whitelistUrl(url);
|
||||
} else {
|
||||
auditUrl(url);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -408,6 +408,15 @@ function changeBulkDeleteStates(table) {
|
||||
}
|
||||
}
|
||||
|
||||
function doLogout() {
|
||||
$.ajax({
|
||||
url: "/api/auth",
|
||||
method: "DELETE"
|
||||
}).always(function (data) {
|
||||
if (data.status === 410) location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
window.utils = (function () {
|
||||
return {
|
||||
escapeHtml: escapeHtml,
|
||||
@@ -432,5 +441,6 @@ window.utils = (function () {
|
||||
colorBar: colorBar,
|
||||
checkMessages: checkMessages,
|
||||
changeBulkDeleteStates: changeBulkDeleteStates,
|
||||
doLogout: doLogout,
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
const DEFAULT_FTLCONFFILE = '/etc/pihole/pihole-FTL.conf';
|
||||
const DEFAULT_FTL_IP = '127.0.0.1';
|
||||
const DEFAULT_FTL_PORT = 4711;
|
||||
|
||||
function piholeFTLConfig($piholeFTLConfFile = DEFAULT_FTLCONFFILE, $force = false)
|
||||
{
|
||||
static $piholeFTLConfig;
|
||||
|
||||
if (isset($piholeFTLConfig) && !$force) {
|
||||
return $piholeFTLConfig;
|
||||
}
|
||||
|
||||
if (is_readable($piholeFTLConfFile)) {
|
||||
$piholeFTLConfig = parse_ini_file($piholeFTLConfFile);
|
||||
} else {
|
||||
$piholeFTLConfig = array();
|
||||
}
|
||||
|
||||
return $piholeFTLConfig;
|
||||
}
|
||||
|
||||
function connectFTL()
|
||||
{
|
||||
// We only use the default IP
|
||||
$address = DEFAULT_FTL_IP;
|
||||
|
||||
// Try to read port from FTL config. Use default if not found.
|
||||
$config = piholeFTLConfig();
|
||||
|
||||
// Use the port only if the value is numeric
|
||||
if (isset($config['FTLPORT']) && is_numeric($config['FTLPORT'])) {
|
||||
$port = intval($config['FTLPORT']);
|
||||
} else {
|
||||
$port = DEFAULT_FTL_PORT;
|
||||
}
|
||||
|
||||
// Open Internet socket connection
|
||||
return @fsockopen($address, $port, $errno, $errstr, 1.0);
|
||||
}
|
||||
|
||||
function sendRequestFTL($requestin, $socket)
|
||||
{
|
||||
$request = '>'.$requestin;
|
||||
fwrite($socket, $request) or exit('{"error":"Could not send data to server"}');
|
||||
}
|
||||
|
||||
function getResponseFTL($socket)
|
||||
{
|
||||
$response = array();
|
||||
|
||||
$errCount = 0;
|
||||
while (true) {
|
||||
$out = fgets($socket);
|
||||
if ($out == '') {
|
||||
++$errCount;
|
||||
}
|
||||
|
||||
if ($errCount > 100) {
|
||||
// Tried 100 times, but never got proper reply, fail to prevent busy loop
|
||||
exit('{"error":"Tried 100 times to connect to FTL server, but never got proper reply. Please check Port and logs!"}');
|
||||
}
|
||||
|
||||
if (strrpos($out, '---EOM---') !== false) {
|
||||
break;
|
||||
}
|
||||
|
||||
$out = rtrim($out);
|
||||
if (strlen($out) > 0) {
|
||||
$response[] = $out;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
function disconnectFTL($socket)
|
||||
{
|
||||
if (is_resource($socket)) {
|
||||
fclose($socket);
|
||||
}
|
||||
}
|
||||
|
||||
function callFTLAPI($request)
|
||||
{
|
||||
$socket = connectFTL();
|
||||
|
||||
if (!is_resource($socket)) {
|
||||
$data = array('FTLnotrunning' => true);
|
||||
} else {
|
||||
sendRequestFTL($request, $socket);
|
||||
$data = getResponseFTL($socket);
|
||||
}
|
||||
disconnectFTL($socket);
|
||||
|
||||
return $data;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
require 'auth.php';
|
||||
require 'password.php';
|
||||
check_cors();
|
||||
?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
background: #fff;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.qrcode {
|
||||
text-align: center;
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
.qrcode svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.token {
|
||||
font-size: 14px;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
if ($auth) {
|
||||
if (strlen($pwhash) > 0) {
|
||||
echo '<div class="qrcode">';
|
||||
require_once '../../vendor/qrcode.php';
|
||||
$qr = QRCode::getMinimumQRCode($pwhash, QR_ERROR_CORRECT_LEVEL_Q);
|
||||
// The size of each block (in pixels) should be an integer
|
||||
$qr->printSVG(10);
|
||||
echo '</div>';
|
||||
echo 'Raw API Token: <code class="token">'.$pwhash.'</code></div>';
|
||||
} else {
|
||||
echo '<p>No password set</p>';
|
||||
}
|
||||
} else {
|
||||
echo '<p>Not authorized!</p>';
|
||||
}
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -55,7 +55,12 @@ function check_cors()
|
||||
// Allow user set CORS
|
||||
$cors_hosts = getenv('CORS_HOSTS');
|
||||
if (!empty($cors_hosts)) {
|
||||
array_push($AUTHORIZED_HOSTNAMES, ...explode(',', $cors_hosts));
|
||||
// Push all hosts from comma separated list
|
||||
$cors_hosts_array = explode(',', $cors_hosts);
|
||||
// Add all hosts to the list of authorized hosts
|
||||
foreach ($cors_hosts_array as $host) {
|
||||
array_push($AUTHORIZED_HOSTNAMES, $host);
|
||||
}
|
||||
}
|
||||
|
||||
// Since the Host header is easily manipulated, we can only check if it's wrong and can't use it
|
||||
@@ -93,7 +98,7 @@ function check_cors()
|
||||
if (!in_array($server_origin, $AUTHORIZED_HOSTNAMES)) {
|
||||
log_and_die('Failed CORS: '.htmlspecialchars($server_origin).' vs '.htmlspecialchars(join(', ', $AUTHORIZED_HOSTNAMES)));
|
||||
}
|
||||
header("Access-Control-Allow-Origin: ${_SERVER['HTTP_ORIGIN']}");
|
||||
/* header("Access-Control-Allow-Origin: ${_SERVER['HTTP_ORIGIN']}"); */
|
||||
}
|
||||
// If there's no HTTP_ORIGIN, CORS should not be used
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once 'func.php';
|
||||
require_once 'auth.php';
|
||||
|
||||
// Authentication checks
|
||||
if (!isset($api)) {
|
||||
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:
|
||||
exit('Wrong action');
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once 'func.php';
|
||||
require_once 'auth.php';
|
||||
|
||||
// Authentication checks
|
||||
if (!isset($api)) {
|
||||
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(echoCustomDNSEntries());
|
||||
break;
|
||||
|
||||
case 'add': echo json_encode(addCustomDNSEntry());
|
||||
break;
|
||||
|
||||
case 'delete': echo json_encode(deleteCustomDNSEntry());
|
||||
break;
|
||||
|
||||
default:
|
||||
exit('Wrong action');
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// List Type Constants
|
||||
const LISTTYPE_WHITELIST = 0;
|
||||
const LISTTYPE_BLACKLIST = 1;
|
||||
const LISTTYPE_REGEX_WHITELIST = 2;
|
||||
const LISTTYPE_REGEX_BLACKLIST = 3;
|
||||
|
||||
function getGravityDBFilename()
|
||||
{
|
||||
// Get possible non-standard location of FTL's database
|
||||
$FTLsettings = parse_ini_file('/etc/pihole/pihole-FTL.conf');
|
||||
if (isset($FTLsettings['GRAVITYDB'])) {
|
||||
return $FTLsettings['GRAVITYDB'];
|
||||
}
|
||||
|
||||
return '/etc/pihole/gravity.db';
|
||||
}
|
||||
|
||||
function getQueriesDBFilename()
|
||||
{
|
||||
// Get possible non-standard location of FTL's database
|
||||
$FTLsettings = parse_ini_file('/etc/pihole/pihole-FTL.conf');
|
||||
if (isset($FTLsettings['DBFILE'])) {
|
||||
return $FTLsettings['DBFILE'];
|
||||
}
|
||||
|
||||
return '/etc/pihole/pihole-FTL.db';
|
||||
}
|
||||
|
||||
function SQLite3_connect_try($filename, $mode, $trytoreconnect)
|
||||
{
|
||||
try {
|
||||
// connect to database
|
||||
return new SQLite3($filename, $mode);
|
||||
} catch (Exception $exception) {
|
||||
// sqlite3 throws an exception when it is unable to connect, try to reconnect after 3 seconds
|
||||
if ($trytoreconnect) {
|
||||
sleep(3);
|
||||
|
||||
return SQLite3_connect_try($filename, $mode, false);
|
||||
}
|
||||
// If we should not try again (or are already trying again!), we return the exception string
|
||||
// so the user gets it on the dashboard
|
||||
return $filename.': '.$exception->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
function SQLite3_connect($filename, $mode = SQLITE3_OPEN_READONLY)
|
||||
{
|
||||
if (strlen($filename) > 0) {
|
||||
$db = SQLite3_connect_try($filename, $mode, true);
|
||||
} else {
|
||||
exit('No database available');
|
||||
}
|
||||
if (is_string($db)) {
|
||||
exit("Error connecting to database\n".$db);
|
||||
}
|
||||
|
||||
// Add busy timeout so methods don't fail immediately when, e.g., FTL is currently reading from the DB
|
||||
$db->busyTimeout(5000);
|
||||
|
||||
return $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add domains to a given table.
|
||||
*
|
||||
* @param $db object The SQLite3 database connection object
|
||||
* @param $table string The target table
|
||||
* @param $domains array Array of domains (strings) to be added to the table
|
||||
* @param $wildcardstyle boolean Whether to format the input domains in legacy wildcard notation
|
||||
* @param $returnnum boolean Whether to return an integer or a string
|
||||
* @param $type integer The target type (0 = exact whitelist, 1 = exact blacklist, 2 = regex whitelist, 3 = regex blacklist)
|
||||
* @param mixed|null $comment
|
||||
*
|
||||
* @return string Success/error and number of processed domains
|
||||
*/
|
||||
function add_to_table($db, $table, $domains, $comment = null, $wildcardstyle = false, $returnnum = false, $type = -1)
|
||||
{
|
||||
if (!is_int($type)) {
|
||||
return 'Error: Argument type has to be of type integer (is '.gettype($type).')';
|
||||
}
|
||||
|
||||
// Begin transaction
|
||||
if (!$db->exec('BEGIN TRANSACTION;')) {
|
||||
if ($returnnum) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return "Error: Unable to begin transaction for {$table} table.";
|
||||
}
|
||||
|
||||
// To which column should the record be added to?
|
||||
if ($table === 'adlist') {
|
||||
$field = 'address';
|
||||
} else {
|
||||
$field = 'domain';
|
||||
}
|
||||
|
||||
// Get initial count of domains in this table
|
||||
if ($type === -1) {
|
||||
$countquery = "SELECT COUNT(*) FROM {$table};";
|
||||
} else {
|
||||
$countquery = "SELECT COUNT(*) FROM {$table} WHERE type = {$type};";
|
||||
}
|
||||
$initialcount = intval($db->querySingle($countquery));
|
||||
|
||||
// Prepare INSERT SQLite statement
|
||||
$bindcomment = false;
|
||||
if ($table === 'domain_audit') {
|
||||
$querystr = "INSERT OR IGNORE INTO {$table} ({$field}) VALUES (:{$field});";
|
||||
} elseif ($type === -1) {
|
||||
$querystr = "INSERT OR IGNORE INTO {$table} ({$field},comment) VALUES (:{$field}, :comment);";
|
||||
$bindcomment = true;
|
||||
} else {
|
||||
$querystr = "REPLACE INTO {$table} ({$field},comment,type) VALUES (:{$field}, :comment, {$type});";
|
||||
$bindcomment = true;
|
||||
}
|
||||
$stmt = $db->prepare($querystr);
|
||||
|
||||
// Return early if we failed to prepare the SQLite statement
|
||||
if (!$stmt) {
|
||||
if ($returnnum) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return "Error: Failed to prepare statement for {$table} table (type = {$type}, field = {$field}).";
|
||||
}
|
||||
|
||||
// Loop over domains and inject the lines into the database
|
||||
$num = 0;
|
||||
foreach ($domains as $domain) {
|
||||
// Limit max length for a domain entry to 253 chars
|
||||
if (strlen($domain) > 253) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($wildcardstyle) {
|
||||
$domain = '(\\.|^)'.str_replace('.', '\\.', $domain).'$';
|
||||
}
|
||||
|
||||
$stmt->bindValue(":{$field}", htmlentities($domain), SQLITE3_TEXT);
|
||||
if ($bindcomment) {
|
||||
$stmt->bindValue(':comment', htmlentities($comment), SQLITE3_TEXT);
|
||||
}
|
||||
|
||||
if ($stmt->execute() && $stmt->reset()) {
|
||||
++$num;
|
||||
} else {
|
||||
$stmt->close();
|
||||
if ($returnnum) {
|
||||
return $num;
|
||||
}
|
||||
if ($num === 1) {
|
||||
$plural = '';
|
||||
} else {
|
||||
$plural = 's';
|
||||
}
|
||||
|
||||
return 'Error: '.$db->lastErrorMsg().', added '.$num.' domain'.$plural;
|
||||
}
|
||||
}
|
||||
|
||||
// Close prepared statement and return number of processed rows
|
||||
$stmt->close();
|
||||
$db->exec('COMMIT;');
|
||||
|
||||
if ($returnnum) {
|
||||
return $num;
|
||||
}
|
||||
$finalcount = intval($db->querySingle($countquery));
|
||||
$modified = $finalcount - $initialcount;
|
||||
|
||||
// If we add less domains than the user specified, then they wanted to add duplicates
|
||||
if ($modified !== $num) {
|
||||
$delta = $num - $modified;
|
||||
$extra = ' (skipped '.$delta.' duplicates)';
|
||||
} else {
|
||||
$extra = '';
|
||||
}
|
||||
|
||||
if ($num === 1) {
|
||||
$plural = '';
|
||||
} else {
|
||||
$plural = 's';
|
||||
}
|
||||
|
||||
return 'Success, added '.$modified.' of '.$num.' domain'.$plural.$extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove domains from a given table.
|
||||
*
|
||||
* @param $db object The SQLite3 database connection object
|
||||
* @param $table string The target table
|
||||
* @param $domains array Array of domains (strings) to be removed from the table
|
||||
* @param $returnnum boolean Whether to return an integer or a string
|
||||
* @param $type integer The target type (0 = exact whitelist, 1 = exact blacklist, 2 = regex whitelist, 3 = regex blacklist)
|
||||
*
|
||||
* @return string Success/error and number of processed domains
|
||||
*/
|
||||
function remove_from_table($db, $table, $domains, $returnnum = false, $type = -1)
|
||||
{
|
||||
if (!is_int($type)) {
|
||||
return 'Error: Argument type has to be of type integer (is '.gettype($type).')';
|
||||
}
|
||||
|
||||
// Begin transaction
|
||||
if (!$db->exec('BEGIN TRANSACTION;')) {
|
||||
if ($returnnum) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 'Error: Unable to begin transaction for domainlist table.';
|
||||
}
|
||||
|
||||
// Get initial count of domains in this table
|
||||
if ($type === -1) {
|
||||
$countquery = "SELECT COUNT(*) FROM {$table};";
|
||||
} else {
|
||||
$countquery = "SELECT COUNT(*) FROM {$table} WHERE type = {$type};";
|
||||
}
|
||||
$initialcount = intval($db->querySingle($countquery));
|
||||
|
||||
// Prepare SQLite statement
|
||||
if ($type === -1) {
|
||||
$querystr = "DELETE FROM {$table} WHERE domain = :domain AND type = {$type};";
|
||||
} else {
|
||||
$querystr = "DELETE FROM {$table} WHERE domain = :domain;";
|
||||
}
|
||||
$stmt = $db->prepare($querystr);
|
||||
|
||||
// Return early if we failed to prepare the SQLite statement
|
||||
if (!$stmt) {
|
||||
if ($returnnum) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 'Error: Failed to prepare statement for '.$table.' table (type = '.$type.').';
|
||||
}
|
||||
|
||||
// Loop over domains and remove the lines from the database
|
||||
$num = 0;
|
||||
foreach ($domains as $domain) {
|
||||
$stmt->bindValue(':domain', $domain, SQLITE3_TEXT);
|
||||
|
||||
if ($stmt->execute() && $stmt->reset()) {
|
||||
++$num;
|
||||
} else {
|
||||
$stmt->close();
|
||||
if ($returnnum) {
|
||||
return $num;
|
||||
}
|
||||
if ($num === 1) {
|
||||
$plural = '';
|
||||
} else {
|
||||
$plural = 's';
|
||||
}
|
||||
|
||||
return 'Error: '.$db->lastErrorMsg().', removed '.$num.' domain'.$plural;
|
||||
}
|
||||
}
|
||||
|
||||
// Close prepared statement and return number or processed rows
|
||||
$stmt->close();
|
||||
$db->exec('COMMIT;');
|
||||
|
||||
if ($returnnum) {
|
||||
return $num;
|
||||
}
|
||||
if ($num === 1) {
|
||||
$plural = '';
|
||||
} else {
|
||||
$plural = 's';
|
||||
}
|
||||
|
||||
return 'Success, removed '.$num.' domain'.$plural;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
|
||||
ob_end_flush();
|
||||
ini_set('output_buffering', '0');
|
||||
ob_implicit_flush(true);
|
||||
header('Content-Type: text/event-stream');
|
||||
header('Cache-Control: no-cache');
|
||||
|
||||
require 'password.php';
|
||||
require 'auth.php';
|
||||
|
||||
if (!$auth) {
|
||||
exit('Unauthorized');
|
||||
}
|
||||
|
||||
check_cors();
|
||||
|
||||
$token = isset($_GET['token']) ? $_GET['token'] : '';
|
||||
check_csrf($token);
|
||||
|
||||
function echoEvent($datatext)
|
||||
{
|
||||
$ANSIcolors = array(
|
||||
chr(27).'[1;91m' => '<span class="log-red">',
|
||||
chr(27).'[1;32m' => '<span class="log-green">',
|
||||
chr(27).'[1;33m' => '<span class="log-yellow">',
|
||||
chr(27).'[1;34m' => '<span class="log-blue">',
|
||||
chr(27).'[1;35m' => '<span class="log-purple">',
|
||||
chr(27).'[1;36m' => '<span class="log-cyan">',
|
||||
|
||||
chr(27).'[90m' => '<span class="log-gray">',
|
||||
chr(27).'[91m' => '<span class="log-red">',
|
||||
chr(27).'[32m' => '<span class="log-green">',
|
||||
chr(27).'[33m' => '<span class="log-yellow">',
|
||||
chr(27).'[94m' => '<span class="log-blue">',
|
||||
chr(27).'[95m' => '<span class="log-purple">',
|
||||
chr(27).'[96m' => '<span class="log-cyan">',
|
||||
|
||||
chr(27).'[1m' => '<span class="text-bold">',
|
||||
chr(27).'[4m' => '<span class="text-underline">',
|
||||
|
||||
chr(27).'[0m' => '</span>',
|
||||
);
|
||||
|
||||
$data = str_replace(array_keys($ANSIcolors), $ANSIcolors, htmlspecialchars($datatext));
|
||||
|
||||
if (!isset($_GET['IE'])) {
|
||||
echo 'data: '.implode("\ndata: ", explode("\n", $data))."\n\n";
|
||||
} else {
|
||||
echo $data;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute "pihole" using Web option
|
||||
$command = 'export TERM=dumb && sudo pihole -d -w';
|
||||
|
||||
// Add auto-upload option
|
||||
if (isset($_GET['upload'])) {
|
||||
$command .= ' -a';
|
||||
}
|
||||
|
||||
// Execute database integrity_check
|
||||
if (isset($_GET['dbcheck'])) {
|
||||
$command .= ' -c';
|
||||
}
|
||||
|
||||
$proc = popen($command, 'r');
|
||||
|
||||
while (!feof($proc)) {
|
||||
echoEvent(fread($proc, 4096));
|
||||
}
|
||||
@@ -44,11 +44,6 @@
|
||||
// Flushes the system write buffers of PHP. This attempts to push everything we have so far all the way to the client's browser.
|
||||
flush();
|
||||
|
||||
// Run update checker
|
||||
// - determines local branch each time,
|
||||
// - determines local and remote version every 30 minutes
|
||||
require 'scripts/pi-hole/php/update_checker.php';
|
||||
|
||||
if (isset($core_commit) || isset($web_commit) || isset($FTL_commit)) {
|
||||
$list_class = 'list-unstyled';
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
// Credit: http://stackoverflow.com/a/4694816/2087442
|
||||
// Modified because of https://github.com/pi-hole/AdminLTE/pull/533
|
||||
ini_set('pcre.recursion_limit', 1500);
|
||||
/* ini_set('pcre.recursion_limit', 1500); */
|
||||
/*
|
||||
function validDomain($domain_name, &$message = null)
|
||||
{
|
||||
// special handling of the root zone `.`
|
||||
@@ -101,7 +102,7 @@ function get_ip_type($ip)
|
||||
(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? 6 :
|
||||
0);
|
||||
}
|
||||
|
||||
*/
|
||||
function checkfile($filename)
|
||||
{
|
||||
if (is_readable($filename)) {
|
||||
@@ -207,10 +208,10 @@ function getCustomDNSEntries()
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = new \stdClass();
|
||||
$data->ip = $explodedLine[0];
|
||||
$data->domain = $explodedLine[1];
|
||||
$data->domains = array_slice($explodedLine, 0, -1);
|
||||
$data = array();
|
||||
$data["ip"] = $explodedLine[0];
|
||||
$data["domain"] = $explodedLine[1];
|
||||
$data["domains"] = array_slice($explodedLine, 0, -1);
|
||||
$entries[] = $data;
|
||||
}
|
||||
|
||||
@@ -284,7 +285,7 @@ function addCustomDNSEntry($ip = '', $domain = '', $reload = '', $json = true)
|
||||
}
|
||||
|
||||
return returnSuccess('', $json);
|
||||
} catch (\Exception $ex) {
|
||||
} catch (Exception $ex) {
|
||||
return returnError($ex->getMessage(), $json);
|
||||
}
|
||||
}
|
||||
@@ -323,7 +324,7 @@ function deleteCustomDNSEntry()
|
||||
pihole_execute('-a removecustomdns '.$ip.' '.$domain);
|
||||
|
||||
return returnSuccess();
|
||||
} catch (\Exception $ex) {
|
||||
} catch (Exception $ex) {
|
||||
return returnError($ex->getMessage());
|
||||
}
|
||||
}
|
||||
@@ -340,7 +341,7 @@ function deleteAllCustomDNSEntries($reload = '')
|
||||
foreach ($existingEntries as $entry) {
|
||||
pihole_execute('-a removecustomdns '.$entry->ip.' '.$entry->domain.' '.$reload);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
} catch (Exception $ex) {
|
||||
return returnError($ex->getMessage());
|
||||
}
|
||||
|
||||
@@ -384,10 +385,10 @@ function getCustomCNAMEEntries()
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = new \stdClass();
|
||||
$data->domains = array_slice($explodedLine, 0, -1);
|
||||
$data->domain = implode(',', $data->domains);
|
||||
$data->target = $explodedLine[count($explodedLine) - 1];
|
||||
$data = array();
|
||||
$data["domains"] = array_slice($explodedLine, 0, -1);
|
||||
$data["domain"] = implode(',', $data["domains"]);
|
||||
$data["target"] = $explodedLine[count($explodedLine) - 1];
|
||||
$entries[] = $data;
|
||||
}
|
||||
|
||||
@@ -465,7 +466,7 @@ function addCustomCNAMEEntry($domain = '', $target = '', $reload = '', $json = t
|
||||
}
|
||||
|
||||
return returnSuccess('', $json);
|
||||
} catch (\Exception $ex) {
|
||||
} catch (Exception $ex) {
|
||||
return returnError($ex->getMessage(), $json);
|
||||
}
|
||||
}
|
||||
@@ -504,7 +505,7 @@ function deleteCustomCNAMEEntry()
|
||||
pihole_execute('-a removecustomcname '.$domain.' '.$target);
|
||||
|
||||
return returnSuccess();
|
||||
} catch (\Exception $ex) {
|
||||
} catch (Exception $ex) {
|
||||
return returnError($ex->getMessage());
|
||||
}
|
||||
}
|
||||
@@ -521,7 +522,7 @@ function deleteAllCustomCNAMEEntries($reload = '')
|
||||
foreach ($existingEntries as $entry) {
|
||||
pihole_execute('-a removecustomcname '.$entry->domain.' '.$entry->target.' '.$reload);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
} catch (Exception $ex) {
|
||||
return returnError($ex->getMessage());
|
||||
}
|
||||
|
||||
@@ -566,14 +567,14 @@ function getQueryTypeStr($querytype)
|
||||
// Return Success message in JSON format
|
||||
function JSON_success($message = null)
|
||||
{
|
||||
header('Content-type: application/json');
|
||||
/* header('Content-type: application/json'); */
|
||||
echo json_encode(array('success' => true, 'message' => $message));
|
||||
}
|
||||
|
||||
// Return Error message in JSON format
|
||||
function JSON_error($message = null)
|
||||
{
|
||||
header('Content-type: application/json');
|
||||
/* header('Content-type: application/json'); */
|
||||
$response = array('success' => false, 'message' => $message);
|
||||
if (isset($_POST['action'])) {
|
||||
array_push($response, array('action' => $_POST['action']));
|
||||
@@ -586,7 +587,7 @@ function JSON_error($message = null)
|
||||
// - sends "warning" to use the correct alert type.
|
||||
function JSON_warning($message = null)
|
||||
{
|
||||
header('Content-type: application/json');
|
||||
/* header('Content-type: application/json'); */
|
||||
echo json_encode(array(
|
||||
'success' => true,
|
||||
'warning' => true,
|
||||
@@ -594,51 +595,6 @@ function JSON_warning($message = null)
|
||||
));
|
||||
}
|
||||
|
||||
// Returns an integer representing pihole blocking status
|
||||
function piholeStatus()
|
||||
{
|
||||
// Retrieve DNS Port calling FTL API directly
|
||||
$port = callFTLAPI('dns-port');
|
||||
|
||||
// Retrieve FTL status
|
||||
$FTLstats = callFTLAPI('stats');
|
||||
|
||||
if (array_key_exists('FTLnotrunning', $port) || array_key_exists('FTLnotrunning', $FTLstats)) {
|
||||
// FTL is not running
|
||||
$ret = -1;
|
||||
} elseif (in_array('status enabled', $FTLstats)) {
|
||||
// FTL is enabled
|
||||
if (intval($port[0]) <= 0) {
|
||||
// Port=0; FTL is not listening
|
||||
$ret = -1;
|
||||
} else {
|
||||
// FTL is running on this port
|
||||
$ret = intval($port[0]);
|
||||
}
|
||||
} elseif (in_array('status disabled', $FTLstats)) {
|
||||
// FTL is disabled
|
||||
$ret = 0;
|
||||
} else {
|
||||
// Unknown (unexpected) response
|
||||
$ret = -2;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// Returns the default gateway address and interface
|
||||
function getGateway()
|
||||
{
|
||||
$gateway = callFTLAPI('gateway');
|
||||
if (array_key_exists('FTLnotrunning', $gateway)) {
|
||||
$ret = array('ip' => -1);
|
||||
} else {
|
||||
$ret = array_combine(array('ip', 'iface'), explode(' ', $gateway[0]));
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// Try to convert possible IDNA domain to Unicode
|
||||
function convertIDNAToUnicode($IDNA)
|
||||
{
|
||||
@@ -690,12 +646,6 @@ function convertUnicodeToIDNA($unicode)
|
||||
}
|
||||
}
|
||||
|
||||
// Return PID of FTL (used in settings.php)
|
||||
function pidofFTL()
|
||||
{
|
||||
return shell_exec('pidof pihole-FTL');
|
||||
}
|
||||
|
||||
// Get FTL process information (used in settings.php)
|
||||
function get_FTL_data($FTLpid, $arg)
|
||||
{
|
||||
@@ -722,8 +672,8 @@ function convertseconds($argument)
|
||||
function start_php_session()
|
||||
{
|
||||
// Prevent Session ID from being passed through URLs
|
||||
ini_set('session.use_only_cookies', 1);
|
||||
session_start();
|
||||
/* ini_set('session.use_only_cookies', 1); */
|
||||
/* session_start(); */
|
||||
// HttpOnly: Prevents javascript XSS attacks aimed to steal the session ID
|
||||
//
|
||||
// SameSite=Strict: Allows servers to assert that a cookie ought not to be
|
||||
@@ -732,5 +682,5 @@ function start_php_session()
|
||||
// protection against cross-site request forgery attacks.
|
||||
// Direct support of Samesite has been added to PHP only in version 7.3
|
||||
// We manually set the cookie option ourselves to ensure backwards compatibility
|
||||
header('Set-Cookie: PHPSESSID='.session_id().'; path=/; HttpOnly; SameSite=Strict');
|
||||
/* header('Set-Cookie: PHPSESSID='.session_id().'; path=/; HttpOnly; SameSite=Strict'); */
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ require_once 'scripts/pi-hole/php/database.php';
|
||||
|
||||
function gravity_last_update($raw = false)
|
||||
{
|
||||
return 'Adlists updated ??.?? ago';
|
||||
/*
|
||||
$db = SQLite3_connect(getGravityDBFilename());
|
||||
$date_file_created_unix = $db->querySingle("SELECT value FROM info WHERE property = 'updated';");
|
||||
if ($date_file_created_unix === false) {
|
||||
@@ -50,4 +52,5 @@ function gravity_last_update($raw = false)
|
||||
|
||||
// String output (less than one day ago)
|
||||
return $gravitydiff->format('Adlists updated %H:%I (hh:mm) ago');
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<?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 'password.php';
|
||||
|
||||
if (!$auth) {
|
||||
exit('Not authorized');
|
||||
}
|
||||
|
||||
ob_end_flush();
|
||||
ini_set('output_buffering', '0');
|
||||
ob_implicit_flush(true);
|
||||
header('Content-Type: text/event-stream');
|
||||
header('Cache-Control: no-cache');
|
||||
|
||||
function echoEvent($datatext)
|
||||
{
|
||||
// Detect ${OVER} and replace it with something we can safely transmit
|
||||
$datatext = str_replace("\r[K", '<------', $datatext);
|
||||
$pos = strpos($datatext, '<------');
|
||||
// Detect if the ${OVER} line is within this line, e.g.
|
||||
// "Pending: String to replace${OVER}Done: String has been replaced"
|
||||
// If this is the case, we have to remove everything before ${OVER}
|
||||
// and return only the text thereafter
|
||||
if ($pos !== false && $pos !== 0) {
|
||||
$datatext = substr($datatext, $pos);
|
||||
}
|
||||
echo 'data: '.implode("\ndata: ", explode("\n", $datatext))."\n\n";
|
||||
}
|
||||
|
||||
$proc = popen('sudo pihole -g', 'r');
|
||||
while (!feof($proc)) {
|
||||
echoEvent(fread($proc, 4096));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,133 +8,12 @@
|
||||
* Please see LICENSE file for your rights under this license.
|
||||
*/
|
||||
|
||||
require 'scripts/pi-hole/php/password.php';
|
||||
if (!$auth) {
|
||||
$_SESSION['prev_url'] = $_SERVER['REQUEST_URI'];
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require 'scripts/pi-hole/php/auth.php';
|
||||
require_once 'scripts/pi-hole/php/FTL.php';
|
||||
require_once 'scripts/pi-hole/php/func.php';
|
||||
require 'scripts/pi-hole/php/theme.php';
|
||||
|
||||
// Retrieve layout setting from setupVars
|
||||
if (isset($setupVars['WEBUIBOXEDLAYOUT']) && !($setupVars['WEBUIBOXEDLAYOUT'] === 'boxed')) {
|
||||
$boxedlayout = false;
|
||||
} else {
|
||||
$boxedlayout = true;
|
||||
}
|
||||
|
||||
// Override layout setting if layout is changed via Settings page
|
||||
if (isset($_POST['field'])) {
|
||||
if ($_POST['field'] === 'webUI' && isset($_POST['boxedlayout'])) {
|
||||
$boxedlayout = true;
|
||||
} elseif ($_POST['field'] === 'webUI' && !isset($_POST['boxedlayout'])) {
|
||||
$boxedlayout = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return memory usage to show on status block
|
||||
function getMemUsage()
|
||||
{
|
||||
$data = explode("\n", file_get_contents('/proc/meminfo'));
|
||||
$meminfo = array();
|
||||
if (count($data) > 0) {
|
||||
foreach ($data as $line) {
|
||||
$expl = explode(':', $line);
|
||||
if (count($expl) == 2) {
|
||||
// remove " kB" from the end of the string and make it an integer
|
||||
$meminfo[$expl[0]] = intval(trim(substr($expl[1], 0, -3)));
|
||||
}
|
||||
}
|
||||
$memused = $meminfo['MemTotal'] - $meminfo['MemFree'] - $meminfo['Buffers'] - $meminfo['Cached'];
|
||||
$memusage = $memused / $meminfo['MemTotal'];
|
||||
} else {
|
||||
$memusage = -1;
|
||||
}
|
||||
|
||||
return $memusage;
|
||||
}
|
||||
|
||||
// Try to get temperature value from different places (OS dependent)
|
||||
// - return an array, containing the temperature and limit.
|
||||
function getTemperature()
|
||||
{
|
||||
global $setupVars;
|
||||
|
||||
if (file_exists('/sys/class/thermal/thermal_zone0/temp')) {
|
||||
$output = rtrim(file_get_contents('/sys/class/thermal/thermal_zone0/temp'));
|
||||
} elseif (file_exists('/sys/class/hwmon/hwmon0/temp1_input')) {
|
||||
$output = rtrim(file_get_contents('/sys/class/hwmon/hwmon0/temp1_input'));
|
||||
} else {
|
||||
$output = '';
|
||||
}
|
||||
|
||||
// Test if we succeeded in getting the temperature
|
||||
if (is_numeric($output)) {
|
||||
// $output could be either 4-5 digits or 2-3, and we only divide by 1000 if it's 4-5
|
||||
// ex. 39007 vs 39
|
||||
$celsius = intval($output);
|
||||
|
||||
// If celsius is greater than 1 degree and is in the 4-5 digit format
|
||||
if ($celsius > 1000) {
|
||||
// Use multiplication to get around the division-by-zero error
|
||||
$celsius *= 1e-3;
|
||||
}
|
||||
|
||||
// Get user-defined temperature limit if set
|
||||
if (isset($setupVars['TEMPERATURE_LIMIT'])) {
|
||||
$limit = intval($setupVars['TEMPERATURE_LIMIT']);
|
||||
} else {
|
||||
$limit = 60;
|
||||
}
|
||||
} else {
|
||||
// Nothing can be colder than -273.15 degree Celsius (= 0 Kelvin)
|
||||
// This is the minimum temperature possible (AKA absolute zero)
|
||||
$celsius = -273.16;
|
||||
// Set templimit to null if no tempsensor was found
|
||||
$limit = null;
|
||||
}
|
||||
|
||||
return array($celsius, $limit);
|
||||
}
|
||||
|
||||
check_cors();
|
||||
|
||||
// Generate CSRF token
|
||||
if (empty($_SESSION['token'])) {
|
||||
$_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32));
|
||||
}
|
||||
$token = $_SESSION['token'];
|
||||
|
||||
// For session timer
|
||||
$maxlifetime = ini_get('session.gc_maxlifetime');
|
||||
|
||||
// Get temperature
|
||||
list($celsius, $temperaturelimit) = getTemperature();
|
||||
|
||||
// Get CPU load
|
||||
$loaddata = sys_getloadavg();
|
||||
foreach ($loaddata as $key => $value) {
|
||||
$loaddata[$key] = round($value, 2);
|
||||
}
|
||||
|
||||
// Get number of processing units available to PHP
|
||||
// (may be less than the number of online processors)
|
||||
$nproc = shell_exec('nproc');
|
||||
if (!is_numeric($nproc)) {
|
||||
$cpuinfo = file_get_contents('/proc/cpuinfo');
|
||||
preg_match_all('/^processor/m', $cpuinfo, $matches);
|
||||
$nproc = count($matches[0]);
|
||||
}
|
||||
|
||||
// Get memory usage
|
||||
$memory_usage = getMemUsage();
|
||||
|
||||
$piholeFTLConf = piholeFTLConfig();
|
||||
|
||||
require 'header.php';
|
||||
?>
|
||||
<body class="<?php echo $theme; ?> hold-transition sidebar-mini<?php if ($boxedlayout) { ?> layout-boxed<?php } ?><?php if ($auth) { ?> logged-in<?php } ?>">
|
||||
@@ -208,10 +87,7 @@ if ($auth) {
|
||||
<a class="btn-link" href="https://discourse.pi-hole.net/" rel="noopener" target="_blank"><i class="fa fa-fw menu-icon fab fa-discourse"></i> Pi-hole Forum</a>
|
||||
<a class="btn-link" href="https://github.com/pi-hole" rel="noopener" target="_blank"><i class="fa-fw menu-icon fab fa-github"></i> GitHub</a>
|
||||
<a class="btn-link" href="https://discourse.pi-hole.net/c/announcements/5" rel="noopener" target="_blank"><i class="fa-fw menu-icon fa fa-regular fa-rocket"></i> Pi-hole Releases</a>
|
||||
<?php if (strlen($pwhash) > 0) { // Show "Logout" link only when the user has the password protection enabled.?>
|
||||
<hr>
|
||||
<a class="btn-link" href="logout.php" rel="noopener"><i class="fa fa-fw menu-icon fa-sign-out-alt"></i> Log out</a>
|
||||
<?php } ?>
|
||||
<a class="btn-link" href="#" onclick="utils.doLogout();"><i class="fa fa-fw menu-icon fa-sign-out-alt"></i> Log out</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2021 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_once 'auth.php';
|
||||
require_once 'func.php';
|
||||
require_once 'database.php';
|
||||
|
||||
// Authentication checks
|
||||
if (!isset($api)) {
|
||||
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)!');
|
||||
}
|
||||
}
|
||||
|
||||
$reload = false;
|
||||
|
||||
$QueriesDB = getQueriesDBFilename();
|
||||
$db = SQLite3_connect($QueriesDB, SQLITE3_OPEN_READWRITE);
|
||||
|
||||
// Delete message identified by IDs
|
||||
if ($_POST['action'] == 'delete_message' && isset($_POST['id'])) {
|
||||
try {
|
||||
$ids = json_decode($_POST['id']);
|
||||
if (!is_array($ids)) {
|
||||
throw new Exception('Invalid payload: id is not an array');
|
||||
}
|
||||
// Exploit prevention: Ensure all entries in the ID array are integers
|
||||
foreach ($ids as $value) {
|
||||
if (!is_numeric($value)) {
|
||||
throw new Exception('Invalid payload: id contains non-numeric entries');
|
||||
}
|
||||
}
|
||||
$stmt = $db->prepare('DELETE FROM message WHERE id IN ('.implode(',', $ids).')');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing message statement: '.$db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing message statement: '.$db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} else {
|
||||
log_and_die('Requested action not supported!');
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
<?php
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2021 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_once 'auth.php';
|
||||
require_once 'func.php';
|
||||
require_once 'database.php';
|
||||
|
||||
// Authentication checks
|
||||
if (!isset($api)) {
|
||||
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)!');
|
||||
}
|
||||
}
|
||||
|
||||
$reload = false;
|
||||
|
||||
$QueriesDB = getQueriesDBFilename();
|
||||
$db = SQLite3_connect($QueriesDB, SQLITE3_OPEN_READWRITE);
|
||||
|
||||
if ($_POST['action'] == 'delete_network_entry' && isset($_POST['id'])) {
|
||||
// Delete netwwork and network_addresses table entry identified by ID
|
||||
try {
|
||||
$stmt = $db->prepare('DELETE FROM network_addresses WHERE network_id=:id');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing message statement: '.$db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding id to message statement: '.$db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing message statement: '.$db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$stmt = $db->prepare('DELETE FROM network WHERE id=:id');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing message statement: '.$db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding id to message statement: '.$db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing message statement: '.$db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} else {
|
||||
log_and_die('Requested action not supported!');
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
<?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_once 'func.php';
|
||||
require_once 'persistentlogin_token.php';
|
||||
|
||||
// Start a new PHP session (or continue an existing one)
|
||||
start_php_session();
|
||||
|
||||
// Read setupVars.conf file
|
||||
$setupVars = parse_ini_file('/etc/pihole/setupVars.conf');
|
||||
// Try to read password hash from setupVars.conf
|
||||
if (isset($setupVars['WEBPASSWORD'])) {
|
||||
$pwhash = $setupVars['WEBPASSWORD'];
|
||||
} else {
|
||||
$pwhash = '';
|
||||
}
|
||||
|
||||
function verifyPassword($pwhash, $use_api = false)
|
||||
{
|
||||
$validpassword = true;
|
||||
|
||||
// Test if password is set
|
||||
if (strlen($pwhash) > 0) {
|
||||
// Check for and authorize from persistent cookie
|
||||
if (isset($_COOKIE['persistentlogin'])) {
|
||||
if (checkValidityPersistentLoginToken($_COOKIE['persistentlogin'])) {
|
||||
$_SESSION['auth'] = true;
|
||||
} else {
|
||||
// Invalid cookie
|
||||
$_SESSION['auth'] = false;
|
||||
setcookie('persistentlogin', '', 1);
|
||||
}
|
||||
} elseif (isset($_POST['pw'])) {
|
||||
// Compare doubly hashes password input with saved hash
|
||||
$postinput = hash('sha256', hash('sha256', $_POST['pw']));
|
||||
|
||||
if (hash_equals($pwhash, $postinput)) {
|
||||
// Save previously accessed page, before clear the session
|
||||
$redirect_url = 'index.php';
|
||||
if (isset($_SESSION['prev_url'])) {
|
||||
$redirect_url = $_SESSION['prev_url'];
|
||||
}
|
||||
|
||||
// Regenerate session ID to prevent session fixation
|
||||
session_regenerate_id();
|
||||
|
||||
// Clear the old session
|
||||
$_SESSION = array();
|
||||
|
||||
// Set hash in new session
|
||||
$_SESSION['hash'] = $pwhash;
|
||||
|
||||
// Set persistent cookie if selected
|
||||
if (isset($_POST['persistentlogin'])) {
|
||||
// Generate cookie with new expiry
|
||||
$token = genPersistentLoginToken();
|
||||
$time = time() + 60 * 60 * 24 * 7; // 7 days
|
||||
writePersistentLoginToken($token, $time);
|
||||
// setcookie($name, $value, $expire, $path, $domain, $secure, $httponly)
|
||||
setcookie('persistentlogin', $token, $time, null, null, null, true);
|
||||
}
|
||||
|
||||
$_SESSION['auth'] = true;
|
||||
|
||||
// Login successful, redirect the user to the original requested page
|
||||
if (
|
||||
$_SERVER['REQUEST_METHOD'] === 'POST' &&
|
||||
strlen($_SERVER['SCRIPT_NAME']) >= 10 &&
|
||||
substr_compare($_SERVER['SCRIPT_NAME'], '/login.php', -10) === 0
|
||||
) {
|
||||
header('Location: '.$redirect_url);
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
$_SESSION['auth'] = false;
|
||||
$validpassword = false;
|
||||
}
|
||||
} elseif (isset($_SESSION['hash'])) {
|
||||
// Compare auth hash with saved hash
|
||||
if (hash_equals($pwhash, $_SESSION['hash'])) {
|
||||
$_SESSION['auth'] = true;
|
||||
}
|
||||
} elseif ($use_api && isset($_GET['auth'])) {
|
||||
// API can use the hash to get data without logging in via plain-text password
|
||||
if (hash_equals($pwhash, $_GET['auth'])) {
|
||||
$_SESSION['auth'] = true;
|
||||
}
|
||||
} else {
|
||||
// Password or hash wrong
|
||||
$_SESSION['auth'] = false;
|
||||
}
|
||||
} else {
|
||||
// No password set
|
||||
$_SESSION['auth'] = true;
|
||||
}
|
||||
|
||||
return $validpassword;
|
||||
}
|
||||
|
||||
$wrongpassword = !verifyPassword($pwhash, isset($api));
|
||||
$auth = $_SESSION['auth'];
|
||||
@@ -1,88 +0,0 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
function genPersistentLoginToken()
|
||||
{
|
||||
return bin2hex(openssl_random_pseudo_bytes(16));
|
||||
}
|
||||
|
||||
function checkSafetyPersistentLoginToken($token)
|
||||
{
|
||||
// return true only if the token is and alphanumeric string of 32 chars, else return false
|
||||
if (ctype_alnum($token) and strlen($token) == 32) {
|
||||
return true;
|
||||
}
|
||||
error_log('Security alert: presented "persistentlogin" token did not pass safety check!', 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getPathPersistentLoginToken($token)
|
||||
{
|
||||
// safely return the path of the persistentlogin token file, if token is not safe return false
|
||||
$session_path = session_save_path();
|
||||
|
||||
if ($session_path and checkSafetyPersistentLoginToken($token)) {
|
||||
$token_file = $session_path.'/ph_plt_'.$token.'.txt';
|
||||
|
||||
return $token_file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkValidityPersistentLoginToken($token)
|
||||
{
|
||||
// return true if persistentlogin token is safe, valid and not expired
|
||||
$token_file = getPathPersistentLoginToken($token);
|
||||
|
||||
if ($token_file and file_exists($token_file) and is_readable($token_file)) {
|
||||
$t_file = fopen($token_file, 'r');
|
||||
if ($t_file) {
|
||||
$time = fread($t_file, filesize($token_file));
|
||||
fclose($t_file);
|
||||
// make sure that token is not expired
|
||||
if ($time && intval($time) >= time()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function writePersistentLoginToken($token, $time)
|
||||
{
|
||||
$token_file = getPathPersistentLoginToken($token);
|
||||
|
||||
if ($token_file and !file_exists($token_file)) {
|
||||
$t_file = fopen($token_file, 'w');
|
||||
if ($t_file) {
|
||||
// make sure persistent login token file is not readable by other users
|
||||
chmod($token_file, 0600);
|
||||
|
||||
fwrite($t_file, $time);
|
||||
fclose($t_file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function logoutPersistentLoginToken($token)
|
||||
{
|
||||
setcookie('persistentlogin', '', 1);
|
||||
|
||||
$token_file = getPathPersistentLoginToken($token);
|
||||
if ($token_file and file_exists($token_file) and is_writable($token_file)) {
|
||||
unlink($token_file);
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?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 'password.php';
|
||||
if (!$auth) {
|
||||
exit('Not authorized');
|
||||
}
|
||||
|
||||
while (ob_get_level() > 0) {
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
require_once 'func.php';
|
||||
ini_set('output_buffering', '0');
|
||||
ob_implicit_flush(true);
|
||||
header('Content-Type: text/event-stream');
|
||||
header('Cache-Control: no-cache');
|
||||
|
||||
function echoEvent($datatext, $url = '')
|
||||
{
|
||||
if (!isset($_GET['IE'])) {
|
||||
$txt = 'data:'.implode("\ndata:", explode("\n", $datatext))."\n\n";
|
||||
} else {
|
||||
$txt = $datatext;
|
||||
}
|
||||
|
||||
$txt = str_replace('This can be overridden using the -all option', 'Select the checkbox to remove the limitation', $txt);
|
||||
$txt = str_replace($url, '<strong class="text-blue">'.$url.'</strong>', $txt);
|
||||
|
||||
echo $txt;
|
||||
}
|
||||
|
||||
// Test if domain is set
|
||||
if (isset($_GET['domain'])) {
|
||||
// Is this a valid domain?
|
||||
// Convert domain name to IDNA ASCII form for international domains
|
||||
$url = convertUnicodeToIDNA($_GET['domain']);
|
||||
if (!validDomain($url)) {
|
||||
echoEvent(htmlentities($url).' is an invalid domain!', $url);
|
||||
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
echoEvent('No domain provided');
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
$options = '';
|
||||
if (isset($_GET['exact'])) {
|
||||
$options .= ' -exact';
|
||||
}
|
||||
|
||||
if (isset($_GET['showall'])) {
|
||||
$options .= ' -all';
|
||||
}
|
||||
|
||||
$proc = popen('sudo pihole -q '.$url.$options, 'r');
|
||||
while (!feof($proc)) {
|
||||
echoEvent(fread($proc, 4096), $url);
|
||||
}
|
||||
@@ -1,605 +0,0 @@
|
||||
<?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_once 'func.php';
|
||||
|
||||
if (!in_array(basename($_SERVER['SCRIPT_FILENAME']), array('settings.php', 'teleporter.php'), true)) {
|
||||
exit('Direct access to this script is forbidden!');
|
||||
}
|
||||
|
||||
// Check for existence of variable
|
||||
// and test it only if it exists
|
||||
function istrue(&$argument)
|
||||
{
|
||||
if (isset($argument)) {
|
||||
if ($argument) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function formatMAC($mac_addr)
|
||||
{
|
||||
preg_match('/([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})/', $mac_addr, $matches);
|
||||
if (count($matches) > 0) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$dhcp_static_leases = array();
|
||||
function readStaticLeasesFile($origin_file = '/etc/dnsmasq.d/04-pihole-static-dhcp.conf')
|
||||
{
|
||||
global $dhcp_static_leases;
|
||||
$dhcp_static_leases = array();
|
||||
if (!file_exists($origin_file) || !is_readable($origin_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dhcpstatic = @fopen($origin_file, 'r');
|
||||
if (!is_resource($dhcpstatic)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!feof($dhcpstatic)) {
|
||||
// Remove any possibly existing variable with this name
|
||||
$mac = '';
|
||||
$one = '';
|
||||
$two = '';
|
||||
sscanf(trim(fgets($dhcpstatic)), 'dhcp-host=%[^,],%[^,],%[^,]', $mac, $one, $two);
|
||||
if (strlen($mac) > 0 && validMAC($mac)) {
|
||||
if (validIP($one) && strlen($two) == 0) {
|
||||
// dhcp-host=mac,IP - no HOST
|
||||
array_push($dhcp_static_leases, array('hwaddr' => $mac, 'IP' => $one, 'host' => ''));
|
||||
} elseif (strlen($two) == 0) {
|
||||
// dhcp-host=mac,hostname - no IP
|
||||
array_push($dhcp_static_leases, array('hwaddr' => $mac, 'IP' => '', 'host' => $one));
|
||||
} else {
|
||||
// dhcp-host=mac,IP,hostname
|
||||
array_push($dhcp_static_leases, array('hwaddr' => $mac, 'IP' => $one, 'host' => $two));
|
||||
}
|
||||
} elseif (validIP($one) && validDomain($mac)) {
|
||||
// dhcp-host=hostname,IP - no MAC
|
||||
array_push($dhcp_static_leases, array('hwaddr' => '', 'IP' => $one, 'host' => $mac));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isequal(&$argument, &$compareto)
|
||||
{
|
||||
if (isset($argument)) {
|
||||
if ($argument === $compareto) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isinserverlist($addr)
|
||||
{
|
||||
global $DNSserverslist;
|
||||
foreach ($DNSserverslist as $key => $value) {
|
||||
if (isequal($value['v4_1'], $addr) || isequal($value['v4_2'], $addr)) {
|
||||
return true;
|
||||
}
|
||||
if (isequal($value['v6_1'], $addr) || isequal($value['v6_2'], $addr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$DNSserverslist = array();
|
||||
function readDNSserversList()
|
||||
{
|
||||
// Reset list
|
||||
$list = array();
|
||||
$handle = @fopen('/etc/pihole/dns-servers.conf', 'r');
|
||||
if ($handle) {
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$line = rtrim($line);
|
||||
$line = explode(';', $line);
|
||||
$name = $line[0];
|
||||
$values = array();
|
||||
if (!empty($line[1]) && validIP($line[1])) {
|
||||
$values['v4_1'] = $line[1];
|
||||
}
|
||||
if (!empty($line[2]) && validIP($line[2])) {
|
||||
$values['v4_2'] = $line[2];
|
||||
}
|
||||
if (!empty($line[3]) && validIP($line[3])) {
|
||||
$values['v6_1'] = $line[3];
|
||||
}
|
||||
if (!empty($line[4]) && validIP($line[4])) {
|
||||
$values['v6_2'] = $line[4];
|
||||
}
|
||||
$list[$name] = $values;
|
||||
}
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
require_once 'database.php';
|
||||
|
||||
function addStaticDHCPLease($mac, $ip, $hostname)
|
||||
{
|
||||
global $error, $success, $dhcp_static_leases;
|
||||
|
||||
try {
|
||||
if (!validMAC($mac)) {
|
||||
throw new Exception('MAC address ('.htmlspecialchars($mac).') is invalid!<br>', 0);
|
||||
}
|
||||
$mac = strtoupper($mac);
|
||||
|
||||
if (!validIP($ip) && strlen($ip) > 0) {
|
||||
throw new Exception('IP address ('.htmlspecialchars($ip).') is invalid!<br>', 1);
|
||||
}
|
||||
|
||||
if (!validDomain($hostname) && strlen($hostname) > 0) {
|
||||
throw new Exception('Host name ('.htmlspecialchars($hostname).') is invalid!<br>', 2);
|
||||
}
|
||||
|
||||
if (strlen($hostname) == 0 && strlen($ip) == 0) {
|
||||
throw new Exception('You can not omit both the IP address and the host name!<br>', 3);
|
||||
}
|
||||
|
||||
if (strlen($hostname) == 0) {
|
||||
$hostname = 'nohost';
|
||||
}
|
||||
|
||||
if (strlen($ip) == 0) {
|
||||
$ip = 'noip';
|
||||
}
|
||||
|
||||
// Test if this lease is already included
|
||||
readStaticLeasesFile();
|
||||
|
||||
foreach ($dhcp_static_leases as $lease) {
|
||||
if ($lease['hwaddr'] === $mac) {
|
||||
throw new Exception('Static lease for MAC address ('.htmlspecialchars($mac).') already defined!<br>', 4);
|
||||
}
|
||||
if ($ip !== 'noip' && $lease['IP'] === $ip) {
|
||||
throw new Exception('Static lease for IP address ('.htmlspecialchars($ip).') already defined!<br>', 5);
|
||||
}
|
||||
if ($lease['host'] === $hostname) {
|
||||
throw new Exception('Static lease for hostname ('.htmlspecialchars($hostname).') already defined!<br>', 6);
|
||||
}
|
||||
}
|
||||
|
||||
pihole_execute('-a addstaticdhcp '.$mac.' '.$ip.' '.$hostname);
|
||||
$success .= 'A new static address has been added';
|
||||
|
||||
return true;
|
||||
} catch (Exception $exception) {
|
||||
$error .= $exception->getMessage();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Read available DNS server list
|
||||
$DNSserverslist = readDNSserversList();
|
||||
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
if (isset($_POST['field'])) {
|
||||
// Handle CSRF
|
||||
check_csrf(isset($_POST['token']) ? $_POST['token'] : '');
|
||||
|
||||
// Process request
|
||||
switch ($_POST['field']) {
|
||||
// Set DNS server
|
||||
case 'DNS':
|
||||
$DNSservers = array();
|
||||
// Add selected predefined servers to list
|
||||
foreach ($DNSserverslist as $key => $value) {
|
||||
foreach (array('v4_1', 'v4_2', 'v6_1', 'v6_2') as $type) {
|
||||
// Skip if this IP type does not
|
||||
// exist (e.g. IPv4-only or only
|
||||
// one IPv6 address upstream
|
||||
// server)
|
||||
if (!array_key_exists($type, $value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If server exists and is set
|
||||
// (POST), we add it to the
|
||||
// array of DNS servers
|
||||
$server = str_replace('.', '_', $value[$type]);
|
||||
if (array_key_exists('DNSserver'.$server, $_POST)) {
|
||||
array_push($DNSservers, $value[$type]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test custom server fields
|
||||
for ($i = 1; $i <= 4; ++$i) {
|
||||
if (array_key_exists('custom'.$i, $_POST)) {
|
||||
$exploded = explode('#', $_POST['custom'.$i.'val'], 2);
|
||||
$IP = trim($exploded[0]);
|
||||
|
||||
if (!validIP($IP)) {
|
||||
$error .= 'IP ('.htmlspecialchars($IP).') is invalid!<br>';
|
||||
} else {
|
||||
if (count($exploded) > 1) {
|
||||
$port = trim($exploded[1]);
|
||||
if (!is_numeric($port)) {
|
||||
$error .= 'Port ('.htmlspecialchars($port).') is invalid!<br>';
|
||||
} else {
|
||||
$IP .= '#'.$port;
|
||||
}
|
||||
}
|
||||
|
||||
array_push($DNSservers, $IP);
|
||||
}
|
||||
}
|
||||
}
|
||||
$DNSservercount = count($DNSservers);
|
||||
|
||||
// Check if at least one DNS server has been added
|
||||
if ($DNSservercount < 1) {
|
||||
$error .= 'No DNS server has been selected.<br>';
|
||||
}
|
||||
|
||||
// Check if domain-needed is requested
|
||||
if (isset($_POST['DNSrequiresFQDN'])) {
|
||||
$extra = 'domain-needed ';
|
||||
} else {
|
||||
$extra = 'domain-not-needed ';
|
||||
}
|
||||
|
||||
// Check if domain-needed is requested
|
||||
if (isset($_POST['DNSbogusPriv'])) {
|
||||
$extra .= 'bogus-priv ';
|
||||
} else {
|
||||
$extra .= 'no-bogus-priv ';
|
||||
}
|
||||
|
||||
// Check if DNSSEC is requested
|
||||
if (isset($_POST['DNSSEC'])) {
|
||||
$extra .= 'dnssec';
|
||||
} else {
|
||||
$extra .= 'no-dnssec';
|
||||
}
|
||||
|
||||
// Check if rev-server is requested
|
||||
if (isset($_POST['rev_server'])) {
|
||||
// Validate CIDR IP
|
||||
$cidr = trim($_POST['rev_server_cidr']);
|
||||
if (!validCIDRIP($cidr)) {
|
||||
$error .= 'Conditional forwarding subnet ("'.htmlspecialchars($cidr).'") is invalid!<br>'.
|
||||
'This field requires CIDR notation for local subnets (e.g., 192.168.0.0/16).<br>';
|
||||
}
|
||||
|
||||
// Validate target IP
|
||||
$target = trim($_POST['rev_server_target']);
|
||||
if (!validIP($target)) {
|
||||
$error .= 'Conditional forwarding target IP ("'.htmlspecialchars($target).'") is invalid!<br>';
|
||||
}
|
||||
|
||||
// Validate conditional forwarding domain name (empty is okay)
|
||||
$domain = trim($_POST['rev_server_domain']);
|
||||
if (strlen($domain) > 0 && !validDomain($domain)) {
|
||||
$error .= 'Conditional forwarding domain name ("'.htmlspecialchars($domain).'") is invalid!<br>';
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$extra .= ' rev-server '.$cidr.' '.$target.' '.$domain;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if DNSinterface is set
|
||||
if (isset($_POST['DNSinterface'])) {
|
||||
if ($_POST['DNSinterface'] === 'single') {
|
||||
$DNSinterface = 'single';
|
||||
} elseif ($_POST['DNSinterface'] === 'bind') {
|
||||
$DNSinterface = 'bind';
|
||||
} elseif ($_POST['DNSinterface'] === 'all') {
|
||||
$DNSinterface = 'all';
|
||||
} else {
|
||||
$DNSinterface = 'local';
|
||||
}
|
||||
} else {
|
||||
// Fallback
|
||||
$DNSinterface = 'local';
|
||||
}
|
||||
pihole_execute('-a -i '.$DNSinterface.' -web');
|
||||
|
||||
// Add rate-limiting settings
|
||||
if (isset($_POST['rate_limit_count'], $_POST['rate_limit_interval'])) {
|
||||
// Restart of FTL is delayed
|
||||
pihole_execute('-a ratelimit '.intval($_POST['rate_limit_count']).' '.intval($_POST['rate_limit_interval']).' false');
|
||||
}
|
||||
|
||||
// If there has been no error we can save the new DNS server IPs
|
||||
if (!strlen($error)) {
|
||||
$IPs = implode(',', $DNSservers);
|
||||
$return = pihole_execute('-a setdns "'.$IPs.'" '.$extra);
|
||||
$success .= htmlspecialchars(end($return)).'<br>';
|
||||
$success .= 'The DNS settings have been updated (using '.$DNSservercount.' DNS servers)';
|
||||
} else {
|
||||
$error .= 'The settings have been reset to their previous values';
|
||||
}
|
||||
|
||||
break;
|
||||
// Set query logging
|
||||
case 'Logging':
|
||||
if ($_POST['action'] === 'Disable') {
|
||||
pihole_execute('-l off');
|
||||
$success .= 'Logging has been disabled and logs have been flushed';
|
||||
} elseif ($_POST['action'] === 'Disable-noflush') {
|
||||
pihole_execute('-l off noflush');
|
||||
$success .= 'Logging has been disabled, your logs have <strong>not</strong> been flushed';
|
||||
} else {
|
||||
pihole_execute('-l on');
|
||||
$success .= 'Logging has been enabled';
|
||||
}
|
||||
|
||||
break;
|
||||
// Set domains to be excluded from being shown in Top Domains (or Ads) and Top Clients
|
||||
case 'API':
|
||||
// Explode the contents of the textareas into PHP arrays
|
||||
// \n (Unix) and \r\n (Win) will be considered as newline
|
||||
// array_filter( ... ) will remove any empty lines
|
||||
$domains = array_filter(preg_split('/\r\n|[\r\n]/', $_POST['domains']));
|
||||
$clients = array_filter(preg_split('/\r\n|[\r\n]/', $_POST['clients']));
|
||||
|
||||
$domainlist = '';
|
||||
$first = true;
|
||||
foreach ($domains as $domain) {
|
||||
if (!validDomainWildcard($domain) || validIP($domain)) {
|
||||
$error .= 'Top Domains/Ads entry '.htmlspecialchars($domain).' is invalid (use only domains)!<br>';
|
||||
}
|
||||
if (!$first) {
|
||||
$domainlist .= ',';
|
||||
} else {
|
||||
$first = false;
|
||||
}
|
||||
$domainlist .= $domain;
|
||||
}
|
||||
|
||||
$clientlist = '';
|
||||
$first = true;
|
||||
foreach ($clients as $client) {
|
||||
if (!validDomainWildcard($client) && !validIP($client)) {
|
||||
$error .= 'Top Clients entry '.htmlspecialchars($client).' is invalid (use only host names and IP addresses)!<br>';
|
||||
}
|
||||
if (!$first) {
|
||||
$clientlist .= ',';
|
||||
} else {
|
||||
$first = false;
|
||||
}
|
||||
$clientlist .= $client;
|
||||
}
|
||||
|
||||
// Set Top Lists options
|
||||
if (!strlen($error)) {
|
||||
// All entries are okay
|
||||
pihole_execute('-a setexcludedomains '.$domainlist);
|
||||
pihole_execute('-a setexcludeclients '.$clientlist);
|
||||
$success .= 'The API settings have been updated<br>';
|
||||
} else {
|
||||
$error .= 'The settings have been reset to their previous values';
|
||||
}
|
||||
|
||||
// Set query log options
|
||||
if (isset($_POST['querylog-permitted'], $_POST['querylog-blocked'])) {
|
||||
pihole_execute('-a setquerylog all');
|
||||
$success .= 'All entries will be shown in Query Log';
|
||||
} elseif (isset($_POST['querylog-permitted'])) {
|
||||
pihole_execute('-a setquerylog permittedonly');
|
||||
$success .= 'Only permitted will be shown in Query Log';
|
||||
} elseif (isset($_POST['querylog-blocked'])) {
|
||||
pihole_execute('-a setquerylog blockedonly');
|
||||
$success .= 'Only blocked entries will be shown in Query Log';
|
||||
} else {
|
||||
pihole_execute('-a setquerylog nothing');
|
||||
$success .= 'No entries will be shown in Query Log';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'webUI':
|
||||
if (isset($_POST['boxedlayout'])) {
|
||||
pihole_execute('-a layout boxed');
|
||||
} else {
|
||||
pihole_execute('-a layout traditional');
|
||||
}
|
||||
if (isset($_POST['webtheme'])) {
|
||||
global $available_themes;
|
||||
if (array_key_exists($_POST['webtheme'], $available_themes)) {
|
||||
exec('sudo pihole -a theme '.$_POST['webtheme']);
|
||||
}
|
||||
}
|
||||
$success .= 'The webUI settings have been updated';
|
||||
|
||||
break;
|
||||
|
||||
case 'poweroff':
|
||||
pihole_execute('-a poweroff');
|
||||
$success = 'The system will poweroff in 5 seconds...';
|
||||
|
||||
break;
|
||||
|
||||
case 'reboot':
|
||||
pihole_execute('-a reboot');
|
||||
$success = 'The system will reboot in 5 seconds...';
|
||||
|
||||
break;
|
||||
|
||||
case 'restartdns':
|
||||
pihole_execute('-a restartdns');
|
||||
$success = 'The DNS server has been restarted';
|
||||
|
||||
break;
|
||||
|
||||
case 'flushlogs':
|
||||
pihole_execute('-f');
|
||||
$success = 'The Pi-hole log file has been flushed';
|
||||
|
||||
break;
|
||||
|
||||
case 'DHCP':
|
||||
if (isset($_POST['addstatic'])) {
|
||||
$mac = trim($_POST['AddMAC']);
|
||||
$ip = trim($_POST['AddIP']);
|
||||
$hostname = trim($_POST['AddHostname']);
|
||||
|
||||
addStaticDHCPLease($mac, $ip, $hostname);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($_POST['removestatic'])) {
|
||||
$mac = $_POST['removestatic'];
|
||||
if (!validMAC($mac)) {
|
||||
$error .= 'MAC address ('.htmlspecialchars($mac).') is invalid!<br>';
|
||||
}
|
||||
$mac = strtoupper($mac);
|
||||
|
||||
if (!strlen($error)) {
|
||||
pihole_execute('-a removestaticdhcp '.$mac);
|
||||
$success .= 'The static address with MAC address '.htmlspecialchars($mac).' has been removed';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($_POST['active'])) {
|
||||
// Validate from IP
|
||||
$from = $_POST['from'];
|
||||
if (!validIP($from)) {
|
||||
$error .= 'From IP ('.htmlspecialchars($from).') is invalid!<br>';
|
||||
}
|
||||
|
||||
// Validate to IP
|
||||
$to = $_POST['to'];
|
||||
if (!validIP($to)) {
|
||||
$error .= 'To IP ('.htmlspecialchars($to).') is invalid!<br>';
|
||||
}
|
||||
|
||||
// Validate router IP
|
||||
$router = $_POST['router'];
|
||||
if (!validIP($router)) {
|
||||
$error .= 'Router IP ('.htmlspecialchars($router).') is invalid!<br>';
|
||||
}
|
||||
|
||||
$domain = $_POST['domain'];
|
||||
|
||||
// Validate Domain name
|
||||
if (!validDomain($domain)) {
|
||||
$error .= 'Domain name '.htmlspecialchars($domain).' is invalid!<br>';
|
||||
}
|
||||
|
||||
$leasetime = $_POST['leasetime'];
|
||||
|
||||
// Validate Lease time length
|
||||
if (!is_numeric($leasetime) || intval($leasetime) < 0) {
|
||||
$error .= 'Lease time '.htmlspecialchars($leasetime).' is invalid!<br>';
|
||||
}
|
||||
|
||||
if (isset($_POST['useIPv6'])) {
|
||||
$ipv6 = 'true';
|
||||
$type = '(IPv4 + IPv6)';
|
||||
} else {
|
||||
$ipv6 = 'false';
|
||||
$type = '(IPv4)';
|
||||
}
|
||||
|
||||
if (isset($_POST['DHCP_rapid_commit'])) {
|
||||
$rapidcommit = 'true';
|
||||
} else {
|
||||
$rapidcommit = 'false';
|
||||
}
|
||||
|
||||
if (!strlen($error)) {
|
||||
pihole_execute('-a enabledhcp '.$from.' '.$to.' '.$router.' '.$leasetime.' '.$domain.' '.$ipv6.' '.$rapidcommit);
|
||||
$success .= 'The DHCP server has been activated '.htmlspecialchars($type);
|
||||
}
|
||||
} else {
|
||||
pihole_execute('-a disabledhcp');
|
||||
$success = 'The DHCP server has been deactivated';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'privacyLevel':
|
||||
$level = intval($_POST['privacylevel']);
|
||||
if ($level >= 0 && $level <= 4) {
|
||||
// Check if privacylevel is already set
|
||||
if (isset($piholeFTLConf['PRIVACYLEVEL'])) {
|
||||
$privacylevel = intval($piholeFTLConf['PRIVACYLEVEL']);
|
||||
} else {
|
||||
$privacylevel = 0;
|
||||
}
|
||||
|
||||
// Store privacy level
|
||||
pihole_execute('-a privacylevel '.$level);
|
||||
|
||||
if ($privacylevel > $level) {
|
||||
pihole_execute('-a restartdns');
|
||||
$success .= 'The privacy level has been decreased and the DNS resolver has been restarted';
|
||||
} elseif ($privacylevel < $level) {
|
||||
$success .= 'The privacy level has been increased';
|
||||
} else {
|
||||
$success .= 'The privacy level has not been changed';
|
||||
}
|
||||
} else {
|
||||
$error .= 'Invalid privacy level ('.$level.')!';
|
||||
}
|
||||
|
||||
break;
|
||||
// Flush network table
|
||||
case 'flusharp':
|
||||
$output = pihole_execute('arpflush quiet');
|
||||
$error = '';
|
||||
if (is_array($output)) {
|
||||
$error = implode('<br>', $output);
|
||||
}
|
||||
if (strlen($error) == 0) {
|
||||
$success .= 'The network table has been flushed';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// Option not found
|
||||
$error = 'Invalid option';
|
||||
}
|
||||
}
|
||||
|
||||
// Credit: http://stackoverflow.com/a/5501447/2087442
|
||||
function formatSizeUnits($bytes)
|
||||
{
|
||||
if ($bytes >= 1073741824) {
|
||||
$bytes = number_format($bytes / 1073741824, 2).' GB';
|
||||
} elseif ($bytes >= 1048576) {
|
||||
$bytes = number_format($bytes / 1048576, 2).' MB';
|
||||
} elseif ($bytes >= 1024) {
|
||||
$bytes = number_format($bytes / 1024, 2).' kB';
|
||||
} elseif ($bytes > 1) {
|
||||
$bytes = $bytes.' bytes';
|
||||
} elseif ($bytes == 1) {
|
||||
$bytes = $bytes.' byte';
|
||||
} else {
|
||||
$bytes = '0 bytes';
|
||||
}
|
||||
|
||||
return $bytes;
|
||||
}
|
||||
@@ -10,18 +10,18 @@
|
||||
<div class="pull-left info">
|
||||
<p>Status</p>
|
||||
<?php
|
||||
$pistatus = piholeStatus();
|
||||
/*$pistatus = piholeStatus();
|
||||
if ($pistatus == 53) {
|
||||
echo '<span id="status"><i class="fa fa-w fa-circle text-green-light"></i> Active</span>';
|
||||
} elseif ($pistatus == 0) {
|
||||
echo '<span id="status"><i class="fa fa-w fa-circle text-red"></i> Blocking disabled</span>';
|
||||
} elseif ($pistatus == -1) {
|
||||
echo '<span id="status"><i class="fa fa-w fa-circle text-red"></i> DNS service not running</span>';
|
||||
} elseif ($pistatus == -2) {
|
||||
echo '<span id="status"><i class="fa fa-w fa-circle text-red"></i> Unknown</span>';
|
||||
} elseif ($pistatus == -2) {*/
|
||||
echo '<span id="status"><i class="fa fa-w fa-circle text-orange"></i> Unknown</span>'; /*
|
||||
} else {
|
||||
echo '<span id="status"><i class="fa fa-w fa-circle text-orange"></i> DNS service on port '.$pistatus.'</span>';
|
||||
}
|
||||
}*/
|
||||
?>
|
||||
<br/>
|
||||
<?php
|
||||
@@ -199,7 +199,7 @@
|
||||
|
||||
<li class="header text-uppercase">System</li>
|
||||
<!-- Tools -->
|
||||
<li class="menu-system treeview<?php if (in_array($scriptname, array('messages.php', 'gravity.php', 'queryads.php', 'auditlog.php', 'taillog.php', 'taillog-FTL.php', 'debug.php', 'network.php'))) { ?> active<?php } ?>">
|
||||
<li class="menu-system treeview<?php if (in_array($scriptname, array('messages.php', 'gravity.php', 'queryads.php', 'taillog.php', 'taillog-FTL.php', 'debug.php', 'network.php'))) { ?> active<?php } ?>">
|
||||
<a href="#">
|
||||
<i class="fa fa-fw menu-icon fa-tools"></i> <span>Tools</span>
|
||||
<span class="warning-count hidden"></span>
|
||||
@@ -227,12 +227,6 @@
|
||||
<i class="fa fa-fw menu-icon fa-search"></i> Search Adlists
|
||||
</a>
|
||||
</li>
|
||||
<!-- Audit log -->
|
||||
<li class="<?php if ($scriptname === 'auditlog.php') { ?> active<?php } ?>">
|
||||
<a href="auditlog.php">
|
||||
<i class="fa fa-fw menu-icon fa-balance-scale"></i> Audit log
|
||||
</a>
|
||||
</li>
|
||||
<!-- Tail pihole.log -->
|
||||
<li class="<?php if ($scriptname === 'taillog.php') { ?> active<?php } ?>">
|
||||
<a href="taillog.php">
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<?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 'password.php';
|
||||
if (!$auth) {
|
||||
exit('Not authorized');
|
||||
}
|
||||
|
||||
function formatLine($line)
|
||||
{
|
||||
$txt = preg_replace('/ dnsmasq\\[[0-9]*\\]/', '', htmlspecialchars($line));
|
||||
|
||||
if (strpos($line, 'blacklisted') || strpos($line, 'gravity blocked')) {
|
||||
$txt = '<b class="log-red">'.$txt.'</b>';
|
||||
} elseif (strpos($line, 'query[A') || strpos($line, 'query[DHCP')) {
|
||||
$txt = '<b>'.$txt.'</b>';
|
||||
} else {
|
||||
$txt = '<span class="text-muted">'.$txt.'</span>';
|
||||
}
|
||||
|
||||
return $txt;
|
||||
}
|
||||
|
||||
// Not using SplFileObject here, since direct
|
||||
// usage of f-streams will be much faster for
|
||||
// files as large as the pihole.log
|
||||
if (isset($_GET['FTL'])) {
|
||||
$file = fopen('/var/log/pihole/FTL.log', 'r');
|
||||
} else {
|
||||
$file = fopen('/var/log/pihole/pihole.log', 'r');
|
||||
}
|
||||
|
||||
if (!$file) {
|
||||
exit(json_encode(array('offset' => 0, 'lines' => array("Failed to open log file. Check permissions!\n"))));
|
||||
}
|
||||
|
||||
if (isset($_GET['offset'])) {
|
||||
$offset = intval($_GET['offset']);
|
||||
if ($offset > 0) {
|
||||
// If offset is grater then current file end it means the file was truncated (log rotation)
|
||||
fseek($file, 0, SEEK_END);
|
||||
if ($offset > ftell($file)) {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
// Seeks on the file pointer where we want to continue reading is known
|
||||
fseek($file, $offset);
|
||||
|
||||
$lines = array();
|
||||
while (!feof($file)) {
|
||||
array_push($lines, formatLine(fgets($file)));
|
||||
}
|
||||
|
||||
exit(json_encode(array('offset' => ftell($file), 'lines' => $lines)));
|
||||
}
|
||||
}
|
||||
|
||||
// Locate the current position of the file read/write pointer
|
||||
fseek($file, -1, SEEK_END);
|
||||
|
||||
// Add one to skip the very last "\n" in the log file
|
||||
exit(json_encode(array('offset' => ftell($file) + 1)));
|
||||
@@ -1,620 +0,0 @@
|
||||
<?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 'password.php';
|
||||
require 'auth.php'; // Also imports func.php
|
||||
require 'database.php';
|
||||
require 'savesettings.php';
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
if (!$auth) {
|
||||
exit('Not authorized');
|
||||
}
|
||||
check_csrf(isset($_POST['token']) ? $_POST['token'] : '');
|
||||
}
|
||||
|
||||
$db = SQLite3_connect(getGravityDBFilename(), SQLITE3_OPEN_READWRITE);
|
||||
|
||||
$flushed_tables = array();
|
||||
|
||||
function archive_add_file($path, $name, $subdir = '')
|
||||
{
|
||||
global $archive;
|
||||
if (file_exists($path.$name)) {
|
||||
$archive[$subdir.$name] = file_get_contents($path.$name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the contents of a table to the archive.
|
||||
*
|
||||
* @param $name string The name of the file in the archive to save the table to
|
||||
* @param $table string The table to export
|
||||
* @param $type integer Type of domains to store
|
||||
*/
|
||||
function archive_add_table($name, $table, $type = -1)
|
||||
{
|
||||
global $archive, $db;
|
||||
|
||||
if ($type > -1) {
|
||||
$querystr = "SELECT * FROM \"{$table}\" WHERE type = {$type};";
|
||||
} else {
|
||||
$querystr = "SELECT * FROM \"{$table}\";";
|
||||
}
|
||||
$results = $db->query($querystr);
|
||||
|
||||
// Return early without creating a file if the
|
||||
// requested table cannot be accessed
|
||||
if (is_null($results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$content = array();
|
||||
while ($row = $results->fetchArray(SQLITE3_ASSOC)) {
|
||||
array_push($content, $row);
|
||||
}
|
||||
|
||||
$archive[$name] = json_encode($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the contents of a table from an uploaded archive.
|
||||
*
|
||||
* @param $file object The file in the archive to restore the table from
|
||||
* @param $table string The table to import
|
||||
* @param $flush boolean Whether to flush the table before importing the archived data
|
||||
*
|
||||
* @return int Number of restored rows
|
||||
*/
|
||||
function archive_restore_table($file, $table, $flush = false)
|
||||
{
|
||||
global $db, $flushed_tables;
|
||||
|
||||
$json_string = file_get_contents($file);
|
||||
// Return early if we cannot extract the JSON string
|
||||
if (is_null($json_string)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$contents = json_decode($json_string, true);
|
||||
// Return early if we cannot decode the JSON string
|
||||
if (is_null($contents)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Flush table if requested. Only flush each table once, and only if it exists
|
||||
if ($flush && !in_array($table, $flushed_tables)) {
|
||||
$tableExists = $db->querySingle("SELECT name FROM sqlite_master WHERE type='table' AND name='".$table."';");
|
||||
if ($tableExists) {
|
||||
$db->exec('DELETE FROM "'.$table.'"');
|
||||
array_push($flushed_tables, $table);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare fields depending on the table we restore to
|
||||
if ($table === 'adlist') {
|
||||
$sql = 'INSERT OR IGNORE INTO adlist';
|
||||
$sql .= ' (id,address,enabled,date_added,comment)';
|
||||
$sql .= ' VALUES (:id,:address,:enabled,:date_added,:comment);';
|
||||
} elseif ($table === 'domain_audit') {
|
||||
$sql = 'INSERT OR IGNORE INTO domain_audit';
|
||||
$sql .= ' (id,domain,date_added)';
|
||||
$sql .= ' VALUES (:id,:domain,:date_added);';
|
||||
} elseif ($table === 'domainlist') {
|
||||
$sql = 'INSERT OR IGNORE INTO domainlist';
|
||||
$sql .= ' (id,domain,enabled,date_added,comment,type)';
|
||||
$sql .= ' VALUES (:id,:domain,:enabled,:date_added,:comment,:type);';
|
||||
} elseif ($table === 'group') {
|
||||
$sql = 'INSERT OR IGNORE INTO "group"';
|
||||
$sql .= ' (id,name,date_added,description)';
|
||||
$sql .= ' VALUES (:id,:name,:date_added,:description);';
|
||||
} elseif ($table === 'client') {
|
||||
$sql = 'INSERT OR IGNORE INTO client';
|
||||
$sql .= ' (id,ip,date_added,comment)';
|
||||
$sql .= ' VALUES (:id,:ip,:date_added,:comment);';
|
||||
} elseif ($table === 'domainlist_by_group') {
|
||||
$sql = 'INSERT OR IGNORE INTO domainlist_by_group';
|
||||
$sql .= ' (domainlist_id,group_id)';
|
||||
$sql .= ' VALUES (:domainlist_id,:group_id);';
|
||||
} elseif ($table === 'client_by_group') {
|
||||
$sql = 'INSERT OR IGNORE INTO client_by_group';
|
||||
$sql .= ' (client_id,group_id)';
|
||||
$sql .= ' VALUES (:client_id,:group_id);';
|
||||
} elseif ($table === 'adlist_by_group') {
|
||||
$sql = 'INSERT OR IGNORE INTO adlist_by_group';
|
||||
$sql .= ' (adlist_id,group_id)';
|
||||
$sql .= ' VALUES (:adlist_id,:group_id);';
|
||||
} else {
|
||||
if ($table === 'whitelist') {
|
||||
$type = 0;
|
||||
} elseif ($table === 'blacklist') {
|
||||
$type = 1;
|
||||
} elseif ($table === 'regex_whitelist') {
|
||||
$type = 2;
|
||||
} elseif ($table === 'regex_blacklist') {
|
||||
$type = 3;
|
||||
}
|
||||
|
||||
$sql = 'INSERT OR IGNORE INTO domainlist';
|
||||
$sql .= ' (id,domain,enabled,date_added,comment,type)';
|
||||
$sql .= " VALUES (:id,:domain,:enabled,:date_added,:comment,{$type});";
|
||||
$field = 'domain';
|
||||
}
|
||||
|
||||
// Prepare SQLite statement
|
||||
$stmt = $db->prepare($sql);
|
||||
|
||||
// Return early if we fail to prepare the SQLite statement
|
||||
if (!$stmt) {
|
||||
echo 'Failed to prepare statement for '.$table.' table.';
|
||||
echo $sql;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Loop over rows and inject the entries into the database
|
||||
$num = 0;
|
||||
foreach ($contents as $row) {
|
||||
// Limit max length for a domain entry to 253 chars
|
||||
if (isset($field) && strlen($row[$field]) > 253) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bind properties from JSON data
|
||||
// Note that only defined above are actually used
|
||||
// so even maliciously modified Teleporter files
|
||||
// cannot be dangerous in any way
|
||||
foreach ($row as $key => $value) {
|
||||
$type = gettype($value);
|
||||
$sqltype = null;
|
||||
|
||||
switch ($type) {
|
||||
case 'integer':
|
||||
$sqltype = SQLITE3_INTEGER;
|
||||
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
$sqltype = SQLITE3_TEXT;
|
||||
|
||||
break;
|
||||
|
||||
case 'NULL':
|
||||
$sqltype = SQLITE3_NULL;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$sqltype = 'UNK';
|
||||
}
|
||||
$stmt->bindValue(':'.$key, htmlentities($value), $sqltype);
|
||||
}
|
||||
|
||||
if ($stmt->execute() && $stmt->reset() && $stmt->clear()) {
|
||||
++$num;
|
||||
} else {
|
||||
$stmt->close();
|
||||
|
||||
return $num;
|
||||
}
|
||||
}
|
||||
|
||||
// Close database connection and return number or processed rows
|
||||
$stmt->close();
|
||||
|
||||
return $num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create table rows from an uploaded archive file.
|
||||
*
|
||||
* @param $file object The file in the archive to import
|
||||
* @param $table string The target table
|
||||
* @param $flush boolean Whether to flush the table before importing the archived data
|
||||
* @param $wildcardstyle boolean Whether to format the input domains in legacy wildcard notation
|
||||
*
|
||||
* @return int Number of processed rows from the imported file
|
||||
*/
|
||||
function archive_insert_into_table($file, $table, $flush = false, $wildcardstyle = false)
|
||||
{
|
||||
global $db;
|
||||
|
||||
$domains = array_filter(explode("\n", file_get_contents($file)));
|
||||
// Return early if we cannot extract the lines in the file
|
||||
if (is_null($domains)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Generate comment
|
||||
$prefix = 'phar:///tmp/';
|
||||
if (substr($file, 0, strlen($prefix)) == $prefix) {
|
||||
$file = substr($file, strlen($prefix));
|
||||
}
|
||||
$comment = 'Imported from '.$file;
|
||||
|
||||
// Determine table and type to import to
|
||||
$type = null;
|
||||
if ($table === 'whitelist') {
|
||||
$table = 'domainlist';
|
||||
$type = LISTTYPE_WHITELIST;
|
||||
} elseif ($table === 'blacklist') {
|
||||
$table = 'domainlist';
|
||||
$type = LISTTYPE_BLACKLIST;
|
||||
} elseif ($table === 'regex_blacklist') {
|
||||
$table = 'domainlist';
|
||||
$type = LISTTYPE_REGEX_BLACKLIST;
|
||||
} elseif ($table === 'domain_audit') {
|
||||
$table = 'domain_audit';
|
||||
$type = -1; // -1 -> not used inside add_to_table()
|
||||
} elseif ($table === 'adlist') {
|
||||
$table = 'adlist';
|
||||
$type = -1; // -1 -> not used inside add_to_table()
|
||||
}
|
||||
|
||||
// Flush table if requested
|
||||
if ($flush) {
|
||||
flush_table($table, $type);
|
||||
}
|
||||
|
||||
// Add domains to requested table
|
||||
return add_to_table($db, $table, $domains, $comment, $wildcardstyle, true, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush table if requested. This subroutine flushes each table only once.
|
||||
*
|
||||
* @param $table string The target table
|
||||
* @param $type integer Type of item to flush in table (applies only to domainlist table)
|
||||
*/
|
||||
function flush_table($table, $type = null)
|
||||
{
|
||||
global $db, $flushed_tables;
|
||||
|
||||
if (!in_array($table, $flushed_tables)) {
|
||||
if ($type !== null) {
|
||||
$sql = 'DELETE FROM "'.$table.'" WHERE type = '.$type;
|
||||
array_push($flushed_tables, $table.$type);
|
||||
} else {
|
||||
$sql = 'DELETE FROM "'.$table.'"';
|
||||
array_push($flushed_tables, $table);
|
||||
}
|
||||
$db->exec($sql);
|
||||
}
|
||||
}
|
||||
|
||||
function archive_add_directory($path, $subdir = '')
|
||||
{
|
||||
if ($dir = opendir($path)) {
|
||||
while (false !== ($entry = readdir($dir))) {
|
||||
if ($entry !== '.' && $entry !== '..') {
|
||||
archive_add_file($path, $entry, $subdir);
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
}
|
||||
}
|
||||
|
||||
function limit_length(&$item, $key)
|
||||
{
|
||||
// limit max length for a domain entry to 253 chars
|
||||
// return only a part of the string if it is longer
|
||||
$item = substr($item, 0, 253);
|
||||
}
|
||||
|
||||
function process_file($contents)
|
||||
{
|
||||
$domains = array_filter(explode("\n", $contents));
|
||||
// Walk array and apply a max string length
|
||||
// function to every member of the array of domains
|
||||
array_walk($domains, 'limit_length');
|
||||
|
||||
return $domains;
|
||||
}
|
||||
|
||||
function noun($num)
|
||||
{
|
||||
if ($num === 1) {
|
||||
return ' entry';
|
||||
}
|
||||
|
||||
return ' entries';
|
||||
}
|
||||
|
||||
if (isset($_POST['action'])) {
|
||||
if ($_FILES['zip_file']['name'] && $_POST['action'] == 'in') {
|
||||
$filename = $_FILES['zip_file']['name'];
|
||||
$source = $_FILES['zip_file']['tmp_name'];
|
||||
$type = mime_content_type($source);
|
||||
|
||||
// verify the file mime type
|
||||
$accepted_types = array('application/gzip', 'application/tar', 'application/x-compressed', 'application/x-gzip');
|
||||
$mime_valid = in_array($type, $accepted_types);
|
||||
|
||||
// verify the file extension (Looking for ".tar.gz" at the end of the file name)
|
||||
$ext = array_slice(explode('.', $filename), -2, 2);
|
||||
$ext_valid = strtolower($ext[0]) == 'tar' && strtolower($ext[1]) == 'gz' ? true : false;
|
||||
|
||||
if (!$ext_valid || !$mime_valid) {
|
||||
exit('The file you are trying to upload is not a .tar.gz file (filename: '.htmlentities($filename).', type: '.htmlentities($type).'). Please try again.');
|
||||
}
|
||||
|
||||
$fullfilename = sys_get_temp_dir().'/'.$filename;
|
||||
|
||||
if (!move_uploaded_file($source, $fullfilename)) {
|
||||
exit('Failed moving '.htmlentities($source).' to '.htmlentities($fullfilename));
|
||||
}
|
||||
|
||||
$archive = new PharData($fullfilename);
|
||||
|
||||
$importedsomething = false;
|
||||
$fullpiholerestart = false;
|
||||
$reloadsettingspage = false;
|
||||
|
||||
$flushtables = isset($_POST['flushtables']);
|
||||
|
||||
foreach (new RecursiveIteratorIterator($archive) as $file) {
|
||||
if (isset($_POST['blacklist']) && $file->getFilename() === 'blacklist.txt') {
|
||||
$num = archive_insert_into_table($file, 'blacklist', $flushtables);
|
||||
echo 'Processed blacklist (exact) ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['whitelist']) && $file->getFilename() === 'whitelist.txt') {
|
||||
$num = archive_insert_into_table($file, 'whitelist', $flushtables);
|
||||
echo 'Processed whitelist (exact) ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['regexlist']) && $file->getFilename() === 'regex.list') {
|
||||
$num = archive_insert_into_table($file, 'regex_blacklist', $flushtables);
|
||||
echo 'Processed blacklist (regex) ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
// Also try to import legacy wildcard list if found
|
||||
if (isset($_POST['regexlist']) && $file->getFilename() === 'wildcardblocking.txt') {
|
||||
$num = archive_insert_into_table($file, 'regex_blacklist', $flushtables, true);
|
||||
echo 'Processed blacklist (regex, wildcard style) ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['auditlog']) && $file->getFilename() === 'auditlog.list') {
|
||||
$num = archive_insert_into_table($file, 'domain_audit', $flushtables);
|
||||
echo 'Processed audit log ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['adlist']) && $file->getFilename() === 'adlists.list') {
|
||||
$num = archive_insert_into_table($file, 'adlist', $flushtables);
|
||||
echo 'Processed adlists ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['blacklist']) && $file->getFilename() === 'blacklist.exact.json') {
|
||||
$num = archive_restore_table($file, 'blacklist', $flushtables);
|
||||
echo 'Processed blacklist (exact) ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['regexlist']) && $file->getFilename() === 'blacklist.regex.json') {
|
||||
$num = archive_restore_table($file, 'regex_blacklist', $flushtables);
|
||||
echo 'Processed blacklist (regex) ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['whitelist']) && $file->getFilename() === 'whitelist.exact.json') {
|
||||
$num = archive_restore_table($file, 'whitelist', $flushtables);
|
||||
echo 'Processed whitelist (exact) ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['regex_whitelist']) && $file->getFilename() === 'whitelist.regex.json') {
|
||||
$num = archive_restore_table($file, 'regex_whitelist', $flushtables);
|
||||
echo 'Processed whitelist (regex) ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['adlist']) && $file->getFilename() === 'adlist.json') {
|
||||
$num = archive_restore_table($file, 'adlist', $flushtables);
|
||||
echo 'Processed adlist ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['auditlog']) && $file->getFilename() === 'domain_audit.json') {
|
||||
$num = archive_restore_table($file, 'domain_audit', $flushtables);
|
||||
echo 'Processed domain_audit ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['group']) && $file->getFilename() === 'group.json') {
|
||||
$num = archive_restore_table($file, 'group', $flushtables);
|
||||
echo 'Processed group ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['client']) && $file->getFilename() === 'client.json') {
|
||||
$num = archive_restore_table($file, 'client', $flushtables);
|
||||
echo 'Processed client ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['client']) && $file->getFilename() === 'client_by_group.json') {
|
||||
$num = archive_restore_table($file, 'client_by_group', $flushtables);
|
||||
echo 'Processed client group assignments ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if ((isset($_POST['whitelist']) || isset($_POST['regex_whitelist'])
|
||||
|| isset($_POST['blacklist']) || isset($_POST['regex_blacklist']))
|
||||
&& $file->getFilename() === 'domainlist_by_group.json') {
|
||||
$num = archive_restore_table($file, 'domainlist_by_group', $flushtables);
|
||||
echo 'Processed black-/whitelist group assignments ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['adlist']) && $file->getFilename() === 'adlist_by_group.json') {
|
||||
$num = archive_restore_table($file, 'adlist_by_group', $flushtables);
|
||||
echo 'Processed adlist group assignments ('.$num.noun($num).")<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if (isset($_POST['staticdhcpleases']) && $file->getFilename() === '04-pihole-static-dhcp.conf') {
|
||||
if ($flushtables) {
|
||||
$local_file = @fopen('/etc/dnsmasq.d/04-pihole-static-dhcp.conf', 'r+');
|
||||
if ($local_file !== false) {
|
||||
ftruncate($local_file, 0);
|
||||
fclose($local_file);
|
||||
}
|
||||
}
|
||||
$num = 0;
|
||||
$staticdhcpleases = process_file(file_get_contents($file));
|
||||
foreach ($staticdhcpleases as $lease) {
|
||||
list($mac, $ip, $hostname) = explode(',', $lease);
|
||||
$mac = formatMAC($mac);
|
||||
if (addStaticDHCPLease($mac, $ip, $hostname)) {
|
||||
++$num;
|
||||
}
|
||||
}
|
||||
|
||||
readStaticLeasesFile();
|
||||
echo 'Processed static DHCP leases ('.$num.noun($num).")<br>\n";
|
||||
if ($num > 0) {
|
||||
$importedsomething = true;
|
||||
$reloadsettingspage = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['localdnsrecords']) && $file->getFilename() === 'custom.list') {
|
||||
ob_start();
|
||||
$reload = 'false';
|
||||
if ($flushtables) {
|
||||
// Defined in func.php included via auth.php
|
||||
// passing reload="false" will not restart Pi-hole
|
||||
deleteAllCustomDNSEntries($reload);
|
||||
}
|
||||
$num = 0;
|
||||
$localdnsrecords = process_file(file_get_contents($file));
|
||||
foreach ($localdnsrecords as $record) {
|
||||
list($ip, $domain) = explode(' ', $record);
|
||||
if (addCustomDNSEntry($ip, $domain, $reload, false)) {
|
||||
++$num;
|
||||
}
|
||||
}
|
||||
ob_end_clean();
|
||||
echo 'Processed local DNS records ('.$num.noun($num).")<br>\n";
|
||||
if ($num > 0) {
|
||||
// we need a full pihole restart
|
||||
$fullpiholerestart = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['localcnamerecords']) && $file->getFilename() === '05-pihole-custom-cname.conf') {
|
||||
ob_start();
|
||||
$reload = 'false';
|
||||
if ($flushtables) {
|
||||
// Defined in func.php included via auth.php
|
||||
// passing reload="false" will not restart Pi-hole
|
||||
deleteAllCustomCNAMEEntries($reload);
|
||||
}
|
||||
|
||||
$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, $reload, false)) {
|
||||
++$num;
|
||||
}
|
||||
}
|
||||
ob_end_clean();
|
||||
echo 'Processed local CNAME records ('.$num.noun($num).")<br>\n";
|
||||
if ($num > 0) {
|
||||
// we need a full pihole restart
|
||||
$fullpiholerestart = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do we need a full restart of Pi-hole or reloading the lists?
|
||||
if ($fullpiholerestart) {
|
||||
pihole_execute('restartdns');
|
||||
} else {
|
||||
if ($importedsomething) {
|
||||
pihole_execute('restartdns reload');
|
||||
}
|
||||
}
|
||||
|
||||
unlink($fullfilename);
|
||||
echo 'OK';
|
||||
if ($reloadsettingspage) {
|
||||
echo "<br>\n<span data-forcereload></span>";
|
||||
}
|
||||
} else {
|
||||
exit('No file transmitted or parameter error.');
|
||||
}
|
||||
} else {
|
||||
$hostname = gethostname() ? str_replace('.', '_', gethostname()).'-' : '';
|
||||
$tarname = 'pi-hole-'.$hostname.'teleporter_'.date('Y-m-d_H-i-s').'.tar';
|
||||
$filename = $tarname.'.gz';
|
||||
$archive_file_name = tempnam(sys_get_temp_dir(), 'pihole_teleporter_'); // create a random file name in the system's tmp dir for the intermediate archive
|
||||
unlink($archive_file_name); // remove intermediate file created by tempnam()
|
||||
$archive_file_name .= '.tar'; // Append ".tar" extension
|
||||
|
||||
$archive = new PharData($archive_file_name);
|
||||
|
||||
if ($archive->isWritable() !== true) {
|
||||
exit('cannot open/create '.htmlentities($archive_file_name)."<br>\nPHP user: ".exec('whoami')."\n");
|
||||
}
|
||||
|
||||
archive_add_table('whitelist.exact.json', 'domainlist', LISTTYPE_WHITELIST);
|
||||
archive_add_table('whitelist.regex.json', 'domainlist', LISTTYPE_REGEX_WHITELIST);
|
||||
archive_add_table('blacklist.exact.json', 'domainlist', LISTTYPE_BLACKLIST);
|
||||
archive_add_table('blacklist.regex.json', 'domainlist', LISTTYPE_REGEX_BLACKLIST);
|
||||
archive_add_table('adlist.json', 'adlist');
|
||||
archive_add_table('domain_audit.json', 'domain_audit');
|
||||
archive_add_table('group.json', 'group');
|
||||
archive_add_table('client.json', 'client');
|
||||
|
||||
// Group linking tables
|
||||
archive_add_table('domainlist_by_group.json', 'domainlist_by_group');
|
||||
archive_add_table('adlist_by_group.json', 'adlist_by_group');
|
||||
archive_add_table('client_by_group.json', 'client_by_group');
|
||||
|
||||
archive_add_file('/etc/pihole/', 'setupVars.conf');
|
||||
archive_add_file('/etc/pihole/', 'dhcp.leases');
|
||||
archive_add_file('/etc/pihole/', 'custom.list');
|
||||
archive_add_file('/etc/pihole/', 'pihole-FTL.conf');
|
||||
archive_add_file('/etc/', 'hosts', 'etc/');
|
||||
archive_add_directory('/etc/dnsmasq.d/', 'dnsmasq.d/');
|
||||
|
||||
$archive->compress(Phar::GZ); // Creates a gzipped copy
|
||||
unlink($archive_file_name); // Unlink original tar file as it is not needed anymore
|
||||
$archive_file_name .= '.gz'; // Append ".gz" extension to ".tar"
|
||||
|
||||
header('Content-type: application/gzip');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Content-Disposition: attachment; filename='.$filename);
|
||||
header('Content-length: '.filesize($archive_file_name));
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
if (ob_get_length() > 0) {
|
||||
ob_end_clean();
|
||||
}
|
||||
readfile($archive_file_name);
|
||||
ignore_user_abort(true);
|
||||
unlink($archive_file_name);
|
||||
|
||||
exit;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
<?php
|
||||
|
||||
function checkUpdate($currentVersion, $latestVersion)
|
||||
{
|
||||
// This logic allows the local core version to be newer than the upstream version
|
||||
// The update indicator is only shown if the upstream version is NEWER
|
||||
if ($currentVersion !== 'vDev') {
|
||||
return version_compare($currentVersion, $latestVersion) < 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$versionsfile = '/etc/pihole/versions';
|
||||
|
||||
if (!is_readable($versionsfile)) {
|
||||
$core_branch = 'master';
|
||||
$core_current = 'N/A';
|
||||
$core_update = false;
|
||||
|
||||
$web_branch = 'master';
|
||||
$web_current = 'N/A';
|
||||
$web_update = false;
|
||||
|
||||
$FTL_current = 'N/A';
|
||||
$FTL_update = false;
|
||||
|
||||
$docker_current = 'N/A';
|
||||
$docker_update = false;
|
||||
} else {
|
||||
$versions = parse_ini_file($versionsfile);
|
||||
|
||||
// Get Pi-hole core branch / version / commit
|
||||
// Check if on a dev branch
|
||||
$core_branch = $versions['CORE_BRANCH'];
|
||||
if ($core_branch !== 'master') {
|
||||
$core_current = 'vDev';
|
||||
$core_commit = $versions['CORE_VERSION'];
|
||||
} else {
|
||||
$core_current = explode('-', $versions['CORE_VERSION'])[0];
|
||||
}
|
||||
|
||||
// Get Pi-hole web branch / version / commit
|
||||
$web_branch = $versions['WEB_BRANCH'];
|
||||
if ($web_branch !== 'master') {
|
||||
$web_current = 'vDev';
|
||||
$web_commit = $versions['WEB_VERSION'];
|
||||
} else {
|
||||
$web_current = explode('-', $versions['WEB_VERSION'])[0];
|
||||
}
|
||||
|
||||
// Get Pi-hole FTL (not a git repository)
|
||||
$FTL_branch = $versions['FTL_BRANCH'];
|
||||
if (substr($versions['FTL_VERSION'], 0, 4) === 'vDev') {
|
||||
$FTL_current = 'vDev';
|
||||
$FTL_commit = $versions['FTL_VERSION'];
|
||||
} else {
|
||||
$FTL_current = $versions['FTL_VERSION'];
|
||||
}
|
||||
|
||||
// Get Pi-hole Docker Tag, if available
|
||||
if (isset($versions['DOCKER_VERSION'])) {
|
||||
$docker_current = $versions['DOCKER_VERSION'];
|
||||
} else {
|
||||
$docker_current = '';
|
||||
}
|
||||
|
||||
// Get data from GitHub
|
||||
$core_latest = $versions['GITHUB_CORE_VERSION'];
|
||||
$web_latest = $versions['GITHUB_WEB_VERSION'];
|
||||
$FTL_latest = $versions['GITHUB_FTL_VERSION'];
|
||||
if (isset($versions['GITHUB_DOCKER_VERSION'])) {
|
||||
$docker_latest = $versions['GITHUB_DOCKER_VERSION'];
|
||||
} else {
|
||||
$docker_latest = '';
|
||||
}
|
||||
|
||||
$core_update = false;
|
||||
$web_update = false;
|
||||
$FTL_update = false;
|
||||
|
||||
// Version comparison
|
||||
if ($docker_current) {
|
||||
// It's a container: do not check for individual component updates
|
||||
if ($docker_current == 'nightly' || $docker_current == 'dev') {
|
||||
// Special container - no update messages
|
||||
$docker_update = false;
|
||||
} else {
|
||||
$docker_update = checkUpdate($docker_current, $docker_latest);
|
||||
}
|
||||
} else {
|
||||
// Components comparison
|
||||
$core_update = checkUpdate($core_current, $core_latest);
|
||||
$web_update = checkUpdate($web_current, $web_latest);
|
||||
$FTL_update = checkUpdate($FTL_current, $FTL_latest);
|
||||
|
||||
// Not a docker container
|
||||
$docker_update = false;
|
||||
}
|
||||
}
|
||||
|
||||
// URLs for the links
|
||||
$coreUrl = 'https://github.com/pi-hole/pi-hole/releases';
|
||||
$webUrl = 'https://github.com/pi-hole/AdminLTE/releases';
|
||||
$ftlUrl = 'https://github.com/pi-hole/FTL/releases';
|
||||
$dockerUrl = 'https://github.com/pi-hole/docker-pi-hole/releases';
|
||||
|
||||
// Version strings (encoded to avoid code execution)
|
||||
// If "vDev" show branch/commit, else show link
|
||||
if (isset($core_commit)) {
|
||||
$coreVersionStr = htmlentities($core_current.' ('.$core_branch.', '.$core_commit.')');
|
||||
} else {
|
||||
$coreVersionStr = '<a href="'.$coreUrl.'/'.rawurlencode($core_current).'" rel="noopener" target="_blank">'.htmlentities($core_current).'</a>';
|
||||
}
|
||||
|
||||
if (isset($web_commit)) {
|
||||
$webVersionStr = htmlentities($web_current.' ('.$web_branch.', '.$web_commit.')');
|
||||
} else {
|
||||
$webVersionStr = '<a href="'.$webUrl.'/'.rawurlencode($web_current).'" rel="noopener" target="_blank">'.htmlentities($web_current).'</a>';
|
||||
}
|
||||
|
||||
if (isset($FTL_commit)) {
|
||||
$ftlVersionStr = htmlentities($FTL_current.' ('.$FTL_branch.', '.$FTL_commit.')');
|
||||
} else {
|
||||
$ftlVersionStr = '<a href="'.$ftlUrl.'/'.rawurlencode($FTL_current).'" rel="noopener" target="_blank">'.htmlentities($FTL_current).'</a>';
|
||||
}
|
||||
|
||||
if ($docker_current) {
|
||||
if ($docker_current == 'dev' || $docker_current == 'nightly') {
|
||||
$dockerVersionStr = htmlentities($docker_current);
|
||||
} else {
|
||||
$dockerVersionStr = '<a href="'.$dockerUrl.'/'.rawurlencode($docker_current).'" rel="noopener" target="_blank">'.htmlentities($docker_current).'</a>';
|
||||
}
|
||||
} else {
|
||||
$dockerVersionStr = '';
|
||||
}
|
||||
1737
scripts/vendor/qrcode.php
vendored
1737
scripts/vendor/qrcode.php
vendored
File diff suppressed because it is too large
Load Diff
26
settings.php
26
settings.php
@@ -9,26 +9,24 @@
|
||||
*/
|
||||
|
||||
require 'scripts/pi-hole/php/header_authenticated.php';
|
||||
require 'scripts/pi-hole/php/savesettings.php';
|
||||
require_once 'scripts/pi-hole/php/FTL.php';
|
||||
|
||||
// Reread ini file as things might have been changed
|
||||
// DEFAULT_FTLCONFFILE is set in "scripts/pi-hole/php/FTL.php";
|
||||
$setupVars = parse_ini_file('/etc/pihole/setupVars.conf');
|
||||
$piholeFTLConf = piholeFTLConfig(DEFAULT_FTLCONFFILE, true);
|
||||
$piholeFTLConf = array();
|
||||
|
||||
// Handling of PHP internal errors
|
||||
$last_error = error_get_last();
|
||||
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']);
|
||||
}
|
||||
|
||||
/*
|
||||
// Timezone is set in docker via ENV otherwise get it from commandline
|
||||
$timezone = htmlspecialchars(getenv('TZ'));
|
||||
if (empty($timezone)) {
|
||||
$timezone = shell_exec("date +'%Z'");
|
||||
}
|
||||
|
||||
*/
|
||||
?>
|
||||
<style>
|
||||
.tooltip-inner {
|
||||
@@ -74,7 +72,7 @@ if (isset($setupVars['PIHOLE_INTERFACE'])) {
|
||||
}
|
||||
|
||||
// get the gateway IP
|
||||
$IPv4GW = getGateway()['ip'];
|
||||
//$IPv4GW = getGateway()['ip'];
|
||||
|
||||
// if the default gateway address is unknown or FTL is not running
|
||||
if ($IPv4GW == '0.0.0.0' || $IPv4GW == -1) {
|
||||
@@ -84,7 +82,7 @@ if ($IPv4GW == '0.0.0.0' || $IPv4GW == -1) {
|
||||
// DNS settings
|
||||
$DNSservers = array();
|
||||
$DNSactive = array();
|
||||
|
||||
/*
|
||||
$i = 1;
|
||||
while (isset($setupVars['PIHOLE_DNS_'.$i])) {
|
||||
if (isinserverlist($setupVars['PIHOLE_DNS_'.$i])) {
|
||||
@@ -104,7 +102,7 @@ while (isset($setupVars['PIHOLE_DNS_'.$i])) {
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
|
||||
*/
|
||||
if (isset($setupVars['DNS_FQDN_REQUIRED'])) {
|
||||
if ($setupVars['DNS_FQDN_REQUIRED']) {
|
||||
$DNSrequiresFQDN = true;
|
||||
@@ -238,7 +236,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array('sysadmin', 'dns', 'piho
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<?php
|
||||
$FTLpid = intval(pidofFTL());
|
||||
$FTLpid = 0;//intval(pidofFTL());
|
||||
if ($FTLpid !== 0) {
|
||||
$FTLversion = exec('/usr/bin/pihole-FTL version'); ?>
|
||||
<table class="table table-striped table-bordered nowrap">
|
||||
@@ -603,7 +601,7 @@ if ($DHCP) {
|
||||
}
|
||||
}
|
||||
|
||||
readStaticLeasesFile();
|
||||
//readStaticLeasesFile();
|
||||
?>
|
||||
<div class="col-md-12">
|
||||
<div class="box box-warning">
|
||||
@@ -1303,7 +1301,7 @@ if (isset($piholeFTLConf['RATE_LIMIT'])) {
|
||||
<!-- ######################################################### Teleporter ######################################################### -->
|
||||
<div id="teleporter" class="tab-pane fade<?php if ($tab === 'teleporter') { ?> in active<?php } ?>">
|
||||
<div class="row">
|
||||
<?php if (extension_loaded('Phar')) { ?>
|
||||
<?php /*if (extension_loaded('Phar')) { ?>
|
||||
<form role="form" method="post" id="takeoutform"
|
||||
action="scripts/pi-hole/php/teleporter.php"
|
||||
target="teleporter_iframe" enctype="multipart/form-data">
|
||||
@@ -1366,10 +1364,6 @@ if (isset($piholeFTLConf['RATE_LIMIT'])) {
|
||||
<input type="checkbox" name="group" id="tele_group" value="true" checked>
|
||||
<label for="tele_group">Group</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" name="auditlog" id="tele_auditlog" value="true" checked>
|
||||
<label for="tele_auditlog">Audit log</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" name="staticdhcpleases" id="tele_staticdhcpleases" value="true" checked>
|
||||
<label for="tele_staticdhcpleases">Static DHCP Leases</label>
|
||||
@@ -1443,7 +1437,7 @@ if (isset($piholeFTLConf['RATE_LIMIT'])) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<?php } else */{ ?>
|
||||
<div class="col-lg-12">
|
||||
<div class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
|
||||
Reference in New Issue
Block a user