├── .codespellignore ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md ├── dco.yml ├── dependabot.yml ├── release.yml └── workflows │ ├── codeql.yml │ ├── codespell.yml │ ├── editorconfig-checker │ ├── editorconfig-checker.yml │ ├── merge-conflict.yml │ ├── php-cs-fixer.yml │ ├── phpstan.yml │ ├── stale.yml │ ├── stale_pr.yml │ ├── sync-back-to-dev.yml │ └── test.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── .stickler.yml ├── .yamllint.conf ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── api.php ├── api_FTL.php ├── api_db.php ├── api_speedtest.php ├── auditlog.php ├── cname_records.php ├── composer.json ├── composer.lock ├── db_graph.php ├── db_lists.php ├── db_queries.php ├── debug.php ├── dns_records.php ├── gravity.php ├── groups-adlists.php ├── groups-clients.php ├── groups-domains.php ├── groups.php ├── img ├── boxed-bg-dark.png ├── boxed-bg.png ├── favicons │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── manifest.json │ ├── mstile-150x150.png │ └── safari-pinned-tab.svg ├── logo.svg ├── pihole_icon.svg ├── st-chart.png └── st-pref.png ├── index.php ├── login.php ├── logout.php ├── messages.php ├── network.php ├── package-lock.json ├── package.json ├── phpstan.neon.dist ├── queries.php ├── queryads.php ├── scripts ├── pi-hole │ ├── js │ │ ├── auditlog.js │ │ ├── customcname.js │ │ ├── customdns.js │ │ ├── db_graph.js │ │ ├── db_lists.js │ │ ├── db_queries.js │ │ ├── debug.js │ │ ├── footer.js │ │ ├── gravity.js │ │ ├── groups-adlists.js │ │ ├── groups-clients.js │ │ ├── groups-domains.js │ │ ├── groups.js │ │ ├── index.js │ │ ├── ip-address-sorting.js │ │ ├── messages.js │ │ ├── network.js │ │ ├── queries.js │ │ ├── queryads.js │ │ ├── restartdns.js │ │ ├── settings.js │ │ ├── speedresults.js │ │ ├── speedtest.js │ │ ├── taillog-FTL.js │ │ ├── taillog.js │ │ └── utils.js │ └── php │ │ ├── FTL.php │ │ ├── api_token.php │ │ ├── auth.php │ │ ├── customcname.php │ │ ├── customdns.php │ │ ├── database.php │ │ ├── debug.php │ │ ├── footer.php │ │ ├── func.php │ │ ├── gravity.php │ │ ├── gravity.sh.php │ │ ├── groups.php │ │ ├── header.php │ │ ├── header_authenticated.php │ │ ├── message.php │ │ ├── network.php │ │ ├── password.php │ │ ├── persistentlogin_token.php │ │ ├── queryads.php │ │ ├── savesettings.php │ │ ├── sidebar.php │ │ ├── tailLog.php │ │ ├── teleporter.php │ │ ├── theme.php │ │ └── update_checker.php └── vendor │ ├── LICENSE │ ├── adminlte.min.js │ ├── bootstrap-notify.min.js │ ├── bootstrap-select.min.js │ ├── bootstrap-select.min.js.map │ ├── bootstrap-toggle.min.js │ ├── bootstrap-toggle.min.js.map │ ├── chart.min.js │ ├── chartjs-adapter-moment.js │ ├── chartjs-adapter-moment.min.js.map │ ├── datatables.buttons.min.js │ ├── datatables.min.js │ ├── datatables.select.min.js │ ├── daterangepicker.min.js │ ├── daterangepicker.min.js.map │ ├── jquery.confirm.min.js │ ├── jquery.min.js │ ├── moment.min.js │ ├── qrcode.php │ └── select2.min.js ├── settings.php ├── speedtest.php ├── style ├── pi-hole.css ├── themes │ ├── default-auto.css │ ├── default-dark.css │ ├── default-darker.css │ ├── default-light.css │ ├── high-contrast-dark.css │ ├── high-contrast.css │ ├── lcars-picard.css │ └── lcars.css └── vendor │ ├── AdminLTE.min.css │ ├── LICENSE │ ├── SourceSansPro │ ├── SourceSansPro.css │ ├── source-sans-pro-v13-latin-300.woff │ ├── source-sans-pro-v13-latin-300.woff2 │ ├── source-sans-pro-v13-latin-300italic.woff │ ├── source-sans-pro-v13-latin-300italic.woff2 │ ├── source-sans-pro-v13-latin-600.woff │ ├── source-sans-pro-v13-latin-600.woff2 │ ├── source-sans-pro-v13-latin-600italic.woff │ ├── source-sans-pro-v13-latin-600italic.woff2 │ ├── source-sans-pro-v13-latin-700.woff │ ├── source-sans-pro-v13-latin-700.woff2 │ ├── source-sans-pro-v13-latin-italic.woff │ ├── source-sans-pro-v13-latin-italic.woff2 │ ├── source-sans-pro-v13-latin-regular.woff │ └── source-sans-pro-v13-latin-regular.woff2 │ ├── animate.min.css │ ├── bootstrap-select.min.css │ ├── bootstrap-toggle.min.css │ ├── bootstrap │ ├── css │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ │ └── bootstrap.min.js │ ├── datatables.min.css │ ├── datatables_extensions.min.css │ ├── daterangepicker.min.css │ ├── font-awesome │ └── js │ │ └── all.min.js │ ├── fonts │ ├── antonio │ │ ├── antonio-v1-latin-ext_latin-100.woff │ │ ├── antonio-v1-latin-ext_latin-100.woff2 │ │ ├── antonio-v1-latin-ext_latin-700.woff │ │ ├── antonio-v1-latin-ext_latin-700.woff2 │ │ ├── antonio-v1-latin-ext_latin-regular.woff │ │ ├── antonio-v1-latin-ext_latin-regular.woff2 │ │ └── antonio.css │ └── ubuntu-mono │ │ ├── ubuntu-mono-v10-latin-ext_latin-700.woff │ │ ├── ubuntu-mono-v10-latin-ext_latin-700.woff2 │ │ ├── ubuntu-mono-v10-latin-ext_latin-700italic.woff │ │ ├── ubuntu-mono-v10-latin-ext_latin-700italic.woff2 │ │ ├── ubuntu-mono-v10-latin-ext_latin-italic.woff │ │ ├── ubuntu-mono-v10-latin-ext_latin-italic.woff2 │ │ ├── ubuntu-mono-v10-latin-ext_latin-regular.woff │ │ ├── ubuntu-mono-v10-latin-ext_latin-regular.woff2 │ │ └── ubuntu-mono.css │ ├── icheck-bootstrap.min.css │ ├── icheck-material.min.css │ ├── js-warn.css │ └── select2.min.css ├── taillog-FTL.php └── taillog.php /.codespellignore: -------------------------------------------------------------------------------- 1 | ede 2 | EDE 3 | doubleclick 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://editorconfig.org/ 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # A newline ending every file 7 | [*] 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 4 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | 14 | [*.css] 15 | indent_size = 2 16 | 17 | [*.js] 18 | indent_size = 2 19 | 20 | [package.json] 21 | indent_size = 2 22 | 23 | [.yamllint.conf] 24 | indent_size = 2 25 | 26 | [*.yml] 27 | indent_size = 2 28 | 29 | [*.md] 30 | indent_size = 2 31 | 32 | # Ignore paths 33 | [**/vendor/**] 34 | charset = unset 35 | end_of_line = unset 36 | insert_final_newline = unset 37 | trim_trailing_whitespace = unset 38 | indent_style = unset 39 | indent_size = unset 40 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **In raising this issue, I confirm the following:** `{please fill the checkboxes, e.g: [X]}` 2 | 3 | - [] I have read and understood the [contributors guide](https://github.com/pi-hole/AdminLTE/blob/master/CONTRIBUTING.md). 4 | - [] The issue I am reporting can be _replicated_. 5 | - [] The issue I am reporting isn't a duplicate (see [FAQs](https://github.com/pi-hole/pi-hole/wiki/FAQs), [closed issues](https://github.com/pi-hole/AdminLTE/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), and [open issues](https://github.com/pi-hole/AdminLTE/issues)). 6 | 7 | **How familiar are you with the the source code relevant to this issue?:** 8 | 9 | `{Replace this with a number from 1 to 10. 1 being not familiar, and 10 being very familiar}` 10 | 11 | --- 12 | 13 | **Expected behavior:** 14 | 15 | `{A detailed description of what you expect to see}` 16 | 17 | **Actual behavior:** 18 | 19 | `{A detailed description and/or screenshots of what you do see}` 20 | 21 | **Steps to reproduce:** 22 | 23 | `{Detailed steps of how we can reproduce this}` 24 | 25 | **Debug token provided by [uploading `pihole -d` log](https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738#debug):** 26 | 27 | `{Alphanumeric token}` 28 | 29 | **Troubleshooting undertaken, and/or other relevant information:** 30 | 31 | `{Steps of what you have done to fix this}` 32 | 33 | > - `{Please delete this quoted section when opening your issue}` 34 | > - You must follow the template instructions. Failure to do so will result in your issue being closed. 35 | > - Please [submit any feature requests here](https://discourse.pi-hole.net/c/feature-requests), so it is votable and trackable by the community. 36 | > - Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time. 37 | > - Detail helps us understand and resolve an issue quicker, but please ensure it's relevant. 38 | > - _This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._ 39 | -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | require: 2 | members: false 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | day: saturday 8 | time: "10:00" 9 | open-pull-requests-limit: 10 10 | target-branch: devel 11 | versioning-strategy: increase 12 | reviewers: 13 | - "pi-hole/web-maintainers" 14 | - package-ecosystem: github-actions 15 | directory: "/" 16 | schedule: 17 | interval: weekly 18 | day: saturday 19 | time: "10:00" 20 | open-pull-requests-limit: 10 21 | target-branch: devel 22 | reviewers: 23 | - "pi-hole/web-maintainers" 24 | - package-ecosystem: composer 25 | directory: "/" 26 | schedule: 27 | interval: weekly 28 | day: saturday 29 | time: "10:00" 30 | open-pull-requests-limit: 10 31 | target-branch: devel 32 | reviewers: 33 | - "pi-hole/web-maintainers" 34 | 35 | # As above, but for development-v6 36 | - package-ecosystem: npm 37 | directory: "/" 38 | schedule: 39 | interval: weekly 40 | day: saturday 41 | time: "10:00" 42 | open-pull-requests-limit: 10 43 | target-branch: development-v6 44 | versioning-strategy: increase 45 | reviewers: 46 | - "pi-hole/web-maintainers" 47 | - package-ecosystem: github-actions 48 | directory: "/" 49 | schedule: 50 | interval: weekly 51 | day: saturday 52 | time: "10:00" 53 | open-pull-requests-limit: 10 54 | target-branch: development-v6 55 | reviewers: 56 | - "pi-hole/web-maintainers" 57 | - package-ecosystem: composer 58 | directory: "/" 59 | schedule: 60 | interval: weekly 61 | day: saturday 62 | time: "10:00" 63 | open-pull-requests-limit: 10 64 | target-branch: development-v6 65 | reviewers: 66 | - "pi-hole/web-maintainers" 67 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - internal 5 | authors: 6 | - dependabot 7 | - github-actions 8 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - devel 8 | - "!dependabot/**" 9 | pull_request: 10 | # The branches below must be a subset of the branches above 11 | branches: 12 | - master 13 | - devel 14 | schedule: 15 | - cron: "0 0 * * 0" 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4.1.1 25 | # Initializes the CodeQL tools for scanning. 26 | - name: Initialize CodeQL 27 | uses: github/codeql-action/init@v2 28 | with: 29 | languages: "javascript" 30 | 31 | - name: Autobuild 32 | uses: github/codeql-action/autobuild@v2 33 | 34 | - name: Perform CodeQL Analysis 35 | uses: github/codeql-action/analyze@v2 36 | -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | name: Codespell 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened, ready_for_review] 6 | 7 | jobs: 8 | spell-check: 9 | if: github.event.pull_request.draft == false 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout repository 14 | uses: actions/checkout@v4.1.1 15 | - 16 | name: Spell-Checking 17 | uses: codespell-project/actions-codespell@master 18 | with: 19 | ignore_words_file: .codespellignore 20 | skip: ./scripts/vendor,./style/vendor,./package.json,./package-lock.json,./composer.json,./composer.lock 21 | -------------------------------------------------------------------------------- /.github/workflows/editorconfig-checker: -------------------------------------------------------------------------------- 1 | name: editorconfig-checker 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | jobs: 8 | build: 9 | name: editorconfig-checker 10 | runs-on: ubuntu-20.04 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: editorconfig-checker/action-editorconfig-checker@main 14 | - run: editorconfig-checker 15 | -------------------------------------------------------------------------------- /.github/workflows/editorconfig-checker.yml: -------------------------------------------------------------------------------- 1 | name: editorconfig-checker 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | jobs: 8 | build: 9 | name: editorconfig-checker 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4.1.1 13 | - uses: editorconfig-checker/action-editorconfig-checker@main 14 | - run: editorconfig-checker 15 | -------------------------------------------------------------------------------- /.github/workflows/merge-conflict.yml: -------------------------------------------------------------------------------- 1 | name: "Check for merge conflicts" 2 | on: 3 | # So that PRs touching the same files as the push are updated 4 | push: 5 | # So that the `dirtyLabel` is removed if conflicts are resolve 6 | # We recommend `pull_request_target` so that github secrets are available. 7 | # In `pull_request` we wouldn't be able to change labels of fork PRs 8 | pull_request_target: 9 | types: [synchronize] 10 | 11 | jobs: 12 | main: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check if PRs are have merge conflicts 16 | uses: eps1lon/actions-label-merge-conflict@v2.1.0 17 | with: 18 | dirtyLabel: "Merge Conflicts" 19 | repoToken: "${{ secrets.GITHUB_TOKEN }}" 20 | commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request." 21 | commentOnClean: "Conflicts have been resolved." 22 | -------------------------------------------------------------------------------- /.github/workflows/php-cs-fixer.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/php-cs-fixer.yml 2 | on: [push, pull_request] 3 | name: Lint 4 | jobs: 5 | php-cs-fixer: 6 | name: PHP-CS-Fixer 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4.1.1 10 | - name: PHP-CS-Fixer 11 | uses: docker://oskarstark/php-cs-fixer-ga 12 | with: 13 | args: --diff --dry-run 14 | -------------------------------------------------------------------------------- /.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: PHPStan 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4.1.1 12 | 13 | - name: Install composer 14 | uses: php-actions/composer@v6 15 | 16 | - name: Run PHPStan 17 | uses: php-actions/phpstan@v3 18 | with: 19 | configuration: phpstan.neon.dist 20 | memory_limit: 256M 21 | 22 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues 2 | 3 | on: 4 | schedule: 5 | - cron: '0 8 * * *' 6 | workflow_dispatch: 7 | issue_comment: 8 | 9 | env: 10 | stale_label: stale 11 | 12 | jobs: 13 | stale_action: 14 | if: github.event_name != 'issue_comment' 15 | runs-on: ubuntu-latest 16 | permissions: 17 | issues: write 18 | 19 | steps: 20 | - uses: actions/stale@v8.0.0 21 | with: 22 | repo-token: ${{ secrets.GITHUB_TOKEN }} 23 | days-before-stale: 30 24 | days-before-close: 5 25 | 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.' 26 | stale-issue-label: '${{ env.stale_label }}' 27 | exempt-issue-labels: 'internal, Fixed In Next Release, Bug, never-stale' 28 | exempt-all-issue-assignees: true 29 | operations-per-run: 300 30 | close-issue-reason: 'not_planned' 31 | 32 | remove_stale: 33 | # trigger "stale" removal immediately when stale issues are commented on 34 | # we need to explicitly check that the trigger does not run on comment on a PR as 35 | # 'issue_comment' triggers on issues AND PR comments 36 | # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment-on-issues-only-or-pull-requests-only 37 | if: ${{ !github.event.issue.pull_request && github.event_name != 'schedule' }} 38 | permissions: 39 | contents: read # for actions/checkout 40 | issues: write # to edit issues label 41 | runs-on: ubuntu-latest 42 | steps: 43 | - name: Checkout 44 | uses: actions/checkout@v4.1.1 45 | - name: Remove 'stale' label 46 | run: gh issue edit ${{ github.event.issue.number }} --remove-label ${{ env.stale_label }} 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | -------------------------------------------------------------------------------- /.github/workflows/stale_pr.yml: -------------------------------------------------------------------------------- 1 | name: Close stale PR 2 | # This action will add a `stale` label and close immediately every PR that meets the following conditions: 3 | # - it is already marked with "merge conflict" label 4 | # - there was no update/comment on the PR in the last 30 days. 5 | 6 | on: 7 | schedule: 8 | - cron: '0 10 * * *' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | stale: 13 | 14 | runs-on: ubuntu-latest 15 | permissions: 16 | issues: write 17 | pull-requests: write 18 | 19 | steps: 20 | - uses: actions/stale@v8.0.0 21 | with: 22 | repo-token: ${{ secrets.GITHUB_TOKEN }} 23 | # Do not automatically mark PR/issue as stale 24 | days-before-stale: -1 25 | # Override 'days-before-stale' for PR only 26 | days-before-pr-stale: 30 27 | # Close PRs immediately, after marking them 'stale' 28 | days-before-pr-close: 0 29 | # only run the action on merge conflict PR 30 | any-of-labels: 'Merge Conflicts' 31 | exempt-pr-labels: 'internal, never-stale, ON HOLD, WIP' 32 | exempt-all-pr-assignees: true 33 | operations-per-run: 300 34 | stale-pr-message: '' 35 | close-pr-message: 'Existing merge conflicts have not been addressed. This PR is considered abandoned.' 36 | -------------------------------------------------------------------------------- /.github/workflows/sync-back-to-dev.yml: -------------------------------------------------------------------------------- 1 | name: Sync Back to Development 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | sync-branches: 10 | runs-on: ubuntu-latest 11 | name: Syncing branches 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4.1.1 15 | - name: Opening pull request 16 | run: gh pr create -B devel -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal' 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - devel 7 | - master 8 | pull_request: 9 | branches: 10 | - "**" 11 | 12 | env: 13 | FORCE_COLOR: 2 14 | 15 | jobs: 16 | run: 17 | name: Node 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Clone repository 22 | uses: actions/checkout@v4.1.1 23 | 24 | - name: Set up Node.js 25 | uses: actions/setup-node@v4.0.0 26 | with: 27 | node-version: "16.x" 28 | cache: npm 29 | 30 | - name: Install npm dependencies 31 | run: npm ci 32 | 33 | - name: Run tests 34 | run: npm run testpr 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | .idea 3 | /nbproject/private/ 4 | /nbproject/ 5 | /node_modules/ 6 | TODO 7 | *.zip 8 | test.html 9 | *.log 10 | .php-cs-fixer.cache 11 | 12 | # Intellij IDEA Project Files 13 | *.iml 14 | *.ipr 15 | *.iws 16 | 17 | # vim 18 | *.swp 19 | 20 | # Composer 21 | /vendor/ 22 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | ignoreDotFiles(false) 15 | ->ignoreVCSIgnored(true) 16 | ->exclude('scripts/vendor') 17 | ->in(__DIR__) 18 | ; 19 | 20 | $config = new PhpCsFixer\Config(); 21 | $config 22 | ->setRules(array( 23 | '@Symfony' => true, 24 | 'array_syntax' => array('syntax' => 'long'), 25 | 'yoda_style' => array('equal' => false, 'identical' => false, 'less_and_greater' => false, 'always_move_variable' => false), 26 | )) 27 | ->setLineEnding(PHP_EOL) 28 | ->setFinder($finder) 29 | ; 30 | 31 | return $config; 32 | -------------------------------------------------------------------------------- /.stickler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | linters: 3 | yamllint: 4 | config: ./.yamllint.conf 5 | remarklint: 6 | files: 7 | ignore: 8 | - 'scripts/vendor/*' 9 | - 'style/vendor/*' 10 | -------------------------------------------------------------------------------- /.yamllint.conf: -------------------------------------------------------------------------------- 1 | rules: 2 | line-length: disable 3 | document-start: disable 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | _This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._ 2 | 3 | # Contributors Guide 4 | 5 | Please read and understand the contribution guide before creating an issue or pull request. 6 | 7 | ## Etiquette 8 | 9 | - Our goal for Pi-hole is **stability before features**. This means we focus on squashing critical bugs before adding new features. Often, we can do both in tandem, but bugs will take priority over a new feature. 10 | - Pi-hole is open source and [powered by donations](https://pi-hole.net/donate/), and as such, we give our **free time** to build, maintain, and **provide user support** for this project. It would be extremely unfair for us to suffer abuse or anger for our hard work, so please take a moment to consider that. 11 | - Please be considerate towards the developers and other users when raising issues or presenting pull requests. 12 | - Respect our decision(s), and do not be upset or abusive if your submission is not used. 13 | 14 | ## Viability 15 | 16 | When requesting or submitting new features, first consider whether it might be useful to others. Open source projects are used by many people, who may have entirely different needs to your own. Think about whether or not your feature is likely to be used by other users of the project. 17 | 18 | ## Procedure 19 | 20 | **Before filing an issue:** 21 | 22 | - Attempt to replicate and **document** the problem, to ensure that it wasn't a coincidental incident. 23 | - Check to make sure your feature suggestion isn't already present within the project. 24 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. 25 | - Check the pull requests tab to ensure that the feature isn't already in progress. 26 | 27 | **Before submitting a pull request:** 28 | 29 | - Check the codebase to ensure that your feature doesn't already exist. 30 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 31 | 32 | ## Technical Requirements 33 | 34 | - Submit Pull Requests to the **devel branch only**. 35 | - Before Submitting your Pull Request, merge `devel` with your new branch and fix any conflicts. (Make sure you don't break anything in development!) 36 | - Commit Unix line endings. 37 | - Please use the Pi-hole brand: **Pi-hole** (Take a special look at the capitalized 'P' and a low 'h' with a hyphen) 38 | - (Optional fun) keep to the theme of Star Trek/black holes/gravity. 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # Pi-hole Speedtest Modded Web 4 | 5 | [![Join the chat at https://gitter.im/pihole-speedtest/community](https://badges.gitter.im/pihole-speedtest/community.svg)](https://gitter.im/pihole-speedtest/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/TW9TfyM) 6 | 7 | Test your connection speed directly in the Pi-hole web interface! 8 | 9 |
10 | 11 | --- 12 | 13 | Please go to the [main repository](https://github.com/arevindh/pihole-speedtest) for more information, including (un)installation instructions, pull requests, and issues. 14 | 15 | ## Disclaimer 16 | 17 | We are not affiliated with or endorsed by [Pi-hole](https://github.com/pi-hole/web) 18 | -------------------------------------------------------------------------------- /auditlog.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 18 | 19 |
20 |
21 |
22 |
23 |

Allowed queries

24 |
25 | 26 |
27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
DomainHitsActions
39 |
40 |
41 |
42 | 43 |
44 | 45 |
46 | 47 |
48 | 49 | 50 |
51 |
52 |
53 |

Blocked queries

54 |
55 | 56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
DomainHitsActions
69 |
70 |
71 |
72 | 73 |
74 | 75 |
76 | 77 |
78 | 79 |
80 | 81 | 82 | 83 | 84 | 87 | -------------------------------------------------------------------------------- /cname_records.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 19 | 20 | 21 |
22 |
23 |
24 | 25 |
26 |

27 | Add a new CNAME record 28 |

29 |
30 | 31 |
32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 |
41 |
42 |
43 | 51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 |
59 |

60 | List of local CNAME records 61 |

62 |
63 | 64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
DomainTargetAction
74 | 75 |
76 | 77 |
78 | 79 |
80 |
81 | 82 | 83 | 84 | 87 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pi-hole/adminlte", 3 | "type": "project", 4 | "description": "Pi-hole Dashboard for stats and more", 5 | "require": { 6 | "php": ">=5.4", 7 | "phpstan/phpstan": "1.*" 8 | }, 9 | "license": "EUPL-1.2", 10 | "minimum-stability": "stable", 11 | "autoload": { 12 | "files": [ 13 | "scripts/vendor/qrcode.php" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "6b761e5cfc801bc1942ad8d0d675ddb9", 8 | "packages": [ 9 | { 10 | "name": "phpstan/phpstan", 11 | "version": "1.10.42", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/phpstan/phpstan.git", 15 | "reference": "fc2316508de5453140b5cb3d3f8683a33e92f26a" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc2316508de5453140b5cb3d3f8683a33e92f26a", 20 | "reference": "fc2316508de5453140b5cb3d3f8683a33e92f26a", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": "^7.2|^8.0" 25 | }, 26 | "conflict": { 27 | "phpstan/phpstan-shim": "*" 28 | }, 29 | "bin": [ 30 | "phpstan", 31 | "phpstan.phar" 32 | ], 33 | "type": "library", 34 | "autoload": { 35 | "files": [ 36 | "bootstrap.php" 37 | ] 38 | }, 39 | "notification-url": "https://packagist.org/downloads/", 40 | "license": [ 41 | "MIT" 42 | ], 43 | "description": "PHPStan - PHP Static Analysis Tool", 44 | "keywords": [ 45 | "dev", 46 | "static analysis" 47 | ], 48 | "support": { 49 | "docs": "https://phpstan.org/user-guide/getting-started", 50 | "forum": "https://github.com/phpstan/phpstan/discussions", 51 | "issues": "https://github.com/phpstan/phpstan/issues", 52 | "security": "https://github.com/phpstan/phpstan/security/policy", 53 | "source": "https://github.com/phpstan/phpstan-src" 54 | }, 55 | "funding": [ 56 | { 57 | "url": "https://github.com/ondrejmirtes", 58 | "type": "github" 59 | }, 60 | { 61 | "url": "https://github.com/phpstan", 62 | "type": "github" 63 | }, 64 | { 65 | "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", 66 | "type": "tidelift" 67 | } 68 | ], 69 | "time": "2023-11-17T15:26:57+00:00" 70 | } 71 | ], 72 | "packages-dev": [], 73 | "aliases": [], 74 | "minimum-stability": "stable", 75 | "stability-flags": [], 76 | "prefer-stable": false, 77 | "prefer-lowest": false, 78 | "platform": { 79 | "php": ">=5.4" 80 | }, 81 | "platform-dev": [], 82 | "plugin-api-version": "2.6.0" 83 | } 84 | -------------------------------------------------------------------------------- /db_graph.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 |
18 |
19 |
20 |
21 |

