diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index d9341eea93..7202fb3e07 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -7,20 +7,8 @@ updates:
day: saturday
time: "10:00"
open-pull-requests-limit: 10
- target-branch: devel
+ target-branch: development
versioning-strategy: increase
- ignore:
- - dependency-name: postcss
- versions:
- - 8.2.6
- - 8.2.7
- - dependency-name: xo
- versions:
- - 0.38.1
- - dependency-name: autoprefixer
- versions:
- - 10.2.3
- - 10.2.4
reviewers:
- "pi-hole/web-maintainers"
- package-ecosystem: github-actions
@@ -30,6 +18,16 @@ updates:
day: saturday
time: "10:00"
open-pull-requests-limit: 10
- target-branch: devel
+ target-branch: development
+ reviewers:
+ - "pi-hole/web-maintainers"
+- package-ecosystem: composer
+ directory: "/"
+ schedule:
+ interval: weekly
+ day: saturday
+ time: "10:00"
+ open-pull-requests-limit: 10
+ target-branch: development
reviewers:
- "pi-hole/web-maintainers"
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 113a6312fd..5f702bf705 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -4,13 +4,13 @@ on:
push:
branches:
- master
- - devel
+ - development
- "!dependabot/**"
pull_request:
# The branches below must be a subset of the branches above
branches:
- master
- - devel
+ - development
schedule:
- cron: "0 0 * * 0"
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3.0.2
+ uses: actions/checkout@v4.1.1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml
index ab60520e23..9e1e9258b8 100644
--- a/.github/workflows/codespell.yml
+++ b/.github/workflows/codespell.yml
@@ -11,10 +11,10 @@ jobs:
steps:
-
name: Checkout repository
- uses: actions/checkout@v3.0.2
+ uses: actions/checkout@v4.1.1
-
name: Spell-Checking
uses: codespell-project/actions-codespell@master
with:
ignore_words_file: .codespellignore
- skip: ./scripts/vendor,./style/vendor
+ skip: ./scripts/vendor,./style/vendor,./package.json,./package-lock.json,./composer.json,./composer.lock
diff --git a/.github/workflows/editorconfig-checker.yml b/.github/workflows/editorconfig-checker.yml
index f919df158e..db28dc97a6 100644
--- a/.github/workflows/editorconfig-checker.yml
+++ b/.github/workflows/editorconfig-checker.yml
@@ -9,6 +9,6 @@ jobs:
name: editorconfig-checker
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3.0.2
+ - uses: actions/checkout@v4.1.1
- uses: editorconfig-checker/action-editorconfig-checker@main
- run: editorconfig-checker
diff --git a/.github/workflows/merge-conflict.yml b/.github/workflows/merge-conflict.yml
new file mode 100644
index 0000000000..438b23c6d6
--- /dev/null
+++ b/.github/workflows/merge-conflict.yml
@@ -0,0 +1,21 @@
+name: "Check for merge conflicts"
+on:
+ # So that PRs touching the same files as the push are updated
+ push:
+ # So that the `dirtyLabel` is removed if conflicts are resolve
+ # We recommend `pull_request_target` so that github secrets are available.
+ # In `pull_request` we wouldn't be able to change labels of fork PRs
+ pull_request_target:
+ types: [synchronize]
+
+jobs:
+ main:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check if PRs are have merge conflicts
+ uses: eps1lon/actions-label-merge-conflict@v2.1.0
+ with:
+ dirtyLabel: "Merge Conflicts"
+ repoToken: "${{ secrets.GITHUB_TOKEN }}"
+ commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
+ commentOnClean: "Conflicts have been resolved."
diff --git a/.github/workflows/php-cs-fixer.yml b/.github/workflows/php-cs-fixer.yml
index 55eedc483d..35604906a7 100644
--- a/.github/workflows/php-cs-fixer.yml
+++ b/.github/workflows/php-cs-fixer.yml
@@ -6,7 +6,7 @@ jobs:
name: PHP-CS-Fixer
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3.0.2
+ - uses: actions/checkout@v4.1.1
- name: PHP-CS-Fixer
uses: docker://oskarstark/php-cs-fixer-ga
with:
diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml
index 61dfd8a4cf..23e257780b 100644
--- a/.github/workflows/phpstan.yml
+++ b/.github/workflows/phpstan.yml
@@ -8,27 +8,14 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3.0.2
- - name: Change PHP version
- run: |
- sudo update-alternatives --set php /usr/bin/php8.0
- sudo update-alternatives --set phar /usr/bin/phar8.0
- sudo update-alternatives --set phpdbg /usr/bin/phpdbg8.0
- sudo update-alternatives --set php-cgi /usr/bin/php-cgi8.0
- sudo update-alternatives --set phar.phar /usr/bin/phar.phar8.0
- php -version
- - name: Validate composer.json and composer.lock
- run: composer validate
- - name: Cache Composer packages
- id: composer-cache
- uses: actions/cache@v3.0.6
+ - uses: actions/checkout@v4.1.1
+
+ - name: Install composer
+ uses: php-actions/composer@v6
+
+ - name: Run PHPStan
+ uses: php-actions/phpstan@v3
with:
- path: vendor
- key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
- restore-keys: |
- ${{ runner.os }}-php-
- - name: Install dependencies
- if: steps.composer-cache.outputs.cache-hit != 'true'
- run: composer install --prefer-dist --no-progress --no-suggest
- - name: Run phpstan
- run: composer run-script phpstan
+ configuration: phpstan.neon.dist
+ memory_limit: 256M
+
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 803c33779a..055d59c519 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -4,23 +4,45 @@ on:
schedule:
- cron: '0 8 * * *'
workflow_dispatch:
+ issue_comment:
-jobs:
- stale:
+env:
+ stale_label: stale
+jobs:
+ stale_action:
+ if: github.event_name != 'issue_comment'
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- - uses: actions/stale@v5.1.1
+ - uses: actions/stale@v8.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 30
days-before-close: 5
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days.'
- stale-issue-label: 'stale'
- exempt-issue-labels: 'internal, Fixed In Next Release, Bug'
+ stale-issue-label: '${{ env.stale_label }}'
+ exempt-issue-labels: 'internal, Fixed In Next Release, Bug, never-stale'
exempt-all-issue-assignees: true
operations-per-run: 300
close-issue-reason: 'not_planned'
+
+ remove_stale:
+ # trigger "stale" removal immediately when stale issues are commented on
+ # we need to explicitly check that the trigger does not run on comment on a PR as
+ # 'issue_comment' triggers on issues AND PR comments
+ # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment-on-issues-only-or-pull-requests-only
+ if: ${{ !github.event.issue.pull_request && github.event_name != 'schedule' }}
+ permissions:
+ contents: read # for actions/checkout
+ issues: write # to edit issues label
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4.1.1
+ - name: Remove 'stale' label
+ run: gh issue edit ${{ github.event.issue.number }} --remove-label ${{ env.stale_label }}
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/stale_pr.yml b/.github/workflows/stale_pr.yml
new file mode 100644
index 0000000000..17cd31e380
--- /dev/null
+++ b/.github/workflows/stale_pr.yml
@@ -0,0 +1,35 @@
+name: Close stale PR
+# This action will add a `stale` label and close immediately every PR that meets the following conditions:
+# - it is already marked with "merge conflict" label
+# - there was no update/comment on the PR in the last 30 days.
+
+on:
+ schedule:
+ - cron: '0 10 * * *'
+ workflow_dispatch:
+
+jobs:
+ stale:
+
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+
+ steps:
+ - uses: actions/stale@v8.0.0
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ # Do not automatically mark PR/issue as stale
+ days-before-stale: -1
+ # Override 'days-before-stale' for PR only
+ days-before-pr-stale: 30
+ # Close PRs immediately, after marking them 'stale'
+ days-before-pr-close: 0
+ # only run the action on merge conflict PR
+ any-of-labels: 'Merge Conflicts'
+ exempt-pr-labels: 'internal, never-stale, ON HOLD, WIP'
+ exempt-all-pr-assignees: true
+ operations-per-run: 300
+ stale-pr-message: ''
+ close-pr-message: 'Existing merge conflicts have not been addressed. This PR is considered abandoned.'
diff --git a/.github/workflows/sync-back-to-dev.yml b/.github/workflows/sync-back-to-dev.yml
index 7e166a24a2..b391a14306 100644
--- a/.github/workflows/sync-back-to-dev.yml
+++ b/.github/workflows/sync-back-to-dev.yml
@@ -11,17 +11,8 @@ jobs:
name: Syncing branches
steps:
- name: Checkout
- uses: actions/checkout@v3.0.2
+ uses: actions/checkout@v4.1.1
- name: Opening pull request
- id: pull
- uses: tretuna/sync-branches@1.4.0
- with:
+ run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal'
+ env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- FROM_BRANCH: 'master'
- TO_BRANCH: 'devel'
- - name: Label the pull request to ignore for release note generation
- uses: actions-ecosystem/action-add-labels@v1.1.3
- with:
- labels: internal
- repo: ${{ github.repository }}
- number: ${{ steps.pull.outputs.PULL_REQUEST_NUMBER }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 59838b8df1..0ebafb7e8a 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -3,7 +3,7 @@ name: Tests
on:
push:
branches:
- - devel
+ - development
- master
pull_request:
branches:
@@ -19,10 +19,10 @@ jobs:
steps:
- name: Clone repository
- uses: actions/checkout@v3.0.2
+ uses: actions/checkout@v4.1.1
- name: Set up Node.js
- uses: actions/setup-node@v3.4.1
+ uses: actions/setup-node@v4.0.0
with:
node-version: "16.x"
cache: npm
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index c1e2ed6b91..dfcb0c3ead 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -22,6 +22,7 @@
->setRules(array(
'@Symfony' => true,
'array_syntax' => array('syntax' => 'long'),
+ 'yoda_style' => array('equal' => false, 'identical' => false, 'less_and_greater' => false, 'always_move_variable' => false),
))
->setLineEnding(PHP_EOL)
->setFinder($finder)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4a308af579..de32ff715c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -31,8 +31,8 @@ When requesting or submitting new features, first consider whether it might be u
## Technical Requirements
-- Submit Pull Requests to the **devel branch only**.
-- Before Submitting your Pull Request, merge `devel` with your new branch and fix any conflicts. (Make sure you don't break anything in development!)
+- Submit Pull Requests to the **development branch only**.
+- Before Submitting your Pull Request, merge `development` with your new branch and fix any conflicts. (Make sure you don't break anything in development!)
- Commit Unix line endings.
- Please use the Pi-hole brand: **Pi-hole** (Take a special look at the capitalized 'P' and a low 'h' with a hyphen)
- (Optional fun) keep to the theme of Star Trek/black holes/gravity.
diff --git a/README.md b/README.md
index 1de6119bc0..ad323bab6b 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
-
+
+
## Detailed graphs and doughnut charts
diff --git a/api.php b/api.php
index 7176e243c4..6fcf8768b3 100644
--- a/api.php
+++ b/api.php
@@ -8,8 +8,8 @@
*/
$api = true;
-require_once 'scripts/pi-hole/php/FTL.php';
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();
@@ -71,10 +71,28 @@
$branches = array('core_branch' => $core_branch,
'web_branch' => $web_branch,
'FTL_branch' => $FTL_branch, );
+ if (isset($versions['DOCKER_VERSION'])) {
+ // Docker info is available only inside containers
+ $updates['docker_update'] = $docker_update;
+ $current['docker_current'] = $docker_current;
+ $latest['docker_latest'] = $docker_latest;
+ }
+
$data = array_merge($data, $updates);
$data = array_merge($data, $current);
$data = array_merge($data, $latest);
$data = array_merge($data, $branches);
+} elseif (isset($_GET['setTempUnit'])) {
+ $unit = strtolower($_GET['setTempUnit']);
+ if ($unit == 'c' || $unit == 'f' || $unit == 'k') {
+ pihole_execute('-a -'.$unit);
+ $result = 'success';
+ } else {
+ // invalid unit
+ $result = 'error';
+ }
+
+ $data = array_merge($data, array('result' => $result));
} elseif (isset($_GET['list'])) {
if (!$auth) {
exit('Not authorized!');
@@ -86,22 +104,22 @@
switch ($_GET['list']) {
case 'black':
- $_POST['type'] = ListType::blacklist;
+ $_POST['type'] = LISTTYPE_BLACKLIST;
break;
case 'regex_black':
- $_POST['type'] = ListType::regex_blacklist;
+ $_POST['type'] = LISTTYPE_REGEX_BLACKLIST;
break;
case 'white':
- $_POST['type'] = ListType::whitelist;
+ $_POST['type'] = LISTTYPE_WHITELIST;
break;
case 'regex_white':
- $_POST['type'] = ListType::regex_whitelist;
+ $_POST['type'] = LISTTYPE_REGEX_WHITELIST;
break;
diff --git a/api_FTL.php b/api_FTL.php
index f1b7c237d9..927bbbba7b 100644
--- a/api_FTL.php
+++ b/api_FTL.php
@@ -19,7 +19,7 @@
$data['version'] = 3;
}
-if (isset($_GET['status'])) {
+if (isset($_GET['status']) && $auth) {
$return = callFTLAPI('stats');
if (array_key_exists('FTLnotrunning', $return)) {
$data = array('FTLnotrunning' => true);
@@ -32,7 +32,7 @@
}
}
-if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) {
+if ((isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) && $auth) {
require_once 'scripts/pi-hole/php/gravity.php';
$return = callFTLAPI('stats');
@@ -43,12 +43,12 @@
foreach ($return as $line) {
$tmp = explode(' ', $line);
- if ('domains_being_blocked' === $tmp[0] && !is_numeric($tmp[1]) || 'status' === $tmp[0]) {
+ 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 ('ads_percentage_today' !== $tmp[0]) {
+ if ($tmp[0] !== 'ads_percentage_today') {
$stats[$tmp[0]] = number_format($tmp[1]);
} else {
$stats[$tmp[0]] = number_format($tmp[1], 1, '.', '');
@@ -64,30 +64,37 @@
}
if (isset($_GET['getMaxlogage']) && $auth) {
- $return = callFTLAPI('maxlogage');
- if (array_key_exists('FTLnotrunning', $return)) {
+ $maxlogage = getMaxlogage();
+
+ if ($maxlogage < 0) {
+ // FTL is offline
$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));
+ $data = array_merge($data, array('maxlogage' => $maxlogage));
}
}
-if (isset($_GET['overTimeData10mins'])) {
+if (isset($_GET['overTimeData10mins']) && $auth) {
+ $maxlogage = getMaxlogage();
+
$return = callFTLAPI('overTime');
- if (array_key_exists('FTLnotrunning', $return)) {
+ if (array_key_exists('FTLnotrunning', $return) || $maxlogage < 0) {
$data = array('FTLnotrunning' => true);
} else {
$domains_over_time = array();
$ads_over_time = array();
+
+ // Use current time and maxlogage to limit the time range
+ $time_end = time();
+ $time_start = $time_end - ($maxlogage * 3600);
+
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]);
+ $timeslot = intval($tmp[0]);
+ if ($timeslot >= $time_start && $timeslot <= $time_end) {
+ $domains_over_time[$timeslot] = intval($tmp[1]);
+ $ads_over_time[$timeslot] = intval($tmp[2]);
+ }
}
$result = array(
@@ -100,7 +107,7 @@
}
if (isset($_GET['topItems']) && $auth) {
- if ('audit' === $_GET['topItems']) {
+ if ($_GET['topItems'] === 'audit') {
$return = callFTLAPI('top-domains for audit');
} elseif (is_numeric($_GET['topItems'])) {
$return = callFTLAPI('top-domains ('.$_GET['topItems'].')');
@@ -114,7 +121,7 @@
$top_queries = array();
foreach ($return as $line) {
$tmp = explode(' ', $line);
- if (2 == count($tmp)) {
+ if (count($tmp) == 2) {
$tmp[2] = '';
}
$domain = utf8_encode($tmp[2]);
@@ -122,7 +129,7 @@
}
}
- if ('audit' === $_GET['topItems']) {
+ if ($_GET['topItems'] === 'audit') {
$return = callFTLAPI('top-ads for audit');
} elseif (is_numeric($_GET['topItems'])) {
$return = callFTLAPI('top-ads ('.$_GET['topItems'].')');
@@ -216,7 +223,7 @@
}
if (isset($_GET['getForwardDestinations']) && $auth) {
- if ('unsorted' === $_GET['getForwardDestinations']) {
+ if ($_GET['getForwardDestinations'] === 'unsorted') {
$return = callFTLAPI('forward-dest unsorted');
} else {
$return = callFTLAPI('forward-dest');
@@ -282,7 +289,7 @@
} elseif (isset($_GET['domain'])) {
// Get specific domain only
$return = callFTLAPI('getallqueries-domain '.$_GET['domain']);
- } elseif (isset($_GET['client']) && (isset($_GET['type']) && 'blocked' === $_GET['type'])) {
+ } 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'])) {
@@ -401,16 +408,25 @@
}
if (isset($_GET['overTimeDataClients']) && $auth) {
- $return = callFTLAPI('ClientsoverTime');
+ $maxlogage = getMaxlogage();
- if (array_key_exists('FTLnotrunning', $return)) {
+ $return = callFTLAPI('ClientsoverTime');
+ if (array_key_exists('FTLnotrunning', $return) || $maxlogage < 0) {
$data = array('FTLnotrunning' => true);
} else {
$over_time = array();
+
+ // Use current time and maxlogage to limit the time range
+ $time_end = time();
+ $time_start = $time_end - ($maxlogage * 3600);
+
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]);
+ $timeslot = intval($tmp[0]);
+ if ($timeslot >= $time_start && $timeslot <= $time_end) {
+ $over_time[$timeslot][$i] = floatval($tmp[$i + 1]);
+ }
}
}
$result = array('over_time' => $over_time);
diff --git a/api_db.php b/api_db.php
index 1ab47ff1b6..f060d47bbb 100644
--- a/api_db.php
+++ b/api_db.php
@@ -8,9 +8,10 @@
*/
$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/password.php';
require 'scripts/pi-hole/php/auth.php';
require_once 'scripts/pi-hole/php/func.php';
check_cors();
@@ -30,16 +31,16 @@
$network = array();
$results = $db->query('SELECT * FROM network');
- while (false !== $results && $res = $results->fetchArray(SQLITE3_ASSOC)) {
+ 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 (false !== $network_addresses && $network_address = $network_addresses->fetchArray(SQLITE3_ASSOC)) {
+ while ($network_addresses !== false && $network_address = $network_addresses->fetchArray(SQLITE3_ASSOC)) {
array_push($res['ip'], $network_address['ip']);
- if (null !== $network_address['name']) {
+ if ($network_address['name'] !== null) {
array_push($res['name'], utf8_encode($network_address['name']));
} else {
array_push($res['name'], '');
@@ -58,7 +59,7 @@
if (isset($_GET['getAllQueries']) && $auth) {
$allQueries = array();
- if ('empty' !== $_GET['getAllQueries']) {
+ if ($_GET['getAllQueries'] !== 'empty') {
$from = intval($_GET['from']);
$until = intval($_GET['until']);
@@ -75,17 +76,18 @@
$dbquery .= ' status, reply_type, reply_time, dnssec';
$dbquery .= ' FROM query_storage q';
$dbquery .= ' WHERE timestamp >= :from AND timestamp <= :until ';
- if (isset($_GET['types'])) {
- $types = $_GET['types'];
- if (1 === preg_match('/^[0-9]+(?:,[0-9]+)*$/', $types)) {
+ 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 IN ('.$types.') ';
+ $dbquery .= 'AND status NOT IN ('.$excludedStatus.') ';
} else {
- exit('Error. Selector types specified using an invalid format.');
+ exit('Error. Selector status specified using an invalid format.');
}
}
$dbquery .= 'ORDER BY timestamp ASC';
@@ -190,7 +192,7 @@
$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)'.$limit.' GROUP by domain order by count(domain) desc limit 20');
+ $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();
@@ -293,11 +295,11 @@
$limit = '';
if (isset($_GET['from'], $_GET['until'])) {
- $limit = ' AND timestamp >= :from AND timestamp <= :until';
+ $limit = 'timestamp >= :from AND timestamp <= :until';
} elseif (isset($_GET['from']) && !isset($_GET['until'])) {
- $limit = ' AND timestamp >= :from';
+ $limit = 'timestamp >= :from';
} elseif (!isset($_GET['from']) && isset($_GET['until'])) {
- $limit = ' AND timestamp <= :until';
+ $limit = 'timestamp <= :until';
}
$interval = 600;
@@ -313,8 +315,24 @@
$from = intval((intval($_GET['from']) / $interval) * $interval);
$until = intval((intval($_GET['until']) / $interval) * $interval);
- // Count permitted queries in intervals
- $stmt = $db->prepare('SELECT (timestamp/:interval)*:interval interval, COUNT(*) FROM queries WHERE (status != 0 )'.$limit.' GROUP by interval ORDER by 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);
@@ -323,57 +341,45 @@
// Parse the DB result into graph data, filling in missing interval sections with zero
function parseDBData($results, $interval, $from, $until)
{
- $data = array();
+ $domains = array();
+ $blocked = array();
$first_db_timestamp = -1;
if (!is_bool($results)) {
// Read in the data
while ($row = $results->fetchArray()) {
- // $data[timestamp] = value_in_this_interval
- $data[$row[0]] = intval($row[1]);
- if (-1 === $first_db_timestamp) {
+ $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. Hence, we re-align the FROM
- // timestamp to avoid unaligned holes appearing as additional
- // (incorrect) data points
+ // 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, $data)) {
- $data[$i] = 0;
+ if (!array_key_exists($i, $domains)) {
+ $domains[$i] = 0;
+ $blocked[$i] = 0;
}
}
- return $data;
+ return array('domains_over_time' => $domains, 'ads_over_time' => $blocked);
}
- $domains = parseDBData($results, $interval, $from, $until);
-
- $result = array('domains_over_time' => $domains);
- $data = array_merge($data, $result);
-
- // Count blocked queries in intervals
- $stmt = $db->prepare('SELECT (timestamp/:interval)*:interval interval, COUNT(*) FROM queries WHERE status IN (1,4,5,6,7,8,9,10,11)'.$limit.' GROUP by interval ORDER by interval');
- $stmt->bindValue(':from', $from, SQLITE3_INTEGER);
- $stmt->bindValue(':until', $until, SQLITE3_INTEGER);
- $stmt->bindValue(':interval', $interval, SQLITE3_INTEGER);
- $results = $stmt->execute();
-
- $addomains = parseDBData($results, $interval, $from, $until);
-
- $result = array('ads_over_time' => $addomains);
- $data = array_merge($data, $result);
+ $over_time = parseDBData($results, $interval, $from, $until);
+ $data = array_merge($data, $over_time);
}
if (isset($_GET['status']) && $auth) {
$extra = ';';
- if (isset($_GET['ignore']) && 'DNSMASQ_WARN' === $_GET['ignore']) {
+ if (isset($_GET['ignore']) && $_GET['ignore'] === 'DNSMASQ_WARN') {
$extra = "WHERE type != 'DNSMASQ_WARN';";
}
$results = $db->query('SELECT COUNT(*) FROM message '.$extra);
@@ -389,14 +395,14 @@ function parseDBData($results, $interval, $from, $until)
if (isset($_GET['messages']) && $auth) {
$extra = ';';
- if (isset($_GET['ignore']) && 'DNSMASQ_WARN' === $_GET['ignore']) {
+ if (isset($_GET['ignore']) && $_GET['ignore'] === 'DNSMASQ_WARN') {
$extra = "WHERE type != 'DNSMASQ_WARN';";
}
$messages = array();
$results = $db->query('SELECT * FROM message '.$extra);
- while (false !== $results && $res = $results->fetchArray(SQLITE3_ASSOC)) {
+ 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) {
diff --git a/auditlog.php b/auditlog.php
index b1e7cfb35a..3c311ac604 100644
--- a/auditlog.php
+++ b/auditlog.php
@@ -8,7 +8,7 @@
* Please see LICENSE file for your rights under this license.
*/
-require 'scripts/pi-hole/php/header.php';
+require 'scripts/pi-hole/php/header_authenticated.php';
?>
@@ -79,7 +79,7 @@
-
+
@@ -32,7 +32,7 @@
Queries Blocked
+Total Queries
Queries Blocked (Wildcards)
+Queries Blocked
Queries Total
+Queries Blocked (Wildcards)
Queries Blocked
+Percentage Blocked
After installing Pi-hole for the first time, a password is generated and displayed + to the user. The password cannot be retrieved later on, but it is possible to set + a new password (or explicitly disable the password by setting an empty password) + using the command +
+sudo pihole -a -p+