From 88944a31ecbd2e17f23783570d4dbda3487a2bfd Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Tue, 2 May 2017 17:05:24 +1000 Subject: [PATCH 01/19] Update blockingpage.css * Block page UI overhaul to replicate the style of the Admin Console * Block page UI is now mobile friendly * Users can safely customise text in order to make the block page more friendly for their household --- advanced/blockingpage.css | 470 +++++++++++++++++++++++++++++--------- 1 file changed, 358 insertions(+), 112 deletions(-) diff --git a/advanced/blockingpage.css b/advanced/blockingpage.css index 7e11dbd0..a176e84c 100644 --- a/advanced/blockingpage.css +++ b/advanced/blockingpage.css @@ -1,136 +1,382 @@ -/* CSS Reset */ -html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } -article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } -body { line-height: 1; } -ol, ul { list-style: none; } -blockquote, q { quotes: none; } -blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } -table { border-collapse: collapse; border-spacing: 0; } -html { height: 100%; overflow-x: hidden; } +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ -/* General Style */ -a { color: rgba(0,60,120,0.95); text-decoration: none; } /* 1E3C5A */ -a:hover { color: rgba(210,120,0,0.95); transition-duration: .2s; } /* 255, 128, 0 */ -divs a { border-bottom: 1px dashed rgba(30,60,90,0.3); } -b { font-weight: bold; } -i { font-style: italic; } +/* Text Customisation Options ======> */ +.title:before { content: "Website Blocked"; } +.altBtn:before { content: "Why am I here?"; } +.linkPH:before { content: "About Pi-hole"; } +.linkEmail:before { content: "Contact Admin"; } -footer, pre, td { font-family: monospace; padding-left: 15px; } -/*body, header { background: #E1E1E1; }*/ +.add:before { content: "Info"; } +.add:after { content: "The domain is being whitelisted..."; } +.error:before, .unhandled:before { content: "Error"; } +.unhandled:after { content: "An unhandled exception occured. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; } +.success:before { content: "Success"; } +.success:after { content: "Website has been whitelisted! You may need to flush your DNS cache"; } + +.recentwl:before { content: "This site appears to have been recently whitelisted. Please flush your DNS cache and/or restart your browser."; } +.cname:before { content: "This site is an alias for "; } /* cname.com */ +.cname:after { content: ", which may be blocked by Pi-hole."; } + +.blacklist:before { content: "Manually Blacklisted"; } +.wildcard:before { content: "Manually Blacklisted by Wildcard"; } +.noblock:before { content: "Not found on any Blacklist"; } + +#bpBlock:before { content: "Access to the following website has been denied:"; } +#bpFlag:before { content: "This is primarily due to being flagged as:"; } + +#bpHelpTxt:before { content: "If you have an ongoing use for this website, please "; } +#bpHelpTxt a:before, #bpHelpTxt span:before { content: "ask the administrator"; } +#bpHelpTxt:after{ content: " of the Pi-hole on this network to have it whitelisted"; } + +#bpBack:before { content: "Back to safety"; } +#bpInfo:before { content: "Technical Info"; } +#bpFoundIn:before { content: "This site is found in "; } +#bpFoundIn span:after { content: " of "; } +#bpFoundIn:after { content: " lists:"; } +#bpWhitelist:before { content: "Whitelist"; } + +footer span:before { content: "Page generated on "; } + +/* Hide whitelisting form entirely */ +/* #bpWLButtons { display: none; } */ +/* Text Customisation Options <=============================== */ + +/* http://necolas.github.io/normalize.css ======> */ +html { font-family: sans-serif; line-height: 1.15; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } +body { margin: 0; } +article, aside, footer, header, nav, section { display: block; } +h1 { font-size: 2em; margin: 0.67em 0; } +figcaption, figure, main { display: block; } +figure { margin: 1em 40px; } +hr { box-sizing: content-box; height: 0; overflow: visible; } +pre { font-family: monospace, monospace; font-size: 1em; } +a { background-color: transparent; -webkit-text-decoration-skip: objects; } +a:active, a:hover { outline-width: 0; } +abbr[title] { border-bottom: none; text-decoration: underline; text-decoration: underline dotted; } +b, strong { font-weight: inherit; } +b, strong { font-weight: bolder; } +code, kbd, samp { font-family: monospace, monospace; font-size: 1em; } +dfn { font-style: italic; } +mark { background-color: #ff0; color: #000; } +small { font-size: 80%; } +sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } +sub { bottom: -0.25em; } +sup { top: -0.5em; } +audio, video { display: inline-block; } +audio:not([controls]) { display: none; height: 0; } +img { border-style: none; } +svg:not(:root) { overflow: hidden; } +button, input, optgroup, select, textarea { font-family: sans-serif; font-size: 100%; line-height: 1.15; margin: 0; } +button, input { overflow: visible; } +button, select { text-transform: none; } +button, html [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; } +button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { border-style: none; padding: 0; } +button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring { outline: 1px dotted ButtonText; } +fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } +legend { box-sizing: border-box; color: inherit; display: table; max-width: 100%; padding: 0; white-space: normal; } +progress { display: inline-block; vertical-align: baseline; } +textarea { overflow: auto; } +[type="checkbox"], [type="radio"] { box-sizing: border-box; padding: 0; } +[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { height: auto; } +[type="search"] { -webkit-appearance: textfield; outline-offset: -2px; } +[type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-decoration { -webkit-appearance: none; } +::-webkit-file-upload-button { -webkit-appearance: button; font: inherit; } +details, menu { display: block; } +summary { display: list-item; } +canvas { display: inline-block; } +template { display: none; } +[hidden] { display: none; } +/* Normalize.css <=============================== */ + +html { font-size: 62.5%; } + +a { color: #3c8dbc; text-decoration: none; } +a:hover { color: #72afda; text-decoration: underline; } +b { color: rgb(68,68,68); } +p { margin: 0; } + +label, .buttons a { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Touch device dark tap highlight */ +header h1 a, label, .buttons * { -webkit-tap-highlight-color: transparent; } + +/* Webkit Focus Glow */ +textarea, input, button { outline: none; } + +@font-face { + font-family: "Source Sans Pro"; + font-style: normal; + font-weight: 400; + src: local("Source Sans Pro"), local("SourceSansPro-Regular"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Regular.ttf") format("truetype"); +} + +@font-face { + font-family: "Source Sans Pro"; + font-style: normal; + font-weight: 700; + src: local("Source Sans Pro Bold"), local("SourceSansPro-Bold"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Bold.ttf") format("truetype"); +} body { - background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(190,190,190,0.95)); - background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(190,190,190,0.95)); - background-attachment: fixed; - color: rgba(64,64,64,0.95); - font: 14px, sans-serif; - line-height: 1em; + background: #dbdbdb url("/admin/img/boxed-bg.jpg") repeat fixed; + color: #333; + font: 1.4rem "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 2.2rem; +} + +/* User is greeted with a splash page when browsing to Pi-hole IP address */ +#splashpage { background: #222; color: rgba(255,255,255,0.7); text-align: center; } +#splashpage img { margin: 5px; width: 256px; } +#splashpage b { color: inherit; } + +#bpWrapper { + margin: 0 auto; + max-width: 1250px; + box-shadow: 0 0 8px rgba(0,0,0,0.5); +} + +@media only screen and (min-width: 1251px) { + #bpWrapper, footer { border-radius: 0 0 5px 5px; } + #bpAbout { border-right-width: 1px !important; } } header { - min-width: 320px; - width: 100%; - text-shadow: 0 1px rgba(255,255,255,0.6); - display: table; - table-layout: fixed; - border: 1px solid rgba(0,0,0,0.25); - border-top-color: rgba(255,255,255,0.85); - border-style: solid none; - background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(220,220,220,0.95)); - background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(220,220,220,0.95)); - box-shadow: 0 0 1px 1px rgba(0,0,0,0.04); + background: #3c8dbc; + display: table; + position: relative; + width: 100%; } -header h1, header div { - display: table-cell; - color: inherit; - font-weight: bold; - vertical-align: middle; - white-space: nowrap; - overflow: hidden; - box-sizing: border-box; +header h1, header h1 a, header .spc, header #bpAlt label { + display: table-cell; + color: #fff; + white-space: nowrap; + vertical-align: middle; + height: 50px; /* Must match #bpAbout top value */ } -header h1 { - font-size: 22px; - font-weight: bold; - width: 100%; - padding: 8px 0; - text-indent: 32px; - background: url("http://pi.hole/admin/img/logo.svg") left no-repeat; - background-size: 30px 22px; +h1 a { + background-color: rgba(0,0,0,0.1); + font-family: "Helvetica Neue", Helvetica, Arial ,sans-serif; + font-size: 2rem; + font-weight: normal; + min-width: 230px; + text-align: center; } -header h1 a, h1 a:hover { color: inherit; } -header .alt { width: 85px; font-size: 0.8em; padding-right: 4px; text-align: right; line-height: 1.25em; } -.active { color: green; } -.inactive { color: red; } +h1 a:hover, header #bpAlt:hover { background-color: rgba(0,0,0,0.12); color: inherit; text-decoration: none; } + +header .spc { width: 100%; } + +header #bpAlt label { + background: url("/admin/img/logo.svg") no-repeat center left 15px; + background-size: 15px 23px; + padding: 0 15px; + text-indent: 30px; +} + +[type=checkbox][id$="Toggle"] { display: none; } +[type=checkbox][id$="Toggle"]:checked ~ #bpAbout, +[type=checkbox][id$="Toggle"]:checked ~ #bpMoreInfo { + display: block; } + +/* Click anywhere else on screen to hide #bpAbout */ +#bpAboutToggle:checked { + display: block; + height: 300px; /* VH Fallback */ + height: 100vh; + left: 0; + top: 0; + opacity: 0; + position: absolute; + width: 100%; +} + +#bpAbout { + background: #3c8dbc; + border-bottom-left-radius: 5px; + border: 1px solid #FFF; + border-right-width: 0; + box-shadow: -1px 1px 1px rgba(0,0,0,0.12); + box-sizing: border-box; + display: none; + font-size: 1.7rem; + top: 50px; + position: absolute; + right: 0; + width: 280px; + z-index: 1; +} + +.aboutPH { + box-sizing: border-box; + color: rgba(255,255,255,0.8); + display: block; + padding: 10px; + width: 100%; + text-align: center; +} + +.aboutImg { + background: url("/admin/img/logo.svg") no-repeat center; + background-size: 90px 90px; + border: 3px solid rgba(255,255,255,0.2); + height: 90px; + margin: 0 auto; + padding: 2px; + width: 90px; +} + +.aboutPH p { margin: 10px 0; } +.aboutPH small { display: block; font-size: 1.2rem; } + +.aboutLink { + background: #fff; + border-top: 1px solid #ddd; + display: table; + font-size: 1.4rem; + text-align: center; + width: 100%; +} + +.aboutLink a { + display: table-cell; + padding: 14px; + min-width: 50%; +} main { - display: block; - width: 80%; - padding: 10px; - font-size: 1em; - background-color: rgba(255,255,255,0.85); - margin: 8px auto; - box-sizing: border-box; - border: 1px solid rgba(0,0,0,0.25); - box-shadow: 4px 4px rgba(0,0,0,0.1); - line-height: 1.2em; - border-radius: 8px; + background: #ecf0f5; + font-size: 1.65rem; + padding: 10px; } -h2 { /* Rgba is shared with .transparent th */ - font: 1.15em sans-serif; - background-color: rgba(255,0,0,0.4); - text-shadow: none; - line-height: 1.1em; - padding-bottom: 1px; - margin-top: 8px; - margin-bottom: 4px; - background: -webkit-linear-gradient(left, rgba(0,0,0,0.25), transparent 80%) no-repeat; - background: linear-gradient(to right, rgba(0,0,0,0.25), transparent 80%) no-repeat; - background-size: 100% 1px; - background-position: 0 17px; +#bpOutput { + background: #00c0ef; + border-radius: 3px; + border: 1px solid rgba(0,0,0,0.1); + color: #fff; + font-size: 1.4rem; + margin-bottom: 10px; + margin-top: 5px; + padding: 15px; } -h2:first-child { margin-top: 0; } -h2 ~ *:not(h2) { margin-left: 4px; } -li { padding: 2px 0; } -li::before { content: "\00BB\00a0"; } -li a { position: relative; top: 1px; } /* Center bullet-point arrows */ - -/* Button Style */ -.buttons a, button, input, .transparent th a { /* Swapped rgba is shared with input[type='url'] */ - display: inline-block; - color: rgba(32,32,32,0.9); - font-weight: bold; - text-align: center; - cursor: pointer; - text-shadow: 0 1px rgba(255,255,255,0.2); - line-height: 0.86em; - font-size: 1em; - padding: 4px 8px; - background: #FAFAFA; - background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.05), rgba(0,0,0,0.05)); - background-image: linear-gradient(to bottom, rgba(255,255,255,0.05), rgba(0,0,0,0.05)); - border: 1px solid rgba(0,0,0,0.25); - border-radius: 4px; - box-shadow: 0 1px 0 rgba(0,0,0,0.04); +#bpOutput:before { + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='14' viewBox='0 0 7 14'%3E%3Cpath fill='%23fff' d='M6,11a1.371,1.371,0,0,1,1,1v1a1.371,1.371,0,0,1-1,1H1a1.371,1.371,0,0,1-1-1V12a1.371,1.371,0,0,1,1-1H2V8H1A1.371,1.371,0,0,1,0,7V6A1.371,1.371,0,0,1,1,5H4A1.371,1.371,0,0,1,5,6v5H6ZM3.5,0A1.5,1.5,0,1,1,2,1.5,1.5,1.5,0,0,1,3.5,0Z'/%3E%3C/svg%3E") no-repeat center left; + display: block; + font-size: 1.8rem; + text-indent: 15px; } -.buttons { white-space: nowrap; width: 100%; display: table; } -.buttons33 { white-space: nowrap; width: 33.333%; display: table; text-align: center; margin-left: 33.333% } -.mini a { width: 50%; } -a.safe { background-color: rgba(0,220,0,0.5); } -button.safe { background-color: rgba(0,220,0,0.5); } -a.warn { background-color: rgba(220,0,0,0.5); } +#bpOutput.hidden { display: none; } +#bpOutput.success { background: #00a65a; } +#bpOutput.error { background: #dd4b39; } -.blocked a, .mini a { display: table-cell; } -.blocked a.safe50 { width: 50%; background-color: rgba(0,220,0,0.5); } -.blocked a.safe33 { width: 33.333%; background-color: rgba(0,220,0,0.5); } +.blockMsg, .flagMsg { + font: bold 1.8rem Consolas, Courier, monospace; + padding: 5px 10px 10px 10px; + text-indent: 15px; +} -/* Types of text */ -.msg { white-space: pre; overflow: auto; -webkit-overflow-scrolling: touch; display: block; line-height: 1.2em; font-weight: bold; font-size: 1.15em; margin: 4px 8px 8px 8px; white-space: pre-line; } +#bpHelpTxt { padding-bottom: 10px; } -footer { font-size: 0.8em; text-align: center; width: 87%; margin: 4px auto; } +.buttons { + border-spacing: 5px 0; + display: table; + width: 100%; +} + +.buttons * { + -moz-appearance: none; + -webkit-appearance: none; + border-radius: 3px; + border: 1px solid rgba(0,0,0,0.1); + box-sizing: content-box; + display: table-cell; + font-size: 1.65rem; + margin-right: 5px; + min-height: 20px; + padding: 6px 12px; + position: relative; + text-align: center; + vertical-align: top; + white-space: nowrap; + width: auto; +} + +#bpButtons * { width: 50%; color: #FFF; } +#bpBack { background-color: #00a65a; } +#bpInfo { background-color: #3c8dbc; } +#bpWhitelist { background-color: #dd4b39; } + +.buttons a { text-decoration: none; } + +/* Button hover dark overlay */ +.buttons *:not(input):not([disabled]):hover { + background-image: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.1)); + color: #FFF; +} + +/* Button active shadow inset */ +.buttons *:not([disabled]):not(input):active { + box-shadow: inset 0 3px 5px rgba(0,0,0,0.125); +} + +/* Input border colour */ +.buttons *:not([disabled]):hover, .buttons input:focus { + border-color: rgba(0,0,0,0.25); +} + +:-ms-input-placeholder { color: rgba(51,51,51,0.8) !important; } +input[type=password] { font-size: 1.4rem; } + +.buttons [type=password][disabled] { color: rgba(0,0,0,1) !important; } +.buttons [disabled] { color: rgba(0,0,0,0.55) !important; background-color: #e3e3e3 !important; } + +@keyframes slidein { from { max-height: 0; opacity: 0; } to { max-height: 300px; opacity: 1; } } +#bpMoreToggle:checked ~ #bpMoreInfo { display: block; margin-top: 8px; animation: slidein 0.05s linear; } +#bpMoreInfo { display: none; margin-top: 10px; } + +#bpQueryOutput { + font-size: 1.2rem; + line-height: 1.65rem; + margin: 5px 0 0 0; + overflow: auto; + padding: 0 5px; + -webkit-overflow-scrolling: touch; +} + +#bpQueryOutput span { margin-right: 4px; } + +#bpWLButtons { width: auto; margin-top: 10px; } +#bpWLButtons * { display: inline-block; } +#bpWLDomain { display: none; } +#bpWLPassword { width: 160px; } +#bpWhitelist { color: #fff; cursor: pointer; } + +footer { + background: #fff; + border-top: 1px solid #d2d6de; + color: #444; + font: 1.2rem Consolas, Courier, monospace; + padding: 8px; +} + +/* Responsive small-screen content */ +@media only screen and (max-width: 500px) { + h1 a { font-size: 1.8rem; min-width: 170px; } + footer span:before { content: "Generated "; } + footer span { display: block; } +} From 551add5f447b25e7c5c3e54c325b287212dfd9c8 Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Tue, 2 May 2017 17:06:31 +1000 Subject: [PATCH 02/19] Update index.php * An "About Pi-hole" link on the block page provides an ELI5 explanation to those not familiar with Pi-hole * An email contact link on the block page provides users of your Pi-hole with a means to easily get in touch with you * Browsing to your Pi-hole's address will show a simple "landing page", which can be replaced by adding "landing.php" within "/var/www/html" * Users manually browsing to file/image based content (i.e: non HTML based content) on blocked sites will be greeted with a small "Blocked by Pi-hole" image * Sites that are manually blacklisted will display a notice of this on the block page * Sites that aren't directly blocked, but have a CNAME record, will show a notification on the block page (e.g: If raw.githubusercontent.com is not blocked, but github.map.fastly.net is) * On the block page, "Back to Safety" now directs the user to "about:home" if Javascript is disabled * Whitelisting is disabled for installs without a password, or if a client does not have Javascript * Known issues: * Admin Console needs a text field under "Web User Interface" where the admin can enter a preferred contact email when a site needs to be whitelisted, to be saved to setupVars.conf with the key "ADMIN_EMAIL" * Admin Console needs a text field under "Networking" where the admin can enter their Pi-hole's externally contactable FQDN, allowing access to their landing page when browsing to mypi.duckdns.org, to be saved to setupVars.conf with the key "FQDN" * I am not aware of expected output of `$_SERVER["VIRTUAL_HOST"]`, so I have assumed it should be filtered as if it's a domain --- advanced/index.php | 487 ++++++++++++++++++++++++++++----------------- 1 file changed, 299 insertions(+), 188 deletions(-) diff --git a/advanced/index.php b/advanced/index.php index bfc44a1d..aeb607ca 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -1,224 +1,335 @@ SERVER_NAME header output does not appear to be valid: ".$_SERVER["SERVER_NAME"].""); } -// Retrieve server URI extension (EG: jpg, exe, php) -ini_set('pcre.recursion_limit',100); -$uriExt = pathinfo($uri, PATHINFO_EXTENSION); - -// Define which URL extensions get rendered as "Website Blocked" -$webExt = array('asp', 'htm', 'html', 'php', 'rss', 'xml'); - -// Get IPv4 and IPv6 addresses from setupVars.conf (if available) +// Get values from setupVars.conf $setupVars = parse_ini_file("/etc/pihole/setupVars.conf"); -$ipv4 = isset($setupVars["IPV4_ADDRESS"]) ? explode("/", $setupVars["IPV4_ADDRESS"])[0] : $_SERVER['SERVER_ADDR']; -$ipv6 = isset($setupVars["IPV6_ADDRESS"]) ? explode("/", $setupVars["IPV6_ADDRESS"])[0] : $_SERVER['SERVER_ADDR']; +$svFQDN = (!empty($setupVars["FQDN"]) && validate_server_name($setupVars["FQDN"]) === TRUE) ? $setupVars["FQDN"] : ""; +$svPasswd = !empty($setupVars["WEBPASSWORD"]); +$svEmail = (!empty($setupVars["ADMIN_EMAIL"]) && filter_var($setupVars["ADMIN_EMAIL"], FILTER_VALIDATE_EMAIL)) ? $setupVars["ADMIN_EMAIL"] : ""; +unset($setupVars); -$AUTHORIZED_HOSTNAMES = array( - $ipv4, - $ipv6, - str_replace(array("[","]"), array("",""), $_SERVER["SERVER_ADDR"]), - "pi.hole", - "localhost"); -// Allow user set virtual hostnames -$virtual_host = getenv('VIRTUAL_HOST'); -if (!empty($virtual_host)) - array_push($AUTHORIZED_HOSTNAMES, $virtual_host); +// Set landing page name, found within /var/www/html/ +$landPage = "landing.php"; -// Immediately quit since we didn't block this page (the IP address or pi.hole is explicitly requested) -if(validIP($serverName) || in_array($serverName,$AUTHORIZED_HOSTNAMES)) -{ - http_response_code(404); - die(); +// Set empty array for hostnames to be accepted as self address for splash page +$authorizedHosts = []; + +// Append FQDN to $authorizedHosts +if (!empty($svFQDN)) array_push($authorizedHosts, $svFQDN); + +// Append virtual hostname to $authorizedHosts +if (!empty($_SERVER["VIRTUAL_HOST"])) { + if (validate_server_name($_SERVER["VIRTUAL_HOST"]) === TRUE) { + array_push($authorizedHosts, $_SERVER["VIRTUAL_HOST"]); + } else { + die("[ERROR]: VIRTUAL_HOST header output does not appear to be valid: ".$_SERVER["VIRTUAL_HOST"].""); + } } -if(in_array($uriExt, $webExt) || empty($uriExt)) -{ - // Requested resource has an extension listed in $webExt - // or no extension (index access to some folder incl. the root dir) - $showPage = true; -} -else -{ - // Something else - $showPage = false; +// Set which extension types get rendered as "Website Blocked" (Including "" for index file extensions) +$validExtTypes = array("asp", "htm", "html", "php", "rss", "xml", ""); + +// Get extension of current URL +$currentUrlExt = pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION); + +// Set mobile friendly viewport +$viewPort = ''; + +// Set response header +function setHeader($type = "x") { + header("X-Pi-hole: A black hole for Internet advertisements."); + if (isset($type) && $type === "js") header("Content-Type: application/javascript"); } -// Handle incoming URI types -if (!$showPage) -{ -?> - - - - - - - - + $viewPort + +
Pi-hole: Your black hole for Internet advertisements + "; + $pageType = is_file(getcwd()."/$landPage") ? include $landPage : "$splashPage"; + unset($serverName, $svFQDN, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort); + exit($pageType); +} elseif ($currentUrlExt === "js") { + // Set Javascript redirect for blocked sources + exit(setHeader("js").'var x = "Pi-hole: A black hole for Internet advertisements."'); +} elseif (strpos($_SERVER["REQUEST_URI"], "?") !== FALSE && isset($_SERVER["HTTP_REFERER"])) { + // Set blank image upon receiving REQUEST_URI w/ query string & HTTP_REFERRER (Presumably from iframe) + exit(setHeader().' + + + '); +} elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) { + // Set svg image upon receiving non $validExtTypes URL extension or query string (Presumably not from an iframe) + $blockImg = 'Blocked by Pi-hole'; + exit(setHeader()." + $viewPort + $blockImg + "); } -// Get Pi-hole version -$piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0'); +/* Start processing block page from here */ -// Don't show the URI if it is the root directory -if($uri == "/") -{ - $uri = ""; +// Get Pi-hole core branch name +$phBranch = exec("cd /etc/.pihole/ && git rev-parse --abbrev-ref HEAD"); +if ($phBranch !== "master") { + error_reporting(E_ALL); + ini_set("display_errors", 1); + ini_set("display_startup_errors", 1); } +// Validate SERVER_IP output +if (filter_var($_SERVER['SERVER_ADDR'], FILTER_VALIDATE_IP)) { + $serverAddr = $_SERVER["SERVER_ADDR"]; +} else { + die("[ERROR]: SERVER_IP header output does not appear to be valid: ".$_SERVER["SERVER_ADDR"].""); +} + +// Determine placeholder text based off $svPasswd presence +$wlPlaceHolder = empty($svPasswd) ? "No admin password set" : "Javascript disabled"; + +// Get admin email address +$bpAskAdmin = !empty($svEmail) ? '' : ""; + +// Determine if at least one block list has been generated +if (empty(glob("/etc/pihole/list.0.*.domains"))) die("[ERROR]: There are no domain lists generated lists within /etc/pihole/! Please update gravity by running pihole -g, or repair Pi-hole using pihole -r."); + +// Get contents of adlist.list +$adLists = is_file("/etc/pihole/adlists.list") ? "/etc/pihole/adlists.list" : "/etc/pihole/adlists.default"; +if (!is_file($adLists)) die("[ERROR]: Unable to find file: $adLists"); + +// Get all URLs starting with "http" or "www" from $adLists and re-index array numerically +$adlistsUrls = array_values(preg_grep("/(^http)|(^www)/i", file($adLists, FILE_IGNORE_NEW_LINES))); +if (empty($adlistsUrls)) die("[ERROR]: There are no adlist URL's found within $adLists"); +$adlistsCount = count($adlistsUrls) + 3; // +1 because array starts at 0, +2 for Blacklist & Wildcard lists + +// Get results of queryads.php exact search +ini_set("default_socket_timeout", 3); +function queryAds($serverName) { + $preQueryTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]; + $queryAds = file("http://127.0.0.1/admin/scripts/pi-hole/php/queryads.php?domain=$serverName&exact", FILE_IGNORE_NEW_LINES); + $queryTime = sprintf("%.0f", (microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]) - $preQueryTime); + try { + if ($queryTime >= ini_get("default_socket_timeout")) { + throw new Exception ("Connection timeout (".ini_get("default_socket_timeout")."s)"); + } elseif ($queryAds[0][0] === ":") { + if (strpos($queryAds[0], "Invalid") !== FALSE) throw new Exception ("Invalid Domain ($serverName)"); + if (strpos($queryAds[0], "No exact") !== FALSE) return array("0" => "none"); + throw new Exception ("Unhandled error message ($queryAds[0])"); + } elseif ($queryAds[0][0] !== "/") { + throw new Exception ("Unexpected output ($queryAds[0])"); + } + return $queryAds; + } catch (Exception $e) { + return array("0" => "error", "1" => $e->getMessage()); + } +} +$queryAds = queryAds($serverName); + +if ($queryAds[0] === "error") { + die("[ERROR]: Unable to parse results from queryads.php: ".$queryAds[1].""); +} + +// Filter, sort, and count $queryAds array +if ($queryAds[0] !== "none") { + $queryAds = preg_replace("/(\/etc\/pihole\/)|(\/etc\/dnsmasq\.d\/)/", "", $queryAds); + $queryAds = preg_replace("/(^list\.)|(\..*domains)/", "", $queryAds); + $featuredTotal = count($queryAds); +} + +// Determine if domain has been blacklisted or wildcarded +if ($queryAds[0] === "blacklist.txt") { + $intBlacklist = array("π" => $queryAds[0]); + $queryAds[0] = "π"; // Manually blacklisted sites do not have a number + $notableFlagClass = "blacklist"; +} elseif ($queryAds[0] === "03-pihole-wildcard.conf") { + $intBlacklist = array("π" => $queryAds[0]); + $queryAds[0] = "π"; + $notableFlagClass = "wildcard"; +} elseif ($queryAds[0] === "none") { + $featuredTotal = "0"; + $notableFlagClass = "noblock"; + + // Determine appropriate info message if CNAME exists + $dnsRecord = dns_get_record("$serverName")[0]; + if (array_key_exists("target", $dnsRecord)) { + $wlInfo = $dnsRecord['target']; + } else { + $wlInfo = "recentwl"; + } +} + +// Merge $intBlacklist with $adlistsUrls if domain has been blacklisted or wildcarded +if (isset($intBlacklist)) $adlistsUrls = array_merge($intBlacklist, $adlistsUrls); + +// Set #bpOutput notification +$wlOutputClass = (isset($wlInfo) && $wlInfo === "recentwl") ? $wlInfo : "hidden"; +$wlOutput = (isset($wlInfo) && $wlInfo !== "recentwl") ? "$wlInfo" : ""; + +// Get Pi-hole core version +if ($phBranch !== "master") { + $phVersion = exec("cd /etc/.pihole/ && git describe --long --dirty --tags"); + $execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]; +} else { + $phVersion = exec("cd /etc/.pihole/ && git describe --tags --abbrev=0"); +} ?> + - - Website Blocked - - - - + + + + + + + + ● <?=$serverName ?> + + - +
-

Website Blocked

+

+ +

+
+ + +
+
+
+

Open Source Ad Blocker + Designed for Raspberry Pi +

+
+ +
+ +
+ +
+
-
Access to the following site has been blocked:
-
-
If you have an ongoing use for this website, please ask the owner of the Pi-hole in your network to have it whitelisted.
- - - - This page is blocked because it is explicitly contained within the following block list(s): -
- - +
+
+

+
+ +
+

+
+ +
+
+ + 0) echo ''; ?> +
+ +
+ +
 0) foreach ($queryAds as $num) { echo "[$num]:$adlistsUrls[$num]\n"; } ?>