22 | Select date and time range 23 |

24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 | 33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 |
42 |
43 | 46 |
47 |
48 | 49 |
50 |
51 |
52 |
53 |

54 | Queries over the selected time period 55 |

56 |
57 |
58 |
59 |
60 |
61 | 62 |
63 |
64 |
65 |
66 | 69 |
70 |
71 |
72 | 73 | 74 | 75 | 78 | -------------------------------------------------------------------------------- /db_lists.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 18 | 19 |
20 |
21 |
22 |
23 |

24 | Select date and time range 25 |

26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 | 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 43 | 46 | 47 | 54 |
55 |
56 |
57 |
58 |

Top Domains

59 |
60 | 61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
DomainHitsFrequency
74 |
75 |
76 | 79 | 80 |
81 | 82 |
83 | 84 |
85 |
86 |
87 |

Top Blocked Domains

88 |
89 | 90 |
91 |
92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
DomainHitsFrequency
103 |
104 |
105 | 108 | 109 |
110 | 111 |
112 | 113 |
114 |
115 |
116 |

Top Clients

117 |
118 | 119 |
120 |
121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |
ClientRequestsFrequency
132 |
133 |
134 | 137 | 138 |
139 | 140 |
141 | 142 |
143 | 144 | 145 | 146 | 147 | 150 | -------------------------------------------------------------------------------- /debug.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 |
18 |

