From 19da9999dfa6f7534033cb4364adef5f7c0cc885 Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Mon, 9 Dec 2019 16:23:57 +0200 Subject: [PATCH 01/58] New favicons. Signed-off-by: XhmikosR --- img/favicon.png | Bin 4272 -> 0 bytes img/favicons/android-chrome-192x192.png | Bin 0 -> 4009 bytes img/favicons/android-chrome-512x512.png | Bin 0 -> 9646 bytes img/favicons/apple-touch-icon.png | Bin 0 -> 3776 bytes img/favicons/favicon-16x16.png | Bin 0 -> 508 bytes img/favicons/favicon-32x32.png | Bin 0 -> 1082 bytes img/favicons/favicon.ico | Bin 0 -> 15086 bytes img/favicons/manifest.json | 19 +++++++++++++++++++ img/favicons/mstile-150x150.png | Bin 0 -> 3042 bytes img/favicons/safari-pinned-tab.svg | 1 + scripts/pi-hole/php/header.php | 16 ++++++++-------- 11 files changed, 28 insertions(+), 8 deletions(-) delete mode 100644 img/favicon.png create mode 100644 img/favicons/android-chrome-192x192.png create mode 100644 img/favicons/android-chrome-512x512.png create mode 100644 img/favicons/apple-touch-icon.png create mode 100644 img/favicons/favicon-16x16.png create mode 100644 img/favicons/favicon-32x32.png create mode 100644 img/favicons/favicon.ico create mode 100644 img/favicons/manifest.json create mode 100644 img/favicons/mstile-150x150.png create mode 100644 img/favicons/safari-pinned-tab.svg diff --git a/img/favicon.png b/img/favicon.png deleted file mode 100644 index 7e0d4d2b95cddda1a554a5d4045410ebe3a5e180..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4272 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 Ks9H^jsQ&=k@WHwO diff --git a/img/favicons/android-chrome-192x192.png b/img/favicons/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..74da2e5dd84f1e25423519587659aaf7f07cc1cc GIT binary patch literal 4009 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 ZrqsAg1PdDEpno~QVS{7(9}n3F{1@p@ISl{+ literal 0 HcmV?d00001 diff --git a/img/favicons/android-chrome-512x512.png b/img/favicons/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..6a6281f65505881ff781d4b80a3cbcf738185217 GIT binary patch literal 9646 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 K{X#vbnEwGI{yG%^ literal 0 HcmV?d00001 diff --git a/img/favicons/apple-touch-icon.png b/img/favicons/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1b4b721576ffec02bfa59fe15cf801f7ae093c2c GIT binary patch literal 3776 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 ({8|MO6K#+} z$(#X#d#`&C14#xxTCZC_GSJ@e)=*#jtlg4P`HF!m`2+jhKIc*A8U|tv+%Vra&o{g_ z9MV7ON=D>tC>Zkw2+re9pY6PTpy`Hnez^;#(cXK;lhUsbMN;~5-GdA-OA7ssPjvFz zaO#s2=YtX*_5ylQ0R?E(gT9bb6aWHS1^V>VG9v|siNZi?t;FY<5`AV%=vf1Y1>|vo z?xzL5+!S~p1MYJ;O5x`Mm*-1FI2@qfq-6w_OVr__x3eS^#d*Z39BxL9mPkzJkoxHZ z)z1jbk?8u@BEmMFvVnAd-tymnJu?FQu_5_?E#leCQ9w1SwNzpXLn%^oB?1d28fOa_ zX(UDG(tUyB9DYlm!oZbt0`CqAL=Omz-Xmbl2>gtgd6dzsc$5$j=dk)ql*W)H?>-pC y$R?hNABf}_F1!;LNmxH}APw#ZqVZ(Qw)QI|RZtze=8Pu*0000XnF($c1hiTYK(ZWh9LIm%gSXy|Z99e7b_%iWlo8u@o^9Knul z#FxbjW2<7{MAt=+C3F_whYUzCV2K;h!hj3|RBUz3iLQ@MjckgXO87q_D1}D=*fTz_nUAkKl<0Z>1QBGDez>cNMoU!u!H8}WlAxp6pc(Fw1)NC&>Q6@S>@z_kI%l{m4K$CEXy_`T zc)mP9MC-%T9A3xaRY0x*sB?fu1vsSqlmc6ONmTQ^B29!QV3oi_GX#o<@}hnc*+IN` zw!pG_V3Yz5ctUbCd!j(!t`fPt073;lMIvy(G=WP8O6=ujig*eCrau|dU7|-NaH#^` zq+ZmGo?R_)KKCqhhk6yz6@dc=NPOoW^i#t@Hxcsj`90Ls%DF!R1q_ym^pKe6IqdZw z!Q(y6nI#n02JllHcwcj7CGSoW^1=In8zuE?<)R!tL-4q_wJ$*riNS*; z3_?EsK^{DCH`S2x>MF5&pv1WfxP+dkSLynd0w?_`F|Mn3KlPTV8!mA_!3%y5dC;Pd zsl|KQ0EvMP@RI_{$RS1bH2;vmFFe1l$3LG5rziK1;2m=WK07F26TJ9`KVA@~ S| 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 literal 0 HcmV?d00001 diff --git a/img/favicons/safari-pinned-tab.svg b/img/favicons/safari-pinned-tab.svg new file mode 100644 index 00000000..ecbe5a9f --- /dev/null +++ b/img/favicons/safari-pinned-tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php index 8e594b47..58e19760 100644 --- a/scripts/pi-hole/php/header.php +++ b/scripts/pi-hole/php/header.php @@ -190,15 +190,15 @@ - - - - - - + + + + + + - - + + From 1c4e8056c29a387e3ba719254acf584f96b7fc70 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 12 Jan 2020 13:16:55 +0100 Subject: [PATCH 02/58] Allow underscores in the middle of TLDs. Fixes #1036 Signed-off-by: DL6ER --- scripts/pi-hole/php/savesettings.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index a7524b77..8ab0922a 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -35,7 +35,7 @@ function istrue(&$argument) { // Credit: http://stackoverflow.com/a/4694816/2087442 function validDomain($domain_name) { - $validChars = preg_match("/^([_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([a-z\d])*)*$/i", $domain_name); + $validChars = preg_match("/^([_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([_a-z\d])*)*$/i", $domain_name); $lengthCheck = preg_match("/^.{1,253}$/", $domain_name); $labelLengthCheck = preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name); return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label @@ -44,7 +44,7 @@ function validDomain($domain_name) function validDomainWildcard($domain_name) { // There has to be either no or at most one "*" at the beginning of a line - $validChars = preg_match("/^((\*.)?[_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([a-z\d])*)*$/i", $domain_name); + $validChars = preg_match("/^((\*.)?[_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([_a-z\d])*)*$/i", $domain_name); $lengthCheck = preg_match("/^.{1,253}$/", $domain_name); $labelLengthCheck = preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name); return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label From 65be9d89ef3e0fa26f56b912e89c23c02f066bee Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 12 Jan 2020 13:33:05 +0100 Subject: [PATCH 03/58] Add support for international domain names. Note that this relies on php-intl to be installed Signed-off-by: DL6ER --- scripts/pi-hole/php/add.php | 16 +++++++++++----- scripts/pi-hole/php/auth.php | 15 ++++++--------- scripts/pi-hole/php/get.php | 7 +++++++ scripts/pi-hole/php/groups.php | 10 +++++++++- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/scripts/pi-hole/php/add.php b/scripts/pi-hole/php/add.php index d6059a51..94da8863 100644 --- a/scripts/pi-hole/php/add.php +++ b/scripts/pi-hole/php/add.php @@ -16,17 +16,23 @@ if (empty($api)) { list_verify($list); } +// Split individual domains into array +$domains = preg_split('/\s+/', trim($_POST['domain'])); +$comment = trim($_POST['comment']); + +// Convert domain name to IDNA ASCII form for international domains +foreach($domains as &$domain) +{ + $domain = idn_to_ascii($domain); +} + // Only check domains we add to the exact lists. // Regex are validated by FTL during import $check_lists = ["white","black","audit"]; if(in_array($list, $check_lists)) { - check_domain(); + check_domain($domains); } -// Split individual domains into array -$domains = preg_split('/\s+/', trim($_POST['domain'])); -$comment = trim($_POST['comment']); - require_once("func.php"); require_once("database.php"); $GRAVITYDB = getGravityDBFilename(); diff --git a/scripts/pi-hole/php/auth.php b/scripts/pi-hole/php/auth.php index 56f84520..7398cbdf 100644 --- a/scripts/pi-hole/php/auth.php +++ b/scripts/pi-hole/php/auth.php @@ -113,15 +113,12 @@ function check_csrf($token) { } } -function check_domain() { - if(isset($_POST['domain'])){ - $domains = preg_split('/\s+/', $_POST['domain']); - foreach($domains as $domain) - { - $validDomain = is_valid_domain_name($domain); - if(!$validDomain){ - log_and_die(htmlspecialchars($domain. ' is not a valid domain')); - } +function check_domain(&$domains) { + foreach($domains as &$domain) + { + $validDomain = is_valid_domain_name($domain); + if(!$validDomain){ + log_and_die(htmlspecialchars($domain. ' is not a valid domain')); } } } diff --git a/scripts/pi-hole/php/get.php b/scripts/pi-hole/php/get.php index f47f2c84..5a230bdb 100644 --- a/scripts/pi-hole/php/get.php +++ b/scripts/pi-hole/php/get.php @@ -30,6 +30,13 @@ function getTableContent($type) { while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) { + $utf8_domain = idn_to_utf8($res['domain']); + // Convert domain name to international form + // if applicable + if($res['domain'] !== $utf8_domain) + { + $res['domain'] = $utf8_domain.' ('.$res['domain'].')'; + } array_push($entries, $res); } diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index ff6f7aa5..fcea7e22 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -346,6 +346,13 @@ if ($_POST['action'] == 'get_groups') { array_push($groups, $gres['group_id']); } $res['groups'] = $groups; + $utf8_domain = idn_to_utf8($res['domain']); + // Convert domain name to international form + // if applicable + if($res['domain'] !== $utf8_domain) + { + $res['domain'] = $utf8_domain.' ('.$res['domain'].')'; + } array_push($data, $res); } @@ -364,7 +371,8 @@ if ($_POST['action'] == 'get_groups') { $type = intval($_POST['type']); - $domain = $_POST['domain']; + // Convert domain name to IDNA ASCII form for international domains + $domain = idn_to_ascii($_POST['domain']); if($type === ListType::whitelist || $type === ListType::blacklist) { // If adding to the exact lists, we convert the domain lower case and check whether it is valid From dfe89b43114f54145221c8c3b67fe199dcc88e3a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 19 Jan 2020 21:00:40 +0100 Subject: [PATCH 04/58] Remove null coalescing operator from list.php. This feature is only availabe in PHP 7. Signed-off-by: DL6ER --- list.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/list.php b/list.php index 3f24c07c..95fbcd1a 100644 --- a/list.php +++ b/list.php @@ -8,7 +8,7 @@ require "scripts/pi-hole/php/header.php"; -$list = $_GET['l'] ?? ''; +$list = isset($_GET['l']) ? $_GET['l'] : ''; if ($list == "white") { From f92812d4cd849b35a6dd60487bc0853ad18c0f63 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 19 Jan 2020 22:32:28 +0100 Subject: [PATCH 05/58] Substitute null client names by empty strings. Signed-off-by: DL6ER --- scripts/pi-hole/php/groups.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index ff6f7aa5..252e52cd 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -209,7 +209,7 @@ if ($_POST['action'] == 'get_groups') { // Loop over results $ips = array(); while ($res = $query->fetchArray(SQLITE3_ASSOC)) { - $ips[$res['ip']] = $res['name']; + $ips[$res['ip']] = $res['name'] !== null ? $res['name'] : ''; } $FTLdb->close(); From fa6033ab56dca49c2b2cb32d29d48c019e264bb9 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 20 Jan 2020 19:03:00 +0100 Subject: [PATCH 06/58] Sort by first IP if multiple IPs are available for a given device. Signed-off-by: DL6ER --- scripts/pi-hole/js/ip-address-sorting.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/pi-hole/js/ip-address-sorting.js b/scripts/pi-hole/js/ip-address-sorting.js index 3055b3c1..21343fa2 100644 --- a/scripts/pi-hole/js/ip-address-sorting.js +++ b/scripts/pi-hole/js/ip-address-sorting.js @@ -14,6 +14,12 @@ jQuery.extend(jQuery.fn.dataTableExt.oSort, { } var i, item; + // Use the first IP in case there is a list of IPs + // for a given device + if (Array.isArray(a)) { + a = a[0]; + } + var m = a.split("."), n = a.split(":"), x = "", From 5abbeb2eff9267d3062eeb1f35bd0dca3b50e18c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 21 Jan 2020 23:34:08 +0100 Subject: [PATCH 07/58] Add extra label when blocking happend during deep CNAME inspection. Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 1422e1b4..e6c41507 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -228,6 +228,11 @@ $(document).ready(function() { buttontext = ""; } + // Add extra label when blocking happend during deep CNAME inspection + if (blocked && data.length > 6 && data[6] === "3") { + fieldtext += "
CNAME"; + } + $(row).addClass(colorClass); $("td:eq(4)", row).html(fieldtext); $("td:eq(6)", row).html(buttontext); From 43a861a6a90f6dd9ee1dad7f6a2f59e9b48ce5f1 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 22 Jan 2020 19:10:21 +0100 Subject: [PATCH 08/58] Disable form elements while database operation is pending. Signed-off-by: DL6ER --- groups-adlists.php | 1 + groups-clients.php | 1 + groups-domains.php | 1 + groups.php | 1 + scripts/pi-hole/js/groups-adlists.js | 78 ++++-------------------- scripts/pi-hole/js/groups-clients.js | 74 +++++------------------ scripts/pi-hole/js/groups-common.js | 90 ++++++++++++++++++++++++++++ scripts/pi-hole/js/groups-domains.js | 79 +++++------------------- scripts/pi-hole/js/groups.js | 78 ++++-------------------- 9 files changed, 148 insertions(+), 255 deletions(-) create mode 100644 scripts/pi-hole/js/groups-common.js diff --git a/groups-adlists.php b/groups-adlists.php index 9b806967..49ddb58b 100644 --- a/groups-adlists.php +++ b/groups-adlists.php @@ -72,6 +72,7 @@ + + + + " + title + "
"; - switch (type) { - case "info": - opts = { - type: "info", - icon: "glyphicon glyphicon-time", - title: title, - message: message - }; - info = $.notify(opts); - break; - case "success": - opts = { - type: "success", - icon: icon, - title: title, - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - case "warning": - opts = { - type: "warning", - icon: "glyphicon glyphicon-warning-sign", - title: title, - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - case "error": - opts = { - type: "danger", - icon: "glyphicon glyphicon-remove", - title: " Error, something went wrong!
", - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - default: - } -} function get_groups() { $.post( @@ -83,10 +26,6 @@ function get_groups() { ); } -function datetime(date) { - return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z"); -} - $(document).ready(function() { $("#btnAdd").on("click", addAdlist); @@ -230,6 +169,7 @@ function addAdlist() { var address = $("#new_address").val(); var comment = $("#new_comment").val(); + disableAll(); showAlert("info", "", "Adding adlist...", address); if (address.length === 0) { @@ -248,6 +188,7 @@ function addAdlist() { token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert("success", "glyphicon glyphicon-plus", "Successfully added adlist", address); $("#new_address").val(""); @@ -258,6 +199,7 @@ function addAdlist() { } }, error: function(jqXHR, exception) { + enableAll(); showAlert("error", "", "Error while adding new adlist: ", jqXHR.responseText); console.log(exception); } @@ -289,6 +231,7 @@ function editAdlist() { not_done = "editing groups of"; } + disableAll(); showAlert("info", "", "Editing adlist...", address); $.ajax({ @@ -304,6 +247,7 @@ function editAdlist() { token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert( "success", @@ -321,6 +265,7 @@ function editAdlist() { } }, error: function(jqXHR, exception) { + enableAll(); showAlert( "error", "", @@ -337,6 +282,7 @@ function deleteAdlist() { var tr = $(this).closest("tr"); var address = tr.find("#address").text(); + disableAll(); showAlert("info", "", "Deleting adlist...", address); $.ajax({ url: "scripts/pi-hole/php/groups.php", @@ -344,6 +290,7 @@ function deleteAdlist() { dataType: "json", data: { action: "delete_adlist", id: id, token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert("success", "glyphicon glyphicon-trash", "Successfully deleted adlist ", address); table @@ -353,6 +300,7 @@ function deleteAdlist() { } else showAlert("error", "", "Error while deleting adlist with ID " + id, response.message); }, error: function(jqXHR, exception) { + enableAll(); showAlert("error", "", "Error while deleting adlist with ID " + id, jqXHR.responseText); console.log(exception); } diff --git a/scripts/pi-hole/js/groups-clients.js b/scripts/pi-hole/js/groups-clients.js index 68fbed9e..0edb1a50 100644 --- a/scripts/pi-hole/js/groups-clients.js +++ b/scripts/pi-hole/js/groups-clients.js @@ -5,69 +5,13 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ +/* global showAlert:false */ +/* global disableAll:false */ +/* global enableAll:false */ + var table; var groups = []; var token = $("#token").html(); -var info = null; - -function showAlert(type, icon, title, message) { - var opts = {}; - title = " " + title + "
"; - switch (type) { - case "info": - opts = { - type: "info", - icon: "glyphicon glyphicon-time", - title: title, - message: message - }; - info = $.notify(opts); - break; - case "success": - opts = { - type: "success", - icon: icon, - title: title, - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - case "warning": - opts = { - type: "warning", - icon: "glyphicon glyphicon-warning-sign", - title: title, - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - case "error": - opts = { - type: "danger", - icon: "glyphicon glyphicon-remove", - title: " Error, something went wrong!
", - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - default: - } -} function reload_client_suggestions() { $.post( @@ -238,9 +182,11 @@ function addClient() { ip = $("#ip-custom").val(); } + disableAll(); showAlert("info", "", "Adding client...", ip); if (ip.length === 0) { + enableAll(); showAlert("warning", "", "Warning", "Please specify a client IP address"); return; } @@ -251,6 +197,7 @@ function addClient() { dataType: "json", data: { action: "add_client", ip: ip, token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert("success", "glyphicon glyphicon-plus", "Successfully added client", ip); reload_client_suggestions(); @@ -260,6 +207,7 @@ function addClient() { } }, error: function(jqXHR, exception) { + enableAll(); showAlert("error", "", "Error while adding new client", jqXHR.responseText); console.log(exception); } @@ -286,6 +234,7 @@ function editClient() { ip_name += " (" + name + ")"; } + disableAll(); showAlert("info", "", "Editing client...", ip_name); $.ajax({ url: "scripts/pi-hole/php/groups.php", @@ -293,6 +242,7 @@ function editClient() { dataType: "json", data: { action: "edit_client", id: id, groups: groups, token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert( "success", @@ -305,6 +255,7 @@ function editClient() { } }, error: function(jqXHR, exception) { + enableAll(); showAlert( "error", "", @@ -327,6 +278,7 @@ function deleteClient() { ip_name += " (" + name + ")"; } + disableAll(); showAlert("info", "", "Deleting client...", ip_name); $.ajax({ url: "scripts/pi-hole/php/groups.php", @@ -334,6 +286,7 @@ function deleteClient() { dataType: "json", data: { action: "delete_client", id: id, token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert("success", "glyphicon glyphicon-trash", "Successfully deleted client ", ip_name); table @@ -346,6 +299,7 @@ function deleteClient() { } }, error: function(jqXHR, exception) { + enableAll(); showAlert("error", "", "Error while deleting client with ID " + id, jqXHR.responseText); console.log(exception); } diff --git a/scripts/pi-hole/js/groups-common.js b/scripts/pi-hole/js/groups-common.js new file mode 100644 index 00000000..dce071f8 --- /dev/null +++ b/scripts/pi-hole/js/groups-common.js @@ -0,0 +1,90 @@ +/* Pi-hole: A black hole for Internet advertisements + * (c) 2020 Pi-hole, LLC (https://pi-hole.net) + * Network-wide ad blocking via your own hardware. + * + * This file is copyright under the latest version of the EUPL. + * Please see LICENSE file for your rights under this license. */ + +/* global moment:false */ +/* global showAlert:false */ +/* global datetime:false */ +/* global disableAll:false */ +/* global enableAll:false */ + +var info = null; +function showAlert(type, icon, title, message) { + var opts = {}; + title = " " + title + "
"; + switch (type) { + case "info": + opts = { + type: "info", + icon: "glyphicon glyphicon-time", + title: title, + message: message + }; + info = $.notify(opts); + break; + case "success": + opts = { + type: "success", + icon: icon, + title: title, + message: message + }; + if (info) { + info.update(opts); + } else { + $.notify(opts); + } + + break; + case "warning": + opts = { + type: "warning", + icon: "glyphicon glyphicon-warning-sign", + title: title, + message: message + }; + if (info) { + info.update(opts); + } else { + $.notify(opts); + } + + break; + case "error": + opts = { + type: "danger", + icon: "glyphicon glyphicon-remove", + title: " Error, something went wrong!
", + message: message + }; + if (info) { + info.update(opts); + } else { + $.notify(opts); + } + + break; + default: + } +} + +function datetime(date) { + return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z"); +} + +function disableAll() { + $("input").attr("disabled", true); + $("select").attr("disabled", true); + $("button").attr("disabled", true); + $("textarea").attr("disabled", true); +} + +function enableAll() { + $("input").attr("disabled", false); + $("select").attr("disabled", false); + $("button").attr("disabled", false); + $("textarea").attr("disabled", false); +} diff --git a/scripts/pi-hole/js/groups-domains.js b/scripts/pi-hole/js/groups-domains.js index b83c5296..ecdf696b 100644 --- a/scripts/pi-hole/js/groups-domains.js +++ b/scripts/pi-hole/js/groups-domains.js @@ -5,71 +5,14 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global moment:false */ +/* global showAlert:false */ +/* global datetime:false */ +/* global disableAll:false */ +/* global enableAll:false */ var table; var groups = []; var token = $("#token").html(); -var info = null; - -function showAlert(type, icon, title, message) { - var opts = {}; - title = " " + title + "
"; - switch (type) { - case "info": - opts = { - type: "info", - icon: "glyphicon glyphicon-time", - title: title, - message: message - }; - info = $.notify(opts); - break; - case "success": - opts = { - type: "success", - icon: icon, - title: title, - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - case "warning": - opts = { - type: "warning", - icon: "glyphicon glyphicon-warning-sign", - title: title, - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - case "error": - opts = { - type: "danger", - icon: "glyphicon glyphicon-remove", - title: " Error, something went wrong!
", - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - default: - } -} function get_groups() { $.post( @@ -83,10 +26,6 @@ function get_groups() { ); } -function datetime(date) { - return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z"); -} - $(document).ready(function() { $("#btnAdd").on("click", addDomain); @@ -248,9 +187,11 @@ function addDomain() { var type = $("#new_type").val(); var comment = $("#new_comment").val(); + disableAll(); showAlert("info", "", "Adding domain...", domain); if (domain.length === 0) { + enableAll(); showAlert("warning", "", "Warning", "Please specify a domain"); return; } @@ -267,6 +208,7 @@ function addDomain() { token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert("success", "glyphicon glyphicon-plus", "Successfully added domain", domain); $("#new_domain").val(""); @@ -275,6 +217,7 @@ function addDomain() { } else showAlert("error", "", "Error while adding new domain", response.message); }, error: function(jqXHR, exception) { + enableAll(); showAlert("error", "", "Error while adding new domain", jqXHR.responseText); console.log(exception); } @@ -313,6 +256,7 @@ function editDomain() { not_done = "editing groups of"; } + disableAll(); showAlert("info", "", "Editing domain...", name); $.ajax({ url: "scripts/pi-hole/php/groups.php", @@ -328,6 +272,7 @@ function editDomain() { token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert( "success", @@ -344,6 +289,7 @@ function editDomain() { ); }, error: function(jqXHR, exception) { + enableAll(); showAlert( "error", "", @@ -360,6 +306,7 @@ function deleteDomain() { var tr = $(this).closest("tr"); var domain = tr.find("#domain").text(); + disableAll(); showAlert("info", "", "Deleting domain...", domain); $.ajax({ url: "scripts/pi-hole/php/groups.php", @@ -367,6 +314,7 @@ function deleteDomain() { dataType: "json", data: { action: "delete_domain", id: id, token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert("success", "glyphicon glyphicon-trash", "Successfully deleted domain", domain); table @@ -376,6 +324,7 @@ function deleteDomain() { } else showAlert("error", "", "Error while deleting domain with ID " + id, response.message); }, error: function(jqXHR, exception) { + enableAll(); showAlert("error", "", "Error while deleting domain with ID " + id, jqXHR.responseText); console.log(exception); } diff --git a/scripts/pi-hole/js/groups.js b/scripts/pi-hole/js/groups.js index b7ec372c..48798451 100644 --- a/scripts/pi-hole/js/groups.js +++ b/scripts/pi-hole/js/groups.js @@ -5,74 +5,13 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global moment:false */ +/* global showAlert:false */ +/* global datetime:false */ +/* global disableAll:false */ +/* global enableAll:false */ var table; var token = $("#token").html(); -var info = null; - -function showAlert(type, icon, title, message) { - var opts = {}; - title = " " + title + "
"; - switch (type) { - case "info": - opts = { - type: "info", - icon: "glyphicon glyphicon-time", - title: title, - message: message - }; - info = $.notify(opts); - break; - case "success": - opts = { - type: "success", - icon: icon, - title: title, - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - case "warning": - opts = { - type: "warning", - icon: "glyphicon glyphicon-warning-sign", - title: title, - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - case "error": - opts = { - type: "danger", - icon: "glyphicon glyphicon-remove", - title: " Error, something went wrong!
", - message: message - }; - if (info) { - info.update(opts); - } else { - $.notify(opts); - } - - break; - default: - } -} - -function datetime(date) { - return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z"); -} $(document).ready(function() { $("#btnAdd").on("click", addGroup); @@ -191,6 +130,7 @@ function addGroup() { var name = $("#new_name").val(); var desc = $("#new_desc").val(); + disableAll(); showAlert("info", "", "Adding group...", name); if (name.length === 0) { @@ -204,6 +144,7 @@ function addGroup() { dataType: "json", data: { action: "add_group", name: name, desc: desc, token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert("success", "glyphicon glyphicon-plus", "Successfully added group", name); $("#new_name").val(""); @@ -214,6 +155,7 @@ function addGroup() { } }, error: function(jqXHR, exception) { + enableAll(); showAlert("error", "", "Error while adding new group", jqXHR.responseText); console.log(exception); } @@ -244,6 +186,7 @@ function editGroup() { not_done = "editing description of"; } + disableAll(); showAlert("info", "", "Editing group...", name); $.ajax({ url: "scripts/pi-hole/php/groups.php", @@ -258,6 +201,7 @@ function editGroup() { token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert("success", "glyphicon glyphicon-pencil", "Successfully " + done + " group", name); } else { @@ -270,6 +214,7 @@ function editGroup() { } }, error: function(jqXHR, exception) { + enableAll(); showAlert( "error", "", @@ -286,6 +231,7 @@ function deleteGroup() { var tr = $(this).closest("tr"); var name = tr.find("#name").val(); + disableAll(); showAlert("info", "", "Deleting group...", name); $.ajax({ url: "scripts/pi-hole/php/groups.php", @@ -293,6 +239,7 @@ function deleteGroup() { dataType: "json", data: { action: "delete_group", id: id, token: token }, success: function(response) { + enableAll(); if (response.success) { showAlert("success", "glyphicon glyphicon-trash", "Successfully deleted group ", name); table @@ -304,6 +251,7 @@ function deleteGroup() { } }, error: function(jqXHR, exception) { + enableAll(); showAlert("error", "", "Error while deleting group with ID " + id, jqXHR.responseText); console.log(exception); } From 88da85f5534bc8d2338bf64bf98ad3c3a8558e06 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 26 Jan 2020 02:46:38 +0100 Subject: [PATCH 09/58] Add responsive design to new group tables to ensure they render properly on very narrow screens. (#1137) Signed-off-by: DL6ER Co-authored-by: Dan Schaper --- scripts/pi-hole/js/groups-adlists.js | 4 ++++ scripts/pi-hole/js/groups-clients.js | 4 ++++ scripts/pi-hole/js/groups-domains.js | 4 ++++ scripts/pi-hole/js/groups.js | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/scripts/pi-hole/js/groups-adlists.js b/scripts/pi-hole/js/groups-adlists.js index 53a137ba..625a3f83 100644 --- a/scripts/pi-hole/js/groups-adlists.js +++ b/scripts/pi-hole/js/groups-adlists.js @@ -183,6 +183,10 @@ function initTable() { ""; $("td:eq(4)", row).html(button); }, + dom: + "<'row'<'col-sm-4'l><'col-sm-8'f>>" + + "<'row'<'col-sm-12'<'table-responsive'tr>>>" + + "<'row'<'col-sm-5'i><'col-sm-7'p>>", lengthMenu: [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] diff --git a/scripts/pi-hole/js/groups-clients.js b/scripts/pi-hole/js/groups-clients.js index 68fbed9e..bb07ea7b 100644 --- a/scripts/pi-hole/js/groups-clients.js +++ b/scripts/pi-hole/js/groups-clients.js @@ -189,6 +189,10 @@ function initTable() { ""; $("td:eq(2)", row).html(button); }, + dom: + "<'row'<'col-sm-4'l><'col-sm-8'f>>" + + "<'row'<'col-sm-12'<'table-responsive'tr>>>" + + "<'row'<'col-sm-5'i><'col-sm-7'p>>", lengthMenu: [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] diff --git a/scripts/pi-hole/js/groups-domains.js b/scripts/pi-hole/js/groups-domains.js index b83c5296..6646a957 100644 --- a/scripts/pi-hole/js/groups-domains.js +++ b/scripts/pi-hole/js/groups-domains.js @@ -200,6 +200,10 @@ function initTable() { ""; $("td:eq(5)", row).html(button); }, + dom: + "<'row'<'col-sm-4'l><'col-sm-8'f>>" + + "<'row'<'col-sm-12'<'table-responsive'tr>>>" + + "<'row'<'col-sm-5'i><'col-sm-7'p>>", lengthMenu: [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] diff --git a/scripts/pi-hole/js/groups.js b/scripts/pi-hole/js/groups.js index b7ec372c..2ea23c5b 100644 --- a/scripts/pi-hole/js/groups.js +++ b/scripts/pi-hole/js/groups.js @@ -144,6 +144,10 @@ $(document).ready(function() { $("td:eq(3)", row).html(button); } }, + dom: + "<'row'<'col-sm-4'l><'col-sm-8'f>>" + + "<'row'<'col-sm-12'<'table-responsive'tr>>>" + + "<'row'<'col-sm-5'i><'col-sm-7'p>>", lengthMenu: [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] From 516c9e946a2f9c9c62065d31c09317e678ec9c29 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 27 Jan 2020 11:01:48 +0000 Subject: [PATCH 10/58] Add utils object for common code. Signed-off-by: DL6ER --- scripts/pi-hole/js/groups-adlists.js | 65 ++++++++++++++++------------ scripts/pi-hole/js/groups-clients.js | 59 ++++++++++++++----------- scripts/pi-hole/js/groups-common.js | 13 ++++-- scripts/pi-hole/js/groups-domains.js | 62 +++++++++++++------------- scripts/pi-hole/js/groups.js | 63 +++++++++++++++------------ 5 files changed, 147 insertions(+), 115 deletions(-) diff --git a/scripts/pi-hole/js/groups-adlists.js b/scripts/pi-hole/js/groups-adlists.js index 5c571724..f16e1bc5 100644 --- a/scripts/pi-hole/js/groups-adlists.js +++ b/scripts/pi-hole/js/groups-adlists.js @@ -5,10 +5,7 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global showAlert:false */ -/* global datetime:false */ -/* global disableAll:false */ -/* global enableAll:false */ +/* global utils:false */ var table; var groups = []; @@ -59,9 +56,9 @@ function initTable() { rowCallback: function(row, data) { var tooltip = "Added: " + - datetime(data.date_added) + + utils.datetime(data.date_added) + "\nLast modified: " + - datetime(data.date_modified) + + utils.datetime(data.date_modified) + "\nDatabase ID: " + data.id; $("td:eq(0)", row).html( @@ -173,11 +170,11 @@ function addAdlist() { var address = $("#new_address").val(); var comment = $("#new_comment").val(); - disableAll(); - showAlert("info", "", "Adding adlist...", address); + utils.disableAll(); + utils.showAlert("info", "", "Adding adlist...", address); if (address.length === 0) { - showAlert("warning", "", "Warning", "Please specify an adlist address"); + utils.showAlert("warning", "", "Warning", "Please specify an adlist address"); return; } @@ -192,19 +189,24 @@ function addAdlist() { token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert("success", "glyphicon glyphicon-plus", "Successfully added adlist", address); + utils.showAlert( + "success", + "glyphicon glyphicon-plus", + "Successfully added adlist", + address + ); $("#new_address").val(""); $("#new_comment").val(""); table.ajax.reload(); } else { - showAlert("error", "", "Error while adding new adlist: ", response.message); + utils.showAlert("error", "", "Error while adding new adlist: ", response.message); } }, error: function(jqXHR, exception) { - enableAll(); - showAlert("error", "", "Error while adding new adlist: ", jqXHR.responseText); + utils.enableAll(); + utils.showAlert("error", "", "Error while adding new adlist: ", jqXHR.responseText); console.log(exception); } }); @@ -235,8 +237,8 @@ function editAdlist() { not_done = "editing groups of"; } - disableAll(); - showAlert("info", "", "Editing adlist...", address); + utils.disableAll(); + utils.showAlert("info", "", "Editing adlist...", address); $.ajax({ url: "scripts/pi-hole/php/groups.php", @@ -251,16 +253,16 @@ function editAdlist() { token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert( + utils.showAlert( "success", "glyphicon glyphicon-pencil", "Successfully " + done + " adlist ", address ); } else { - showAlert( + utils.showAlert( "error", "", "Error while " + not_done + " adlist with ID " + id, @@ -269,8 +271,8 @@ function editAdlist() { } }, error: function(jqXHR, exception) { - enableAll(); - showAlert( + utils.enableAll(); + utils.showAlert( "error", "", "Error while " + not_done + " adlist with ID " + id, @@ -286,26 +288,33 @@ function deleteAdlist() { var tr = $(this).closest("tr"); var address = tr.find("#address").text(); - disableAll(); - showAlert("info", "", "Deleting adlist...", address); + utils.disableAll(); + utils.showAlert("info", "", "Deleting adlist...", address); $.ajax({ url: "scripts/pi-hole/php/groups.php", method: "post", dataType: "json", data: { action: "delete_adlist", id: id, token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert("success", "glyphicon glyphicon-trash", "Successfully deleted adlist ", address); + utils.showAlert( + "success", + "glyphicon glyphicon-trash", + "Successfully deleted adlist ", + address + ); table .row(tr) .remove() .draw(false); - } else showAlert("error", "", "Error while deleting adlist with ID " + id, response.message); + } else { + utils.showAlert("error", "", "Error while deleting adlist with ID " + id, response.message); + } }, error: function(jqXHR, exception) { - enableAll(); - showAlert("error", "", "Error while deleting adlist with ID " + id, jqXHR.responseText); + utils.enableAll(); + utils.showAlert("error", "", "Error while deleting adlist with ID " + id, jqXHR.responseText); console.log(exception); } }); diff --git a/scripts/pi-hole/js/groups-clients.js b/scripts/pi-hole/js/groups-clients.js index 3a4cae4e..71e0e585 100644 --- a/scripts/pi-hole/js/groups-clients.js +++ b/scripts/pi-hole/js/groups-clients.js @@ -5,9 +5,7 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global showAlert:false */ -/* global disableAll:false */ -/* global enableAll:false */ +/* global utils:false */ var table; var groups = []; @@ -186,12 +184,12 @@ function addClient() { ip = $("#ip-custom").val(); } - disableAll(); - showAlert("info", "", "Adding client...", ip); + utils.disableAll(); + utils.showAlert("info", "", "Adding client...", ip); if (ip.length === 0) { - enableAll(); - showAlert("warning", "", "Warning", "Please specify a client IP address"); + utils.enableAll(); + utils.showAlert("warning", "", "Warning", "Please specify a client IP address"); return; } @@ -201,18 +199,18 @@ function addClient() { dataType: "json", data: { action: "add_client", ip: ip, token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert("success", "glyphicon glyphicon-plus", "Successfully added client", ip); + utils.showAlert("success", "glyphicon glyphicon-plus", "Successfully added client", ip); reload_client_suggestions(); table.ajax.reload(); } else { - showAlert("error", "", "Error while adding new client", response.message); + utils.showAlert("error", "", "Error while adding new client", response.message); } }, error: function(jqXHR, exception) { - enableAll(); - showAlert("error", "", "Error while adding new client", jqXHR.responseText); + utils.enableAll(); + utils.showAlert("error", "", "Error while adding new client", jqXHR.responseText); console.log(exception); } }); @@ -238,29 +236,33 @@ function editClient() { ip_name += " (" + name + ")"; } - disableAll(); - showAlert("info", "", "Editing client...", ip_name); + utils.disableAll(); + utils.showAlert("info", "", "Editing client...", ip_name); $.ajax({ url: "scripts/pi-hole/php/groups.php", method: "post", dataType: "json", data: { action: "edit_client", id: id, groups: groups, token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert( + utils.showAlert( "success", "glyphicon glyphicon-plus", "Successfully " + done + " client", ip_name ); } else { - showAlert("error", "Error while " + not_done + " client with ID " + id, response.message); + utils.showAlert( + "error", + "Error while " + not_done + " client with ID " + id, + response.message + ); } }, error: function(jqXHR, exception) { - enableAll(); - showAlert( + utils.enableAll(); + utils.showAlert( "error", "", "Error while " + not_done + " client with ID " + id, @@ -282,29 +284,34 @@ function deleteClient() { ip_name += " (" + name + ")"; } - disableAll(); - showAlert("info", "", "Deleting client...", ip_name); + utils.disableAll(); + utils.showAlert("info", "", "Deleting client...", ip_name); $.ajax({ url: "scripts/pi-hole/php/groups.php", method: "post", dataType: "json", data: { action: "delete_client", id: id, token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert("success", "glyphicon glyphicon-trash", "Successfully deleted client ", ip_name); + utils.showAlert( + "success", + "glyphicon glyphicon-trash", + "Successfully deleted client ", + ip_name + ); table .row(tr) .remove() .draw(false); reload_client_suggestions(); } else { - showAlert("error", "", "Error while deleting client with ID " + id, response.message); + utils.showAlert("error", "", "Error while deleting client with ID " + id, response.message); } }, error: function(jqXHR, exception) { - enableAll(); - showAlert("error", "", "Error while deleting client with ID " + id, jqXHR.responseText); + utils.enableAll(); + utils.showAlert("error", "", "Error while deleting client with ID " + id, jqXHR.responseText); console.log(exception); } }); diff --git a/scripts/pi-hole/js/groups-common.js b/scripts/pi-hole/js/groups-common.js index dce071f8..8cff4727 100644 --- a/scripts/pi-hole/js/groups-common.js +++ b/scripts/pi-hole/js/groups-common.js @@ -6,10 +6,6 @@ * Please see LICENSE file for your rights under this license. */ /* global moment:false */ -/* global showAlert:false */ -/* global datetime:false */ -/* global disableAll:false */ -/* global enableAll:false */ var info = null; function showAlert(type, icon, title, message) { @@ -88,3 +84,12 @@ function enableAll() { $("button").attr("disabled", false); $("textarea").attr("disabled", false); } + +window.utils = (function() { + return { + showAlert: showAlert, + datetime: datetime, + disableAll: disableAll, + enableAll: enableAll + }; +})(); diff --git a/scripts/pi-hole/js/groups-domains.js b/scripts/pi-hole/js/groups-domains.js index 36748b40..85ae0647 100644 --- a/scripts/pi-hole/js/groups-domains.js +++ b/scripts/pi-hole/js/groups-domains.js @@ -5,10 +5,7 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global showAlert:false */ -/* global datetime:false */ -/* global disableAll:false */ -/* global enableAll:false */ +/* global utils:false */ var table; var groups = []; @@ -60,9 +57,9 @@ function initTable() { rowCallback: function(row, data) { var tooltip = "Added: " + - datetime(data.date_added) + + utils.datetime(data.date_added) + "\nLast modified: " + - datetime(data.date_modified) + + utils.datetime(data.date_modified) + "\nDatabase ID: " + data.id; $("td:eq(0)", row).html( @@ -191,12 +188,12 @@ function addDomain() { var type = $("#new_type").val(); var comment = $("#new_comment").val(); - disableAll(); - showAlert("info", "", "Adding domain...", domain); + utils.disableAll(); + utils.showAlert("info", "", "Adding domain...", domain); if (domain.length === 0) { - enableAll(); - showAlert("warning", "", "Warning", "Please specify a domain"); + utils.enableAll(); + utils.showAlert("warning", "", "Warning", "Please specify a domain"); return; } @@ -212,17 +209,17 @@ function addDomain() { token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert("success", "glyphicon glyphicon-plus", "Successfully added domain", domain); + utils.showAlert("success", "glyphicon glyphicon-plus", "Successfully added domain", domain); $("#new_domain").val(""); $("#new_comment").val(""); table.ajax.reload(); - } else showAlert("error", "", "Error while adding new domain", response.message); + } else utils.showAlert("error", "", "Error while adding new domain", response.message); }, error: function(jqXHR, exception) { - enableAll(); - showAlert("error", "", "Error while adding new domain", jqXHR.responseText); + utils.enableAll(); + utils.showAlert("error", "", "Error while adding new domain", jqXHR.responseText); console.log(exception); } }); @@ -260,8 +257,8 @@ function editDomain() { not_done = "editing groups of"; } - disableAll(); - showAlert("info", "", "Editing domain...", name); + utils.disableAll(); + utils.showAlert("info", "", "Editing domain...", name); $.ajax({ url: "scripts/pi-hole/php/groups.php", method: "post", @@ -276,16 +273,16 @@ function editDomain() { token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert( + utils.showAlert( "success", "glyphicon glyphicon-pencil", "Successfully " + done + " domain", domain ); } else - showAlert( + utils.showAlert( "error", "", "Error while " + not_done + " domain with ID " + id, @@ -293,8 +290,8 @@ function editDomain() { ); }, error: function(jqXHR, exception) { - enableAll(); - showAlert( + utils.enableAll(); + utils.showAlert( "error", "", "Error while " + not_done + " domain with ID " + id, @@ -310,26 +307,33 @@ function deleteDomain() { var tr = $(this).closest("tr"); var domain = tr.find("#domain").text(); - disableAll(); - showAlert("info", "", "Deleting domain...", domain); + utils.disableAll(); + utils.showAlert("info", "", "Deleting domain...", domain); $.ajax({ url: "scripts/pi-hole/php/groups.php", method: "post", dataType: "json", data: { action: "delete_domain", id: id, token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert("success", "glyphicon glyphicon-trash", "Successfully deleted domain", domain); + utils.showAlert( + "success", + "glyphicon glyphicon-trash", + "Successfully deleted domain", + domain + ); table .row(tr) .remove() .draw(false); - } else showAlert("error", "", "Error while deleting domain with ID " + id, response.message); + } else { + utils.showAlert("error", "", "Error while deleting domain with ID " + id, response.message); + } }, error: function(jqXHR, exception) { - enableAll(); - showAlert("error", "", "Error while deleting domain with ID " + id, jqXHR.responseText); + utils.enableAll(); + utils.showAlert("error", "", "Error while deleting domain with ID " + id, jqXHR.responseText); console.log(exception); } }); diff --git a/scripts/pi-hole/js/groups.js b/scripts/pi-hole/js/groups.js index d4a6bbc7..0ffa3dcc 100644 --- a/scripts/pi-hole/js/groups.js +++ b/scripts/pi-hole/js/groups.js @@ -5,10 +5,7 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global showAlert:false */ -/* global datetime:false */ -/* global disableAll:false */ -/* global enableAll:false */ +/* global utils:false */ var table; var token = $("#token").html(); @@ -36,9 +33,9 @@ $(document).ready(function() { rowCallback: function(row, data) { var tooltip = "Added: " + - datetime(data.date_added) + + utils.datetime(data.date_added) + "\nLast modified: " + - datetime(data.date_modified) + + utils.datetime(data.date_modified) + "\nDatabase ID: " + data.id; $("td:eq(0)", row).html( @@ -134,11 +131,11 @@ function addGroup() { var name = $("#new_name").val(); var desc = $("#new_desc").val(); - disableAll(); - showAlert("info", "", "Adding group...", name); + utils.disableAll(); + utils.showAlert("info", "", "Adding group...", name); if (name.length === 0) { - showAlert("warning", "", "Warning", "Please specify a group name"); + utils.showAlert("warning", "", "Warning", "Please specify a group name"); return; } @@ -148,19 +145,19 @@ function addGroup() { dataType: "json", data: { action: "add_group", name: name, desc: desc, token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert("success", "glyphicon glyphicon-plus", "Successfully added group", name); + utils.showAlert("success", "glyphicon glyphicon-plus", "Successfully added group", name); $("#new_name").val(""); $("#new_desc").val(""); table.ajax.reload(); } else { - showAlert("error", "", "Error while adding new group", response.message); + utils.showAlert("error", "", "Error while adding new group", response.message); } }, error: function(jqXHR, exception) { - enableAll(); - showAlert("error", "", "Error while adding new group", jqXHR.responseText); + utils.enableAll(); + utils.showAlert("error", "", "Error while adding new group", jqXHR.responseText); console.log(exception); } }); @@ -190,8 +187,8 @@ function editGroup() { not_done = "editing description of"; } - disableAll(); - showAlert("info", "", "Editing group...", name); + utils.disableAll(); + utils.showAlert("info", "", "Editing group...", name); $.ajax({ url: "scripts/pi-hole/php/groups.php", method: "post", @@ -205,11 +202,16 @@ function editGroup() { token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert("success", "glyphicon glyphicon-pencil", "Successfully " + done + " group", name); + utils.showAlert( + "success", + "glyphicon glyphicon-pencil", + "Successfully " + done + " group", + name + ); } else { - showAlert( + utils.showAlert( "error", "", "Error while " + not_done + " group with ID " + id, @@ -218,8 +220,8 @@ function editGroup() { } }, error: function(jqXHR, exception) { - enableAll(); - showAlert( + utils.enableAll(); + utils.showAlert( "error", "", "Error while " + not_done + " group with ID " + id, @@ -235,28 +237,33 @@ function deleteGroup() { var tr = $(this).closest("tr"); var name = tr.find("#name").val(); - disableAll(); - showAlert("info", "", "Deleting group...", name); + utils.disableAll(); + utils.showAlert("info", "", "Deleting group...", name); $.ajax({ url: "scripts/pi-hole/php/groups.php", method: "post", dataType: "json", data: { action: "delete_group", id: id, token: token }, success: function(response) { - enableAll(); + utils.enableAll(); if (response.success) { - showAlert("success", "glyphicon glyphicon-trash", "Successfully deleted group ", name); + utils.showAlert( + "success", + "glyphicon glyphicon-trash", + "Successfully deleted group ", + name + ); table .row(tr) .remove() .draw(false); } else { - showAlert("error", "", "Error while deleting group with ID " + id, response.message); + utils.showAlert("error", "", "Error while deleting group with ID " + id, response.message); } }, error: function(jqXHR, exception) { - enableAll(); - showAlert("error", "", "Error while deleting group with ID " + id, jqXHR.responseText); + utils.enableAll(); + utils.showAlert("error", "", "Error while deleting group with ID " + id, jqXHR.responseText); console.log(exception); } }); From abe0b760bdecc34f02bdb0755faf8ac7943bdd8d Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 27 Jan 2020 12:40:45 +0000 Subject: [PATCH 11/58] Ensure we reach the end of groups.php to send SIGRTMIN to FTL. Signed-off-by: DL6ER --- scripts/pi-hole/php/groups.php | 58 +++++++++++++++++----------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index 252e52cd..81a9c31b 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -49,7 +49,7 @@ if ($_POST['action'] == 'get_groups') { } echo json_encode(array('data' => $data)); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'add_group') { // Add new group @@ -72,9 +72,9 @@ if ($_POST['action'] == 'get_groups') { } $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'edit_group') { // Edit group identified by ID @@ -111,9 +111,9 @@ if ($_POST['action'] == 'get_groups') { } $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'delete_group') { // Delete group identified by ID @@ -142,9 +142,9 @@ if ($_POST['action'] == 'get_groups') { } } $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'get_clients') { // List all available groups @@ -193,7 +193,7 @@ if ($_POST['action'] == 'get_groups') { echo json_encode(array('data' => $data)); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'get_unconfigured_clients') { // List all available clients WITHOUT already configured clients @@ -227,7 +227,7 @@ if ($_POST['action'] == 'get_groups') { echo json_encode($ips); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'add_client') { // Add new client @@ -246,9 +246,9 @@ if ($_POST['action'] == 'get_groups') { } $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'edit_client') { // Edit client identified by ID @@ -288,9 +288,9 @@ if ($_POST['action'] == 'get_groups') { $db->query('COMMIT;'); $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'delete_client') { // Delete client identified by ID @@ -322,9 +322,9 @@ if ($_POST['action'] == 'get_groups') { } $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'get_domains') { // List all available groups @@ -352,7 +352,7 @@ if ($_POST['action'] == 'get_groups') { echo json_encode(array('data' => $data)); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'add_domain') { // Add new domain @@ -392,9 +392,9 @@ if ($_POST['action'] == 'get_groups') { } $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'edit_domain') { // Edit domain identified by ID @@ -469,9 +469,9 @@ if ($_POST['action'] == 'get_groups') { $db->query('COMMIT;'); $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'delete_domain') { // Delete domain identified by ID @@ -503,9 +503,9 @@ if ($_POST['action'] == 'get_groups') { } $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'get_adlists') { // List all available groups @@ -533,7 +533,7 @@ if ($_POST['action'] == 'get_groups') { echo json_encode(array('data' => $data)); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'add_adlist') { // Add new adlist @@ -556,9 +556,9 @@ if ($_POST['action'] == 'get_groups') { } $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'edit_adlist') { // Edit adlist identified by ID @@ -629,9 +629,9 @@ if ($_POST['action'] == 'get_groups') { $db->query('COMMIT;'); $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } elseif ($_POST['action'] == 'delete_adlist') { // Delete adlist identified by ID @@ -663,9 +663,9 @@ if ($_POST['action'] == 'get_groups') { } $reload = true; - return JSON_success(); + JSON_success(); } catch (\Exception $ex) { - return JSON_error($ex->getMessage()); + JSON_error($ex->getMessage()); } } else { log_and_die('Requested action not supported!'); From 870ab035fcd346e9c3e8ca6e83f5b869e31bd6d6 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 28 Jan 2020 22:26:19 +0100 Subject: [PATCH 12/58] Fix teleporter's ability to import v4.x archives. Signed-off-by: DL6ER --- scripts/pi-hole/php/teleporter.php | 60 ++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/scripts/pi-hole/php/teleporter.php b/scripts/pi-hole/php/teleporter.php index 7c34c47f..d43ee28b 100644 --- a/scripts/pi-hole/php/teleporter.php +++ b/scripts/pi-hole/php/teleporter.php @@ -185,7 +185,7 @@ function archive_restore_table($file, $table, $flush=false) /** * Create table rows from an uploaded archive file * - * @param $file object The file of the file in the archive to import + * @param $file object The file in the archive to import * @param $table string The target table * @param $flush boolean Whether to flush the table before importing the archived data * @param $wildcardstyle boolean Whether to format the input domains in legacy wildcard notation @@ -193,22 +193,66 @@ function archive_restore_table($file, $table, $flush=false) */ function archive_insert_into_table($file, $table, $flush=false, $wildcardstyle=false) { - global $db, $flushed_tables; + global $db; $domains = array_filter(explode("\n",file_get_contents($file))); // Return early if we cannot extract the lines in the file if(is_null($domains)) return 0; - // Flush table if requested, only flush each table once - if($flush && !in_array($table, $flushed_tables)) - { - $db->exec("DELETE FROM ".$table); - array_push($flushed_tables, $table); + // Generate comment + $prefix = "phar:///tmp/"; + if (substr($file, 0, strlen($prefix)) == $prefix) { + $file = substr($file, strlen($prefix)); + } + $comment = "Imported from ".$file; + + // Determine table and type to import to + $type = null; + if($table === "whitelist") { + $table = "domainlist"; + $type = ListType::whitelist; + } else if($table === "blacklist") { + $table = "domainlist"; + $type = ListType::blacklist; + } else if($table === "regex_blacklist") { + $table = "domainlist"; + $type = ListType::regex_blacklist; + } else if($table === "domain_audit") { + $table = "domain_audit"; + } + + // Flush table if requested + if($flush) { + flush_table($table, $type); } // Add domains to requested table - return add_to_table($db, $table, $domains, $wildcardstyle, true); + return add_to_table($db, $table, $domains, $comment, $wildcardstyle, true, $type); +} + +/** + * Flush table if requested. This subroutine flushes each table only once + * + * @param $table string The target table + * @param $type integer Type of item to flush in table (applies only to domainlist table) + */ +function flush_table($table, $type=null) +{ + global $db, $flushed_tables; + + if(!in_array($table, $flushed_tables)) + { + if($type !== null) { + $sql = "DELETE FROM ".$table." WHERE type = ".$type; + array_push($flushed_tables, $table.$type); + } else { + $sql = "DELETE FROM ".$table; + array_push($flushed_tables, $table); + } + echo $sql."
"; + $db->exec($sql); + } } function archive_add_directory($path,$subdir="") From 2fdc676979e6e65dc4260dcc71ab57824aaa8702 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 29 Jan 2020 22:37:26 +0100 Subject: [PATCH 13/58] Add new CNAME-specific blocking modes [QUERY_GRAVITY_CNAME, QUERY_REGEX_CNAME, QUERY_BLACKLIST_CNAME] and show domain that was the reason for blocking during a CNAME inspection Signed-off-by: DL6ER --- db_queries.php | 33 ++++++++++++---------- scripts/pi-hole/js/db_queries.js | 34 +++++++++++++++++++++-- scripts/pi-hole/js/queries.js | 47 ++++++++++++++++++++++++++------ 3 files changed, 90 insertions(+), 24 deletions(-) diff --git a/db_queries.php b/db_queries.php index 601b56ad..0035807f 100644 --- a/db_queries.php +++ b/db_queries.php @@ -45,34 +45,39 @@ $token = $_SESSION['token'];
-
+
- + +
+
+
-
+
- + +
+
+
-
+
- + +
+
+
-
+
- +
-
-
- +
-
-
- +
diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js index d7f4fe12..f8c22119 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -195,6 +195,18 @@ function getQueryTypes() { queryType.push([6, 7, 8]); } + if ($("#type_gravity_CNAME").prop("checked")) { + queryType.push(9); + } + + if ($("#type_regex_CNAME").prop("checked")) { + queryType.push(10); + } + + if ($("#type_blacklist_CNAME").prop("checked")) { + queryType.push(11); + } + return queryType.join(","); } @@ -282,13 +294,13 @@ $(document).ready(function() { break; case 4: color = "red"; - fieldtext = "Blocked (regex/wildcard)"; + fieldtext = "Blocked (regex blacklist)"; buttontext = ''; break; case 5: color = "red"; - fieldtext = "Blocked (blacklist)"; + fieldtext = "Blocked (exact blacklist)"; buttontext = ''; break; @@ -307,6 +319,24 @@ $(document).ready(function() { fieldtext = "Blocked (external, NXRA)"; buttontext = ""; break; + case 9: + color = "red"; + fieldtext = "Blocked (gravity, CNAME)"; + buttontext = + ''; + break; + case 10: + color = "red"; + fieldtext = "Blocked (regex blacklist, CNAME)"; + buttontext = + ''; + break; + case 11: + color = "red"; + fieldtext = "Blocked (exact blacklist, CNAME)"; + buttontext = + ''; + break; default: color = "black"; fieldtext = "Unknown"; diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index e6c41507..ba508fde 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -166,7 +166,12 @@ $(document).ready(function() { } // Query status - var blocked, fieldtext, buttontext, colorClass; + var blocked, + fieldtext, + buttontext, + colorClass, + isCNAME = false; + switch (data[4]) { case "1": blocked = true; @@ -192,14 +197,14 @@ $(document).ready(function() { case "4": blocked = true; colorClass = "text-red"; - fieldtext = "Blocked (regex/wildcard)"; + fieldtext = "Blocked (regex blacklist)"; buttontext = ''; break; case "5": blocked = true; colorClass = "text-red"; - fieldtext = "Blocked (blacklist)"; + fieldtext = "Blocked (exact blacklist)"; buttontext = ''; break; @@ -221,6 +226,30 @@ $(document).ready(function() { fieldtext = "Blocked (external, NXRA)"; buttontext = ""; break; + case "9": + blocked = true; + colorClass = "text-red"; + fieldtext = "Blocked (gravity, CNAME)"; + buttontext = + ''; + isCNAME = true; + break; + case "10": + blocked = true; + colorClass = "text-red"; + fieldtext = "Blocked (regex blacklist, CNAME)"; + buttontext = + ''; + isCNAME = true; + break; + case "11": + blocked = true; + colorClass = "text-red"; + fieldtext = "Blocked (exact blacklist, CNAME)"; + buttontext = + ''; + isCNAME = true; + break; default: blocked = false; colorClass = "text-black"; @@ -228,15 +257,17 @@ $(document).ready(function() { buttontext = ""; } - // Add extra label when blocking happend during deep CNAME inspection - if (blocked && data.length > 6 && data[6] === "3") { - fieldtext += "
CNAME"; - } - $(row).addClass(colorClass); $("td:eq(4)", row).html(fieldtext); $("td:eq(6)", row).html(buttontext); + // Add domain in CNAME chain causing the query to have been blocked + var domain = data[2]; + var CNAME_domain = data[8]; + if (isCNAME) { + $("td:eq(2)", row).text(domain + "\n(blocked " + CNAME_domain + ")"); + } + // Check for existence of sixth column and display only if not Pi-holed var replytext; if (data.length > 6 && !blocked) { From a97f52ad768a77d151296a7fe60d41f0f0c67ebb Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 Jan 2020 21:03:25 +0000 Subject: [PATCH 14/58] Add possibility to highlight line on the Groups Domain Management page with specifying the domainid as GET parameter. Signed-off-by: DL6ER --- scripts/pi-hole/js/groups-domains.js | 27 +++++++++++++++++++++++++++ style/pi-hole.css | 4 ++++ 2 files changed, 31 insertions(+) diff --git a/scripts/pi-hole/js/groups-domains.js b/scripts/pi-hole/js/groups-domains.js index 6646a957..907e89f7 100644 --- a/scripts/pi-hole/js/groups-domains.js +++ b/scripts/pi-hole/js/groups-domains.js @@ -11,6 +11,7 @@ var table; var groups = []; var token = $("#token").html(); var info = null; +var GETDict = {}; function showAlert(type, icon, title, message) { var opts = {}; @@ -88,6 +89,13 @@ function datetime(date) { } $(document).ready(function() { + window.location.search + .substr(1) + .split("&") + .forEach(function(item) { + GETDict[item.split("=")[0]] = item.split("=")[1]; + }); + $("#btnAdd").on("click", addDomain); get_groups(); @@ -186,6 +194,13 @@ function initTable() { ); } + // Highlight row + if ("domainid" in GETDict && data.id === parseInt(GETDict.domainid)) { + $(row) + .find("td") + .addClass("highlight"); + } + // Select assigned groups sel.val(data.groups); // Initialize multiselect @@ -230,6 +245,18 @@ function initTable() { data.columns[0].visible = false; // Apply loaded state to table return data; + }, + initComplete: function() { + if ("domainid" in GETDict) { + var pos = table + .column(0, { order: "current" }) + .data() + .indexOf(parseInt(GETDict.domainid)); + if (pos >= 0) { + var page = Math.floor(pos / table.page.info().length); + table.page(page).draw(false); + } + } } }); diff --git a/style/pi-hole.css b/style/pi-hole.css index 419db78f..eb986c22 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -239,3 +239,7 @@ .text-vivid-blue { color: #36f !important; } + +td.highlight { + background-color: yellow; +} \ No newline at end of file From 90a09d1cd0cc1c951c52143f0da6db29f8c74342 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 31 Jan 2020 06:38:01 +0000 Subject: [PATCH 15/58] Fix domain selection for domains containing additional information (such as blocked CNAMEs) Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index ba508fde..a5c64e1b 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -418,14 +418,16 @@ $(document).ready(function() { // Domain api.$("td:eq(2)").click(function() { if (autofilter()) { - api.search(this.textContent).draw(); + var domain = this.textContent.split("\n")[0]; + api.search(domain).draw(); $("#resetButton").show(); } }); api.$("td:eq(2)").hover( function() { if (autofilter()) { - this.title = "Click to show only queries with domain " + this.textContent; + var domain = this.textContent.split("\n")[0]; + this.title = "Click to show only queries with domain " + domain; this.style.color = "#72afd2"; } else { this.title = ""; From 6ad2430f9580c8019de695b2a49bb7b52b105518 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 2 Feb 2020 19:10:26 +0100 Subject: [PATCH 16/58] Import legacy adlist.list and fix/remove debugging output Signed-off-by: DL6ER --- scripts/pi-hole/php/database.php | 18 ++++++++++++++---- scripts/pi-hole/php/teleporter.php | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/scripts/pi-hole/php/database.php b/scripts/pi-hole/php/database.php index 2bad73db..cf250d13 100644 --- a/scripts/pi-hole/php/database.php +++ b/scripts/pi-hole/php/database.php @@ -107,6 +107,16 @@ function add_to_table($db, $table, $domains, $comment, $wildcardstyle=false, $re return "Error: Unable to begin transaction for $table table."; } + // To which column should the record be added to? + if ($table === "adlist") + { + $field = "address"; + } + else + { + $field = "domain"; + } + // Get initial count of domains in this table if($type === -1) { @@ -121,11 +131,11 @@ function add_to_table($db, $table, $domains, $comment, $wildcardstyle=false, $re // Prepare INSERT SQLite statememt if($type === -1) { - $querystr = "INSERT OR IGNORE INTO $table (domain,comment) VALUES (:domain, :comment);"; + $querystr = "INSERT OR IGNORE INTO $table ($field,comment) VALUES (:$field, :comment);"; } else { - $querystr = "INSERT OR IGNORE INTO $table (domain,comment,type) VALUES (:domain, :comment, $type);"; + $querystr = "INSERT OR IGNORE INTO $table ($field,comment,type) VALUES (:$field, :comment, $type);"; } $stmt = $db->prepare($querystr); @@ -135,7 +145,7 @@ function add_to_table($db, $table, $domains, $comment, $wildcardstyle=false, $re if($returnnum) return 0; else - return "Error: Failed to prepare statement for $table table (type = $type)."; + return "Error: Failed to prepare statement for $table table (type = $type, field = $field)."; } // Loop over domains and inject the lines into the database @@ -149,7 +159,7 @@ function add_to_table($db, $table, $domains, $comment, $wildcardstyle=false, $re if($wildcardstyle) $domain = "(\\.|^)".str_replace(".","\\.",$domain)."$"; - $stmt->bindValue(":domain", $domain, SQLITE3_TEXT); + $stmt->bindValue(":$field", $domain, SQLITE3_TEXT); $stmt->bindValue(":comment", $comment, SQLITE3_TEXT); if($stmt->execute() && $stmt->reset()) diff --git a/scripts/pi-hole/php/teleporter.php b/scripts/pi-hole/php/teleporter.php index d43ee28b..84f96bc8 100644 --- a/scripts/pi-hole/php/teleporter.php +++ b/scripts/pi-hole/php/teleporter.php @@ -220,6 +220,10 @@ function archive_insert_into_table($file, $table, $flush=false, $wildcardstyle=f $type = ListType::regex_blacklist; } else if($table === "domain_audit") { $table = "domain_audit"; + $type = -1; // -1 -> not used inside add_to_table() + } else if($table === "adlist") { + $table = "adlist"; + $type = -1; // -1 -> not used inside add_to_table() } // Flush table if requested @@ -250,7 +254,6 @@ function flush_table($table, $type=null) $sql = "DELETE FROM ".$table; array_push($flushed_tables, $table); } - echo $sql."
"; $db->exec($sql); } } @@ -355,7 +358,14 @@ if(isset($_POST["action"])) if(isset($_POST["auditlog"]) && $file->getFilename() === "auditlog.list") { $num = archive_insert_into_table($file, "domain_audit", $flushtables); - echo "Processed blacklist (regex) (".$num." entries)
\n"; + echo "Processed audit log (".$num." entries)
\n"; + $importedsomething = true; + } + + if(isset($_POST["adlist"]) && $file->getFilename() === "adlists.list") + { + $num = archive_insert_into_table($file, "adlist", $flushtables); + echo "Processed adlists (".$num." entries)
\n"; $importedsomething = true; } From 86f56ee5df302effd424b93d17490140fb3afd7f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 20 Jan 2020 19:35:43 +0100 Subject: [PATCH 17/58] Make IP addresses links that send to the Query Log. Signed-off-by: DL6ER --- scripts/pi-hole/js/network.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/scripts/pi-hole/js/network.js b/scripts/pi-hole/js/network.js index 2320dfcd..f8e6f181 100644 --- a/scripts/pi-hole/js/network.js +++ b/scripts/pi-hole/js/network.js @@ -101,16 +101,21 @@ $(document).ready(function() { // Set number of queries to localized string (add thousand separators) $("td:eq(6)", row).html(data.numQueries.toLocaleString()); - var ips = data.ip; - var shortips = ips; - if (ips.length > MAXIPDISPLAY) { - shortips = ips.slice(0, MAXIPDISPLAY - 1); - shortips.push("..."); + var ips = []; + var maxiter = Math.min(data.ip.length, MAXIPDISPLAY); + for (let index = 0; index < maxiter; index++) { + var ip = data.ip[index]; + ips.push('' + ip + ''); + } + if (data.ip.length > MAXIPDISPLAY) { + // We hit the maximum above, add "..." to symbolize we would + // have more to show here + ips.push("..."); } - $("td:eq(0)", row).html(shortips.join("
")); + $("td:eq(0)", row).html(ips.join("
")); $("td:eq(0)", row).hover(function() { - this.title = ips.join("\n"); + this.title = data.ip.join("\n"); }); // MAC + Vendor field if available From 4228d883365aa8ed0de8f65b2cccb202390f02a0 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 3 Feb 2020 23:15:25 +0100 Subject: [PATCH 18/58] xo does neither like const nor let. Use var everywhere. Signed-off-by: DL6ER --- scripts/pi-hole/js/network.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/network.js b/scripts/pi-hole/js/network.js index f8e6f181..6203d81d 100644 --- a/scripts/pi-hole/js/network.js +++ b/scripts/pi-hole/js/network.js @@ -103,10 +103,11 @@ $(document).ready(function() { var ips = []; var maxiter = Math.min(data.ip.length, MAXIPDISPLAY); - for (let index = 0; index < maxiter; index++) { + for (var index = 0; index < maxiter; index++) { var ip = data.ip[index]; - ips.push('' + ip + ''); + ips.push('' + ip + ""); } + if (data.ip.length > MAXIPDISPLAY) { // We hit the maximum above, add "..." to symbolize we would // have more to show here From 8fd1631ee39f4a10084bd05545ee0eb1192349c4 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 4 Feb 2020 08:14:40 +0100 Subject: [PATCH 19/58] Add link to regex filter if blocked by one. Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index a5c64e1b..6de308eb 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -257,6 +257,10 @@ $(document).ready(function() { buttontext = ""; } + if (data.length > 9 && data[9] > -1) { + fieldtext += "
Jump to regex"; + } + $(row).addClass(colorClass); $("td:eq(4)", row).html(fieldtext); $("td:eq(6)", row).html(buttontext); From 249dfc793c892944997602d2dcc147702ae7255e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 4 Feb 2020 21:52:04 +0100 Subject: [PATCH 20/58] Integrate link to regex filter in status column. Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 6de308eb..58c64cd2 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -198,6 +198,18 @@ $(document).ready(function() { blocked = true; colorClass = "text-red"; fieldtext = "Blocked (regex blacklist)"; + + if (data.length > 9 && data[9] > -1) { + fieldtext = + "" + + fieldtext + + ' '; + } + buttontext = ''; break; @@ -257,10 +269,6 @@ $(document).ready(function() { buttontext = ""; } - if (data.length > 9 && data[9] > -1) { - fieldtext += "
Jump to regex"; - } - $(row).addClass(colorClass); $("td:eq(4)", row).html(fieldtext); $("td:eq(6)", row).html(buttontext); From 280b73bf8d37d713b28d278dc49d651989d491fa Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 4 Feb 2020 22:40:11 +0100 Subject: [PATCH 21/58] Fix computation of percentages in the over-time graph. Signed-off-by: DL6ER --- scripts/pi-hole/js/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 8348a96e..d41a6d64 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -792,8 +792,9 @@ $(document).ready(function() { label: function(tooltipItems, data) { if (tooltipItems.datasetIndex === 1) { var percentage = 0.0; - var total = parseInt(data.datasets[0].data[tooltipItems.index]); + var permitted = parseInt(data.datasets[0].data[tooltipItems.index]); var blocked = parseInt(data.datasets[1].data[tooltipItems.index]); + var total = permitted + blocked; if (total > 0) { percentage = (100.0 * blocked) / total; } From ae470b2d3dd09f3318a797e2cf95804cdcf45e2b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 11 Feb 2020 19:58:03 +0100 Subject: [PATCH 22/58] Add ability to mass-inject space-separated domains. Signed-off-by: DL6ER --- scripts/pi-hole/php/groups.php | 85 +++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index 2482c6f6..f17476b6 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -54,21 +54,24 @@ if ($_POST['action'] == 'get_groups') { } elseif ($_POST['action'] == 'add_group') { // Add new group try { + $names = explode(' ', $_POST['name']); $stmt = $db->prepare('INSERT INTO "group" (name,description) VALUES (:name,:desc)'); if (!$stmt) { throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); } - if (!$stmt->bindValue(':name', $_POST['name'], SQLITE3_TEXT)) { - throw new Exception('While binding name: ' . $db->lastErrorMsg()); - } - if (!$stmt->bindValue(':desc', $_POST['desc'], SQLITE3_TEXT)) { throw new Exception('While binding desc: ' . $db->lastErrorMsg()); } - if (!$stmt->execute()) { - throw new Exception('While executing: ' . $db->lastErrorMsg()); + foreach ($names as $name) { + if (!$stmt->bindValue(':name', $name, SQLITE3_TEXT)) { + throw new Exception('While binding name: ' . $db->lastErrorMsg()); + } + + if (!$stmt->execute()) { + throw new Exception('While executing: ' . $db->lastErrorMsg()); + } } $reload = true; @@ -157,7 +160,6 @@ if ($_POST['action'] == 'get_groups') { throw new Exception('Error while querying gravity\'s client table: ' . $db->lastErrorMsg()); } - $data = array(); while (($res = $query->fetchArray(SQLITE3_ASSOC)) !== false) { $group_query = $db->query('SELECT group_id FROM client_by_group WHERE client_id = ' . $res['id'] . ';'); @@ -232,17 +234,20 @@ if ($_POST['action'] == 'get_groups') { } elseif ($_POST['action'] == 'add_client') { // Add new client try { + $ips = explode(' ', $_POST['ip']); $stmt = $db->prepare('INSERT INTO client (ip) VALUES (:ip)'); if (!$stmt) { throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); } - if (!$stmt->bindValue(':ip', $_POST['ip'], SQLITE3_TEXT)) { - throw new Exception('While binding ip: ' . $db->lastErrorMsg()); - } + foreach ($ips as $ip) { + if (!$stmt->bindValue(':ip', $ip, SQLITE3_TEXT)) { + throw new Exception('While binding ip: ' . $db->lastErrorMsg()); + } - if (!$stmt->execute()) { - throw new Exception('While executing: ' . $db->lastErrorMsg()); + if (!$stmt->execute()) { + throw new Exception('While executing: ' . $db->lastErrorMsg()); + } } $reload = true; @@ -326,7 +331,7 @@ if ($_POST['action'] == 'get_groups') { } catch (\Exception $ex) { JSON_error($ex->getMessage()); } -} elseif ($_POST['action'] == 'get_domains') { +} elseif ($_POST['action'] == 'get_addresses') { // List all available groups try { $query = $db->query('SELECT * FROM domainlist;'); @@ -364,6 +369,7 @@ if ($_POST['action'] == 'get_groups') { } elseif ($_POST['action'] == 'add_domain') { // Add new domain try { + $domains = explode(' ', $_POST['domain']); $stmt = $db->prepare('INSERT INTO domainlist (domain,type,comment) VALUES (:domain,:type,:comment)'); if (!$stmt) { throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); @@ -371,22 +377,6 @@ if ($_POST['action'] == 'get_groups') { $type = intval($_POST['type']); - // Convert domain name to IDNA ASCII form for international domains - $domain = idn_to_ascii($_POST['domain']); - if($type === ListType::whitelist || $type === ListType::blacklist) - { - // If adding to the exact lists, we convert the domain lower case and check whether it is valid - $domain = strtolower($domain); - if(filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) - { - throw new Exception('Domain ' . htmlentities(utf8_encode($domain)) . 'is not a valid domain.'); - } - } - - if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) { - throw new Exception('While binding domain: ' . $db->lastErrorMsg()); - } - if (!$stmt->bindValue(':type', $type, SQLITE3_TEXT)) { throw new Exception('While binding type: ' . $db->lastErrorMsg()); } @@ -395,8 +385,26 @@ if ($_POST['action'] == 'get_groups') { throw new Exception('While binding comment: ' . $db->lastErrorMsg()); } - if (!$stmt->execute()) { - throw new Exception('While executing: ' . $db->lastErrorMsg()); + foreach ($domains as $domain) { + // Convert domain name to IDNA ASCII form for international domains + $domain = idn_to_ascii($domain); + if($type === ListType::whitelist || $type === ListType::blacklist) + { + // If adding to the exact lists, we convert the domain lower case and check whether it is valid + $domain = strtolower($domain); + if(filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) + { + throw new Exception('Domain ' . htmlentities(utf8_encode($domain)) . 'is not a valid domain.'); + } + } + + if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) { + throw new Exception('While binding domain: ' . $db->lastErrorMsg()); + } + + if (!$stmt->execute()) { + throw new Exception('While executing: ' . $db->lastErrorMsg()); + } } $reload = true; @@ -546,21 +554,24 @@ if ($_POST['action'] == 'get_groups') { } elseif ($_POST['action'] == 'add_adlist') { // Add new adlist try { + $addresses = explode(' ', $_POST['address']); $stmt = $db->prepare('INSERT INTO adlist (address,comment) VALUES (:address,:comment)'); if (!$stmt) { throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); } - if (!$stmt->bindValue(':address', $_POST['address'], SQLITE3_TEXT)) { - throw new Exception('While binding address: ' . $db->lastErrorMsg()); - } - if (!$stmt->bindValue(':comment', $_POST['comment'], SQLITE3_TEXT)) { throw new Exception('While binding comment: ' . $db->lastErrorMsg()); } - if (!$stmt->execute()) { - throw new Exception('While executing: ' . $db->lastErrorMsg()); + foreach ($addresses as $address) { + if (!$stmt->bindValue(':address', $address, SQLITE3_TEXT)) { + throw new Exception('While binding address: ' . $db->lastErrorMsg()); + } + + if (!$stmt->execute()) { + throw new Exception('While executing: ' . $db->lastErrorMsg()); + } } $reload = true; From 13b7b3d1a97614026032d9c810365752cac3b9eb Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 11 Feb 2020 19:58:46 +0100 Subject: [PATCH 23/58] Remove redundant adlist management on settings page. This is still using CLI callbacks instead of working directly on the database. Signed-off-by: DL6ER --- scripts/pi-hole/js/settings.js | 22 ---------- scripts/pi-hole/php/groups.php | 2 +- scripts/pi-hole/php/savesettings.php | 55 ------------------------- settings.php | 61 +--------------------------- 4 files changed, 3 insertions(+), 137 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index b4fb0479..8652eb3c 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -217,28 +217,6 @@ $(document).ready(function() { $('[data-toggle="tooltip"]').tooltip({ html: true, container: "body" }); }); -// Handle list deletion -$("button[id^='adlist-btn-']").on("click", function(e) { - var id = parseInt($(this).context.id.replace(/[^0-9.]/g, ""), 10); - e.preventDefault(); - - var status = $('input[name="adlist-del-' + id + '"]').is(":checked"); - var textType = status ? "none" : "line-through"; - - // Check hidden delete box (or reset) - $('input[name="adlist-del-' + id + '"]').prop("checked", !status); - // Untick and disable check box (or reset) - $('input[name="adlist-enable-' + id + '"]') - .prop("checked", status) - .prop("disabled", !status); - // Strike through text (or reset) - $('a[id="adlist-text-' + id + '"]').css("text-decoration", textType); - // Highlight that the button has to be clicked in order to make the change live - $('button[id="blockinglistsaveupdate"]') - .addClass("btn-danger") - .css("font-weight", "bold"); -}); - // Change "?tab=" parameter in URL for save and reload $(".nav-tabs a").on("shown.bs.tab", function(e) { var tab = e.target.hash.substring(1); diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index f17476b6..c9693c2c 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -331,7 +331,7 @@ if ($_POST['action'] == 'get_groups') { } catch (\Exception $ex) { JSON_error($ex->getMessage()); } -} elseif ($_POST['action'] == 'get_addresses') { +} elseif ($_POST['action'] == 'get_domains') { // List all available groups try { $query = $db->query('SELECT * FROM domainlist;'); diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index 8ab0922a..60d7a52f 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -174,24 +174,6 @@ function readDNSserversList() } require_once("database.php"); -$adlist = []; -function readAdlists() -{ - // Reset list - $list = []; - $db = SQLite3_connect(getGravityDBFilename()); - if ($db) - { - $results = $db->query("SELECT * FROM adlist"); - - while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) - { - array_push($list, $res); - } - $db->close(); - } - return $list; -} function addStaticDHCPLease($mac, $ip, $hostname) { global $error, $success, $dhcp_static_leases; @@ -251,8 +233,6 @@ function addStaticDHCPLease($mac, $ip, $hostname) { } } - // Read available adlists - $adlist = readAdlists(); // Read available DNS server list $DNSserverslist = readDNSserversList(); @@ -698,41 +678,6 @@ function addStaticDHCPLease($mac, $ip, $hostname) { break; - case "adlists": - foreach ($adlist as $key => $value) - { - if(isset($_POST["adlist-del-".$key])) - { - // Delete list - exec("sudo pihole -a adlist del ".escapeshellcmd($value["address"])); - } - elseif(isset($_POST["adlist-enable-".$key]) && $value["enabled"] !== 1) - { - // Is not enabled, but should be - exec("sudo pihole -a adlist enable ".escapeshellcmd($value["address"])); - - } - elseif(!isset($_POST["adlist-enable-".$key]) && $value["enabled"] === 1) - { - // Is enabled, but shouldn't be - exec("sudo pihole -a adlist disable ".escapeshellcmd($value["address"])); - } - } - - if(strlen($_POST["newuserlists"]) > 1) - { - $domains = array_filter(preg_split('/\r\n|[\r\n]/', $_POST["newuserlists"])); - $comment = "'".$_POST["newusercomment"]."'"; - foreach($domains as $domain) - { - exec("sudo pihole -a adlist add ".escapeshellcmd($domain)." ".escapeshellcmd($comment)); - } - } - - // Reread available adlists - $adlist = readAdlists(); - break; - case "privacyLevel": $level = intval($_POST["privacylevel"]); if($level >= 0 && $level <= 4) diff --git a/settings.php b/settings.php index 1861eb36..b7383c96 100644 --- a/settings.php +++ b/settings.php @@ -250,67 +250,10 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
-

Blocklists used to generate Pi-hole's Gravity:

+

Blocklists used to generate Pi-hole's Gravity

-
- - - - - - - - - - - - - $value) { ?> - - - - - - - - - - -
EnabledListAddedLast modifiedCommentDelete
- checked> - - " target="_new" id="adlist-text-"> - - - - - - - - - -
-
-
-
- - -
-
- - -
-
- - -
-
From b2cc349abfd163468bdb3f5e015675fb6e25e39f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 14 Feb 2020 17:24:01 +0100 Subject: [PATCH 24/58] Only try to convert exact domains using the IDN library. Signed-off-by: DL6ER --- scripts/pi-hole/js/list.js | 6 ++++++ scripts/pi-hole/php/add.php | 9 ++++++--- scripts/pi-hole/php/get.php | 14 ++++++++------ scripts/pi-hole/php/groups.php | 22 ++++++++++++---------- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 8a369c34..4f01c563 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -123,6 +123,12 @@ function sub(index, entry, arg) { list = "#list-regex"; heading = "#h3-regex"; locallistType = arg; + } else { + // Extract possible IDN part + // This extracts "xn--allestrungen-9ib.de" from, + // e.g. "allestörungen.de (xn--allestrungen-9ib.de)" + var raw_domain = entry.split("("); + entry = raw_domain[raw_domain.length - 1].split(")")[0]; } var alInfo = $("#alInfo"); diff --git a/scripts/pi-hole/php/add.php b/scripts/pi-hole/php/add.php index 94da8863..621e613f 100644 --- a/scripts/pi-hole/php/add.php +++ b/scripts/pi-hole/php/add.php @@ -21,9 +21,12 @@ $domains = preg_split('/\s+/', trim($_POST['domain'])); $comment = trim($_POST['comment']); // Convert domain name to IDNA ASCII form for international domains -foreach($domains as &$domain) -{ - $domain = idn_to_ascii($domain); +// Do this only for exact domains, not for regex filters +if ($list === "white" || $list === "black") { + foreach($domains as &$domain) + { + $domain = idn_to_ascii($domain); + } } // Only check domains we add to the exact lists. diff --git a/scripts/pi-hole/php/get.php b/scripts/pi-hole/php/get.php index 5a230bdb..09c238f9 100644 --- a/scripts/pi-hole/php/get.php +++ b/scripts/pi-hole/php/get.php @@ -30,12 +30,14 @@ function getTableContent($type) { while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) { - $utf8_domain = idn_to_utf8($res['domain']); - // Convert domain name to international form - // if applicable - if($res['domain'] !== $utf8_domain) - { - $res['domain'] = $utf8_domain.' ('.$res['domain'].')'; + if ($res['type'] === ListType::whitelist || $res['type'] === ListType::blacklist) { + $utf8_domain = idn_to_utf8($res['domain']); + // Convert domain name to international form + // if applicable + if($res['domain'] !== $utf8_domain) + { + $res['domain'] = $utf8_domain.' ('.$res['domain'].')'; + } } array_push($entries, $res); } diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index 2482c6f6..6ccdbf1f 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -346,12 +346,14 @@ if ($_POST['action'] == 'get_groups') { array_push($groups, $gres['group_id']); } $res['groups'] = $groups; - $utf8_domain = idn_to_utf8($res['domain']); - // Convert domain name to international form - // if applicable - if($res['domain'] !== $utf8_domain) - { - $res['domain'] = $utf8_domain.' ('.$res['domain'].')'; + if ($res['type'] === ListType::whitelist || $res['type'] === ListType::blacklist) { + $utf8_domain = idn_to_utf8($res['domain']); + // Convert domain name to international form + // if applicable + if($res['domain'] !== $utf8_domain) + { + $res['domain'] = $utf8_domain.' ('.$res['domain'].')'; + } } array_push($data, $res); } @@ -371,10 +373,10 @@ if ($_POST['action'] == 'get_groups') { $type = intval($_POST['type']); - // Convert domain name to IDNA ASCII form for international domains - $domain = idn_to_ascii($_POST['domain']); - if($type === ListType::whitelist || $type === ListType::blacklist) - { + if ($type === ListType::whitelist || $type === ListType::blacklist) { + // Convert domain name to IDNA ASCII form for international domains + $domain = idn_to_ascii($_POST['domain']); + // If adding to the exact lists, we convert the domain lower case and check whether it is valid $domain = strtolower($domain); if(filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) From 623cecc64cfba15f2095d4007ee43eb517d099d1 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 14 Feb 2020 17:41:03 +0100 Subject: [PATCH 25/58] Also show regex link when this is CNAME regex blocked. Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 58c64cd2..6e659ea9 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -250,6 +250,18 @@ $(document).ready(function() { blocked = true; colorClass = "text-red"; fieldtext = "Blocked (regex blacklist, CNAME)"; + + if (data.length > 9 && data[9] > -1) { + fieldtext = + "" + + fieldtext + + ' '; + } + buttontext = ''; isCNAME = true; From 1d7889d7549e40ae0f61d9f3cbd7793caa64cbf8 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 14 Feb 2020 17:50:51 +0100 Subject: [PATCH 26/58] Add hover title to regex link icon. Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 6e659ea9..483927a7 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -207,7 +207,7 @@ $(document).ready(function() { colorClass + ">" + fieldtext + - ' '; + ' '; } buttontext = @@ -259,7 +259,7 @@ $(document).ready(function() { colorClass + ">" + fieldtext + - ' '; + ' '; } buttontext = From facb5b07a941ebeb36cccb563965dda9241eb0d8 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 14 Feb 2020 17:54:34 +0100 Subject: [PATCH 27/58] Regex IDs start from 1 by default. 0 means the query has been imported from the database. It makes no sense to show a link when we don't know which regex was responsible for blocking (this is not stored in the database). Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 483927a7..d68aa29c 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -199,7 +199,7 @@ $(document).ready(function() { colorClass = "text-red"; fieldtext = "Blocked (regex blacklist)"; - if (data.length > 9 && data[9] > -1) { + if (data.length > 9 && data[9] > 0) { fieldtext = "(regex blacklist, CNAME)"; - if (data.length > 9 && data[9] > -1) { + if (data.length > 9 && data[9] > 0) { fieldtext = " @@ -20,7 +28,7 @@

- Add a new domain + Add a new domain

@@ -33,10 +41,17 @@
@@ -56,7 +71,7 @@

- List of configured domains + List of domains

diff --git a/list.php b/list.php deleted file mode 100644 index 95fbcd1a..00000000 --- a/list.php +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - -
-
- - -
-
- -
- - - - - - - -
-
-
- - - - - - - - - -
    -
    -
      - - - - diff --git a/scripts/pi-hole/js/groups-domains.js b/scripts/pi-hole/js/groups-domains.js index e48045e2..7e38d4b8 100644 --- a/scripts/pi-hole/js/groups-domains.js +++ b/scripts/pi-hole/js/groups-domains.js @@ -11,6 +11,7 @@ var table; var groups = []; var token = $("#token").html(); var GETDict = {}; +var showtype = "all"; function get_groups() { $.post( @@ -32,6 +33,10 @@ $(document).ready(function() { GETDict[item.split("=")[0]] = item.split("=")[1]; }); + if ("type" in GETDict && (GETDict.type === "white" || GETDict.type === "black")) { + showtype = GETDict.type; + } + $("#btnAdd").on("click", addDomain); get_groups(); @@ -46,7 +51,7 @@ function initTable() { table = $("#domainsTable").DataTable({ ajax: { url: "scripts/pi-hole/php/groups.php", - data: { action: "get_domains", token: token }, + data: { action: "get_domains", showtype: showtype, token: token }, type: "POST" }, order: [[0, "asc"]], @@ -74,20 +79,32 @@ function initTable() { '' + data.domain + "" ); - $("td:eq(1)", row).html( - '' + + whitelist_options + + blacklist_options + "" ); $("#type", row).on("change", editDomain); @@ -113,21 +130,30 @@ function initTable() { $("#comment", row).val(data.comment); $("#comment", row).on("change", editDomain); - $("td:eq(4)", row).empty(); - $("td:eq(4)", row).append(''); - var sel = $("#multiselect", row); - // Add all known groups - for (var i = 0; i < groups.length; i++) { - var extra = ""; - if (!groups[i].enabled) { - extra = " (disabled)"; + // Show group assignment field only if in full domain management mode + if (showtype === "all") { + $("td:eq(4)", row).empty(); + $("td:eq(4)", row).append(''); + var sel = $("#multiselect", row); + // Add all known groups + for (var i = 0; i < groups.length; i++) { + var extra = ""; + if (!groups[i].enabled) { + extra = " (disabled)"; + } + + sel.append( + $("
      class="active"> - + Whitelist class="active"> - + Blacklist diff --git a/scripts/pi-hole/php/sub.php b/scripts/pi-hole/php/sub.php deleted file mode 100644 index cdee3ceb..00000000 --- a/scripts/pi-hole/php/sub.php +++ /dev/null @@ -1,51 +0,0 @@ - From 88eb4c85b8d0e54d117f67722a24bb066ac76286 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 14 Feb 2020 22:08:23 +0100 Subject: [PATCH 29/58] Optimizations to regex link generating code. Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 40 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index d68aa29c..95f847da 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -170,7 +170,8 @@ $(document).ready(function() { fieldtext, buttontext, colorClass, - isCNAME = false; + isCNAME = false, + regexLink = false; switch (data[4]) { case "1": @@ -200,14 +201,7 @@ $(document).ready(function() { fieldtext = "Blocked (regex blacklist)"; if (data.length > 9 && data[9] > 0) { - fieldtext = - "" + - fieldtext + - ' '; + regexLink = true; } buttontext = @@ -252,14 +246,7 @@ $(document).ready(function() { fieldtext = "Blocked (regex blacklist, CNAME)"; if (data.length > 9 && data[9] > 0) { - fieldtext = - "" + - fieldtext + - ' '; + regexLink = true; } buttontext = @@ -285,6 +272,25 @@ $(document).ready(function() { $("td:eq(4)", row).html(fieldtext); $("td:eq(6)", row).html(buttontext); + if (regexLink) { + $("td:eq(4)", row).hover( + function() { + this.title = "Click to show matching regex filter"; + this.style.color = "#72afd2"; + }, + function() { + this.style.color = ""; + } + ); + $("td:eq(4)", row).click(function() { + var new_tab = window.open("groups-domains.php?domainid=" + data[9], "_blank"); + if (new_tab) { + new_tab.focus(); + } + }); + $("td:eq(4)", row).css("cursor", "pointer"); + } + // Add domain in CNAME chain causing the query to have been blocked var domain = data[2]; var CNAME_domain = data[8]; From 268a6d9144959dd2402217f96f8a7637a83273be Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Feb 2020 07:35:37 +0100 Subject: [PATCH 30/58] Extract domain in addition request for all domain types, not just the exact ones. Signed-off-by: DL6ER --- scripts/pi-hole/php/groups.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index 6ccdbf1f..aa6e3f83 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -372,13 +372,14 @@ if ($_POST['action'] == 'get_groups') { } $type = intval($_POST['type']); + $domain = $_POST['domain']; if ($type === ListType::whitelist || $type === ListType::blacklist) { - // Convert domain name to IDNA ASCII form for international domains - $domain = idn_to_ascii($_POST['domain']); + // Convert domain name to IDNA ASCII form for international + // domains and convert the domain to lower case + $domain = strtolower(idn_to_ascii($domain)); - // If adding to the exact lists, we convert the domain lower case and check whether it is valid - $domain = strtolower($domain); + // Check validity of domain if(filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) { throw new Exception('Domain ' . htmlentities(utf8_encode($domain)) . 'is not a valid domain.'); From ccb9cb45b2bdbce558de739925b53b72be590f86 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Feb 2020 09:26:55 +0100 Subject: [PATCH 31/58] Add info that users should run pihole -g (can also be doen on the web interface) after modifying their adlists. Signed-off-by: DL6ER --- gravity.php | 2 +- groups-adlists.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gravity.php b/gravity.php index d86be424..b73cf514 100644 --- a/gravity.php +++ b/gravity.php @@ -15,7 +15,7 @@
      From 09c7ba99fa393d04ffd59915c7aea7e7946076f0 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Feb 2020 12:25:07 +0100 Subject: [PATCH 32/58] Underline clickable regex links on Query Log page. Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 3 ++- style/pi-hole.css | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 95f847da..ca999435 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -288,7 +288,8 @@ $(document).ready(function() { new_tab.focus(); } }); - $("td:eq(4)", row).css("cursor", "pointer"); + $("td:eq(4)", row).addClass("underline"); + $("td:eq(4)", row).addClass("pointer"); } // Add domain in CNAME chain causing the query to have been blocked diff --git a/style/pi-hole.css b/style/pi-hole.css index eb986c22..b17a4a17 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -242,4 +242,12 @@ td.highlight { background-color: yellow; -} \ No newline at end of file +} + +.underline { + text-decoration: underline; +} + +.pointer { + cursor: pointer; +} From 8d6e2a07fb1adea557fe560ff24108ad92da4e48 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Feb 2020 18:16:56 +0100 Subject: [PATCH 33/58] Reverse logic on Query Log to ensure only permitted queries can be added to the blacklist. Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 58c64cd2..db157011 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -478,10 +478,10 @@ $(document).ready(function() { $("#all-queries tbody").on("click", "button", function() { var data = tableApi.row($(this).parents("tr")).data(); - if (data[4] === "1" || data[4] === "4" || data[4] === "5") { - add(data[2], "white"); - } else { + if (data[4] === "2" || data[4] === "3") { add(data[2], "black"); + } else { + add(data[2], "white"); } }); From d584c2e2c2306a88d1f3774f0c1a3dfd96ce0f95 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Feb 2020 18:18:46 +0100 Subject: [PATCH 34/58] Re-add add.php as it is used by the Query Log as well as the blocking page. Signed-off-by: DL6ER --- scripts/pi-hole/php/add.php | 79 +++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 scripts/pi-hole/php/add.php diff --git a/scripts/pi-hole/php/add.php b/scripts/pi-hole/php/add.php new file mode 100644 index 00000000..1866ebed --- /dev/null +++ b/scripts/pi-hole/php/add.php @@ -0,0 +1,79 @@ + + From ef57abdbdd9ee6ab6249847a5d1ee025b5263dac Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 16 Feb 2020 07:59:07 +0100 Subject: [PATCH 35/58] Enhance wrapping for long regex lines on the domains list pages. Signed-off-by: DL6ER --- scripts/pi-hole/js/groups-domains.js | 2 +- style/pi-hole.css | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/groups-domains.js b/scripts/pi-hole/js/groups-domains.js index 7e38d4b8..6f25da31 100644 --- a/scripts/pi-hole/js/groups-domains.js +++ b/scripts/pi-hole/js/groups-domains.js @@ -76,7 +76,7 @@ function initTable() { "\nDatabase ID: " + data.id; $("td:eq(0)", row).html( - '' + data.domain + "" + '' + data.domain + "" ); var whitelist_options = ""; diff --git a/style/pi-hole.css b/style/pi-hole.css index eb986c22..827e3788 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -242,4 +242,14 @@ td.highlight { background-color: yellow; -} \ No newline at end of file +} + +code.breakall +{ + white-space: -moz-pre-wrap; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + white-space: pre-wrap; + word-break: break-all; + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} From 0c1f3b3d143e2db7d6f0450702c85ad725ac990d Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 16 Feb 2020 08:00:03 +0100 Subject: [PATCH 36/58] Depend display of group assigmnent delect field on number of visible columns. Signed-off-by: DL6ER --- scripts/pi-hole/js/groups-domains.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/groups-domains.js b/scripts/pi-hole/js/groups-domains.js index 6f25da31..7bbf1ec3 100644 --- a/scripts/pi-hole/js/groups-domains.js +++ b/scripts/pi-hole/js/groups-domains.js @@ -131,7 +131,7 @@ function initTable() { $("#comment", row).on("change", editDomain); // Show group assignment field only if in full domain management mode - if (showtype === "all") { + if (table.column(5).visible()) { $("td:eq(4)", row).empty(); $("td:eq(4)", row).append(''); var sel = $("#multiselect", row); @@ -169,7 +169,7 @@ function initTable() { '">' + '' + ""; - if (showtype === "all") { + if (table.column(5).visible()) { $("td:eq(5)", row).html(button); } else { $("td:eq(4)", row).html(button); From ed24f5c41a9bbfcfbc2dbdad58b4742fc5b9cc89 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 16 Feb 2020 09:07:22 +0100 Subject: [PATCH 37/58] Enable visibility of overflow to table-responsive to ensure the group assignment dropdown doesn't get hidden when opened. Signed-off-by: DL6ER --- style/pi-hole.css | 1 + 1 file changed, 1 insertion(+) diff --git a/style/pi-hole.css b/style/pi-hole.css index b17a4a17..1aef8edd 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -72,6 +72,7 @@ .table-responsive { -webkit-overflow-scrolling: touch; + overflow: visible; } /* Optimize Queries-Table for small screens */ From 9248b1eaadc1b92c642894cb88860682bdcefa2c Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sun, 16 Feb 2020 13:31:34 +0000 Subject: [PATCH 38/58] Make dropdown selection boxes scrollable Signed-off-by: Adam Warner --- style/pi-hole.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/style/pi-hole.css b/style/pi-hole.css index 1aef8edd..1f2a3c35 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -252,3 +252,9 @@ td.highlight { .pointer { cursor: pointer; } + +.multiselect-container{ + + max-height:200px; + overflow:auto; + } \ No newline at end of file From 3c3e22bd980e531bac6ce350522c4d18b56ad4d3 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 27 Jan 2020 10:48:09 +0000 Subject: [PATCH 39/58] Add support for client comment and timestamp fields. Signed-off-by: DL6ER --- groups-clients.php | 8 +++--- scripts/pi-hole/js/groups-clients.js | 40 +++++++++++++++++++++++----- scripts/pi-hole/php/groups.php | 33 ++++++++++++++++++++++- 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/groups-clients.php b/groups-clients.php index 53aa7e3f..259ccd01 100644 --- a/groups-clients.php +++ b/groups-clients.php @@ -30,11 +30,12 @@ +
      +
      - - + +
      @@ -59,6 +60,7 @@ ID IP address + Comment Group assignment Action diff --git a/scripts/pi-hole/js/groups-clients.js b/scripts/pi-hole/js/groups-clients.js index 71e0e585..f3440456 100644 --- a/scripts/pi-hole/js/groups-clients.js +++ b/scripts/pi-hole/js/groups-clients.js @@ -38,7 +38,7 @@ function reload_client_suggestions() { sel.append( $("