+
+ + +
+
-
Generated by Pi-hole
- - - +
. Pi-hole ()
+
+ - - + + From dfb5c37d984139537b6fe53c52964125c2c6d724 Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Tue, 2 May 2017 17:21:40 +1000 Subject: [PATCH 03/19] Delete index.js --- advanced/index.js | 1 - 1 file changed, 1 deletion(-) delete mode 100644 advanced/index.js diff --git a/advanced/index.js b/advanced/index.js deleted file mode 100644 index c9da5aff..00000000 --- a/advanced/index.js +++ /dev/null @@ -1 +0,0 @@ -var x = "Pi-hole: A black hole for Internet advertisements." From 356c70cdae29c49d95933434cb9820ca42ccf6af Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Tue, 2 May 2017 17:24:07 +1000 Subject: [PATCH 04/19] Update lighttpd.conf.debian * Disable `include-conf-enabled.pl`, as blindly enabling HTTPS (as Let's Encrypt does by having a file in that folder) creates Block Page inefficiencies * Make Block page handle JS request rewrite, allowing users to better utilise their `lighttpd` service * Make Block page handle debugging Pi-hole header * Make Block page redirect users from `pi.hole` to `http://pi.hole/admin` --- advanced/lighttpd.conf.debian | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/advanced/lighttpd.conf.debian b/advanced/lighttpd.conf.debian index 3b57756e..19b8b186 100644 --- a/advanced/lighttpd.conf.debian +++ b/advanced/lighttpd.conf.debian @@ -50,7 +50,7 @@ compress.filetype = ( "application/javascript", "text/css", "text/html # default listening port for IPv6 falls back to the IPv4 port include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port include_shell "/usr/share/lighttpd/create-mime.assign.pl" -include_shell "/usr/share/lighttpd/include-conf-enabled.pl" +#include_shell "/usr/share/lighttpd/include-conf-enabled.pl" # If the URL starts with /admin, it is the Web interface $HTTP["url"] =~ "^/admin/" { @@ -59,21 +59,9 @@ $HTTP["url"] =~ "^/admin/" { "X-Pi-hole" => "The Pi-hole Web interface is working!", "X-Frame-Options" => "DENY" ) -} - -# Rewite js requests, must be out of $HTTP block due to bug #2526 -url.rewrite = ( "^(?!/admin/).*\.js$" => "pihole/index.js" ) - -# If the URL does not start with /admin, then it is a query for an ad domain -$HTTP["url"] =~ "^(?!/admin)/.*" { - # Create a response header for debugging using curl -I - setenv.add-response-header = ( "X-Pi-hole" => "A black hole for Internet advertisements." ) -} - -# Entering just "pi.hole" into a browser redirects to "pi.hole/admin/" -$HTTP["host"] == "pi.hole" { - $HTTP["url"] == "/" { - url.redirect = ( "" => "/admin/" ) + $HTTP["url"] =~ ".ttf$" { + # Allow Block Page access to local fonts + setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" ) } } From 74b15d10d98fa3e1c42b98cb26c81a09408389dd Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Tue, 2 May 2017 17:24:55 +1000 Subject: [PATCH 05/19] Update lighttpd.conf.fedora Mirror changes found in `lighttpd.conf.debian` --- advanced/lighttpd.conf.fedora | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/advanced/lighttpd.conf.fedora b/advanced/lighttpd.conf.fedora index fd856fbb..773f0142 100644 --- a/advanced/lighttpd.conf.fedora +++ b/advanced/lighttpd.conf.fedora @@ -75,22 +75,13 @@ fastcgi.server = ( ".php" => # If the URL starts with /admin, it is the Web interface $HTTP["url"] =~ "^/admin/" { # Create a response header for debugging using curl -I - setenv.add-response-header = ( "X-Pi-hole" => "The Pi-hole Web interface is working!" ) -} - -# Rewite js requests, must be out of $HTTP block due to bug #2526 -url.rewrite = ( "^(?!/admin/).*\.js$" => "pihole/index.js" ) - -# If the URL does not start with /admin, then it is a query for an ad domain -$HTTP["url"] =~ "^(?!/admin)/.*" { - # Create a response header for debugging using curl -I - setenv.add-response-header = ( "X-Pi-hole" => "A black hole for Internet advertisements." ) -} - -# Entering just "pi.hole" into a browser redirects to "pi.hole/admin/" -$HTTP["host"] == "pi.hole" { - $HTTP["url"] == "/" { - url.redirect = ( "" => "/admin/" ) + setenv.add-response-header = ( + "X-Pi-hole" => "The Pi-hole Web interface is working!", + "X-Frame-Options" => "DENY" + ) + $HTTP["url"] =~ ".ttf$" { + # Allow Block Page access to local fonts + setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" ) } } From 7bcea98d0cc7fc43298c3945b3f262e9f0e73019 Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Tue, 2 May 2017 17:28:51 +1000 Subject: [PATCH 06/19] Do not activate letsencrypt.conf if found --- advanced/lighttpd.conf.debian | 1 + 1 file changed, 1 insertion(+) diff --git a/advanced/lighttpd.conf.debian b/advanced/lighttpd.conf.debian index 19b8b186..47f6af02 100644 --- a/advanced/lighttpd.conf.debian +++ b/advanced/lighttpd.conf.debian @@ -51,6 +51,7 @@ compress.filetype = ( "application/javascript", "text/css", "text/html include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port include_shell "/usr/share/lighttpd/create-mime.assign.pl" #include_shell "/usr/share/lighttpd/include-conf-enabled.pl" +include_shell "find /etc/lighttpd/conf-enabled -name '*.conf' -a ! -name 'letsencrypt.conf' -printf 'include \"%p\"\n' 2>/dev/null" # If the URL starts with /admin, it is the Web interface $HTTP["url"] =~ "^/admin/" { From a8818c16d87d34ebf5fdc2650151b7a5a1a7e7df Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Tue, 2 May 2017 19:51:13 +1000 Subject: [PATCH 07/19] Fixed unnecessary usage of !important --- advanced/blockingpage.css | 49 ++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/advanced/blockingpage.css b/advanced/blockingpage.css index a176e84c..1af96cda 100644 --- a/advanced/blockingpage.css +++ b/advanced/blockingpage.css @@ -11,12 +11,12 @@ .linkPH:before { content: "About Pi-hole"; } .linkEmail:before { content: "Contact Admin"; } -.add:before { content: "Info"; } -.add:after { content: "The domain is being whitelisted..."; } -.error:before, .unhandled:before { content: "Error"; } -.unhandled:after { content: "An unhandled exception occured. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; } -.success:before { content: "Success"; } -.success:after { content: "Website has been whitelisted! You may need to flush your DNS cache"; } +#bpOutput.add:before { content: "Info"; } +#bpOutput.add:after { content: "The domain is being whitelisted..."; } +#bpOutput.error:before, .unhandled:before { content: "Error"; } +#bpOutput.unhandled:after { content: "An unhandled exception occured. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; } +#bpOutput.success:before { content: "Success"; } +#bpOutput.success:after { content: "Website has been whitelisted! You may need to flush your DNS cache"; } .recentwl:before { content: "This site appears to have been recently whitelisted. Please flush your DNS cache and/or restart your browser."; } .cname:before { content: "This site is an alias for "; } /* cname.com */ @@ -101,13 +101,14 @@ b { color: rgb(68,68,68); } p { margin: 0; } label, .buttons a { - cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } +label, .buttons *:not([disabled]) { cursor: pointer; } + /* Touch device dark tap highlight */ header h1 a, label, .buttons * { -webkit-tap-highlight-color: transparent; } @@ -146,11 +147,6 @@ body { box-shadow: 0 0 8px rgba(0,0,0,0.5); } -@media only screen and (min-width: 1251px) { - #bpWrapper, footer { border-radius: 0 0 5px 5px; } - #bpAbout { border-right-width: 1px !important; } -} - header { background: #3c8dbc; display: table; @@ -316,12 +312,7 @@ main { width: auto; } -#bpButtons * { width: 50%; color: #FFF; } -#bpBack { background-color: #00a65a; } -#bpInfo { background-color: #3c8dbc; } -#bpWhitelist { background-color: #dd4b39; } - -.buttons a { text-decoration: none; } +.buttons a:hover { text-decoration: none; } /* Button hover dark overlay */ .buttons *:not(input):not([disabled]):hover { @@ -339,11 +330,16 @@ main { border-color: rgba(0,0,0,0.25); } -:-ms-input-placeholder { color: rgba(51,51,51,0.8) !important; } -input[type=password] { font-size: 1.4rem; } +#bpButtons * { width: 50%; color: #FFF; } +#bpBack { background-color: #00a65a; } +#bpInfo { background-color: #3c8dbc; } +#bpWhitelist { background-color: #dd4b39; } -.buttons [type=password][disabled] { color: rgba(0,0,0,1) !important; } -.buttons [disabled] { color: rgba(0,0,0,0.55) !important; background-color: #e3e3e3 !important; } +#blockpage .buttons [type=password][disabled] { color: rgba(0,0,0,1); } +#blockpage .buttons [disabled] { color: rgba(0,0,0,0.55); background-color: #e3e3e3; } +#blockpage .buttons [type=password]:-ms-input-placeholder { color: rgba(51,51,51,0.8); } + +input[type=password] { font-size: 1.5rem; } @keyframes slidein { from { max-height: 0; opacity: 0; } to { max-height: 300px; opacity: 1; } } #bpMoreToggle:checked ~ #bpMoreInfo { display: block; margin-top: 8px; animation: slidein 0.05s linear; } @@ -364,7 +360,7 @@ input[type=password] { font-size: 1.4rem; } #bpWLButtons * { display: inline-block; } #bpWLDomain { display: none; } #bpWLPassword { width: 160px; } -#bpWhitelist { color: #fff; cursor: pointer; } +#bpWhitelist { color: #fff; } footer { background: #fff; @@ -374,9 +370,14 @@ footer { padding: 8px; } -/* Responsive small-screen content */ +/* Responsive Content */ @media only screen and (max-width: 500px) { h1 a { font-size: 1.8rem; min-width: 170px; } footer span:before { content: "Generated "; } footer span { display: block; } } + +@media only screen and (min-width: 1251px) { + #bpWrapper, footer { border-radius: 0 0 5px 5px; } + #bpAbout { border-right-width: 1px; } +} From f2016f26d72563b14b461ab1c87b09fa6270fc7f Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Wed, 3 May 2017 15:29:43 +1000 Subject: [PATCH 08/19] Updated splash page CSS href --- advanced/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/index.php b/advanced/index.php index aeb607ca..78ca7b17 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -70,7 +70,7 @@ if ($serverName === "pi.hole") { $splashPage = " $viewPort - +
Pi-hole: Your black hole for Internet advertisements "; $pageType = is_file(getcwd()."/$landPage") ? include $landPage : "$splashPage"; From 4d731ca30bfe5d6f39fd8a4ea531fab5174cd217 Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Wed, 3 May 2017 15:33:50 +1000 Subject: [PATCH 09/19] Updated landing page location During development, I had the location of `/pihole/index.php` as just `/index.php`. Just correcting some changes! --- advanced/index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/index.php b/advanced/index.php index 78ca7b17..7fd3c44b 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -29,8 +29,8 @@ $svPasswd = !empty($setupVars["WEBPASSWORD"]); $svEmail = (!empty($setupVars["ADMIN_EMAIL"]) && filter_var($setupVars["ADMIN_EMAIL"], FILTER_VALIDATE_EMAIL)) ? $setupVars["ADMIN_EMAIL"] : ""; unset($setupVars); -// Set landing page name, found within /var/www/html/ -$landPage = "landing.php"; +// Set landing page name, found within /var/www/html/pihole/ +$landPage = "../landing.php"; // Set empty array for hostnames to be accepted as self address for splash page $authorizedHosts = []; From 173ad339bb9470b15c71b9980d5d1bb216f9615c Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Mon, 8 May 2017 19:33:45 +1000 Subject: [PATCH 10/19] Update CSS location --- advanced/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/index.php b/advanced/index.php index 7fd3c44b..82d46d52 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -214,7 +214,7 @@ if ($phBranch !== "master") { - + ● <?=$serverName ?> '); } elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) { - // Set svg image upon receiving non $validExtTypes URL extension or query string (Presumably not from an iframe) + // Serve SVG upon receiving non $validExtTypes URL extension or query string (e.g: not an iframe of a blocked domain) $blockImg = 'Blocked by Pi-hole'; exit(setHeader()." $viewPort @@ -94,89 +83,86 @@ if ($serverName === "pi.hole") { "); } -/* Start processing block page from here */ - -// Get Pi-hole core branch name -$phBranch = exec("cd /etc/.pihole/ && git rev-parse --abbrev-ref HEAD"); -if ($phBranch !== "master") { - error_reporting(E_ALL); - ini_set("display_errors", 1); - ini_set("display_startup_errors", 1); -} - -// Validate SERVER_IP output -if (filter_var($_SERVER['SERVER_ADDR'], FILTER_VALIDATE_IP)) { - $serverAddr = $_SERVER["SERVER_ADDR"]; -} else { - die("[ERROR]: SERVER_IP header output does not appear to be valid: ".$_SERVER["SERVER_ADDR"].""); -} +/* Start processing Block Page from here */ // Determine placeholder text based off $svPasswd presence $wlPlaceHolder = empty($svPasswd) ? "No admin password set" : "Javascript disabled"; -// Get admin email address +// Define admin email address text $bpAskAdmin = !empty($svEmail) ? '' : ""; // Determine if at least one block list has been generated -if (empty(glob("/etc/pihole/list.0.*.domains"))) die("[ERROR]: There are no domain lists generated lists within /etc/pihole/! Please update gravity by running pihole -g, or repair Pi-hole using pihole -r."); +if (empty(glob("/etc/pihole/list.0.*.domains"))) + die("[ERROR] There are no domain lists generated lists within /etc/pihole/! Please update gravity by running pihole -g, or repair Pi-hole using pihole -r."); -// Get contents of adlist.list -$adLists = is_file("/etc/pihole/adlists.list") ? "/etc/pihole/adlists.list" : "/etc/pihole/adlists.default"; -if (!is_file($adLists)) die("[ERROR]: Unable to find file: $adLists"); +// Set location of adlists file +if (is_file("/etc/pihole/adlists.list")) { + $adLists = "/etc/pihole/adlists.list"; +} elseif (is_file("/etc/pihole/adlists.default")) { + $adLists = "/etc/pihole/adlists.default"; +} else { + die("[ERROR] File not found: /etc/pihole/adlists.list"); +} -// Get all URLs starting with "http" or "www" from $adLists and re-index array numerically +// Get all URLs starting with "http" or "www" from adlists and re-index array numerically $adlistsUrls = array_values(preg_grep("/(^http)|(^www)/i", file($adLists, FILE_IGNORE_NEW_LINES))); -if (empty($adlistsUrls)) die("[ERROR]: There are no adlist URL's found within $adLists"); -$adlistsCount = count($adlistsUrls) + 3; // +1 because array starts at 0, +2 for Blacklist & Wildcard lists + +if (empty($adlistsUrls)) + die("[ERROR]: There are no adlist URL's found within $adLists"); + +// Get total number of blocklists (Including Whitelist, Blacklist & Wildcard lists) +$adlistsCount = count($adlistsUrls) + 3; // Get results of queryads.php exact search ini_set("default_socket_timeout", 3); function queryAds($serverName) { + // Determine the time it takes while querying adlists $preQueryTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]; - $queryAds = file("http://127.0.0.1/admin/scripts/pi-hole/php/queryads.php?domain=$serverName&exact", FILE_IGNORE_NEW_LINES); + $queryAds = file("http://127.0.0.1/admin/scripts/pi-hole/php/queryads.php?domain=$serverName&bp", FILE_IGNORE_NEW_LINES); + $queryAds = array_values(array_filter(preg_replace("/data:\s+/", "", $queryAds))); $queryTime = sprintf("%.0f", (microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]) - $preQueryTime); + + // Exception Handling try { if ($queryTime >= ini_get("default_socket_timeout")) { throw new Exception ("Connection timeout (".ini_get("default_socket_timeout")."s)"); - } elseif ($queryAds[0][0] === ":") { - if (strpos($queryAds[0], "Invalid") !== FALSE) throw new Exception ("Invalid Domain ($serverName)"); - if (strpos($queryAds[0], "No exact") !== FALSE) return array("0" => "none"); + } elseif (!strpos($queryAds[0], ".") !== false) { + if (strpos($queryAds[0], "No exact results") !== FALSE) return array("0" => "none"); throw new Exception ("Unhandled error message ($queryAds[0])"); - } elseif ($queryAds[0][0] !== "/") { - throw new Exception ("Unexpected output ($queryAds[0])"); } return $queryAds; } catch (Exception $e) { return array("0" => "error", "1" => $e->getMessage()); } + } + $queryAds = queryAds($serverName); if ($queryAds[0] === "error") { die("[ERROR]: Unable to parse results from queryads.php: ".$queryAds[1].""); -} - -// Filter, sort, and count $queryAds array -if ($queryAds[0] !== "none") { - $queryAds = preg_replace("/(\/etc\/pihole\/)|(\/etc\/dnsmasq\.d\/)/", "", $queryAds); - $queryAds = preg_replace("/(^list\.)|(\..*domains)/", "", $queryAds); +} else { $featuredTotal = count($queryAds); + + // Place results into key => value array + $queryResults = null; + foreach ($queryAds as $str) { + $value = explode(" ", $str); + @$queryResults[$value[0]] .= "$value[1]"; + } } -// Determine if domain has been blacklisted or wildcarded -if ($queryAds[0] === "blacklist.txt") { - $intBlacklist = array("π" => $queryAds[0]); - $queryAds[0] = "π"; // Manually blacklisted sites do not have a number +// Determine if domain has been blacklisted, whitelisted, wildcarded or CNAME blocked +if (strpos($queryAds[0], "blacklist") !== FALSE) { $notableFlagClass = "blacklist"; -} elseif ($queryAds[0] === "whitelist.txt") { - $intBlacklist = array("π" => $queryAds[0]); - $queryAds[0] = "π"; + $adlistsUrls = array("π" => substr($queryAds[0], 2)); +} elseif (strpos($queryAds[0], "whitelist") !== FALSE) { $notableFlagClass = "noblock"; + $adlistsUrls = array("π" => substr($queryAds[0], 2)); $wlInfo = "recentwl"; -} elseif ($queryAds[0] === "03-pihole-wildcard.conf") { - $intBlacklist = array("π" => $queryAds[0]); - $queryAds[0] = "π"; +} elseif (strpos($queryAds[0], "wildcard") !== FALSE) { $notableFlagClass = "wildcard"; + $adlistsUrls = array("π" => substr($queryAds[0], 2)); } elseif ($queryAds[0] === "none") { $featuredTotal = "0"; $notableFlagClass = "noblock"; @@ -190,38 +176,34 @@ if ($queryAds[0] === "blacklist.txt") { } } -// Merge $intBlacklist with $adlistsUrls if domain has been blacklisted or wildcarded -if (isset($intBlacklist)) $adlistsUrls = array_merge($intBlacklist, $adlistsUrls); - // Set #bpOutput notification $wlOutputClass = (isset($wlInfo) && $wlInfo === "recentwl") ? $wlInfo : "hidden"; $wlOutput = (isset($wlInfo) && $wlInfo !== "recentwl") ? "$wlInfo" : ""; -// Get Pi-hole core version -if ($phBranch !== "master") { - $phVersion = exec("cd /etc/.pihole/ && git describe --long --dirty --tags"); - $execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]; -} else { - $phVersion = exec("cd /etc/.pihole/ && git describe --tags --abbrev=0"); -} +// Get Pi-hole Core version +$phVersion = exec("cd /etc/.pihole/ && git describe --long --tags"); + +// Print $execTime on development branches +if (substr_count("-", $phVersion) != "1") + $execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]; ?> - + - - + + ● <?=$serverName ?> - +