Options:

19 |
20 |
21 | 22 | 25 |
26 |
27 | 28 | 31 |
32 |
33 |
34 |
35 |

Once you click this button a debug log will be generated and can automatically be uploaded if we detect a working internet connection.

36 | 37 | 38 | 39 | 40 | 41 | 44 | -------------------------------------------------------------------------------- /dns_records.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 19 | 20 | 21 |
22 |
23 |
24 | 25 |
26 |

27 | Add a new domain/IP combination 28 |

29 |
30 | 31 |
32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 |
41 |
42 |
43 | 55 |
56 |
57 |
58 | 59 |
60 |
61 |
62 |
63 |

64 | List of local DNS domains 65 |

66 |
67 | 68 |
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
DomainIPAction
78 | 79 |
80 | 81 |
82 | 83 |
84 |
85 | 86 | 87 | 88 | 89 | 92 | -------------------------------------------------------------------------------- /gravity.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | 18 | 19 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /groups-adlists.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 18 | 19 | 20 |
21 |
22 |
23 | 24 |
25 |

26 | Add a new adlist 27 |

28 |
29 | 30 |
31 |
32 |
33 | 34 | 35 |
36 |
37 | 38 | 39 |
40 |
41 |
42 | 51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |

59 | List of adlists 60 |

61 |
62 | 63 |
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
IDAddressStatusCommentGroup assignment 
78 | 79 |
80 | 81 |
82 | 83 |
84 |
85 | 86 | 87 | 88 | 89 | 90 | 93 | -------------------------------------------------------------------------------- /groups-clients.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 18 | 19 | 20 |
21 |
22 |
23 | 24 |
25 |

26 | Add a new client 27 |

28 |
29 | 30 |
31 |
32 |
33 | 34 | 37 |
38 |
39 | 40 | 41 |
42 |
43 |
44 |
45 |

You can select an existing client or add a custom one by typing into the field above and confirming your entry with .

46 |

Clients may be described either by their IP addresses (IPv4 and IPv6 are supported), 47 | IP subnets (CIDR notation, like 192.168.2.0/24), 48 | their MAC addresses (like 12:34:56:78:9A:BC), 49 | by their hostnames (like localhost), or by the interface they are connected to (prefaced with a colon, like :eth0). 50 |

51 |

Note that client recognition by IP addresses (incl. subnet ranges) are preferred over MAC address, host name or interface recognition as 52 | the two latter will only be available after some time. 53 | Furthermore, MAC address recognition only works for devices at most one networking hop away from your Pi-hole. 54 |

55 |
56 |
57 |
58 | 61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

69 | List of configured clients 70 |

71 |
72 | 73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
IDClientCommentGroup assignment 
86 | 87 |
88 | 89 |
90 | 91 |
92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 102 | -------------------------------------------------------------------------------- /groups.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 18 | 19 | 20 |
21 |
22 |
23 | 24 |
25 |

26 | Add a new group 27 |

28 |
29 | 30 |
31 |
32 |
33 | 34 | 35 |
36 |
37 | 38 | 39 |
40 |
41 |
42 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |

58 | List of groups 59 |

