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:
DL6ER
2023-01-31 20:10:53 +01:00
parent ef5dc34de6
commit 4cf7a82713
34 changed files with 66 additions and 6888 deletions

View File

@@ -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
View File

@@ -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);
}

View File

@@ -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];
}
}

View File

@@ -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);
}

View File

@@ -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';
?>

View File

@@ -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>

View File

@@ -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;

View File

@@ -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);
}
});
});

View File

@@ -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,
};
})();

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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
}

View File

@@ -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');
}

View File

@@ -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');
}

View File

@@ -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;
}

View File

@@ -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));
}

View File

@@ -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 {

View File

@@ -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'); */
}

View File

@@ -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');
*/
}

View File

@@ -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", '<------', $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

View File

@@ -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>

View File

@@ -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!');
}

View File

@@ -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!');
}

View File

@@ -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'];

View File

@@ -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);
}
}

View 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);
}

View File

@@ -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;
}

View File

@@ -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">

View File

@@ -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)));

View File

@@ -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;
}

View File

@@ -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 = '';
}

File diff suppressed because it is too large Load Diff

View File

@@ -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">