60 |
61 | 62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
IDNameStatusDescription 
75 | 76 |
77 | 78 |
79 | 80 |
81 |
82 | 83 | 84 | 85 | 86 | 87 | 90 | -------------------------------------------------------------------------------- /img/boxed-bg-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/boxed-bg-dark.png -------------------------------------------------------------------------------- /img/boxed-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/boxed-bg.png -------------------------------------------------------------------------------- /img/favicons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/favicons/android-chrome-192x192.png -------------------------------------------------------------------------------- /img/favicons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/favicons/android-chrome-512x512.png -------------------------------------------------------------------------------- /img/favicons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/favicons/apple-touch-icon.png -------------------------------------------------------------------------------- /img/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /img/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /img/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/favicons/favicon.ico -------------------------------------------------------------------------------- /img/favicons/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Pi-hole Admin Console", 3 | "short_name": "Pi-hole", 4 | "icons": [ 5 | { 6 | "src": "android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#367fa9", 17 | "background_color": "#367fa9", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /img/favicons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/favicons/mstile-150x150.png -------------------------------------------------------------------------------- /img/favicons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /img/pihole_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | -------------------------------------------------------------------------------- /img/st-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/st-chart.png -------------------------------------------------------------------------------- /img/st-pref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arevindh/AdminLTE/90c66d6faaa3cf055bfab172e4479f617262f029/img/st-pref.png -------------------------------------------------------------------------------- /login.php: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 |
25 | 31 | 32 | 33 |
34 | 83 | 84 | 89 |
90 |
91 |
92 | 93 |
94 |
95 | Donate if you found this useful. 96 |
97 |
98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /logout.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 19 | 20 |
21 |
22 |
23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
ID TimeTypeMessageData1Data2Data3Data4Data5 
42 |

Note: If errors are shown, you can generate a debug log, which will do a thorough Pi-hole evaluation.

43 |
44 | 45 |
46 | 47 |
48 |
49 | 50 | 51 | 52 | 55 | -------------------------------------------------------------------------------- /network.php: -------------------------------------------------------------------------------- 1 | 12 | 13 |
14 |
15 |
16 |
17 |

Network overview

18 |
19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
IDIP addressHardware addressInterfaceHostnameFirst seenLast QueryNumber of queriesUses Pi-holeAction
IDIP addressHardware addressInterfaceHostnameFirst seenLast QueryNumber of queriesUses Pi-holeAction
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
just now... to ...24 hours ago> 24 hours agoDevice does not use Pi-hole
61 |
62 | 63 |
64 | 65 |
66 |
67 | 68 | 69 | 70 | 71 | 72 | 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AdminLTE", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/pi-hole/AdminLTE.git" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "EUPL-1.2", 13 | "bugs": { 14 | "url": "https://github.com/pi-hole/AdminLTE/issues" 15 | }, 16 | "homepage": "https://github.com/pi-hole/AdminLTE#readme", 17 | "scripts": { 18 | "prefix": "postcss \"style/*.css\" \"style/themes/*.css\" --use autoprefixer --no-map --replace", 19 | "prettier:check": "prettier -l \"style/*.css\" \"style/themes/*.css\" \"scripts/pi-hole/**/*.js\"", 20 | "prettier:fix": "prettier --write \"style/*.css\" \"style/themes/*.css\" \"scripts/pi-hole/**/*.js\"", 21 | "xo": "xo", 22 | "xo:fix": "npm run xo -- --fix", 23 | "test": "npm run prettier:check && npm run xo", 24 | "testpr": "npm run prettier:fix && git diff --ws-error-highlight=all --color=always --exit-code && npm run xo" 25 | }, 26 | "devDependencies": { 27 | "autoprefixer": "^10.4.16", 28 | "eslint-plugin-compat": "^4.2.0", 29 | "postcss": "^8.4.31", 30 | "postcss-cli": "^10.1.0", 31 | "prettier": "^3.1.0", 32 | "xo": "^0.56.0" 33 | }, 34 | "browserslist": [ 35 | "defaults", 36 | "not IE 11" 37 | ], 38 | "prettier": { 39 | "arrowParens": "avoid", 40 | "bracketSpacing": true, 41 | "endOfLine": "auto", 42 | "printWidth": 100, 43 | "singleQuote": false, 44 | "trailingComma": "es5" 45 | }, 46 | "xo": { 47 | "envs": [ 48 | "browser", 49 | "jquery" 50 | ], 51 | "extends": [ 52 | "plugin:compat/recommended" 53 | ], 54 | "parserOptions": { 55 | "ecmaVersion": "latest", 56 | "sourceType": "script" 57 | }, 58 | "prettier": true, 59 | "space": 2, 60 | "ignores": [ 61 | "**/vendor/**" 62 | ], 63 | "rules": { 64 | "camelcase": [ 65 | "error", 66 | { 67 | "properties": "never" 68 | } 69 | ], 70 | "capitalized-comments": "off", 71 | "new-cap": [ 72 | "error", 73 | { 74 | "properties": false 75 | } 76 | ], 77 | "no-alert": "off", 78 | "no-console": "error", 79 | "no-else-return": "off", 80 | "no-negated-condition": "off", 81 | "no-var": "off", 82 | "object-shorthand": "off", 83 | "one-var": "off", 84 | "prefer-arrow-callback": "off", 85 | "spaced-comment": "off", 86 | "unicorn/explicit-length-check": [ 87 | "error", 88 | { 89 | "non-zero": "greater-than" 90 | } 91 | ], 92 | "unicorn/filename-case": "off", 93 | "unicorn/no-array-for-each": "off", 94 | "unicorn/no-for-loop": "off", 95 | "unicorn/no-document-cookie": "off", 96 | "unicorn/numeric-separators-style": "off", 97 | "unicorn/prefer-includes": "off", 98 | "unicorn/prefer-node-append": "off", 99 | "unicorn/prefer-number-properties": "off", 100 | "unicorn/prefer-query-selector": "off", 101 | "unicorn/prefer-string-slice": "off", 102 | "unicorn/prevent-abbreviations": "off", 103 | "unicorn/prefer-logical-operator-over-ternary": "off", 104 | "unicorn/switch-case-braces": "off", 105 | "unicorn/no-negated-condition": "off" 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 0 3 | excludePaths: 4 | - vendor 5 | scanDirectories: 6 | - . 7 | paths: 8 | - . 9 | -------------------------------------------------------------------------------- /queryads.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 |
18 |
19 |
20 |
21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 | 33 |
34 | 35 | 36 |
37 |
38 |
39 |
40 |
41 | 42 | 43 | 44 | 45 | 48 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/auditlog.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | /* global utils:false */ 9 | 10 | // Define global variables 11 | var auditTimeout = null; 12 | 13 | function updateTopLists() { 14 | $.getJSON("api.php?topItems=audit", function (data) { 15 | if ("FTLnotrunning" in data) { 16 | return; 17 | } 18 | 19 | // Clear tables before filling them with data 20 | $("#domain-frequency td").parent().remove(); 21 | $("#ad-frequency td").parent().remove(); 22 | var domaintable = $("#domain-frequency").find("tbody:last"); 23 | var adtable = $("#ad-frequency").find("tbody:last"); 24 | var url, domain; 25 | for (domain in data.top_queries) { 26 | if (Object.prototype.hasOwnProperty.call(data.top_queries, domain)) { 27 | // Sanitize domain 28 | domain = utils.escapeHtml(domain); 29 | url = '' + domain + ""; 30 | domaintable.append( 31 | "" + 32 | url + 33 | " " + 34 | data.top_queries[domain] + 35 | " " + 36 | '' + 37 | '' + 38 | " " 39 | ); 40 | } 41 | } 42 | 43 | for (domain in data.top_ads) { 44 | if (Object.prototype.hasOwnProperty.call(data.top_ads, domain)) { 45 | var input = domain.split(" "); 46 | // Sanitize domain 47 | var printdomain = utils.escapeHtml(input[0]); 48 | if (input.length > 1) { 49 | url = 50 | '' + 53 | printdomain + 54 | " (wildcard blocked)"; 55 | adtable.append( 56 | "" + 57 | url + 58 | " " + 59 | data.top_ads[domain] + 60 | " " + 61 | '' + 62 | " " 63 | ); 64 | } else { 65 | url = '' + printdomain + ""; 66 | adtable.append( 67 | "" + 68 | url + 69 | " " + 70 | data.top_ads[domain] + 71 | " " + 72 | '' + 73 | '' + 74 | " " 75 | ); 76 | } 77 | } 78 | } 79 | 80 | $("#domain-frequency .overlay").hide(); 81 | $("#ad-frequency .overlay").hide(); 82 | // Update top lists data every ten seconds 83 | // Updates are also triggered by button actions 84 | // and reset the running timer 85 | if (auditTimeout !== null) { 86 | window.clearTimeout(auditTimeout); 87 | } 88 | 89 | auditTimeout = setTimeout(updateTopLists, 10000); 90 | }); 91 | } 92 | 93 | function add(domain, list) { 94 | var token = $("#token").text(); 95 | $.ajax({ 96 | url: "scripts/pi-hole/php/groups.php", 97 | method: "post", 98 | data: { 99 | domain: domain, 100 | list: list, 101 | token: token, 102 | action: list === "audit" ? "add_audit" : "add_domain", 103 | comment: "Added from Audit Log", 104 | }, 105 | success: function () { 106 | updateTopLists(); 107 | }, 108 | error: function (jqXHR, exception) { 109 | console.log(exception); // eslint-disable-line no-console 110 | }, 111 | }); 112 | } 113 | 114 | function blacklistUrl(url) { 115 | // We add to audit last as it will reload the table on success 116 | add(url, "black"); 117 | add(url, "audit"); 118 | } 119 | 120 | function whitelistUrl(url) { 121 | // We add to audit last as it will reload the table on success 122 | add(url, "white"); 123 | add(url, "audit"); 124 | } 125 | 126 | function auditUrl(url) { 127 | add(url, "audit"); 128 | } 129 | 130 | $(function () { 131 | // Pull in data via AJAX 132 | updateTopLists(); 133 | 134 | $("#domain-frequency tbody").on("click", "button", function (event) { 135 | var url = $(this).parents("tr")[0].textContent.split(" ")[0]; 136 | 137 | if (event.target.textContent.trim() === "Blacklist") { 138 | blacklistUrl(url); 139 | } else { 140 | auditUrl(url); 141 | } 142 | }); 143 | 144 | $("#ad-frequency tbody").on("click", "button", function (event) { 145 | var url = $(this).parents("tr")[0].textContent.split(" ")[0]; 146 | 147 | if (event.target.textContent.trim() === "Whitelist") { 148 | whitelistUrl(url); 149 | } else { 150 | auditUrl(url); 151 | } 152 | }); 153 | }); 154 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/customcname.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | /* global utils:false */ 9 | 10 | var table; 11 | var token = $("#token").text(); 12 | 13 | $(function () { 14 | $("#btnAdd").on("click", addCustomCNAME); 15 | 16 | table = $("#customCNAMETable").DataTable({ 17 | ajax: { 18 | url: "scripts/pi-hole/php/customcname.php", 19 | data: { action: "get", token: token }, 20 | type: "POST", 21 | }, 22 | columns: [{}, {}, { orderable: false, searchable: false }], 23 | columnDefs: [ 24 | { 25 | targets: 2, 26 | render: function (data, type, row) { 27 | return ( 28 | '" 35 | ); 36 | }, 37 | }, 38 | { 39 | targets: "_all", 40 | render: $.fn.dataTable.render.text(), 41 | }, 42 | ], 43 | lengthMenu: [ 44 | [10, 25, 50, 100, -1], 45 | [10, 25, 50, 100, "All"], 46 | ], 47 | order: [[0, "asc"]], 48 | stateSave: true, 49 | stateDuration: 0, 50 | stateSaveCallback: function (settings, data) { 51 | utils.stateSaveCallback("LocalCNAMETable", data); 52 | }, 53 | stateLoadCallback: function () { 54 | return utils.stateLoadCallback("LocalCNAMETable"); 55 | }, 56 | drawCallback: function () { 57 | $(".deleteCustomCNAME").on("click", deleteCustomCNAME); 58 | }, 59 | }); 60 | 61 | // Disable autocorrect in the search box 62 | var input = document.querySelector("input[type=search]"); 63 | input.setAttribute("autocomplete", "off"); 64 | input.setAttribute("autocorrect", "off"); 65 | input.setAttribute("autocapitalize", "off"); 66 | input.setAttribute("spellcheck", false); 67 | }); 68 | 69 | function addCustomCNAME() { 70 | var domain = utils.escapeHtml($("#domain").val()); 71 | var target = utils.escapeHtml($("#target").val()); 72 | 73 | utils.disableAll(); 74 | utils.showAlert("info", "", "Adding custom CNAME record...", ""); 75 | 76 | $.ajax({ 77 | url: "scripts/pi-hole/php/customcname.php", 78 | method: "post", 79 | dataType: "json", 80 | data: { action: "add", domain: domain, target: target, token: token }, 81 | success: function (response) { 82 | utils.enableAll(); 83 | if (response.success) { 84 | utils.showAlert( 85 | "success", 86 | "far fa-check-circle", 87 | "Custom CNAME added", 88 | domain + ": " + target 89 | ); 90 | 91 | // Clean up field values and reload table data 92 | $("#domain").val(""); 93 | $("#target").val(""); 94 | table.ajax.reload(); 95 | $("#domain").focus(); 96 | } else { 97 | utils.showAlert("error", "fas fa-times", "Failure! Something went wrong", response.message); 98 | } 99 | }, 100 | error: function () { 101 | utils.enableAll(); 102 | utils.showAlert("error", "fas fa-times", "Error while adding custom CNAME record", ""); 103 | }, 104 | }); 105 | } 106 | 107 | function deleteCustomCNAME() { 108 | var domain = $(this).attr("data-domain"); 109 | var target = $(this).attr("data-target"); 110 | 111 | utils.disableAll(); 112 | utils.showAlert("info", "", "Deleting custom CNAME record...", ""); 113 | 114 | $.ajax({ 115 | url: "scripts/pi-hole/php/customcname.php", 116 | method: "post", 117 | dataType: "json", 118 | data: { action: "delete", domain: domain, target: target, token: token }, 119 | success: function (response) { 120 | utils.enableAll(); 121 | if (response.success) { 122 | utils.showAlert( 123 | "success", 124 | "far fa-check-circle", 125 | "Custom CNAME deleted", 126 | domain + ": " + target 127 | ); 128 | table.ajax.reload(); 129 | } else { 130 | utils.showAlert("error", "fas fa-times", "Failure! Something went wrong", response.message); 131 | } 132 | }, 133 | error: function (jqXHR, exception) { 134 | utils.enableAll(); 135 | utils.showAlert("error", "fas fa-times", "Error while deleting custom CNAME record", ""); 136 | console.log(exception); // eslint-disable-line no-console 137 | }, 138 | }); 139 | } 140 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/customdns.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | /* global utils:false */ 9 | 10 | var table; 11 | var token = $("#token").text(); 12 | 13 | $(function () { 14 | $("#btnAdd").on("click", addCustomDNS); 15 | 16 | table = $("#customDNSTable").DataTable({ 17 | ajax: { 18 | url: "scripts/pi-hole/php/customdns.php", 19 | data: { action: "get", token: token }, 20 | type: "POST", 21 | }, 22 | columns: [{}, { type: "ip-address" }, { orderable: false, searchable: false }], 23 | columnDefs: [ 24 | { 25 | targets: 2, 26 | render: function (data, type, row) { 27 | return ( 28 | '" 35 | ); 36 | }, 37 | }, 38 | { 39 | targets: "_all", 40 | render: $.fn.dataTable.render.text(), 41 | }, 42 | ], 43 | lengthMenu: [ 44 | [10, 25, 50, 100, -1], 45 | [10, 25, 50, 100, "All"], 46 | ], 47 | order: [[0, "asc"]], 48 | stateSave: true, 49 | stateDuration: 0, 50 | stateSaveCallback: function (settings, data) { 51 | utils.stateSaveCallback("LocalDNSTable", data); 52 | }, 53 | stateLoadCallback: function () { 54 | return utils.stateLoadCallback("LocalDNSTable"); 55 | }, 56 | drawCallback: function () { 57 | $(".deleteCustomDNS").on("click", deleteCustomDNS); 58 | }, 59 | }); 60 | // Disable autocorrect in the search box 61 | var input = document.querySelector("input[type=search]"); 62 | input.setAttribute("autocomplete", "off"); 63 | input.setAttribute("autocorrect", "off"); 64 | input.setAttribute("autocapitalize", "off"); 65 | input.setAttribute("spellcheck", false); 66 | }); 67 | 68 | function addCustomDNS() { 69 | var ip = utils.escapeHtml($("#ip").val()); 70 | var domain = utils.escapeHtml($("#domain").val()); 71 | 72 | utils.disableAll(); 73 | utils.showAlert("info", "", "Adding custom DNS entry...", ""); 74 | 75 | $.ajax({ 76 | url: "scripts/pi-hole/php/customdns.php", 77 | method: "post", 78 | dataType: "json", 79 | data: { action: "add", ip: ip, domain: domain, token: token }, 80 | success: function (response) { 81 | utils.enableAll(); 82 | if (response.success) { 83 | utils.showAlert("success", "far fa-check-circle", "Custom DNS added", domain + ": " + ip); 84 | 85 | // Clean up field values and reload table data 86 | $("#domain").val(""); 87 | $("#ip").val(""); 88 | table.ajax.reload(); 89 | $("#domain").focus(); 90 | } else { 91 | utils.showAlert("error", "fas fa-times", "Failure! Something went wrong", response.message); 92 | } 93 | }, 94 | error: function () { 95 | utils.enableAll(); 96 | utils.showAlert("error", "fas fa-times", "Error while adding custom DNS entry", ""); 97 | }, 98 | }); 99 | } 100 | 101 | function deleteCustomDNS() { 102 | var ip = $(this).attr("data-ip"); 103 | var domain = $(this).attr("data-domain"); 104 | 105 | utils.disableAll(); 106 | utils.showAlert("info", "", "Deleting custom DNS entry...", ""); 107 | 108 | $.ajax({ 109 | url: "scripts/pi-hole/php/customdns.php", 110 | method: "post", 111 | dataType: "json", 112 | data: { action: "delete", domain: domain, ip: ip, token: token }, 113 | success: function (response) { 114 | utils.enableAll(); 115 | if (response.success) { 116 | utils.showAlert("success", "far fa-check-circle", "Custom DNS deleted", domain + ": " + ip); 117 | table.ajax.reload(); 118 | } else { 119 | utils.showAlert("error", "fas fa-times", "Failure! Something went wrong", response.message); 120 | } 121 | }, 122 | error: function (jqXHR, exception) { 123 | utils.enableAll(); 124 | utils.showAlert("error", "fas fa-times", "Error while deleting custom DNS entry", ""); 125 | console.log(exception); // eslint-disable-line no-console 126 | }, 127 | }); 128 | } 129 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/debug.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | function eventsource() { 9 | var ta = $("#output"); 10 | var upload = $("#upload"); 11 | var dbcheck = $("#dbcheck"); 12 | var checked = ""; 13 | var token = encodeURIComponent($("#token").text()); 14 | 15 | if (upload.prop("checked")) { 16 | // add upload option 17 | checked += "&upload"; 18 | } 19 | 20 | if (dbcheck.prop("checked")) { 21 | // add db integrity check option 22 | checked += "&dbcheck"; 23 | } 24 | 25 | // IE does not support EventSource - load whole content at once 26 | if (typeof EventSource !== "function") { 27 | $.ajax({ 28 | method: "GET", 29 | url: "scripts/pi-hole/php/debug.php?IE&token=" + token + checked, 30 | async: false, 31 | }).done(function (data) { 32 | ta.show(); 33 | ta.empty(); 34 | ta.append(data); 35 | }); 36 | return; 37 | } 38 | 39 | var source = new EventSource("scripts/pi-hole/php/debug.php?&token=" + token + checked); 40 | 41 | // Reset and show field 42 | ta.empty(); 43 | ta.show(); 44 | 45 | source.addEventListener( 46 | "message", 47 | function (e) { 48 | ta.append(e.data); 49 | // scroll to the bottom of #output (most recent data) 50 | var taBottom = ta.offset().top + ta.outerHeight(true); 51 | $("html, body").scrollTop(taBottom - $(window).height()); 52 | }, 53 | false 54 | ); 55 | 56 | // Will be called when script has finished 57 | source.addEventListener( 58 | "error", 59 | function () { 60 | source.close(); 61 | $("#output").removeClass("loading"); 62 | }, 63 | false 64 | ); 65 | } 66 | 67 | $("#debugBtn").on("click", function () { 68 | $("#debugBtn").prop("disabled", true); 69 | $("#upload").prop("disabled", true); 70 | $("#dbcheck").prop("disabled", true); 71 | $("#output").addClass("loading"); 72 | eventsource(); 73 | }); 74 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/gravity.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | function eventsource() { 9 | var alInfo = $("#alInfo"); 10 | var alSuccess = $("#alSuccess"); 11 | var ta = $("#output"); 12 | 13 | // IE does not support EventSource - exit early 14 | if (typeof EventSource !== "function") { 15 | ta.show(); 16 | ta.html("Updating lists of ad-serving domains is not supported with this browser!"); 17 | return; 18 | } 19 | 20 | var source = new EventSource("scripts/pi-hole/php/gravity.sh.php"); 21 | 22 | ta.html(""); 23 | ta.show(); 24 | alInfo.show(); 25 | alSuccess.hide(); 26 | 27 | source.addEventListener( 28 | "message", 29 | function (e) { 30 | if (e.data.indexOf("Pi-hole blocking is") !== -1) { 31 | alSuccess.show(); 32 | } 33 | 34 | // Detect ${OVER} 35 | var newString = "<------"; 36 | 37 | if (e.data.indexOf(newString) === -1) { 38 | ta.append(e.data); 39 | } else { 40 | ta.text(ta.text().substring(0, ta.text().lastIndexOf("\n")) + "\n"); 41 | ta.append(e.data.replace(newString, "")); 42 | } 43 | }, 44 | false 45 | ); 46 | 47 | // Will be called when script has finished 48 | source.addEventListener( 49 | "error", 50 | function () { 51 | alInfo.delay(1000).fadeOut(2000, function () { 52 | alInfo.hide(); 53 | }); 54 | source.close(); 55 | $("#gravityBtn").prop("disabled", false); 56 | }, 57 | false 58 | ); 59 | } 60 | 61 | $("#gravityBtn").on("click", function () { 62 | $("#gravityBtn").prop("disabled", true); 63 | eventsource(); 64 | }); 65 | 66 | // Handle hiding of alerts 67 | $(function () { 68 | $("[data-hide]").on("click", function () { 69 | $(this) 70 | .closest("." + $(this).attr("data-hide")) 71 | .hide(); 72 | }); 73 | 74 | // Do we want to start updating immediately? 75 | // gravity.php?go 76 | var searchString = window.location.search.substring(1); 77 | if (searchString.indexOf("go") !== -1) { 78 | $("#gravityBtn").prop("disabled", true); 79 | eventsource(); 80 | } 81 | }); 82 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/ip-address-sorting.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2019 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | // This code has been taken from 9 | // https://datatables.net/plug-ins/sorting/ip-address 10 | // and was modified by the Pi-hole team to support 11 | // CIDR notation and be more robust against invalid 12 | // input data (like empty IP addresses) 13 | $.extend($.fn.dataTableExt.oSort, { 14 | "ip-address-pre": function (a) { 15 | // Skip empty fields (IP address might have expired or 16 | // reassigned to a different device) 17 | if (!a || a.length === 0) { 18 | return Infinity; 19 | } 20 | 21 | var i, item; 22 | // Use the first IP in case there is a list of IPs 23 | // for a given device 24 | if (Array.isArray(a)) { 25 | a = a[0]; 26 | } 27 | 28 | var m = a.split("."), 29 | n = a.split(":"), 30 | x = "", 31 | xa = "", 32 | cidr = []; 33 | if (m.length === 4) { 34 | // IPV4 (possibly with CIDR) 35 | cidr = m[3].split("/"); 36 | if (cidr.length === 2) { 37 | m.pop(); 38 | m = m.concat(cidr); 39 | } 40 | 41 | for (i = 0; i < m.length; i++) { 42 | item = m[i]; 43 | 44 | if (item.length === 1) { 45 | x += "00" + item; 46 | } else if (item.length === 2) { 47 | x += "0" + item; 48 | } else { 49 | x += item; 50 | } 51 | } 52 | } else if (n.length > 0) { 53 | // IPV6 (possibly with CIDR) 54 | var count = 0; 55 | for (i = 0; i < n.length; i++) { 56 | item = n[i]; 57 | 58 | if (i > 0) { 59 | xa += ":"; 60 | } 61 | 62 | switch (item.length) { 63 | case 0: { 64 | count += 0; 65 | 66 | break; 67 | } 68 | 69 | case 1: { 70 | xa += "000" + item; 71 | count += 4; 72 | 73 | break; 74 | } 75 | 76 | case 2: { 77 | xa += "00" + item; 78 | count += 4; 79 | 80 | break; 81 | } 82 | 83 | case 3: { 84 | xa += "0" + item; 85 | count += 4; 86 | 87 | break; 88 | } 89 | 90 | default: { 91 | xa += item; 92 | count += 4; 93 | } 94 | } 95 | } 96 | 97 | // Padding the :: 98 | n = xa.split(":"); 99 | var paddDone = 0; 100 | 101 | for (i = 0; i < n.length; i++) { 102 | item = n[i]; 103 | if (item.length === 0 && paddDone === 0) { 104 | for (var padding = 0; padding < 32 - count; padding++) { 105 | x += "0"; 106 | paddDone = 1; 107 | } 108 | } else { 109 | x += item; 110 | } 111 | } 112 | 113 | cidr = x.split("/"); 114 | x = cidr[0]; 115 | if (cidr.length === 2) { 116 | item = cidr[1]; 117 | if (item.length === 1) { 118 | x += "00" + item; 119 | } else if (item.length === 2) { 120 | x += "0" + item; 121 | } else { 122 | x += item; 123 | } 124 | } 125 | } 126 | 127 | return x; 128 | }, 129 | 130 | "ip-address-asc": function (a, b) { 131 | return a < b ? -1 : a > b ? 1 : 0; 132 | }, 133 | 134 | "ip-address-desc": function (a, b) { 135 | return a < b ? 1 : a > b ? -1 : 0; 136 | }, 137 | }); 138 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/queryads.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | var exact = ""; 9 | var showAll = ""; 10 | 11 | function eventsource() { 12 | var ta = $("#output"); 13 | // process with the current visible domain input field 14 | var domain = $("input[id^='domain']:visible").val().trim().toLowerCase(); 15 | var unlimited = $("#show-all").is(":checked"); 16 | 17 | if (domain.length === 0) { 18 | return; 19 | } 20 | 21 | if (unlimited === true) { 22 | showAll = "&showall"; 23 | } 24 | 25 | var queryURL = "scripts/pi-hole/php/queryads.php?domain=" + domain + exact + showAll; 26 | 27 | // IE does not support EventSource - load whole content at once 28 | if (typeof EventSource !== "function") { 29 | $.ajax({ 30 | method: "GET", 31 | url: queryURL + "&IE", 32 | async: false, 33 | }).done(function (data) { 34 | ta.show(); 35 | ta.empty(); 36 | ta.append(data); 37 | }); 38 | return; 39 | } 40 | 41 | var source = new EventSource(queryURL); 42 | 43 | // Reset and show field 44 | ta.empty(); 45 | ta.show(); 46 | 47 | source.addEventListener( 48 | "message", 49 | function (e) { 50 | ta.append(e.data); 51 | }, 52 | false 53 | ); 54 | 55 | // Will be called when script has finished 56 | source.addEventListener( 57 | "error", 58 | function () { 59 | source.close(); 60 | }, 61 | false 62 | ); 63 | 64 | // Reset option variables 65 | exact = ""; 66 | showAll = ""; 67 | } 68 | 69 | // Handle enter key 70 | $("#domain").on("keypress", function (e) { 71 | if (e.which === 13) { 72 | // Enter was pressed, and the input has focus 73 | exact = ""; 74 | eventsource(); 75 | } 76 | }); 77 | 78 | // Handle search buttons 79 | $("button[id^='btnSearch']").on("click", function () { 80 | exact = ""; 81 | 82 | if (this.id.match("^btnSearchExact")) { 83 | exact = "&exact"; 84 | } 85 | 86 | eventsource(); 87 | }); 88 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/restartdns.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | var timeleft = 60; 9 | var status = -1; 10 | var reloadMsg = 11 | "FTL was restarted: Reload FTL details."; 12 | var warningMsg = "FTL was not able to reload after " + timeleft + " seconds."; 13 | var counterMsg = "FTL is reloading: "; 14 | 15 | var reloadTimer = setInterval(function () { 16 | $.getJSON("api.php?dns-port", function (data) { 17 | if ("FTLnotrunning" in data) { 18 | return; 19 | } 20 | 21 | status = data["dns-port"]; 22 | }); 23 | 24 | if (timeleft <= 0 || status >= 0) { 25 | clearInterval(reloadTimer); 26 | if (status < 0) { 27 | // FTL was not restarted in 60 seconds. Show warning message 28 | document.getElementById("restart-countdown").innerHTML = warningMsg; 29 | } else { 30 | // FTL restartd. 31 | document.getElementById("restart-countdown").innerHTML = reloadMsg; 32 | } 33 | } else { 34 | document.getElementById("restart-countdown").innerHTML = 35 | counterMsg + timeleft + " seconds remaining..."; 36 | } 37 | 38 | timeleft -= 1; 39 | }, 1000); 40 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/speedresults.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | /* global moment:false */ 8 | 9 | var tableApi; 10 | 11 | function handleAjaxError(xhr, textStatus, _error) { 12 | if (textStatus === "timeout") { 13 | alert("The server took too long to send the data."); 14 | } else if (xhr.responseText.indexOf("Connection refused") >= 0) { 15 | alert("An error occurred while loading the data: Connection refused. Is FTL running?"); 16 | } else { 17 | alert("An unknown error occurred while loading the data.\n" + xhr.responseText); 18 | } 19 | 20 | $("#all-queries_processing").hide(); 21 | tableApi.clear(); 22 | tableApi.draw(); 23 | } 24 | 25 | $(document).ready(function () { 26 | // Do we want to filter queries? 27 | var GETDict = {}; 28 | location.search 29 | .slice(1) 30 | .split("&") 31 | .forEach(function (item) { 32 | GETDict[item.split("=")[0]] = item.split("=")[1]; 33 | }); 34 | 35 | var APIstring = "api.php?getAllSpeedTestData"; 36 | 37 | tableApi = $("#all-queries").DataTable({ 38 | dom: 39 | "<'row'<'col-sm-12'f>>" + 40 | "<'row'<'col-sm-4'l><'col-sm-8'p>>" + 41 | "<'row'<'col-sm-12'tr>>" + 42 | "<'row'<'col-sm-5'i><'col-sm-7'p>>", 43 | ajax: { url: APIstring, error: handleAjaxError }, 44 | autoWidth: true, 45 | processing: true, 46 | order: [[0, "desc"]], 47 | columns: [ 48 | null, 49 | { 50 | render: function (data, type, _full, _meta) { 51 | if (type === "display") { 52 | data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mm"); 53 | } 54 | 55 | return data; 56 | }, 57 | }, 58 | { 59 | render: function (data, type, _full, _meta) { 60 | if (type === "display") { 61 | data = moment.utc(data, "YYYY-MM-DD HH:mm:ss").local().format("YYYY-MM-DD HH:mm"); 62 | } 63 | 64 | return data; 65 | }, 66 | }, 67 | null, 68 | null, 69 | null, 70 | null, 71 | null, 72 | null, 73 | null, 74 | { 75 | render: function (data, _type, _full, _meta) { 76 | data = ' View Result'; 77 | return data; 78 | }, 79 | }, 80 | ], 81 | 82 | columnDefs: [ 83 | { 84 | targets: [0, 2], 85 | visible: false, 86 | }, 87 | ], 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/taillog-FTL.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | var offset, 9 | timer, 10 | pre, 11 | scrolling = true; 12 | 13 | // Check every 200msec for fresh data 14 | var interval = 200; 15 | 16 | // Function that asks the API for new data 17 | function reloadData() { 18 | clearTimeout(timer); 19 | $.getJSON("scripts/pi-hole/php/tailLog.php?FTL&offset=" + offset, function (data) { 20 | pre.append(data.lines); 21 | 22 | if (scrolling && offset !== data.offset) { 23 | pre.scrollTop(pre[0].scrollHeight); 24 | } 25 | 26 | offset = data.offset; 27 | }); 28 | 29 | timer = setTimeout(reloadData, interval); 30 | } 31 | 32 | $(function () { 33 | // Get offset at first loading of page 34 | $.getJSON("scripts/pi-hole/php/tailLog.php?FTL", function (data) { 35 | offset = data.offset; 36 | }); 37 | pre = $("#output"); 38 | // Trigger function that looks for new data 39 | reloadData(); 40 | }); 41 | 42 | $("#chk1").on("click", function () { 43 | $("#chk2").prop("checked", this.checked); 44 | scrolling = this.checked; 45 | }); 46 | $("#chk2").on("click", function () { 47 | $("#chk1").prop("checked", this.checked); 48 | scrolling = this.checked; 49 | }); 50 | -------------------------------------------------------------------------------- /scripts/pi-hole/js/taillog.js: -------------------------------------------------------------------------------- 1 | /* Pi-hole: A black hole for Internet advertisements 2 | * (c) 2017 Pi-hole, LLC (https://pi-hole.net) 3 | * Network-wide ad blocking via your own hardware. 4 | * 5 | * This file is copyright under the latest version of the EUPL. 6 | * Please see LICENSE file for your rights under this license. */ 7 | 8 | var offset, 9 | timer, 10 | pre, 11 | scrolling = true; 12 | 13 | // Check every 200msec for fresh data 14 | var interval = 200; 15 | 16 | // Function that asks the API for new data 17 | function reloadData() { 18 | clearTimeout(timer); 19 | $.getJSON("scripts/pi-hole/php/tailLog.php?offset=" + offset, function (data) { 20 | pre.append(data.lines); 21 | 22 | if (scrolling && offset !== data.offset) { 23 | pre.scrollTop(pre[0].scrollHeight); 24 | } 25 | 26 | offset = data.offset; 27 | }); 28 | 29 | timer = setTimeout(reloadData, interval); 30 | } 31 | 32 | $(function () { 33 | // Get offset at first loading of page 34 | $.getJSON("scripts/pi-hole/php/tailLog.php", function (data) { 35 | offset = data.offset; 36 | }); 37 | pre = $("#output"); 38 | // Trigger function that looks for new data 39 | reloadData(); 40 | }); 41 | 42 | $("#chk1").on("click", function () { 43 | $("#chk2").prop("checked", this.checked); 44 | scrolling = this.checked; 45 | }); 46 | $("#chk2").on("click", function () { 47 | $("#chk1").prop("checked", this.checked); 48 | scrolling = this.checked; 49 | }); 50 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/FTL.php: -------------------------------------------------------------------------------- 1 | '.$requestin; 53 | fwrite($socket, $request) or exit('{"error":"Could not send data to server"}'); 54 | } 55 | 56 | function getResponseFTL($socket) 57 | { 58 | $response = array(); 59 | 60 | $errCount = 0; 61 | while (true) { 62 | $out = fgets($socket); 63 | if ($out == '') { 64 | ++$errCount; 65 | } 66 | 67 | if ($errCount > 100) { 68 | // Tried 100 times, but never got proper reply, fail to prevent busy loop 69 | exit('{"error":"Tried 100 times to connect to FTL server, but never got proper reply. Please check Port and logs!"}'); 70 | } 71 | 72 | if (strrpos($out, '---EOM---') !== false) { 73 | break; 74 | } 75 | 76 | $out = rtrim($out); 77 | if (strlen($out) > 0) { 78 | $response[] = $out; 79 | } 80 | } 81 | 82 | return $response; 83 | } 84 | 85 | function disconnectFTL($socket) 86 | { 87 | if (is_resource($socket)) { 88 | fclose($socket); 89 | } 90 | } 91 | 92 | function callFTLAPI($request) 93 | { 94 | $socket = connectFTL(); 95 | 96 | if (!is_resource($socket)) { 97 | $data = array('FTLnotrunning' => true); 98 | } else { 99 | sendRequestFTL($request, $socket); 100 | $data = getResponseFTL($socket); 101 | } 102 | disconnectFTL($socket); 103 | 104 | return $data; 105 | } 106 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/api_token.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 30 | 31 | 32 | 0) { 35 | echo '
'; 36 | require_once '../../vendor/qrcode.php'; 37 | $qr = QRCode::getMinimumQRCode($pwhash, QR_ERROR_CORRECT_LEVEL_Q); 38 | // The size of each block (in pixels) should be an integer 39 | $qr->printSVG(10); 40 | echo '
'; 41 | echo 'Raw API Token: '.$pwhash.''; 42 | } else { 43 | echo '

No password set

'; 44 | } 45 | } else { 46 | echo '

Not authorized!

'; 47 | } 48 | ?> 49 | 50 | 51 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/auth.php: -------------------------------------------------------------------------------- 1 | '', 25 | chr(27).'[1;32m' => '', 26 | chr(27).'[1;33m' => '', 27 | chr(27).'[1;34m' => '', 28 | chr(27).'[1;35m' => '', 29 | chr(27).'[1;36m' => '', 30 | 31 | chr(27).'[90m' => '', 32 | chr(27).'[91m' => '', 33 | chr(27).'[32m' => '', 34 | chr(27).'[33m' => '', 35 | chr(27).'[94m' => '', 36 | chr(27).'[95m' => '', 37 | chr(27).'[96m' => '', 38 | 39 | chr(27).'[1m' => '', 40 | chr(27).'[4m' => '', 41 | 42 | chr(27).'[0m' => '', 43 | ); 44 | 45 | $data = str_replace(array_keys($ANSIcolors), $ANSIcolors, htmlspecialchars($datatext)); 46 | 47 | if (!isset($_GET['IE'])) { 48 | echo 'data: '.implode("\ndata: ", explode("\n", $data))."\n\n"; 49 | } else { 50 | echo $data; 51 | } 52 | } 53 | 54 | // Execute "pihole" using Web option 55 | $command = 'export TERM=dumb && sudo pihole -d -w'; 56 | 57 | // Add auto-upload option 58 | if (isset($_GET['upload'])) { 59 | $command .= ' -a'; 60 | } 61 | 62 | // Execute database integrity_check 63 | if (isset($_GET['dbcheck'])) { 64 | $command .= ' -c'; 65 | } 66 | 67 | $proc = popen($command, 'r'); 68 | 69 | while (!feof($proc)) { 70 | echoEvent(fread($proc, 4096)); 71 | } 72 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/footer.php: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 42 | 43 | 58 |
59 |
60 |
61 | Donate if you found this useful. 62 |
63 |
64 | 65 |
66 |
67 | 96 | 97 |

98 | 99 | To install updates, replace this old container with a fresh upgraded image. 100 | 101 | To install updates, uninstall Speedtest Mod, run pihole -up if there's a new official Pi-hole release, and reinstall Speedtest Mod. The Reinstall button in [Settings/Speedtest] can do this for you! 102 | 103 | To install updates, reinstall Speedtest Mod. The Reinstall button in [Settings/Speedtest] can do this for you! 104 | 105 |

106 |
107 |
108 |
109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/gravity.php: -------------------------------------------------------------------------------- 1 | querySingle("SELECT value FROM info WHERE property = 'updated';"); 16 | if ($date_file_created_unix === false) { 17 | if ($raw) { 18 | // Array output 19 | return array('file_exists' => false); 20 | } 21 | 22 | // String output 23 | return 'Gravity database not available'; 24 | } 25 | // Now that we know that $date_file_created_unix is a valid response, we can convert it to an integer 26 | $date_file_created_unix = intval($date_file_created_unix); 27 | $date_file_created = date_create('@'.$date_file_created_unix); 28 | $date_now = date_create('now'); 29 | $gravitydiff = date_diff($date_file_created, $date_now); 30 | if ($raw) { 31 | // Array output 32 | return array( 33 | 'file_exists' => true, 34 | 'absolute' => $date_file_created_unix, 35 | 'relative' => array( 36 | 'days' => intval($gravitydiff->format('%a')), 37 | 'hours' => intval($gravitydiff->format('%H')), 38 | 'minutes' => intval($gravitydiff->format('%I')), 39 | ), 40 | ); 41 | } 42 | 43 | if ($gravitydiff->d > 1) { 44 | // String output (more than one day ago) 45 | return $gravitydiff->format('Adlists updated %a days, %H:%I (hh:mm) ago'); 46 | } 47 | if ($gravitydiff->d == 1) { 48 | // String output (one day ago) 49 | return $gravitydiff->format('Adlists updated one day, %H:%I (hh:mm) ago'); 50 | } 51 | 52 | // String output (less than one day ago) 53 | return $gravitydiff->format('Adlists updated %H:%I (hh:mm) ago'); 54 | } 55 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/gravity.sh.php: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Pi-hole<?php echo $hostname ? ' - '.$hostname : ''; ?> 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/message.php: -------------------------------------------------------------------------------- 1 | prepare('DELETE FROM message WHERE id IN ('.implode(',', $ids).')'); 43 | if (!$stmt) { 44 | throw new Exception('While preparing message statement: '.$db->lastErrorMsg()); 45 | } 46 | 47 | if (!$stmt->execute()) { 48 | throw new Exception('While executing message statement: '.$db->lastErrorMsg()); 49 | } 50 | 51 | $reload = true; 52 | JSON_success(); 53 | } catch (Exception $ex) { 54 | JSON_error($ex->getMessage()); 55 | } 56 | } else { 57 | log_and_die('Requested action not supported!'); 58 | } 59 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/network.php: -------------------------------------------------------------------------------- 1 | prepare('DELETE FROM network_addresses WHERE network_id=:id'); 33 | if (!$stmt) { 34 | throw new Exception('While preparing message statement: '.$db->lastErrorMsg()); 35 | } 36 | 37 | if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) { 38 | throw new Exception('While binding id to message statement: '.$db->lastErrorMsg()); 39 | } 40 | 41 | if (!$stmt->execute()) { 42 | throw new Exception('While executing message statement: '.$db->lastErrorMsg()); 43 | } 44 | 45 | $stmt = $db->prepare('DELETE FROM network WHERE id=:id'); 46 | if (!$stmt) { 47 | throw new Exception('While preparing message statement: '.$db->lastErrorMsg()); 48 | } 49 | 50 | if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) { 51 | throw new Exception('While binding id to message statement: '.$db->lastErrorMsg()); 52 | } 53 | 54 | if (!$stmt->execute()) { 55 | throw new Exception('While executing message statement: '.$db->lastErrorMsg()); 56 | } 57 | 58 | $reload = true; 59 | JSON_success(); 60 | } catch (Exception $ex) { 61 | JSON_error($ex->getMessage()); 62 | } 63 | } else { 64 | log_and_die('Requested action not supported!'); 65 | } 66 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/password.php: -------------------------------------------------------------------------------- 1 | 0) { 31 | // Check for and authorize from persistent cookie 32 | if (isset($_COOKIE['persistentlogin'])) { 33 | if (checkValidityPersistentLoginToken($_COOKIE['persistentlogin'])) { 34 | $_SESSION['auth'] = true; 35 | } else { 36 | // Invalid cookie 37 | $_SESSION['auth'] = false; 38 | setcookie('persistentlogin', '', 1); 39 | } 40 | } elseif (isset($_POST['pw'])) { 41 | // Compare doubly hashes password input with saved hash 42 | $postinput = hash('sha256', hash('sha256', $_POST['pw'])); 43 | 44 | if (hash_equals($pwhash, $postinput)) { 45 | // Save previously accessed page, before clear the session 46 | $redirect_url = 'index.php'; 47 | if (isset($_SESSION['prev_url'])) { 48 | $redirect_url = $_SESSION['prev_url']; 49 | } 50 | 51 | // Regenerate session ID to prevent session fixation 52 | session_regenerate_id(); 53 | 54 | // Clear the old session 55 | $_SESSION = array(); 56 | 57 | // Set hash in new session 58 | $_SESSION['hash'] = $pwhash; 59 | 60 | // Set persistent cookie if selected 61 | if (isset($_POST['persistentlogin'])) { 62 | // Generate cookie with new expiry 63 | $token = genPersistentLoginToken(); 64 | $time = time() + 60 * 60 * 24 * 7; // 7 days 65 | writePersistentLoginToken($token, $time); 66 | // setcookie($name, $value, $expire, $path, $domain, $secure, $httponly) 67 | setcookie('persistentlogin', $token, $time, null, null, null, true); 68 | } 69 | 70 | $_SESSION['auth'] = true; 71 | 72 | // Login successful, redirect the user to the original requested page 73 | if ( 74 | $_SERVER['REQUEST_METHOD'] === 'POST' 75 | && strlen($_SERVER['SCRIPT_NAME']) >= 10 76 | && substr_compare($_SERVER['SCRIPT_NAME'], '/login.php', -10) === 0 77 | ) { 78 | header('Location: '.$redirect_url); 79 | exit; 80 | } 81 | } else { 82 | $_SESSION['auth'] = false; 83 | $validpassword = false; 84 | } 85 | } elseif (isset($_SESSION['hash'])) { 86 | // Compare auth hash with saved hash 87 | if (hash_equals($pwhash, $_SESSION['hash'])) { 88 | $_SESSION['auth'] = true; 89 | } 90 | } elseif ($use_api && isset($_GET['auth'])) { 91 | // API can use the hash to get data without logging in via plain-text password 92 | if (hash_equals($pwhash, $_GET['auth'])) { 93 | $_SESSION['auth'] = true; 94 | } 95 | } else { 96 | // Password or hash wrong 97 | $_SESSION['auth'] = false; 98 | } 99 | } else { 100 | // No password set 101 | $_SESSION['auth'] = true; 102 | } 103 | 104 | return $validpassword; 105 | } 106 | 107 | $wrongpassword = !verifyPassword($pwhash, isset($api)); 108 | $auth = $_SESSION['auth']; 109 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/persistentlogin_token.php: -------------------------------------------------------------------------------- 1 | = time()) { 52 | return true; 53 | } 54 | } 55 | } 56 | 57 | return false; 58 | } 59 | 60 | function writePersistentLoginToken($token, $time) 61 | { 62 | $token_file = getPathPersistentLoginToken($token); 63 | 64 | if ($token_file and !file_exists($token_file)) { 65 | $t_file = fopen($token_file, 'w'); 66 | if ($t_file) { 67 | // make sure persistent login token file is not readable by other users 68 | chmod($token_file, 0600); 69 | 70 | fwrite($t_file, $time); 71 | fclose($t_file); 72 | 73 | return true; 74 | } 75 | } 76 | 77 | return false; 78 | } 79 | 80 | function logoutPersistentLoginToken($token) 81 | { 82 | setcookie('persistentlogin', '', 1); 83 | 84 | $token_file = getPathPersistentLoginToken($token); 85 | if ($token_file and file_exists($token_file) and is_writable($token_file)) { 86 | unlink($token_file); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/queryads.php: -------------------------------------------------------------------------------- 1 | 0) { 16 | ob_end_flush(); 17 | } 18 | 19 | require_once 'func.php'; 20 | ini_set('output_buffering', '0'); 21 | ob_implicit_flush(true); 22 | header('Content-Type: text/event-stream'); 23 | header('Cache-Control: no-cache'); 24 | 25 | function echoEvent($datatext, $url = '') 26 | { 27 | if (!isset($_GET['IE'])) { 28 | $txt = 'data:'.implode("\ndata:", explode("\n", $datatext))."\n\n"; 29 | } else { 30 | $txt = $datatext; 31 | } 32 | 33 | $txt = str_replace('This can be overridden using the -all option', 'Select the checkbox to remove the limitation', $txt); 34 | $txt = str_replace($url, ''.$url.'', $txt); 35 | 36 | echo $txt; 37 | } 38 | 39 | // Test if domain is set 40 | if (isset($_GET['domain'])) { 41 | // Is this a valid domain? 42 | // Convert domain name to IDNA ASCII form for international domains 43 | $url = convertUnicodeToIDNA($_GET['domain']); 44 | if (!validDomain($url)) { 45 | echoEvent(htmlentities($url).' is an invalid domain!', $url); 46 | 47 | exit; 48 | } 49 | } else { 50 | echoEvent('No domain provided'); 51 | 52 | exit; 53 | } 54 | 55 | $options = ''; 56 | if (isset($_GET['exact'])) { 57 | $options .= ' -exact'; 58 | } 59 | 60 | if (isset($_GET['showall'])) { 61 | $options .= ' -all'; 62 | } 63 | 64 | $proc = popen('sudo pihole -q '.$url.$options, 'r'); 65 | while (!feof($proc)) { 66 | echoEvent(fread($proc, 4096), $url); 67 | } 68 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/tailLog.php: -------------------------------------------------------------------------------- 1 | '.$txt.''; 21 | } elseif (strpos($line, 'query[A') || strpos($line, 'query[DHCP')) { 22 | $txt = ''.$txt.''; 23 | } else { 24 | $txt = ''.$txt.''; 25 | } 26 | 27 | return $txt; 28 | } 29 | 30 | // Not using SplFileObject here, since direct 31 | // usage of f-streams will be much faster for 32 | // files as large as the pihole.log 33 | if (isset($_GET['FTL'])) { 34 | $file = fopen('/var/log/pihole/FTL.log', 'r'); 35 | } else { 36 | $file = fopen('/var/log/pihole/pihole.log', 'r'); 37 | } 38 | 39 | if (!$file) { 40 | exit(json_encode(array('offset' => 0, 'lines' => array("Failed to open log file. Check permissions!\n")))); 41 | } 42 | 43 | if (isset($_GET['offset'])) { 44 | $offset = intval($_GET['offset']); 45 | if ($offset > 0) { 46 | // If offset is grater then current file end it means the file was truncated (log rotation) 47 | fseek($file, 0, SEEK_END); 48 | if ($offset > ftell($file)) { 49 | $offset = 0; 50 | } 51 | 52 | // Seeks on the file pointer where we want to continue reading is known 53 | fseek($file, $offset); 54 | 55 | $lines = array(); 56 | while (!feof($file)) { 57 | array_push($lines, formatLine(fgets($file))); 58 | } 59 | 60 | exit(json_encode(array('offset' => ftell($file), 'lines' => $lines))); 61 | } 62 | } 63 | 64 | // Locate the current position of the file read/write pointer 65 | fseek($file, -1, SEEK_END); 66 | 67 | // Add one to skip the very last "\n" in the log file 68 | exit(json_encode(array('offset' => ftell($file) + 1))); 69 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/theme.php: -------------------------------------------------------------------------------- 1 | $value) { 55 | ?>
checked> 56 |
57 | 61 | -------------------------------------------------------------------------------- /scripts/pi-hole/php/update_checker.php: -------------------------------------------------------------------------------- 1 | '.htmlentities($core_current).''; 134 | } 135 | 136 | if (isset($web_commit)) { 137 | $webVersionStr = htmlentities($web_current.' ('.$web_branch.', '.$web_commit.')'); 138 | } else { 139 | $webVersionStr = ''.htmlentities($web_current).''; 140 | } 141 | 142 | if (isset($speedtest_commit)) { 143 | $speedtestVersionStr = htmlentities($speedtest_current.' ('.$speedtest_branch.', '.$speedtest_commit.')'); 144 | } else { 145 | $speedtestVersionStr = ''.htmlentities($speedtest_current).''; 146 | } 147 | 148 | if (isset($FTL_commit)) { 149 | $ftlVersionStr = htmlentities($FTL_current.' ('.$FTL_branch.', '.$FTL_commit.')'); 150 | } else { 151 | $ftlVersionStr = ''.htmlentities($FTL_current).''; 152 | } 153 | 154 | if ($docker_current) { 155 | if ($docker_current == 'dev' || $docker_current == 'nightly') { 156 | $dockerVersionStr = htmlentities($docker_current); 157 | } else { 158 | $dockerVersionStr = ''.htmlentities($docker_current).''; 159 | } 160 | } else { 161 | $dockerVersionStr = ''; 162 | } 163 | -------------------------------------------------------------------------------- /scripts/vendor/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2015 almasaeed2010 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /scripts/vendor/bootstrap-toggle.min.js: -------------------------------------------------------------------------------- 1 | /*! ======================================================================== 2 | * Bootstrap Toggle: bootstrap-toggle.js v2.2.0 3 | * http://www.bootstraptoggle.com 4 | * ======================================================================== 5 | * Copyright 2014 Min Hur, The New York Times Company 6 | * Licensed under MIT 7 | * ======================================================================== */ 8 | +function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-lg":"small"===this.options.size?"btn-sm":"mini"===this.options.size?"btn-xs":"",c=a('