From 1b3904f67ee0ee96aa50b6a463053a66bef2aa7e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 14 Feb 2017 14:51:13 +0100 Subject: [PATCH 01/30] Move whole project to EUPL, copy MIT license to scripts/vendor/ and style/vendor/ --- LICENSE | 179 +++++++++++++++++++++++++++++++++++++---- scripts/vendor/LICENSE | 20 +++++ style/vendor/LICENSE | 20 +++++ 3 files changed, 203 insertions(+), 16 deletions(-) create mode 100644 scripts/vendor/LICENSE create mode 100644 style/vendor/LICENSE diff --git a/LICENSE b/LICENSE index e1854e86..da85e8d6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,20 +1,167 @@ -The MIT License (MIT) +Copyright (C) 2017 Pi-hole, LLC (https://pi-hole.net) +Pi-hole web interface -Copyright (c) 2014-2015 almasaeed2010 +This software is licensed under the European Union Public License -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +This license is available in the 22 official languages of the EU. The English version is included here. +Please see https://joinup.ec.europa.eu/community/eupl/og_page/eupl for official translations of the other languages. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +This license applies to the whole project EXCEPT the files under + - styles/vendor, and + - scripts/vendor +whose licenses are located therein. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------- + +European Union Public Licence +V. 1.1 + +EUPL (C) the European Community 2007 + +This European Union Public Licence (the "EUPL") applies to the Work or Software (as defined below) which is provided under the terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such use is covered by a right of the copyright holder of the Work). + +The Original Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following notice immediately following the copyright notice for the Original Work: + +Licensed under the EUPL V.1.1 + +or has expressed by any other mean his willingness to license under the EUPL. + +1. Definitions + +In this Licence, the following terms have the following meaning: + +- The Licence: this Licence. + +- The Original Work or the Software: the software distributed and/or communicated by the Licensor under this Licence, available as Source Code and also as Executable Code as the case may be. + +- Derivative Works: the works or software that could be created by the Licensee, based upon the Original Work or modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in the country mentioned in Article 15. + +- The Work: the Original Work and/or its Derivative Works. + +- The Source Code: the human-readable form of the Work which is the most convenient for people to study and modify. + +- The Executable Code: any code which has generally been compiled and which is meant to be interpreted by a computer as a program. + +- The Licensor: the natural or legal person that distributes and/or communicates the Work under the Licence. + +- Contributor(s): any natural or legal person who modifies the Work under the Licence, or otherwise contributes to the creation of a Derivative Work. + +- The Licensee or "You": any natural or legal person who makes any usage of the Software under the terms of the Licence. + +- Distribution and/or Communication: any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, on-line or off-line, copies of the Work or providing access to its essential functionalities at the disposal of any other natural or legal person. + +2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a world-wide, royalty-free, non-exclusive, sub-licensable licence to do the following, for the duration of copyright vested in the Original Work: + +- use the Work in any circumstance and for all usage, +- reproduce the Work, +- modify the Original Work, and make Derivative Works based upon the Work, +- communicate to the public, including the right to make available or display the Work or copies thereof to the public and perform publicly, as the case may be, the Work, +- distribute the Work or copies thereof, +- lend and rent the Work or copies thereof, +- sub-license rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed by law in order to make effective the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non exclusive usage rights to any patents held by the Licensor, to the extent necessary to make use of the rights granted on the Work under this Licence. + +3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to distribute and/or communicate the Work. + +4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the exclusive rights of the rights owners in the Original Work or Software, of the exhaustion of those rights or of other applicable limitations thereto. + +5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: the Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the Licence with every copy of the Work he/she distributes and/or communicates. The Licensee must cause any Derivative Work to carry prominent notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes and/or communicates copies of the Original Works or Derivative Works based upon the Original Work, this Distribution and/or Communication will be done under the terms of this Licence or of a later version of this Licence unless the Original Work is expressly distributed only under this version of the Licence. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes and/or Communicates Derivative Works or copies thereof based upon both the Original Work and another work licensed under a Compatible Licence, this Distribution and/or Communication can be done under the terms of this Compatible Licence. For the sake of this clause, "Compatible Licence" refers to the licences listed in the appendix attached to this Licence. Should the Licensee’s obligations under the Compatible Licence conflict with his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. + +Provision of Source Code: When distributing and/or communicating copies of the Work, the Licensee will provide a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available for as long as the Licensee continues to distribute and/or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the copyright notice. + +6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions to the Work, under the terms of this Licence. + +7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous contributors. It is not a finished work and may therefore contain defects or "bugs" inherent to this type of software development. + +For the above reason, the Work is provided under the Licence on an "as is" basis and without warranties of any kind concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work. + +8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, the Licensor will be liable under statutory product liability laws as far such laws apply to the Work. + +9. Additional agreements + +While distributing the Original Work or Derivative Works, You may choose to conclude an additional agreement to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or services consistent with this Licence. However, in accepting such obligations, You may act only on your own behalf and on your sole responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by the fact You have accepted any such warranty or additional liability. + +10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon "I agree" placed under the bottom of a window displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution and/or Communication by You of the Work or copies thereof. + +11. Information to the public + +In case of any Distribution and/or Communication of the Work by means of electronic communication by You (for example, by offering to download the Work from a remote location) the distribution channel or media (for example, a website) must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence and the way it may be accessible, concluded, stored and reproduced by the Licensee. + +12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has received the Work from the Licensee under the Licence, provided such persons remain in full compliance with the Licence. + +13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the Work licensed hereunder. + +If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or enforceability of the Licence as a whole. Such provision will be construed and/or reformed so as necessary to make it valid and enforceable. + +The European Commission may publish other linguistic versions and/or new versions of this Licence, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. New versions of the Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take advantage of the linguistic version of their choice. + +14. Jurisdiction + +Any litigation resulting from the interpretation of this License, arising between the European Commission, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice of the European Communities, as laid down in article 238 of the Treaty establishing the European Community. + +Any litigation arising between Parties, other than the European Commission, and resulting from the interpretation of this License, will be subject to the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. + +15. Applicable Law + +This Licence shall be governed by the law of the European Union country where the Licensor resides or has his registered office. + +This licence shall be governed by the Belgian law if: + +- a litigation arises between the European Commission, as a Licensor, and any Licensee; +- the Licensor, other than the European Commission, has no residence or registered office inside a European Union country. + + +=== +Appendix + +"Compatible Licences" according to article 5 EUPL are: +- GNU General Public License (GNU GPL) v. 2 +- Open Software License (OSL) v. 2.1, v. 3.0 +- Common Public License v. 1.0 +- Eclipse Public License v. 1.0 +- Cecill v. 2.0 diff --git a/scripts/vendor/LICENSE b/scripts/vendor/LICENSE new file mode 100644 index 00000000..e1854e86 --- /dev/null +++ b/scripts/vendor/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014-2015 almasaeed2010 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/style/vendor/LICENSE b/style/vendor/LICENSE new file mode 100644 index 00000000..e1854e86 --- /dev/null +++ b/style/vendor/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014-2015 almasaeed2010 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 9b799819bff49cda5dcd3d2534d7fa25a4568170 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 14 Feb 2017 14:57:58 +0100 Subject: [PATCH 02/30] Added header to main PHP files --- api.php | 6 ++++++ gravity.php | 6 ++++++ help.php | 6 ++++++ index.php | 6 ++++++ list.php | 6 ++++++ queries.php | 6 ++++++ queryads.php | 6 ++++++ settings.php | 6 ++++++ taillog.php | 6 ++++++ 9 files changed, 54 insertions(+) diff --git a/api.php b/api.php index 589ae0bd..a9aaa3b2 100644 --- a/api.php +++ b/api.php @@ -1,3 +1,9 @@ + diff --git a/help.php b/help.php index 6f19ad60..e4960973 100644 --- a/help.php +++ b/help.php @@ -1,3 +1,9 @@ + diff --git a/settings.php b/settings.php index d746d952..01172d50 100644 --- a/settings.php +++ b/settings.php @@ -1,3 +1,9 @@ + From 1c73016f6a4bee9e546a9fb2cea43c47c950401b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 14 Feb 2017 15:00:11 +0100 Subject: [PATCH 03/30] Modified scripts in scripts/pi-hole/php --- scripts/pi-hole/php/add.php | 8 ++++++++ scripts/pi-hole/php/auth.php | 8 ++++++++ scripts/pi-hole/php/data.php | 8 ++++++++ scripts/pi-hole/php/footer.php | 8 ++++++++ scripts/pi-hole/php/func.php | 8 ++++++++ scripts/pi-hole/php/get.php | 8 ++++++++ scripts/pi-hole/php/gravity.sh.php | 8 ++++++++ scripts/pi-hole/php/header.php | 8 ++++++++ scripts/pi-hole/php/loginpage.php | 8 ++++++++ scripts/pi-hole/php/password.php | 8 ++++++++ scripts/pi-hole/php/queryads.php | 8 ++++++++ scripts/pi-hole/php/savesettings.php | 8 ++++++++ scripts/pi-hole/php/sub.php | 8 ++++++++ scripts/pi-hole/php/teleporter.php | 8 ++++++++ 14 files changed, 112 insertions(+) diff --git a/scripts/pi-hole/php/add.php b/scripts/pi-hole/php/add.php index 62c930ef..d4ca64db 100644 --- a/scripts/pi-hole/php/add.php +++ b/scripts/pi-hole/php/add.php @@ -1,3 +1,11 @@ + + + + + diff --git a/scripts/pi-hole/php/func.php b/scripts/pi-hole/php/func.php index 37ef790f..57f61f08 100644 --- a/scripts/pi-hole/php/func.php +++ b/scripts/pi-hole/php/func.php @@ -1,3 +1,11 @@ + + + + + +
diff --git a/scripts/pi-hole/php/password.php b/scripts/pi-hole/php/password.php index 4abe9cfc..af9716bf 100644 --- a/scripts/pi-hole/php/password.php +++ b/scripts/pi-hole/php/password.php @@ -1,3 +1,11 @@ + + + + + + Date: Tue, 14 Feb 2017 15:01:40 +0100 Subject: [PATCH 04/30] Added header to scripts/pi-hole/js files --- scripts/pi-hole/js/footer.js | 6 ++++++ scripts/pi-hole/js/gravity.js | 6 ++++++ scripts/pi-hole/js/header.js | 6 ++++++ scripts/pi-hole/js/index.js | 6 ++++++ scripts/pi-hole/js/list.js | 6 ++++++ scripts/pi-hole/js/queries.js | 6 ++++++ scripts/pi-hole/js/queryads.js | 6 ++++++ scripts/pi-hole/js/settings.js | 6 ++++++ scripts/pi-hole/js/taillog.js | 6 ++++++ 9 files changed, 54 insertions(+) diff --git a/scripts/pi-hole/js/footer.js b/scripts/pi-hole/js/footer.js index 6dd4ce65..5cdd042e 100644 --- a/scripts/pi-hole/js/footer.js +++ b/scripts/pi-hole/js/footer.js @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ // User menu toggle $("#dropdown-menu a").on("click", function(event) { $(this).parent().toggleClass("open"); diff --git a/scripts/pi-hole/js/gravity.js b/scripts/pi-hole/js/gravity.js index 02c91e3c..20cf9fcd 100644 --- a/scripts/pi-hole/js/gravity.js +++ b/scripts/pi-hole/js/gravity.js @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ function eventsource() { var alInfo = $("#alInfo"); var alSuccess = $("#alSuccess"); diff --git a/scripts/pi-hole/js/header.js b/scripts/pi-hole/js/header.js index 132f4fa2..e9f0478a 100644 --- a/scripts/pi-hole/js/header.js +++ b/scripts/pi-hole/js/header.js @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ // Remove JS warning var jswarn = document.getElementById("js-warn-exit"); jswarn.parentNode.removeChild(jswarn); diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 89b1d2ba..537188fb 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ // Define global variables var timeLineChart, queryTypeChart, forwardDestinationChart; diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 379a3148..00a0b2ef 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ // IE likes to cache too much :P $.ajaxSetup({cache: false}); diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 56912249..8a7d45a7 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ var tableApi; function escapeRegex(text) { diff --git a/scripts/pi-hole/js/queryads.js b/scripts/pi-hole/js/queryads.js index 0786ada8..1b328566 100644 --- a/scripts/pi-hole/js/queryads.js +++ b/scripts/pi-hole/js/queryads.js @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ var exact = ""; function quietfilter(ta,data) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 92796008..84bf41aa 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ $(function () { $("[data-mask]").inputmask(); diff --git a/scripts/pi-hole/js/taillog.js b/scripts/pi-hole/js/taillog.js index 00cbfb15..ffe370fc 100644 --- a/scripts/pi-hole/js/taillog.js +++ b/scripts/pi-hole/js/taillog.js @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ var offset, timer, pre, scrolling = true; // Check every 200msec for fresh data From 132a19ed84a9127e2173e64403d55388cc618806 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 14 Feb 2017 15:04:35 +0100 Subject: [PATCH 05/30] Added license header to our custom style script --- style/pi-hole.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/style/pi-hole.css b/style/pi-hole.css index 7ff36a0b..80ebba72 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -1,3 +1,9 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ .small-box { -webkit-user-select: none; /* Chrome/Safari */ -moz-user-select: none; /* Firefox */ From 5b48803f90205d8bc6ef1d331a356b3a4a42715d Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 16 Feb 2017 08:50:09 +0100 Subject: [PATCH 06/30] Slight reformulation --- LICENSE | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/LICENSE b/LICENSE index da85e8d6..1a3787f5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,12 +1,11 @@ Copyright (C) 2017 Pi-hole, LLC (https://pi-hole.net) Pi-hole web interface -This software is licensed under the European Union Public License - -This license is available in the 22 official languages of the EU. The English version is included here. +This software is licensed under the European Union Public License (EUPL) +The license is available in the 22 official languages of the EU. The English version is included here. Please see https://joinup.ec.europa.eu/community/eupl/og_page/eupl for official translations of the other languages. -This license applies to the whole project EXCEPT the files under +This license applies to the whole project EXCEPT the files located under - styles/vendor, and - scripts/vendor whose licenses are located therein. From aabff35bef88c00549144cf1f99cb9eb959ecb06 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 16 Feb 2017 08:52:30 +0100 Subject: [PATCH 07/30] Squashed commit of the following: - Dynamically fall back to PHP API functions to ensure API does always repond even if FTL is not runnung for some reason - Update PHP API from current devel branch - Increase flexibility in getQueryTypes API call - Hide top ads list if nothing to display - Proper display of activated privacy mode - Remove spinner (would otherwise not be removed for zero results) - Update tables every 10 seconds - Update query types and forward destinations plots every 10 seconds - More verbose output in top lists (total number next to percentage in tooltip) - Implemented "recentBlocked" keyword for API - Ensure compatibility with PHP5 version < 5.4 - Adjust output format of overTimeData10mins to comply with PHP API - Further speedup of Query Log page by showing only the recent 10 minutes by default - Hide temperature if FTL is not running and show FTL status - Add information to Settings page - Adjust error message when loading of query log failed - Move processing of domainname, clientIP, clientname and time interval filters for the Query Log page to FTL for speed enhancement - Generate link if pi.hole comes up in the Top Domains list - Remove resolve DNS names option - this is now enabled by default (daemon will only do it once per day instead of PHP-API which did it on every reloading of the web interface) - Add socket timeout of 10 seconds + modification to Settings page since FTL backend supports API_EXCLUDE_CLIENTS filtering with both IP addresses and host names (also mixed) - Improved Query Log page --- README.md | 4 +- api.php | 308 ++++++++++----- api_PHP.php | 118 ++++++ help.php | 21 +- index.php | 4 +- queries.php | 33 +- scripts/pi-hole/js/index.js | 94 +++-- scripts/pi-hole/js/queries.js | 81 ++-- scripts/pi-hole/js/taillog.js | 4 +- scripts/pi-hole/php/FTL.php | 103 +++++ scripts/pi-hole/php/header.php | 59 +-- scripts/pi-hole/php/savesettings.php | 33 +- scripts/vendor/moment.min.js | 551 +++++++++++++++++++++++++++ settings.php | 69 ++-- 14 files changed, 1214 insertions(+), 268 deletions(-) create mode 100644 api_PHP.php create mode 100644 scripts/pi-hole/php/FTL.php create mode 100644 scripts/vendor/moment.min.js diff --git a/README.md b/README.md index 12769874..6e7e1668 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ A read-only API can be accessed at `/admin/api.php`. With either no parameters o } ``` -There are many more parameters, such as `summaryRaw`, `overTimeData10mins`, ` topClients` or `getQuerySources`, `getQueryTypes`, `getForwardDestinations`, and `getAllQueries`. -Together with a token it is also possible to enable and disable (also with a set timeout) blocking via the API. +There are many more parameters, such as `summaryRaw`, `overTimeData10mins`, `topItems`, ` topClients` or `getQuerySources`, `getQueryTypes`, `getForwardDestinations`, and finally `getAllQueries`. +Together with a token it is also possible to enable and disable (also with a set timeout) blocking via the API

diff --git a/api.php b/api.php index a9aaa3b2..b8e0024c 100644 --- a/api.php +++ b/api.php @@ -5,120 +5,238 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. --> $domains_over_time, + 'ads_over_time' => $ads_over_time); + $data = array_merge($data, $result); + } - if (isset($_GET['topItems']) && $auth) { - $data = array_merge($data, getTopItems($_GET['topItems'])); - } + if (isset($_GET['topItems']) && $auth) + { + if(is_numeric($_GET['topItems'])) + { + sendRequestFTL("top-domains ".$_GET['topItems']); + } + else + { + sendRequestFTL("top-domains"); + } - if (isset($_GET['recentItems']) && $auth) { - if (is_numeric($_GET['recentItems'])) { - $data = array_merge($data, getRecentItems($_GET['recentItems'])); - } - } + $return = getResponseFTL(); + $top_queries = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + $top_queries[$tmp[2]] = $tmp[1]; + } - if (isset($_GET['getQueryTypes']) && $auth) { - $data = array_merge($data, getIpvType()); - } + if(is_numeric($_GET['topItems'])) + { + sendRequestFTL("top-ads ".$_GET['topItems']); + } + else + { + sendRequestFTL("top-ads"); + } - if (isset($_GET['getForwardDestinations']) && $auth) { - $data = array_merge($data, getForwardDestinations()); - } + $return = getResponseFTL(); + $top_ads = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + $top_ads[$tmp[2]] = $tmp[1]; + } - if (isset($_GET['getQuerySources']) && $auth) { - $data = array_merge($data, getQuerySources()); - } + $result = array('top_queries' => $top_queries, + 'top_ads' => $top_ads); - if (isset($_GET['getAllQueries']) && $auth) { - $data = array_merge($data, getAllQueries($_GET['getAllQueries'])); - } + $data = array_merge($data, $result); + } - if (isset($_GET['enable'], $_GET['token']) && $auth) { - check_csrf($_GET['token']); - exec('sudo pihole enable'); - $data = array_merge($data, Array( - "status" => "enabled" - )); - } - elseif (isset($_GET['disable'], $_GET['token']) && $auth) { - check_csrf($_GET['token']); - $disable = intval($_GET['disable']); - // intval returns the integer value on success, or 0 on failure - if($disable > 0) - { - exec("sudo pihole disable ".$disable."s"); - } - else - { - exec('sudo pihole disable'); - } - $data = array_merge($data, Array( - "status" => "disabled" - )); - } + if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth) + { + sendRequestFTL("top-clients"); + $return = getResponseFTL(); + $top_clients = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + if(count($tmp) == 4) + { + $top_clients[$tmp[3]."|".$tmp[2]] = $tmp[1]; + } + else + { + $top_clients[$tmp[2]] = $tmp[1]; + } + } - if (isset($_GET['getGravityDomains'])) { - $data = array_merge($data, getGravity()); - } + $result = array('top_sources' => $top_clients); + $data = array_merge($data, $result); + } - if (isset($_GET['tailLog']) && $auth) { - $data = array_merge($data, tailPiholeLog($_GET['tailLog'])); - } + if (isset($_GET['getForwardDestinations']) && $auth) + { + sendRequestFTL("forward-dest"); + $return = getResponseFTL(); + $forward_dest = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + if(count($tmp) == 4) + { + $forward_dest[$tmp[3]."|".$tmp[2]] = $tmp[1]; + } + else + { + $forward_dest[$tmp[2]] = $tmp[1]; + } + } - function filterArray(&$inArray) { - $outArray = array(); - foreach ($inArray as $key=>$value) { - if (is_array($value)) { - $outArray[htmlspecialchars($key)] = filterArray($value); - } else { - $outArray[htmlspecialchars($key)] = !is_numeric($value) ? htmlspecialchars($value) : $value; - } - } - return $outArray; - } + $result = array('forward_destinations' => $forward_dest); + $data = array_merge($data, $result); + } - $data = filterArray($data); + if (isset($_GET['getQueryTypes']) && $auth) + { + sendRequestFTL("querytypes"); + $return = getResponseFTL(); + $querytypes = array(); + foreach($return as $ret) + { + $tmp = explode(": ",$ret); + $querytypes[$tmp[0]] = $tmp[1]; + } - if(isset($_GET["jsonForceObject"])) - { - echo json_encode($data, JSON_FORCE_OBJECT); - } - else - { - echo json_encode($data); - } + $result = array('querytypes' => $querytypes); + $data = array_merge($data, $result); + } + + if (isset($_GET['getAllQueries']) && $auth) + { + if(isset($_GET['from']) && isset($_GET['until'])) + { + // Get limited time interval + sendRequestFTL("getallqueries-time ".$_GET['from']." ".$_GET['until']); + } + else if(isset($_GET['domain'])) + { + // Get specific domain only + sendRequestFTL("getallqueries-domain ".$_GET['domain']); + } + else if(isset($_GET['client'])) + { + // Get specific client only + sendRequestFTL("getallqueries-client ".$_GET['client']); + } + else + { + // Get all queries + sendRequestFTL("getallqueries"); + } + $return = getResponseFTL(); + $allQueries = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + array_push($allQueries,$tmp); + } + + $result = array('data' => $allQueries); + $data = array_merge($data, $result); + } + + if (isset($_GET['enable'], $_GET['token']) && $auth) { + check_csrf($_GET['token']); + exec('sudo pihole enable'); + $data = array_merge($data, array("status" => "enabled")); + } + elseif (isset($_GET['disable'], $_GET['token']) && $auth) { + check_csrf($_GET['token']); + $disable = intval($_GET['disable']); + // intval returns the integer value on success, or 0 on failure + if($disable > 0) + { + exec("sudo pihole disable ".$disable."s"); + } + else + { + exec('sudo pihole disable'); + } + $data = array_merge($data, array("status" => "disabled")); + } + + if(isset($_GET["recentBlocked"])) + { + sendRequestFTL("recentBlocked"); + echo getResponseFTL()[0]; + unset($data); + } + + if(isset($data)) + { + echo json_encode($data); + } + + disconnectFTL(); ?> diff --git a/api_PHP.php b/api_PHP.php new file mode 100644 index 00000000..589ae0bd --- /dev/null +++ b/api_PHP.php @@ -0,0 +1,118 @@ + "enabled" + )); + } + elseif (isset($_GET['disable'], $_GET['token']) && $auth) { + check_csrf($_GET['token']); + $disable = intval($_GET['disable']); + // intval returns the integer value on success, or 0 on failure + if($disable > 0) + { + exec("sudo pihole disable ".$disable."s"); + } + else + { + exec('sudo pihole disable'); + } + $data = array_merge($data, Array( + "status" => "disabled" + )); + } + + if (isset($_GET['getGravityDomains'])) { + $data = array_merge($data, getGravity()); + } + + if (isset($_GET['tailLog']) && $auth) { + $data = array_merge($data, tailPiholeLog($_GET['tailLog'])); + } + + function filterArray(&$inArray) { + $outArray = array(); + foreach ($inArray as $key=>$value) { + if (is_array($value)) { + $outArray[htmlspecialchars($key)] = filterArray($value); + } else { + $outArray[htmlspecialchars($key)] = !is_numeric($value) ? htmlspecialchars($value) : $value; + } + } + return $outArray; + } + + $data = filterArray($data); + + if(isset($_GET["jsonForceObject"])) + { + echo json_encode($data, JSON_FORCE_OBJECT); + } + else + { + echo json_encode($data); + } +?> diff --git a/help.php b/help.php index e4960973..c3e729a9 100644 --- a/help.php +++ b/help.php @@ -48,20 +48,13 @@
  • Summary: A summary of statistics showing how many total DNS queries have been blocked today, what percentage of DNS queries have been blocked, and how many domains are in the compiled ad list. This summary is updated every 10 seconds.
  • Queries over time: Graph showing DNS queries (total and blocked) over 10 minute time intervals. More information can be acquired by hovering over the lines. This graph is updated every 10 minutes.
  • -
  • Query Types: Identifies the types of processed queries: -
      -
    • A: address lookup (most commonly used to map hostnames to an IPv4 address of the host)
    • -
    • AAAA: address lookup (most commonly used to map hostnames to an IPv6 address of the host)
    • -
    • PTR: most common use is for implementing reverse DNS lookups
    • -
    • SRV: Service locator (often used by XMPP, SIP, and LDAP)
    • -
    • and others
    • -
    -
  • +
  • Query Types: Identifies the types of processed queries
  • Forward Destinations: Shows to which upstream DNS the permitted requests have been forwarded to.
  • Top Domains: Ranking of requested sites by number of DNS lookups.
  • Top Advertisers: Ranking of requested advertisements by number of DNS lookups.
  • Top Clients: Ranking of how many DNS requests each client has made on the local network.
+

The Top Domains and Top Advertisers lists may be hidden depending on the privacy Settings on the settings page

Note that the login session does not expire on the main page, as the summary is updated every 10 seconds which refreshes the session.

@@ -70,7 +63,7 @@

Query Log

-

Shows the recent queries by parsing Pi-hole's log. It is possible to search through the whole list by using the "Search" input field. If the status is reported as "OK", then the DNS request has been permitted. Otherwise ("Pi-holed") it has been blocked. By clicking on the buttons under "Action" the corresponding domains can quickly be added to the white-/blacklist. The status of the action will be reported on this page.

+

Shows the recent queries by parsing Pi-hole's log. It is possible to search through the whole list by using the "Search" input field. If the status is reported as "OK", then the DNS request has been permitted. Otherwise ("Pi-holed") it has been blocked. By clicking on the buttons under "Action" the corresponding domains can quickly be added to the white-/blacklist. The status of the action will be reported on this page. By default, only the recent 10 minutes are shown to enhance the loading speed of the query log page. All domains can be requested by clicking on the corresponding link in the header of the page. Note that the result heavily depends on your privacy settings (see Settings page).

@@ -118,8 +111,12 @@

Query Logging

Enabled/disable query logging on your Pi-hole + provide option to flush the log

API

- Change settings which apply to the API as well as the web UI
- Note that Top Clients have to be given as IP addresses + Change settings which apply to the API as well as the web UI +
    +
  • Show permitted domain entries: Toogle permitted queries in Query Log + Top Domains on Main Page
  • +
  • Show blockes domain entries: Toogle blocked queries in Query Log + Top Ads on Main Page
  • +
  • Privacy mode: Replace IPs in query log with "hidden"
  • +

Web User Interface

Other settings which affect the webUI but not the API of Pi-hole

System Administration

diff --git a/index.php b/index.php index 659a868a..5ea7b172 100644 --- a/index.php +++ b/index.php @@ -96,7 +96,7 @@
- +
@@ -112,7 +112,7 @@
- +
diff --git a/queries.php b/queries.php index 8e81c677..7dd4d1ae 100644 --- a/queries.php +++ b/queries.php @@ -19,30 +19,52 @@ if(isset($setupVars["API_QUERY_LOG_SHOW"])) { if($setupVars["API_QUERY_LOG_SHOW"] === "all") { - $showing = "(showing all queries)"; + $showing = "(showing all queries"; } elseif($setupVars["API_QUERY_LOG_SHOW"] === "permittedonly") { - $showing = "(showing permitted queries only)"; + $showing = "(showing permitted queries only"; } elseif($setupVars["API_QUERY_LOG_SHOW"] === "blockedonly") { - $showing = "(showing blocked queries only)"; + $showing = "(showing blocked queries only"; } elseif($setupVars["API_QUERY_LOG_SHOW"] === "nothing") { - $showing = "(showing no queries at all)"; + $showing = "(showing no queries at all"; } } +if(isset($_GET["all"])) +{ + $showing .= " within the Pi-hole log"; +} +else if(isset($_GET["client"])) +{ + $showing .= " for client ".htmlentities($_GET["client"]); +} +else if(isset($_GET["domain"])) +{ + $showing .= " for domain ".htmlentities($_GET["domain"]); +} +else if(isset($_GET["from"]) && isset($_GET["until"])) +{ + $showing .= " within limited time interval"; +} +else +{ + $showing .= " within recent 10 minutes, show all"; +} + if(isset($setupVars["API_PRIVACY_MODE"])) { if($setupVars["API_PRIVACY_MODE"]) { // Overwrite string from above - $showing = "(privacy mode enabled)"; + $showing .= ", privacy mode enabled"; } } +$showing .= ")"; ?> @@ -114,4 +136,5 @@ if(isset($setupVars["API_PRIVACY_MODE"])) require "scripts/pi-hole/php/footer.php"; ?> + diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 537188fb..7c0dc79a 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -104,21 +104,26 @@ function updateQueryTypes() { var colors = []; // Get colors from AdminLTE $.each($.AdminLTE.options.colors, function(key, value) { colors.push(value); }); - var v = [], c = []; - // Collect values and colors, immediately push individual labels - $.each(data, function(key , value) { + var v = [], c = [], k = []; + // Collect values and colors, and labels + $.each(data.querytypes, function(key , value) { v.push(value); c.push(colors.shift()); - queryTypeChart.data.labels.push(key.substr(6,key.length - 7)); + k.push(key); }); // Build a single dataset with the data to be pushed var dd = {data: v, backgroundColor: c}; // and push it at once - queryTypeChart.data.datasets.push(dd); + queryTypeChart.data.datasets[0] = dd; + queryTypeChart.data.labels = k; $("#query-types .overlay").remove(); queryTypeChart.update(); - queryTypeChart.chart.config.options.cutoutPercentage=30; + queryTypeChart.chart.config.options.cutoutPercentage=50; queryTypeChart.update(); + // Don't use rotation animation for further updates + queryTypeChart.options.animation.duration=0; + // Update query types data every 10 seconds + setTimeout(updateQueryTypes, 10000); }); } @@ -137,35 +142,39 @@ function escapeHtml(text) { function updateTopClientsChart() { $.getJSON("api.php?summaryRaw&getQuerySources", function(data) { + // Clear tables before filling them with data + $("#client-frequency td").parent().remove(); var clienttable = $("#client-frequency").find("tbody:last"); - var domain, percentage, domainname, domainip; - for (domain in data.top_sources) { + var client, percentage, clientname, clientip; + for (client in data.top_sources) { - if ({}.hasOwnProperty.call(data.top_sources, domain)){ - // Sanitize domain - domain = escapeHtml(domain); - if(domain.indexOf("|") > -1) + if ({}.hasOwnProperty.call(data.top_sources, client)){ + // Sanitize client + client = escapeHtml(client); + if(client.indexOf("|") > -1) { - var idx = domain.indexOf("|"); - domainname = domain.substr(0, idx); - domainip = domain.substr(idx+1, domain.length-idx); + var idx = client.indexOf("|"); + clientname = client.substr(0, idx); + clientip = client.substr(idx+1, client.length-idx); } else { - domainname = domain; - domainip = domain; + clientname = client; + clientip = client; } - var url = ""+domainname+""; - percentage = data.top_sources[domain] / data.dns_queries_today * 100; + var url = ""+clientname+""; + percentage = data.top_sources[client] / data.dns_queries_today * 100; clienttable.append(" " + url + - " " + data.top_sources[domain] + "
" + data.top_sources[client] + "
"); } } $("#client-frequency .overlay").remove(); + // Update top clients list data every 10 seconds + setTimeout(updateTopClientsChart, 10000); }); } @@ -174,9 +183,9 @@ function updateForwardDestinations() { var colors = []; // Get colors from AdminLTE $.each($.AdminLTE.options.colors, function(key, value) { colors.push(value); }); - var v = [], c = []; + var v = [], c = [], k = []; // Collect values and colors, immediately push individual labels - $.each(data, function(key , value) { + $.each(data.forward_destinations, function(key , value) { v.push(value); c.push(colors.shift()); if(key.indexOf("|") > -1) @@ -184,21 +193,29 @@ function updateForwardDestinations() { var idx = key.indexOf("|"); key = key.substr(0, idx)+" ("+key.substr(idx+1, key.length-idx)+")"; } - forwardDestinationChart.data.labels.push(key); + k.push(key); }); // Build a single dataset with the data to be pushed var dd = {data: v, backgroundColor: c}; // and push it at once - forwardDestinationChart.data.datasets.push(dd); + forwardDestinationChart.data.datasets[0] = dd; + forwardDestinationChart.data.labels = k; $("#forward-destinations .overlay").remove(); forwardDestinationChart.update(); - forwardDestinationChart.chart.config.options.cutoutPercentage=30; + forwardDestinationChart.chart.config.options.cutoutPercentage=50; forwardDestinationChart.update(); + // Don't use rotation animation for further updates + forwardDestinationChart.options.animation.duration=0; + // Update forward destinations data every 10 seconds + setTimeout(updateForwardDestinations, 10000); }); } function updateTopLists() { $.getJSON("api.php?summaryRaw&topItems", function(data) { + // Clear tables before filling them with data + $("#domain-frequency td").parent().remove(); + $("#ad-frequency td").parent().remove(); var domaintable = $("#domain-frequency").find("tbody:last"); var adtable = $("#ad-frequency").find("tbody:last"); var url, domain, percentage; @@ -206,17 +223,10 @@ function updateTopLists() { if ({}.hasOwnProperty.call(data.top_queries,domain)){ // Sanitize domain domain = escapeHtml(domain); - if(domain !== "pi.hole") - { - url = ""+domain+""; - } - else - { - url = domain; - } + url = ""+domain+""; percentage = data.top_queries[domain] / data.dns_queries_today * 100; domaintable.append(" " + url + - " " + data.top_queries[domain] + "
" + data.top_queries[domain] + "
"); } } @@ -234,13 +244,21 @@ function updateTopLists() { url = ""+domain+""; percentage = data.top_ads[domain] / data.ads_blocked_today * 100; adtable.append(" " + url + - " " + data.top_ads[domain] + "
" + data.top_ads[domain] + "
"); } } + // Remove table if there are no results (e.g. privacy mode enabled) + if(jQuery.isEmptyObject(data.top_ads)) + { + $("#ad-frequency").parent().remove(); + } + $("#domain-frequency .overlay").remove(); $("#ad-frequency .overlay").remove(); + // Update top lists data every 10 seconds + setTimeout(updateTopLists, 10000); }); } @@ -429,10 +447,8 @@ $(document).ready(function() { var label = timeLineChart.data.labels[clickedElementindex]; //get value by index - //var value = timeLineChart.data.datasets[0].data[clickedElementindex]; - var time = new Date(label); - var from = time.getHours()+":"+time.getMinutes(); - var until = time.getHours()+":"+padNumber(parseInt(time.getMinutes()+9),2); + var from = label/1000; + var until = label/1000 + 600; window.location.href = "queries.php?from="+from+"&until="+until; } return false; diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 8a7d45a7..f5abc74e 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -79,11 +79,17 @@ function add(domain,list) { }); } function handleAjaxError( xhr, textStatus, error ) { - if ( textStatus === "timeout" ) { + if ( textStatus === "timeout" ) + { alert( "The server took too long to send the data." ); } - else { - alert( "An error occured while loading the data. Presumably your log is too large to be processed." ); + else if(xhr.responseText.indexOf("Connection refused") >= 0) + { + alert( "An error occured while loading the data: Connection refused. Is FTL running?" ); + } + else + { + alert( "An unknown error occured while loading the data." ); } $("#all-queries_processing").hide(); tableApi.clear(); @@ -99,32 +105,58 @@ $(document).ready(function() { var APIstring = "api.php?getAllQueries"; - if("from" in GETDict) + if("from" in GETDict && "until" in GETDict) { APIstring += "&from="+GETDict["from"]; - } - - if("until" in GETDict) - { APIstring += "&until="+GETDict["until"]; } + else if("client" in GETDict) + { + APIstring += "&client="+GETDict["client"]; + } + else if("domain" in GETDict) + { + APIstring += "&domain="+GETDict["domain"]; + } + else if(!("all" in GETDict)) + { + var timestamp = Math.floor(Date.now() / 1000); + APIstring += "&from="+(timestamp - 600); + APIstring += "&until="+(timestamp + 100); + } tableApi = $("#all-queries").DataTable( { "rowCallback": function( row, data, index ){ - status = data[4]; - if (status === "Pi-holed (exact)") { + if (data[4] === "1") + { $(row).css("color","red"); + $("td:eq(4)", row).html( "Pi-holed" ); $("td:eq(5)", row).html( "" ); } - else if (status === "Pi-holed (wildcard)") { - $(row).css("color","red"); - $("td:eq(5)", row).html( "" ); - } - else{ + else if (data[4] === "2") + { $(row).css("color","green"); + $("td:eq(4)", row).html( "OK (forwarded)" ); $("td:eq(5)", row).html( "" ); } + else if (data[4] === "3") + { + $(row).css("color","green"); + $("td:eq(4)", row).html( "OK (cached)" ); + $("td:eq(5)", row).html( "" ); + } + else if (data[4] === "4") + { + $(row).css("color","red"); + $("td:eq(4)", row).html( "Pi-holed (wildcard)" ); + $("td:eq(5)", row).html( "" ); + } + else + { + $("td:eq(4)", row).html( "Unknown" ); + $("td:eq(5)", row).html( "" ); + } }, dom: "<'row'<'col-sm-12'f>>" + "<'row'<'col-sm-4'l><'col-sm-8'p>>" + @@ -135,7 +167,7 @@ $(document).ready(function() { "processing": true, "order" : [[0, "desc"]], "columns": [ - { "width" : "20%", "type": "date" }, + { "width" : "20%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(data).format("Y-MM-DD HH:mm:ss z");}else{return data;} }}, { "width" : "10%" }, { "width" : "40%" }, { "width" : "10%" }, @@ -151,8 +183,7 @@ $(document).ready(function() { }); $("#all-queries tbody").on( "click", "button", function () { var data = tableApi.row( $(this).parents("tr") ).data(); - status = data[4]; - if (status.substr(0,2) === "Pi") + if (data[4] === "1") { add(data[2],"white"); } @@ -161,20 +192,6 @@ $(document).ready(function() { add(data[2],"black"); } } ); - - if("client" in GETDict) - { - // Search in third column (zero indexed) - // Use regular expression to only show exact matches, i.e. - // don't show 192.168.0.100 when searching for 192.168.0.1 - // true = use regex, false = don't use smart search - tableApi.column(3).search("^"+escapeRegex(GETDict["client"])+"$",true,false); - } - if("domain" in GETDict) - { - // Search in second column (zero indexed) - tableApi.column(2).search("^"+escapeRegex(GETDict["domain"])+"$",true,false); - } } ); diff --git a/scripts/pi-hole/js/taillog.js b/scripts/pi-hole/js/taillog.js index ffe370fc..32272de9 100644 --- a/scripts/pi-hole/js/taillog.js +++ b/scripts/pi-hole/js/taillog.js @@ -12,7 +12,7 @@ var interval = 200; // Function that asks the API for new data function reloadData(){ clearTimeout(timer); - $.getJSON("api.php?tailLog="+offset, function (data) + $.getJSON("api_PHP.php?tailLog="+offset, function (data) { offset = data["offset"]; pre.append(data["lines"]); @@ -27,7 +27,7 @@ function reloadData(){ $(function(){ // Get offset at first loading of page - $.getJSON("api.php?tailLog", function (data) + $.getJSON("api_PHP.php?tailLog", function (data) { offset = data["offset"]; }); diff --git a/scripts/pi-hole/php/FTL.php b/scripts/pi-hole/php/FTL.php new file mode 100644 index 00000000..91cffdd2 --- /dev/null +++ b/scripts/pi-hole/php/FTL.php @@ -0,0 +1,103 @@ +10, 'usec'=>0]); + socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, ['sec'=>10, 'usec'=>0]); + + if(!$quiet) + { + echo "Success!\n\n"; + } + + return $socket; +} +function sendRequestFTL($requestin, $quiet=true) +{ + global $socket; + + $request = ">".$requestin; + if(!$quiet) + { + echo "Sending request (".$request.")...\n"; + } + + socket_write($socket, $request, strlen($request)) or die("Could not send data to server\n"); + if(!$quiet) + { + echo "OK.\n"; + } +} + +function getResponseFTL($quiet=true) +{ + global $socket; + if(!$quiet) + { + echo "Reading response:\n"; + } + + $response = []; + + while(true) + { + $out = socket_read($socket, 2048, PHP_NORMAL_READ); + if(!$quiet) + { + echo $out; + } + if(strrpos($out,"---EOM---") !== false) + { + break; + } + $out = rtrim($out); + if(strlen($out) > 0) + { + $response[] = $out; + } + } + + return $response; +} + +function disconnectFTL($quiet=true) +{ + global $socket; + if(!$quiet) + { + echo "Closing socket..."; + } + + socket_close($socket); + + if(!$quiet) + { + echo "OK.\n\n"; + } +} diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php index bda379e7..009d0892 100644 --- a/scripts/pi-hole/php/header.php +++ b/scripts/pi-hole/php/header.php @@ -130,6 +130,12 @@ $boxedlayout = false; } } + + if(strpos(exec("ps -p `cat /var/run/pihole-FTL.pid` -o comm="), "pihole-FTL") !== false) + { + $FTL = true; + } + ?> @@ -263,7 +269,7 @@

Status

- Active'; @@ -276,29 +282,36 @@ } // CPU Temp - if ($celsius >= -273.15) { - echo " 60) { - echo "#FF0000"; + if($FTL) + { + if ($celsius >= -273.15) { + echo " 60) { + echo "#FF0000"; + } + else + { + echo "#3366FF"; + } + echo "\"> Temp: "; + if($temperatureunit === "F") + { + echo round($fahrenheit,1) . "°F"; + } + elseif($temperatureunit === "K") + { + echo round($kelvin,1) . "K"; + } + else + { + echo round($celsius,1) . "°C"; + } + echo ""; } - else - { - echo "#3366FF"; - } - echo "\"> Temp: "; - if($temperatureunit === "F") - { - echo round($fahrenheit,1) . "°F"; - } - elseif($temperatureunit === "K") - { - echo round($kelvin,1) . "K"; - } - else - { - echo round($celsius,1) . "°C"; - } - echo ""; + } + else + { + echo ' FTL offline'; } ?>
diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index fcfae51a..ed1d8e17 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -44,6 +44,15 @@ function validDomain($domain_name) return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label } +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); + $lengthCheck = preg_match("/^.{1,253}$/", $domain_name); + $labelLengthCheck = preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name); + return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label +} + function validMAC($mac_addr) { // Accepted input format: 00:01:02:1A:5F:FF (characters may be lower case) @@ -215,9 +224,9 @@ function readStaticLeasesFile() $first = true; foreach($domains as $domain) { - if(!validDomain($domain)) + if(!validDomainWildcard($domain) || validIP($domain)) { - $error .= "Top Domains/Ads entry ".htmlspecialchars($domain)." is invalid!
"; + $error .= "Top Domains/Ads entry ".htmlspecialchars($domain)." is invalid (use only domains)!
"; } if(!$first) { @@ -234,7 +243,7 @@ function readStaticLeasesFile() $first = true; foreach($clients as $client) { - if(!validIP($client)) + if(!validDomainWildcard($client)) { $error .= "Top Clients entry ".htmlspecialchars($client)." is invalid (use only IP addresses)!
"; } @@ -309,24 +318,6 @@ function readStaticLeasesFile() exec("sudo pihole -a privacymode false"); } - if(isset($_POST["resolve-forward"])) - { - exec("sudo pihole -a resolve forward true"); - } - else - { - exec("sudo pihole -a resolve forward false"); - } - - if(isset($_POST["resolve-clients"])) - { - exec("sudo pihole -a resolve clients true"); - } - else - { - exec("sudo pihole -a resolve clients false"); - } - break; case "webUI": diff --git a/scripts/vendor/moment.min.js b/scripts/vendor/moment.min.js new file mode 100644 index 00000000..8c706712 --- /dev/null +++ b/scripts/vendor/moment.min.js @@ -0,0 +1,551 @@ +//! moment.js +//! version : 2.17.1 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com +!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return od.apply(null,arguments)} +// This is done to register the method called with moment() +// without creating circular dependencies. +function b(a){od=a}function c(a){return a instanceof Array||"[object Array]"===Object.prototype.toString.call(a)}function d(a){ +// IE8 will treat undefined and null as object if it wasn't for +// input != null +return null!=a&&"[object Object]"===Object.prototype.toString.call(a)}function e(a){var b;for(b in a) +// even if its not own property I'd still call it non-empty +return!1;return!0}function f(a){return"number"==typeof a||"[object Number]"===Object.prototype.toString.call(a)}function g(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function h(a,b){var c,d=[];for(c=0;c0)for(c in rd)d=rd[c],e=b[d],p(e)||(a[d]=e);return a} +// Moment prototype object +function r(b){q(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)), +// Prevent infinite loop in case updateOffset creates new moment +// objects. +sd===!1&&(sd=!0,a.updateOffset(this),sd=!1)}function s(a){return a instanceof r||null!=a&&null!=a._isAMomentObject}function t(a){return a<0?Math.ceil(a)||0:Math.floor(a)}function u(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=t(b)),c} +// compare two arrays, return the number of differences +function v(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;d0?"future":"past"];return z(c)?c(b):c.replace(/%s/i,b)}function J(a,b){var c=a.toLowerCase();Dd[c]=Dd[c+"s"]=Dd[b]=a}function K(a){return"string"==typeof a?Dd[a]||Dd[a.toLowerCase()]:void 0}function L(a){var b,c,d={};for(c in a)i(a,c)&&(b=K(c),b&&(d[b]=a[c]));return d}function M(a,b){Ed[a]=b}function N(a){var b=[];for(var c in a)b.push({unit:c,priority:Ed[c]});return b.sort(function(a,b){return a.priority-b.priority}),b}function O(b,c){return function(d){return null!=d?(Q(this,b,d),a.updateOffset(this,c),this):P(this,b)}}function P(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function Q(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)} +// MOMENTS +function R(a){return a=K(a),z(this[a])?this[a]():this}function S(a,b){if("object"==typeof a){a=L(a);for(var c=N(a),d=0;d=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d} +// token: 'M' +// padded: ['MM', 2] +// ordinal: 'Mo' +// callback: function () { this.month() + 1 } +function U(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Id[a]=e),b&&(Id[b[0]]=function(){return T(e.apply(this,arguments),b[1],b[2])}),c&&(Id[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function V(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function W(a){var b,c,d=a.match(Fd);for(b=0,c=d.length;b=0&&Gd.test(a);)a=a.replace(Gd,c),Gd.lastIndex=0,d-=1;return a}function Z(a,b,c){$d[a]=z(b)?b:function(a,d){return a&&c?c:b}}function $(a,b){return i($d,a)?$d[a](b._strict,b._locale):new RegExp(_(a))} +// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript +function _(a){return aa(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function aa(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function ba(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),f(b)&&(d=function(a,c){c[b]=u(a)}),c=0;c=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function ta(a){var b=new Date(Date.UTC.apply(null,arguments)); +//the Date.UTC function remaps years 0-99 to 1900-1999 +return a<100&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b} +// start-of-first-week - start-of-year +function ua(a,b,c){var// first-week day -- which january is always in the first week (4 for iso, 1 for other) +d=7+b-c, +// first-week day local weekday -- which local weekday is fwd +e=(7+ta(a,0,d).getUTCDay()-b)%7;return-e+d-1} +//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday +function va(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ua(a,d,e),j=1+7*(b-1)+h+i;return j<=0?(f=a-1,g=pa(f)+j):j>pa(a)?(f=a+1,g=j-pa(a)):(f=a,g=j),{year:f,dayOfYear:g}}function wa(a,b,c){var d,e,f=ua(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return g<1?(e=a.year()-1,d=g+xa(e,b,c)):g>xa(a.year(),b,c)?(d=g-xa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function xa(a,b,c){var d=ua(a,b,c),e=ua(a+1,b,c);return(pa(a)-d+e)/7} +// HELPERS +// LOCALES +function ya(a){return wa(a,this._week.dow,this._week.doy).week}function za(){return this._week.dow}function Aa(){return this._week.doy} +// MOMENTS +function Ba(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function Ca(a){var b=wa(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")} +// HELPERS +function Da(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Ea(a,b){return"string"==typeof a?b.weekdaysParse(a)%7||7:isNaN(a)?null:a}function Fa(a,b){return a?c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]:this._weekdays}function Ga(a){return a?this._weekdaysShort[a.day()]:this._weekdaysShort}function Ha(a){return a?this._weekdaysMin[a.day()]:this._weekdaysMin}function Ia(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;d<7;++d)f=k([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=je.call(this._weekdaysParse,g),e!==-1?e:null):"ddd"===b?(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:null):(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null):"dddd"===b?(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null))):"ddd"===b?(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null))):(e=je.call(this._minWeekdaysParse,g),e!==-1?e:(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:null)))}function Ja(a,b,c){var d,e,f;if(this._weekdaysParseExact)return Ia.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;d<7;d++){ +// test the regex +if( +// make the regex if we don't have it already +e=k([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}} +// MOMENTS +function Ka(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Da(a,this.localeData()),this.add(a-b,"d")):b}function La(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Ma(a){if(!this.isValid())return null!=a?this:NaN; +// behaves the same as moment#day except +// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) +// as a setter, sunday should belong to the previous week. +if(null!=a){var b=Ea(a,this.localeData());return this.day(this.day()%7?b:b-7)}return this.day()||7}function Na(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):(i(this,"_weekdaysRegex")||(this._weekdaysRegex=ue),this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex)}function Oa(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(i(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ve),this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function Pa(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(i(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=we),this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Qa(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],h=[],i=[],j=[];for(b=0;b<7;b++) +// make the regex if we don't have it already +c=k([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),h.push(e),i.push(f),j.push(d),j.push(e),j.push(f);for( +// Sorting makes sure if one weekday (or abbr) is a prefix of another it +// will match the longer piece. +g.sort(a),h.sort(a),i.sort(a),j.sort(a),b=0;b<7;b++)h[b]=aa(h[b]),i[b]=aa(i[b]),j[b]=aa(j[b]);this._weekdaysRegex=new RegExp("^("+j.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")} +// FORMATTING +function Ra(){return this.hours()%12||12}function Sa(){return this.hours()||24}function Ta(a,b){U(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})} +// PARSING +function Ua(a,b){return b._meridiemParse} +// LOCALES +function Va(a){ +// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays +// Using charAt should be more compatible. +return"p"===(a+"").toLowerCase().charAt(0)}function Wa(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Xa(a){return a?a.toLowerCase().replace("_","-"):a} +// pick the locale from the array +// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each +// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root +function Ya(a){for(var b,c,d,e,f=0;f0;){if(d=Za(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&v(e,c,!0)>=b-1) +//the next array item is better than a shallower substring of this one +break;b--}f++}return null}function Za(a){var b=null; +// TODO: Find a better way to register and load all the locales in Node +if(!Be[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=xe._abbr,require("./locale/"+a), +// because defineLocale currently also sets the global locale, we +// want to undo that for lazy loaded locales +$a(b)}catch(a){}return Be[a]} +// This function will load locale and then set the global locale. If +// no arguments are passed in, it will simply return the current global +// locale key. +function $a(a,b){var c; +// moment.duration._locale = moment._locale = data; +return a&&(c=p(b)?bb(a):_a(a,b),c&&(xe=c)),xe._abbr}function _a(a,b){if(null!==b){var c=Ae;if(b.abbr=a,null!=Be[a])y("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),c=Be[a]._config;else if(null!=b.parentLocale){if(null==Be[b.parentLocale])return Ce[b.parentLocale]||(Ce[b.parentLocale]=[]),Ce[b.parentLocale].push({name:a,config:b}),null;c=Be[b.parentLocale]._config} +// backwards compat for now: also set the locale +// make sure we set the locale AFTER all child locales have been +// created, so we won't end up with the child locale set. +return Be[a]=new C(B(c,b)),Ce[a]&&Ce[a].forEach(function(a){_a(a.name,a.config)}),$a(a),Be[a]} +// useful for testing +return delete Be[a],null}function ab(a,b){if(null!=b){var c,d=Ae; +// MERGE +null!=Be[a]&&(d=Be[a]._config),b=B(d,b),c=new C(b),c.parentLocale=Be[a],Be[a]=c, +// backwards compat for now: also set the locale +$a(a)}else +// pass null for config to unupdate, useful for tests +null!=Be[a]&&(null!=Be[a].parentLocale?Be[a]=Be[a].parentLocale:null!=Be[a]&&delete Be[a]);return Be[a]} +// returns locale data +function bb(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return xe;if(!c(a)){if( +//short-circuit everything else +b=Za(a))return b;a=[a]}return Ya(a)}function cb(){return wd(Be)}function db(a){var b,c=a._a;return c&&m(a).overflow===-2&&(b=c[be]<0||c[be]>11?be:c[ce]<1||c[ce]>ea(c[ae],c[be])?ce:c[de]<0||c[de]>24||24===c[de]&&(0!==c[ee]||0!==c[fe]||0!==c[ge])?de:c[ee]<0||c[ee]>59?ee:c[fe]<0||c[fe]>59?fe:c[ge]<0||c[ge]>999?ge:-1,m(a)._overflowDayOfYear&&(bce)&&(b=ce),m(a)._overflowWeeks&&b===-1&&(b=he),m(a)._overflowWeekday&&b===-1&&(b=ie),m(a).overflow=b),a} +// date from iso format +function eb(a){var b,c,d,e,f,g,h=a._i,i=De.exec(h)||Ee.exec(h);if(i){for(m(a).iso=!0,b=0,c=Ge.length;bpa(e)&&(m(a)._overflowDayOfYear=!0),c=ta(e,0,a._dayOfYear),a._a[be]=c.getUTCMonth(),a._a[ce]=c.getUTCDate()),b=0;b<3&&null==a._a[b];++b)a._a[b]=f[b]=d[b]; +// Zero out whatever was not defaulted, including time +for(;b<7;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b]; +// Check for 24:00:00.000 +24===a._a[de]&&0===a._a[ee]&&0===a._a[fe]&&0===a._a[ge]&&(a._nextDay=!0,a._a[de]=0),a._d=(a._useUTC?ta:sa).apply(null,f), +// Apply timezone offset from input. The actual utcOffset can be changed +// with parseZone. +null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[de]=24)}}function jb(a){var b,c,d,e,f,g,h,i;if(b=a._w,null!=b.GG||null!=b.W||null!=b.E)f=1,g=4, +// TODO: We need to take the current isoWeekYear, but that depends on +// how we interpret now (local, utc, fixed offset). So create +// a now version of current config (take local/utc/offset flags, and +// create now). +c=gb(b.GG,a._a[ae],wa(sb(),1,4).year),d=gb(b.W,1),e=gb(b.E,1),(e<1||e>7)&&(i=!0);else{f=a._locale._week.dow,g=a._locale._week.doy;var j=wa(sb(),f,g);c=gb(b.gg,a._a[ae],j.year), +// Default to current week. +d=gb(b.w,j.week),null!=b.d?( +// weekday -- low day numbers are considered next week +e=b.d,(e<0||e>6)&&(i=!0)):null!=b.e?( +// local weekday -- counting starts from begining of week +e=b.e+f,(b.e<0||b.e>6)&&(i=!0)): +// default to begining of week +e=f}d<1||d>xa(c,f,g)?m(a)._overflowWeeks=!0:null!=i?m(a)._overflowWeekday=!0:(h=va(c,d,e,f,g),a._a[ae]=h.year,a._dayOfYear=h.dayOfYear)} +// date from string and format string +function kb(b){ +// TODO: Move this to another part of the creation flow to prevent circular deps +if(b._f===a.ISO_8601)return void eb(b);b._a=[],m(b).empty=!0; +// This array is used to make a Date, either with `new Date` or `Date.UTC` +var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=Y(b._f,b._locale).match(Fd)||[],c=0;c0&&m(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length), +// don't parse if it's not a known token +Id[f]?(d?m(b).empty=!1:m(b).unusedTokens.push(f),da(f,d,b)):b._strict&&!d&&m(b).unusedTokens.push(f); +// add remaining unparsed input length to the string +m(b).charsLeftOver=i-j,h.length>0&&m(b).unusedInput.push(h), +// clear _12h flag if hour is <= 12 +b._a[de]<=12&&m(b).bigHour===!0&&b._a[de]>0&&(m(b).bigHour=void 0),m(b).parsedDateParts=b._a.slice(0),m(b).meridiem=b._meridiem, +// handle meridiem +b._a[de]=lb(b._locale,b._a[de],b._meridiem),ib(b),db(b)}function lb(a,b,c){var d; +// Fallback +return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&b<12&&(b+=12),d||12!==b||(b=0),b):b} +// date from string and array of format strings +function mb(a){var b,c,d,e,f;if(0===a._f.length)return m(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;e +// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset +// +0200, so we adjust the time as needed, to be valid. +// +// Keeping the time actually adds/subtracts (one hour) +// from the actual represented time. That is why we call updateOffset +// a second time. In case it wants us to change the offset again +// _changeInProgress == true case, then we have to adjust, because +// there is no such time in the given timezone. +function Db(b,c){var d,e=this._offset||0;if(!this.isValid())return null!=b?this:NaN;if(null!=b){if("string"==typeof b){if(b=Ab(Xd,b),null===b)return this}else Math.abs(b)<16&&(b=60*b);return!this._isUTC&&c&&(d=Cb(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?Tb(this,Ob(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?e:Cb(this)}function Eb(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Fb(a){return this.utcOffset(0,a)}function Gb(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Cb(this),"m")),this}function Hb(){if(null!=this._tzm)this.utcOffset(this._tzm);else if("string"==typeof this._i){var a=Ab(Wd,this._i);null!=a?this.utcOffset(a):this.utcOffset(0,!0)}return this}function Ib(a){return!!this.isValid()&&(a=a?sb(a).utcOffset():0,(this.utcOffset()-a)%60===0)}function Jb(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Kb(){if(!p(this._isDSTShifted))return this._isDSTShifted;var a={};if(q(a,this),a=pb(a),a._a){var b=a._isUTC?k(a._a):sb(a._a);this._isDSTShifted=this.isValid()&&v(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Lb(){return!!this.isValid()&&!this._isUTC}function Mb(){return!!this.isValid()&&this._isUTC}function Nb(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}function Ob(a,b){var c,d,e,g=a, +// matching against regexp is expensive, do it on demand +h=null;// checks for null or undefined +return xb(a)?g={ms:a._milliseconds,d:a._days,M:a._months}:f(a)?(g={},b?g[b]=a:g.milliseconds=a):(h=Ne.exec(a))?(c="-"===h[1]?-1:1,g={y:0,d:u(h[ce])*c,h:u(h[de])*c,m:u(h[ee])*c,s:u(h[fe])*c,ms:u(yb(1e3*h[ge]))*c}):(h=Oe.exec(a))?(c="-"===h[1]?-1:1,g={y:Pb(h[2],c),M:Pb(h[3],c),w:Pb(h[4],c),d:Pb(h[5],c),h:Pb(h[6],c),m:Pb(h[7],c),s:Pb(h[8],c)}):null==g?g={}:"object"==typeof g&&("from"in g||"to"in g)&&(e=Rb(sb(g.from),sb(g.to)),g={},g.ms=e.milliseconds,g.M=e.months),d=new wb(g),xb(a)&&i(a,"_locale")&&(d._locale=a._locale),d}function Pb(a,b){ +// We'd normally use ~~inp for this, but unfortunately it also +// converts floats to ints. +// inp may be undefined, so careful calling replace on it. +var c=a&&parseFloat(a.replace(",",".")); +// apply sign while we're at it +return(isNaN(c)?0:c)*b}function Qb(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Rb(a,b){var c;return a.isValid()&&b.isValid()?(b=Bb(b,a),a.isBefore(b)?c=Qb(a,b):(c=Qb(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}} +// TODO: remove 'name' arg after deprecation is removed +function Sb(a,b){return function(c,d){var e,f; +//invert the arguments, but complain about it +return null===d||isNaN(+d)||(y(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Ob(c,d),Tb(this,e,a),this}}function Tb(b,c,d,e){var f=c._milliseconds,g=yb(c._days),h=yb(c._months);b.isValid()&&(e=null==e||e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&Q(b,"Date",P(b,"Date")+g*d),h&&ja(b,P(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function Ub(a,b){var c=a.diff(b,"days",!0);return c<-6?"sameElse":c<-1?"lastWeek":c<0?"lastDay":c<1?"sameDay":c<2?"nextDay":c<7?"nextWeek":"sameElse"}function Vb(b,c){ +// We want to compare the start of today, vs this. +// Getting start-of-today depends on whether we're local/utc/offset or not. +var d=b||sb(),e=Bb(d,this).startOf("day"),f=a.calendarFormat(this,e)||"sameElse",g=c&&(z(c[f])?c[f].call(this,d):c[f]);return this.format(g||this.localeData().calendar(f,this,sb(d)))}function Wb(){return new r(this)}function Xb(a,b){var c=s(a)?a:sb(a);return!(!this.isValid()||!c.isValid())&&(b=K(p(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()f&&(b=f),Fc.call(this,a,b,c,d,e))}function Fc(a,b,c,d,e){var f=va(a,b,c,d,e),g=ta(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this} +// MOMENTS +function Gc(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)} +// HELPERS +// MOMENTS +function Hc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function Ic(a,b){b[ge]=u(1e3*("0."+a))} +// MOMENTS +function Jc(){return this._isUTC?"UTC":""}function Kc(){return this._isUTC?"Coordinated Universal Time":""}function Lc(a){return sb(1e3*a)}function Mc(){return sb.apply(null,arguments).parseZone()}function Nc(a){return a}function Oc(a,b,c,d){var e=bb(),f=k().set(d,b);return e[c](f,a)}function Pc(a,b,c){if(f(a)&&(b=a,a=void 0),a=a||"",null!=b)return Oc(a,b,c,"month");var d,e=[];for(d=0;d<12;d++)e[d]=Oc(a,d,c,"month");return e} +// () +// (5) +// (fmt, 5) +// (fmt) +// (true) +// (true, 5) +// (true, fmt, 5) +// (true, fmt) +function Qc(a,b,c,d){"boolean"==typeof a?(f(b)&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,f(b)&&(c=b,b=void 0),b=b||"");var e=bb(),g=a?e._week.dow:0;if(null!=c)return Oc(b,(c+g)%7,d,"day");var h,i=[];for(h=0;h<7;h++)i[h]=Oc(b,(h+g)%7,d,"day");return i}function Rc(a,b){return Pc(a,b,"months")}function Sc(a,b){return Pc(a,b,"monthsShort")}function Tc(a,b,c){return Qc(a,b,c,"weekdays")}function Uc(a,b,c){return Qc(a,b,c,"weekdaysShort")}function Vc(a,b,c){return Qc(a,b,c,"weekdaysMin")}function Wc(){var a=this._data;return this._milliseconds=Ze(this._milliseconds),this._days=Ze(this._days),this._months=Ze(this._months),a.milliseconds=Ze(a.milliseconds),a.seconds=Ze(a.seconds),a.minutes=Ze(a.minutes),a.hours=Ze(a.hours),a.months=Ze(a.months),a.years=Ze(a.years),this}function Xc(a,b,c,d){var e=Ob(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()} +// supports only 2.0-style add(1, 's') or add(duration) +function Yc(a,b){return Xc(this,a,b,1)} +// supports only 2.0-style subtract(1, 's') or subtract(duration) +function Zc(a,b){return Xc(this,a,b,-1)}function $c(a){return a<0?Math.floor(a):Math.ceil(a)}function _c(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data; +// if we have a mix of positive and negative values, bubble down first +// check: https://github.com/moment/moment/issues/2166 +// The following code bubbles up values, see the tests for +// examples of what that means. +// convert days to months +// 12 months -> 1 year +return f>=0&&g>=0&&h>=0||f<=0&&g<=0&&h<=0||(f+=864e5*$c(bd(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=t(f/1e3),i.seconds=a%60,b=t(a/60),i.minutes=b%60,c=t(b/60),i.hours=c%24,g+=t(c/24),e=t(ad(g)),h+=e,g-=$c(bd(e)),d=t(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function ad(a){ +// 400 years have 146097 days (taking into account leap year rules) +// 400 years have 12 months === 4800 +return 4800*a/146097}function bd(a){ +// the reverse of daysToMonths +return 146097*a/4800}function cd(a){var b,c,d=this._milliseconds;if(a=K(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+ad(b),"month"===a?c:c/12;switch( +// handle milliseconds separately because of floating point math errors (issue #1867) +b=this._days+Math.round(bd(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3; +// Math.floor prevents floating point math errors here +case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}} +// TODO: Use this.as('ms')? +function dd(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*u(this._months/12)}function ed(a){return function(){return this.as(a)}}function fd(a){return a=K(a),this[a+"s"]()}function gd(a){return function(){return this._data[a]}}function hd(){return t(this.days()/7)} +// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize +function id(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function jd(a,b,c){var d=Ob(a).abs(),e=of(d.as("s")),f=of(d.as("m")),g=of(d.as("h")),h=of(d.as("d")),i=of(d.as("M")),j=of(d.as("y")),k=e0,k[4]=c,id.apply(null,k)} +// This function allows you to set the rounding function for relative time strings +function kd(a){return void 0===a?of:"function"==typeof a&&(of=a,!0)} +// This function allows you to set a threshold for relative time strings +function ld(a,b){return void 0!==pf[a]&&(void 0===b?pf[a]:(pf[a]=b,!0))}function md(a){var b=this.localeData(),c=jd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function nd(){ +// for ISO strings we do not use the normal bubbling rules: +// * milliseconds bubble up until they become hours +// * days do not bubble at all +// * months bubble up until they become years +// This is because there is no context-free conversion between hours and days +// (think of clock changes) +// and also not between days and months (28-31 days per month) +var a,b,c,d=qf(this._milliseconds)/1e3,e=qf(this._days),f=qf(this._months); +// 3600 seconds -> 60 minutes -> 1 hour +a=t(d/60),b=t(a/60),d%=60,a%=60, +// 12 months -> 1 year +c=t(f/12),f%=12; +// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js +var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(m<0?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var od,pd;pd=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;d68?1900:2e3)}; +// MOMENTS +var pe=O("FullYear",!0); +// FORMATTING +U("w",["ww",2],"wo","week"),U("W",["WW",2],"Wo","isoWeek"), +// ALIASES +J("week","w"),J("isoWeek","W"), +// PRIORITIES +M("week",5),M("isoWeek",5), +// PARSING +Z("w",Od),Z("ww",Od,Kd),Z("W",Od),Z("WW",Od,Kd),ca(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=u(a)});var qe={dow:0,// Sunday is the first day of the week. +doy:6}; +// FORMATTING +U("d",0,"do","day"),U("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),U("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),U("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),U("e",0,0,"weekday"),U("E",0,0,"isoWeekday"), +// ALIASES +J("day","d"),J("weekday","e"),J("isoWeekday","E"), +// PRIORITY +M("day",11),M("weekday",11),M("isoWeekday",11), +// PARSING +Z("d",Od),Z("e",Od),Z("E",Od),Z("dd",function(a,b){return b.weekdaysMinRegex(a)}),Z("ddd",function(a,b){return b.weekdaysShortRegex(a)}),Z("dddd",function(a,b){return b.weekdaysRegex(a)}),ca(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict); +// if we didn't get a weekday name, mark the date as invalid +null!=e?b.d=e:m(c).invalidWeekday=a}),ca(["d","e","E"],function(a,b,c,d){b[d]=u(a)}); +// LOCALES +var re="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),se="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),te="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ue=Zd,ve=Zd,we=Zd;U("H",["HH",2],0,"hour"),U("h",["hh",2],0,Ra),U("k",["kk",2],0,Sa),U("hmm",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)}),U("hmmss",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)+T(this.seconds(),2)}),U("Hmm",0,0,function(){return""+this.hours()+T(this.minutes(),2)}),U("Hmmss",0,0,function(){return""+this.hours()+T(this.minutes(),2)+T(this.seconds(),2)}),Ta("a",!0),Ta("A",!1), +// ALIASES +J("hour","h"), +// PRIORITY +M("hour",13),Z("a",Ua),Z("A",Ua),Z("H",Od),Z("h",Od),Z("HH",Od,Kd),Z("hh",Od,Kd),Z("hmm",Pd),Z("hmmss",Qd),Z("Hmm",Pd),Z("Hmmss",Qd),ba(["H","HH"],de),ba(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),ba(["h","hh"],function(a,b,c){b[de]=u(a),m(c).bigHour=!0}),ba("hmm",function(a,b,c){var d=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d)),m(c).bigHour=!0}),ba("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d,2)),b[fe]=u(a.substr(e)),m(c).bigHour=!0}),ba("Hmm",function(a,b,c){var d=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d))}),ba("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d,2)),b[fe]=u(a.substr(e))});var xe,ye=/[ap]\.?m?\.?/i,ze=O("Hours",!0),Ae={calendar:xd,longDateFormat:yd,invalidDate:zd,ordinal:Ad,ordinalParse:Bd,relativeTime:Cd,months:le,monthsShort:me,week:qe,weekdays:re,weekdaysMin:te,weekdaysShort:se,meridiemParse:ye},Be={},Ce={},De=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ee=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Fe=/Z|[+-]\d\d(?::?\d\d)?/,Ge=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/], +// YYYYMM is NOT allowed by the standard +["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],He=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Ie=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=x("value provided is not in a recognized ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}), +// constant that refers to the ISO standard +a.ISO_8601=function(){};var Je=x("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=sb.apply(null,arguments);return this.isValid()&&a.isValid()?athis?this:a:o()}),Le=function(){return Date.now?Date.now():+new Date};zb("Z",":"),zb("ZZ",""), +// PARSING +Z("Z",Xd),Z("ZZ",Xd),ba(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Ab(Xd,a)}); +// HELPERS +// timezone chunker +// '+10:00' > ['10', '00'] +// '-1530' > ['-15', '30'] +var Me=/([\+\-]|\d\d)/gi; +// HOOKS +// This function will be called whenever a moment is mutated. +// It is intended to keep the offset in sync with the timezone. +a.updateOffset=function(){}; +// ASP.NET json date format regex +var Ne=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Oe=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Ob.fn=wb.prototype;var Pe=Sb(1,"add"),Qe=Sb(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Re=x("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)}); +// FORMATTING +U(0,["gg",2],0,function(){return this.weekYear()%100}),U(0,["GG",2],0,function(){return this.isoWeekYear()%100}),zc("gggg","weekYear"),zc("ggggg","weekYear"),zc("GGGG","isoWeekYear"),zc("GGGGG","isoWeekYear"), +// ALIASES +J("weekYear","gg"),J("isoWeekYear","GG"), +// PRIORITY +M("weekYear",1),M("isoWeekYear",1), +// PARSING +Z("G",Vd),Z("g",Vd),Z("GG",Od,Kd),Z("gg",Od,Kd),Z("GGGG",Sd,Md),Z("gggg",Sd,Md),Z("GGGGG",Td,Nd),Z("ggggg",Td,Nd),ca(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=u(a)}),ca(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}), +// FORMATTING +U("Q",0,"Qo","quarter"), +// ALIASES +J("quarter","Q"), +// PRIORITY +M("quarter",7), +// PARSING +Z("Q",Jd),ba("Q",function(a,b){b[be]=3*(u(a)-1)}), +// FORMATTING +U("D",["DD",2],"Do","date"), +// ALIASES +J("date","D"), +// PRIOROITY +M("date",9), +// PARSING +Z("D",Od),Z("DD",Od,Kd),Z("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),ba(["D","DD"],ce),ba("Do",function(a,b){b[ce]=u(a.match(Od)[0],10)}); +// MOMENTS +var Se=O("Date",!0); +// FORMATTING +U("DDD",["DDDD",3],"DDDo","dayOfYear"), +// ALIASES +J("dayOfYear","DDD"), +// PRIORITY +M("dayOfYear",4), +// PARSING +Z("DDD",Rd),Z("DDDD",Ld),ba(["DDD","DDDD"],function(a,b,c){c._dayOfYear=u(a)}), +// FORMATTING +U("m",["mm",2],0,"minute"), +// ALIASES +J("minute","m"), +// PRIORITY +M("minute",14), +// PARSING +Z("m",Od),Z("mm",Od,Kd),ba(["m","mm"],ee); +// MOMENTS +var Te=O("Minutes",!1); +// FORMATTING +U("s",["ss",2],0,"second"), +// ALIASES +J("second","s"), +// PRIORITY +M("second",15), +// PARSING +Z("s",Od),Z("ss",Od,Kd),ba(["s","ss"],fe); +// MOMENTS +var Ue=O("Seconds",!1); +// FORMATTING +U("S",0,0,function(){return~~(this.millisecond()/100)}),U(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),U(0,["SSS",3],0,"millisecond"),U(0,["SSSS",4],0,function(){return 10*this.millisecond()}),U(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),U(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),U(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),U(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),U(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}), +// ALIASES +J("millisecond","ms"), +// PRIORITY +M("millisecond",16), +// PARSING +Z("S",Rd,Jd),Z("SS",Rd,Kd),Z("SSS",Rd,Ld);var Ve;for(Ve="SSSS";Ve.length<=9;Ve+="S")Z(Ve,Ud);for(Ve="S";Ve.length<=9;Ve+="S")ba(Ve,Ic); +// MOMENTS +var We=O("Milliseconds",!1); +// FORMATTING +U("z",0,0,"zoneAbbr"),U("zz",0,0,"zoneName");var Xe=r.prototype;Xe.add=Pe,Xe.calendar=Vb,Xe.clone=Wb,Xe.diff=bc,Xe.endOf=oc,Xe.format=gc,Xe.from=hc,Xe.fromNow=ic,Xe.to=jc,Xe.toNow=kc,Xe.get=R,Xe.invalidAt=xc,Xe.isAfter=Xb,Xe.isBefore=Yb,Xe.isBetween=Zb,Xe.isSame=$b,Xe.isSameOrAfter=_b,Xe.isSameOrBefore=ac,Xe.isValid=vc,Xe.lang=Re,Xe.locale=lc,Xe.localeData=mc,Xe.max=Ke,Xe.min=Je,Xe.parsingFlags=wc,Xe.set=S,Xe.startOf=nc,Xe.subtract=Qe,Xe.toArray=sc,Xe.toObject=tc,Xe.toDate=rc,Xe.toISOString=ec,Xe.inspect=fc,Xe.toJSON=uc,Xe.toString=dc,Xe.unix=qc,Xe.valueOf=pc,Xe.creationData=yc, +// Year +Xe.year=pe,Xe.isLeapYear=ra, +// Week Year +Xe.weekYear=Ac,Xe.isoWeekYear=Bc, +// Quarter +Xe.quarter=Xe.quarters=Gc, +// Month +Xe.month=ka,Xe.daysInMonth=la, +// Week +Xe.week=Xe.weeks=Ba,Xe.isoWeek=Xe.isoWeeks=Ca,Xe.weeksInYear=Dc,Xe.isoWeeksInYear=Cc, +// Day +Xe.date=Se,Xe.day=Xe.days=Ka,Xe.weekday=La,Xe.isoWeekday=Ma,Xe.dayOfYear=Hc, +// Hour +Xe.hour=Xe.hours=ze, +// Minute +Xe.minute=Xe.minutes=Te, +// Second +Xe.second=Xe.seconds=Ue, +// Millisecond +Xe.millisecond=Xe.milliseconds=We, +// Offset +Xe.utcOffset=Db,Xe.utc=Fb,Xe.local=Gb,Xe.parseZone=Hb,Xe.hasAlignedHourOffset=Ib,Xe.isDST=Jb,Xe.isLocal=Lb,Xe.isUtcOffset=Mb,Xe.isUtc=Nb,Xe.isUTC=Nb, +// Timezone +Xe.zoneAbbr=Jc,Xe.zoneName=Kc, +// Deprecations +Xe.dates=x("dates accessor is deprecated. Use date instead.",Se),Xe.months=x("months accessor is deprecated. Use month instead",ka),Xe.years=x("years accessor is deprecated. Use year instead",pe),Xe.zone=x("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",Eb),Xe.isDSTShifted=x("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Kb);var Ye=C.prototype;Ye.calendar=D,Ye.longDateFormat=E,Ye.invalidDate=F,Ye.ordinal=G,Ye.preparse=Nc,Ye.postformat=Nc,Ye.relativeTime=H,Ye.pastFuture=I,Ye.set=A, +// Month +Ye.months=fa,Ye.monthsShort=ga,Ye.monthsParse=ia,Ye.monthsRegex=na,Ye.monthsShortRegex=ma, +// Week +Ye.week=ya,Ye.firstDayOfYear=Aa,Ye.firstDayOfWeek=za, +// Day of Week +Ye.weekdays=Fa,Ye.weekdaysMin=Ha,Ye.weekdaysShort=Ga,Ye.weekdaysParse=Ja,Ye.weekdaysRegex=Na,Ye.weekdaysShortRegex=Oa,Ye.weekdaysMinRegex=Pa, +// Hours +Ye.isPM=Va,Ye.meridiem=Wa,$a("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===u(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}), +// Side effect imports +a.lang=x("moment.lang is deprecated. Use moment.locale instead.",$a),a.langData=x("moment.langData is deprecated. Use moment.localeData instead.",bb);var Ze=Math.abs,$e=ed("ms"),_e=ed("s"),af=ed("m"),bf=ed("h"),cf=ed("d"),df=ed("w"),ef=ed("M"),ff=ed("y"),gf=gd("milliseconds"),hf=gd("seconds"),jf=gd("minutes"),kf=gd("hours"),lf=gd("days"),mf=gd("months"),nf=gd("years"),of=Math.round,pf={s:45,// seconds to minute +m:45,// minutes to hour +h:22,// hours to day +d:26,// days to month +M:11},qf=Math.abs,rf=wb.prototype; +// Deprecations +// Side effect imports +// FORMATTING +// PARSING +// Side effect imports +return rf.abs=Wc,rf.add=Yc,rf.subtract=Zc,rf.as=cd,rf.asMilliseconds=$e,rf.asSeconds=_e,rf.asMinutes=af,rf.asHours=bf,rf.asDays=cf,rf.asWeeks=df,rf.asMonths=ef,rf.asYears=ff,rf.valueOf=dd,rf._bubble=_c,rf.get=fd,rf.milliseconds=gf,rf.seconds=hf,rf.minutes=jf,rf.hours=kf,rf.days=lf,rf.weeks=hd,rf.months=mf,rf.years=nf,rf.humanize=md,rf.toISOString=nd,rf.toString=nd,rf.toJSON=nd,rf.locale=lc,rf.localeData=mc,rf.toIsoString=x("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",nd),rf.lang=Re,U("X",0,0,"unix"),U("x",0,0,"valueOf"),Z("x",Vd),Z("X",Yd),ba("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),ba("x",function(a,b,c){c._d=new Date(u(a))}),a.version="2.17.1",b(sb),a.fn=Xe,a.min=ub,a.max=vb,a.now=Le,a.utc=k,a.unix=Lc,a.months=Rc,a.isDate=g,a.locale=$a,a.invalid=o,a.duration=Ob,a.isMoment=s,a.weekdays=Tc,a.parseZone=Mc,a.localeData=bb,a.isDuration=xb,a.monthsShort=Sc,a.weekdaysMin=Vc,a.defineLocale=_a,a.updateLocale=ab,a.locales=cb,a.weekdaysShort=Uc,a.normalizeUnits=K,a.relativeTimeRounding=kd,a.relativeTimeThreshold=ld,a.calendarFormat=Ub,a.prototype=Xe,a}); \ No newline at end of file diff --git a/settings.php b/settings.php index 01172d50..99f96aaf 100644 --- a/settings.php +++ b/settings.php @@ -625,24 +625,6 @@ $privacyMode = false; } - if(istrue($setupVars["API_GET_UPSTREAM_DNS_HOSTNAME"])) - { - $resolveForward = true; - } - else - { - $resolveForward = false; - } - - if(istrue($setupVars["API_GET_CLIENT_HOSTNAME"])) - { - $resolveClients = true; - } - else - { - $resolveClients = false; - } - ?>
@@ -661,36 +643,24 @@
- +
-

Reverse DNS lookup

-

Try to determine the domain name via querying the Pi-hole for

+

Privacy settings (Statistics / Query Log)

-
+
-
-
-
-

Query Log

-
-
-
-
-
-
-
-
+

Privacy mode

-
+
@@ -766,6 +736,35 @@
*/ ?> + +
+
+

FTL status (Running, PID: Not running)

+
+
+ Time FTL started:
+ User / Group: /
+ Total CPU utilization: %
+ Total CPU time:
+ Memory utilization: %
+ Used memory:
+ +
+

System Administration

From f88092175e150df4243ff507bf6aed9bd3f7f9a9 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 19 Feb 2017 14:17:52 +0100 Subject: [PATCH 08/30] Conflicts: api.php scripts/pi-hole/php/FTL.php scripts/pi-hole/php/auth.php scripts/pi-hole/php/password.php --- api.php | 400 +++++++++++++-------------- scripts/pi-hole/js/footer.js | 126 ++++++--- scripts/pi-hole/php/FTL.php | 7 + scripts/pi-hole/php/auth.php | 3 +- scripts/pi-hole/php/data.php | 14 +- scripts/pi-hole/php/footer.php | 32 ++- scripts/pi-hole/php/header.php | 7 +- scripts/pi-hole/php/password.php | 3 +- scripts/pi-hole/php/savesettings.php | 4 +- 9 files changed, 347 insertions(+), 249 deletions(-) diff --git a/api.php b/api.php index b8e0024c..128640f8 100644 --- a/api.php +++ b/api.php @@ -1,242 +1,242 @@ - - $domains_over_time, - 'ads_over_time' => $ads_over_time); - $data = array_merge($data, $result); - } - - if (isset($_GET['topItems']) && $auth) - { - if(is_numeric($_GET['topItems'])) - { - sendRequestFTL("top-domains ".$_GET['topItems']); - } else { - sendRequestFTL("top-domains"); + $stats[$tmp[0]] = $tmp[1]; } + } + $data = array_merge($data,$stats); +} - $return = getResponseFTL(); - $top_queries = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - $top_queries[$tmp[2]] = $tmp[1]; - } +if (isset($_GET['overTimeData10mins'])) +{ + sendRequestFTL("overTime"); + $return = getResponseFTL(); - if(is_numeric($_GET['topItems'])) + $domains_over_time = array(); + $ads_over_time = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + $domains_over_time[$tmp[0]] = $tmp[1]; + $ads_over_time[$tmp[0]] = $tmp[2]; + } + $result = array('domains_over_time' => $domains_over_time, + 'ads_over_time' => $ads_over_time); + $data = array_merge($data, $result); +} + +if (isset($_GET['topItems']) && $auth) +{ + if(is_numeric($_GET['topItems'])) + { + sendRequestFTL("top-domains ".$_GET['topItems']); + } + else + { + sendRequestFTL("top-domains"); + } + + $return = getResponseFTL(); + $top_queries = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + $top_queries[$tmp[2]] = $tmp[1]; + } + + if(is_numeric($_GET['topItems'])) + { + sendRequestFTL("top-ads ".$_GET['topItems']); + } + else + { + sendRequestFTL("top-ads"); + } + + $return = getResponseFTL(); + $top_ads = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + $top_ads[$tmp[2]] = $tmp[1]; + } + + $result = array('top_queries' => $top_queries, + 'top_ads' => $top_ads); + + $data = array_merge($data, $result); +} + +if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth) +{ + sendRequestFTL("top-clients"); + $return = getResponseFTL(); + $top_clients = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + if(count($tmp) == 4) { - sendRequestFTL("top-ads ".$_GET['topItems']); + $top_clients[$tmp[3]."|".$tmp[2]] = $tmp[1]; } else { - sendRequestFTL("top-ads"); + $top_clients[$tmp[2]] = $tmp[1]; } - - $return = getResponseFTL(); - $top_ads = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - $top_ads[$tmp[2]] = $tmp[1]; - } - - $result = array('top_queries' => $top_queries, - 'top_ads' => $top_ads); - - $data = array_merge($data, $result); } - if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth) + $result = array('top_sources' => $top_clients); + $data = array_merge($data, $result); +} + +if (isset($_GET['getForwardDestinations']) && $auth) +{ + sendRequestFTL("forward-dest"); + $return = getResponseFTL(); + $forward_dest = array(); + foreach($return as $line) { - sendRequestFTL("top-clients"); - $return = getResponseFTL(); - $top_clients = array(); - foreach($return as $line) + $tmp = explode(" ",$line); + if(count($tmp) == 4) { - $tmp = explode(" ",$line); - if(count($tmp) == 4) - { - $top_clients[$tmp[3]."|".$tmp[2]] = $tmp[1]; - } - else - { - $top_clients[$tmp[2]] = $tmp[1]; - } - } - - $result = array('top_sources' => $top_clients); - $data = array_merge($data, $result); - } - - if (isset($_GET['getForwardDestinations']) && $auth) - { - sendRequestFTL("forward-dest"); - $return = getResponseFTL(); - $forward_dest = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - if(count($tmp) == 4) - { - $forward_dest[$tmp[3]."|".$tmp[2]] = $tmp[1]; - } - else - { - $forward_dest[$tmp[2]] = $tmp[1]; - } - } - - $result = array('forward_destinations' => $forward_dest); - $data = array_merge($data, $result); - } - - if (isset($_GET['getQueryTypes']) && $auth) - { - sendRequestFTL("querytypes"); - $return = getResponseFTL(); - $querytypes = array(); - foreach($return as $ret) - { - $tmp = explode(": ",$ret); - $querytypes[$tmp[0]] = $tmp[1]; - } - - $result = array('querytypes' => $querytypes); - $data = array_merge($data, $result); - } - - if (isset($_GET['getAllQueries']) && $auth) - { - if(isset($_GET['from']) && isset($_GET['until'])) - { - // Get limited time interval - sendRequestFTL("getallqueries-time ".$_GET['from']." ".$_GET['until']); - } - else if(isset($_GET['domain'])) - { - // Get specific domain only - sendRequestFTL("getallqueries-domain ".$_GET['domain']); - } - else if(isset($_GET['client'])) - { - // Get specific client only - sendRequestFTL("getallqueries-client ".$_GET['client']); + $forward_dest[$tmp[3]."|".$tmp[2]] = $tmp[1]; } else { - // Get all queries - sendRequestFTL("getallqueries"); + $forward_dest[$tmp[2]] = $tmp[1]; } - $return = getResponseFTL(); - $allQueries = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - array_push($allQueries,$tmp); - } - - $result = array('data' => $allQueries); - $data = array_merge($data, $result); } - if (isset($_GET['enable'], $_GET['token']) && $auth) { - check_csrf($_GET['token']); - exec('sudo pihole enable'); - $data = array_merge($data, array("status" => "enabled")); - } - elseif (isset($_GET['disable'], $_GET['token']) && $auth) { - check_csrf($_GET['token']); - $disable = intval($_GET['disable']); - // intval returns the integer value on success, or 0 on failure - if($disable > 0) - { - exec("sudo pihole disable ".$disable."s"); - } - else - { - exec('sudo pihole disable'); - } - $data = array_merge($data, array("status" => "disabled")); - } + $result = array('forward_destinations' => $forward_dest); + $data = array_merge($data, $result); +} - if(isset($_GET["recentBlocked"])) +if (isset($_GET['getQueryTypes']) && $auth) +{ + sendRequestFTL("querytypes"); + $return = getResponseFTL(); + $querytypes = array(); + foreach($return as $ret) { - sendRequestFTL("recentBlocked"); - echo getResponseFTL()[0]; - unset($data); + $tmp = explode(": ",$ret); + $querytypes[$tmp[0]] = $tmp[1]; } - if(isset($data)) + $result = array('querytypes' => $querytypes); + $data = array_merge($data, $result); +} + +if (isset($_GET['getAllQueries']) && $auth) +{ + if(isset($_GET['from']) && isset($_GET['until'])) { - echo json_encode($data); + // Get limited time interval + sendRequestFTL("getallqueries-time ".$_GET['from']." ".$_GET['until']); + } + else if(isset($_GET['domain'])) + { + // Get specific domain only + sendRequestFTL("getallqueries-domain ".$_GET['domain']); + } + else if(isset($_GET['client'])) + { + // Get specific client only + sendRequestFTL("getallqueries-client ".$_GET['client']); + } + else + { + // Get all queries + sendRequestFTL("getallqueries"); + } + $return = getResponseFTL(); + $allQueries = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + array_push($allQueries,$tmp); } - disconnectFTL(); + $result = array('data' => $allQueries); + $data = array_merge($data, $result); +} + +if (isset($_GET['enable'], $_GET['token']) && $auth) { + check_csrf($_GET['token']); + exec('sudo pihole enable'); + $data = array_merge($data, array("status" => "enabled")); +} +elseif (isset($_GET['disable'], $_GET['token']) && $auth) { + check_csrf($_GET['token']); + $disable = intval($_GET['disable']); + // intval returns the integer value on success, or 0 on failure + if($disable > 0) + { + exec("sudo pihole disable ".$disable."s"); + } + else + { + exec('sudo pihole disable'); + } + $data = array_merge($data, array("status" => "disabled")); +} + +if(isset($_GET["recentBlocked"])) +{ + sendRequestFTL("recentBlocked"); + echo getResponseFTL()[0]; + unset($data); +} + +if(isset($data)) +{ + echo json_encode($data); +} + +disconnectFTL(); ?> diff --git a/scripts/pi-hole/js/footer.js b/scripts/pi-hole/js/footer.js index 5cdd042e..3af5b3e7 100644 --- a/scripts/pi-hole/js/footer.js +++ b/scripts/pi-hole/js/footer.js @@ -22,17 +22,17 @@ function piholeChanged(action) switch(action) { case "enabled": - status.html(" Active"); - ena.hide(); - dis.show(); - dis.removeClass("active"); - break; + status.html(" Active"); + ena.hide(); + dis.show(); + dis.removeClass("active"); + break; case "disabled": - status.html(" Offline"); - ena.show(); - dis.hide(); - break; + status.html(" Offline"); + ena.show(); + dis.hide(); + break; } } @@ -44,32 +44,76 @@ function piholeChange(action, duration) switch(action) { case "enable": - btnStatus = $("#flip-status-enable"); - btnStatus.html(" "); - $.getJSON("api.php?enable&token=" + token, function(data) { - if(data.status === "enabled") { - btnStatus.html(""); - piholeChanged("enabled"); - } - }); - break; + btnStatus = $("#flip-status-enable"); + btnStatus.html(" "); + $.getJSON("api.php?enable&token=" + token, function(data) { + if(data.status === "enabled") { + btnStatus.html(""); + piholeChanged("enabled"); + } + }); + break; case "disable": - btnStatus = $("#flip-status-disable"); - btnStatus.html(" "); - $.getJSON("api.php?disable=" + duration + "&token=" + token, function(data) { - if(data.status === "disabled") { - btnStatus.html(""); - piholeChanged("disabled"); - } - }); - break; + btnStatus = $("#flip-status-disable"); + btnStatus.html(" "); + $.getJSON("api.php?disable=" + duration + "&token=" + token, function(data) { + if(data.status === "disabled") { + btnStatus.html(""); + piholeChanged("disabled"); + } + }); + break; } } +//The following three functions allow us to display time until pi-hole is enabled after disabling. +//Works between all pages + + +function setCountdownTarget(seconds){ + var target = new Date(); + target = new Date(target.getTime() + seconds * 1000); + localStorage.setItem("countDownTarget", target); +} + +function secondsTimeSpanToHMS(s) { + var h = Math.floor(s/3600); //Get whole hours + s -= h*3600; + var m = Math.floor(s/60); //Get remaining minutes + s -= m*60; + return h+":"+(m < 10 ? "0"+m : m)+":"+(s < 10 ? "0"+s : s); //zero padding on minutes and seconds +} + +function countDown(){ + var ena = $("#enableLabel"); + var target = new Date(localStorage.getItem("countDownTarget")); + var seconds = Math.round((target.getTime() - new Date().getTime()) / 1000); + + if(seconds > 0){ + setTimeout(countDown,1000); + ena.text("Enable (" + secondsTimeSpanToHMS(seconds) + ")"); + } + else + { + ena.text("Enable"); + piholeChanged("enabled"); + localStorage.removeItem("countDownTarget"); + } +} + +$( document ).ready(function() { + var countDownTarget = localStorage.getItem("countDownTarget"); + if (countDownTarget != null) + { + setTimeout(countDown,1000); + } +}); + // Handle Enable/Disable $("#pihole-enable").on("click", function(e){ e.preventDefault(); + localStorage.removeItem("countDownTarget"); piholeChange("enable",""); }); $("#pihole-disable-permanently").on("click", function(e){ @@ -79,18 +123,30 @@ $("#pihole-disable-permanently").on("click", function(e){ $("#pihole-disable-10s").on("click", function(e){ e.preventDefault(); piholeChange("disable","10"); - setTimeout(function(){piholeChanged("enabled");},10000); + setCountdownTarget(10); + setTimeout(countDown,1000); }); $("#pihole-disable-30s").on("click", function(e){ e.preventDefault(); piholeChange("disable","30"); - setTimeout(function(){piholeChanged("enabled");},30000); + setCountdownTarget(30); + setTimeout(countDown,1000); }); $("#pihole-disable-5m").on("click", function(e){ e.preventDefault(); piholeChange("disable","300"); - setTimeout(function(){piholeChanged("enabled");},300000); + setCountdownTarget(300); + setTimeout(countDown,1000); }); +$("#pihole-disable-custom").on("click", function(e){ + e.preventDefault(); + var custVal = $("#customTimeout").val(); + custVal = $("#btnMins").hasClass("active") ? custVal * 60 : custVal; + piholeChange("disable",custVal); + setCountdownTarget(custVal); + setTimeout(countDown,1000); +}); + var piholeVersion = $("#piholeVersion").html(); var webVersion = $("#webVersion").html(); @@ -200,15 +256,15 @@ else // Hide "exact match" button on queryads.php page if version is 2.9.5 or lower if(versionCompare(piholeVersion, "v2.9.5") < 1) { - $("#btnSearchExact").hide(); + $("#btnSearchExact").hide(); } // Handle Strg + Enter button on Login page $(document).keypress(function(e) { - if((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey && $("#loginpw").is(":focus")) { - $("#loginform").attr("action", "settings.php"); - $("#loginform").submit(); - } + if((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey && $("#loginpw").is(":focus")) { + $("#loginform").attr("action", "settings.php"); + $("#loginform").submit(); + } }); function testCookies() diff --git a/scripts/pi-hole/php/FTL.php b/scripts/pi-hole/php/FTL.php index 91cffdd2..c483ecba 100644 --- a/scripts/pi-hole/php/FTL.php +++ b/scripts/pi-hole/php/FTL.php @@ -1,4 +1,10 @@ diff --git a/scripts/pi-hole/php/auth.php b/scripts/pi-hole/php/auth.php index b1c22aea..77871e4d 100644 --- a/scripts/pi-hole/php/auth.php +++ b/scripts/pi-hole/php/auth.php @@ -4,9 +4,8 @@ * 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. */ ?> +* Please see LICENSE file for your rights under this license. */ -format('Y-m-d\TH:i:s')] = $status; + $allQueries['data'][hasHostName($client)][$domain][$time->format('Y-m-d T H:i:s')] = $status; }elseif ($orderBy == "orderByClientTimeDomain"){ - $allQueries['data'][hasHostName($client)][$time->format('Y-m-d\TH:i:s')][$domain] = $status; + $allQueries['data'][hasHostName($client)][$time->format('Y-m-d T H:i:s')][$domain] = $status; }elseif ($orderBy == "orderByTimeClientDomain"){ - $allQueries['data'][$time->format('Y-m-d\TH:i:s')][hasHostName($client)][$domain] = $status; + $allQueries['data'][$time->format('Y-m-d T H:i:s')][hasHostName($client)][$domain] = $status; }elseif ($orderBy == "orderByTimeDomainClient"){ - $allQueries['data'][$time->format('Y-m-d\TH:i:s')][$domain][hasHostName($client)] = $status; + $allQueries['data'][$time->format('Y-m-d T H:i:s')][$domain][hasHostName($client)] = $status; }elseif ($orderBy == "orderByDomainClientTime"){ - $allQueries['data'][$domain][hasHostName($client)][$time->format('Y-m-d\TH:i:s')] = $status; + $allQueries['data'][$domain][hasHostName($client)][$time->format('Y-m-d T H:i:s')] = $status; }elseif ($orderBy == "orderByDomainTimeClient"){ - $allQueries['data'][$domain][$time->format('Y-m-d\TH:i:s')][hasHostName($client)] = $status; + $allQueries['data'][$domain][$time->format('Y-m-d T H:i:s')][hasHostName($client)] = $status; }else{ array_push($allQueries['data'], array( - $time->format('Y-m-d\TH:i:s'), + $time->format('Y-m-d T H:i:s'), $type, $domain, hasHostName($client), diff --git a/scripts/pi-hole/php/footer.php b/scripts/pi-hole/php/footer.php index 1633420b..34d7319f 100644 --- a/scripts/pi-hole/php/footer.php +++ b/scripts/pi-hole/php/footer.php @@ -9,6 +9,36 @@
+ +
Pi-hole Version Web Interface Version
-
Donate if you found this useful.
+
Donate if you found this useful.
diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php index 009d0892..e885f1f4 100644 --- a/scripts/pi-hole/php/header.php +++ b/scripts/pi-hole/php/header.php @@ -419,11 +419,16 @@ For 5 minutes +
  • + + Custom time + +
  • hidden="true"> - Enable    + Enable   
  • active"> diff --git a/scripts/pi-hole/php/password.php b/scripts/pi-hole/php/password.php index af9716bf..40095563 100644 --- a/scripts/pi-hole/php/password.php +++ b/scripts/pi-hole/php/password.php @@ -4,9 +4,8 @@ * 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. */ ?> +* Please see LICENSE file for your rights under this license. */ - "Level3 (Primary)", "199.85.126.10" => "Norton (Primary)", "8.26.56.26" => "Comodo (Primary)", + "84.200.69.80" => "DNS.WATCH (Primary)", "8.8.4.4" => "Google (Secondary)", "208.67.220.220" => "OpenDNS (Secondary)", "4.2.2.2" => "Level3 (Secondary)", "199.85.127.10" => "Norton (Secondary)", - "8.20.247.20" => "Comodo (Secondary)" + "8.20.247.20" => "Comodo (Secondary)", + "84.200.70.40" => "DNS.WATCH (Secondary)", ]; $error = ""; From bbab9f8bfd6616c773597e186c78b61ad7e463b4 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 19 Feb 2017 14:24:23 +0100 Subject: [PATCH 09/30] Removed double license header in api.php --- api.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/api.php b/api.php index a2c81815..128640f8 100644 --- a/api.php +++ b/api.php @@ -1,9 +1,3 @@ - Date: Sun, 19 Feb 2017 14:26:59 +0100 Subject: [PATCH 10/30] Include missing license header in api_PHP.php --- api_PHP.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api_PHP.php b/api_PHP.php index 589ae0bd..eea77080 100644 --- a/api_PHP.php +++ b/api_PHP.php @@ -1,4 +1,11 @@ Date: Sun, 19 Feb 2017 14:50:04 +0100 Subject: [PATCH 11/30] Ensure fallback solution works for all emelemts on the main page --- scripts/pi-hole/js/index.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 7c0dc79a..9bd1401a 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -106,7 +106,11 @@ function updateQueryTypes() { $.each($.AdminLTE.options.colors, function(key, value) { colors.push(value); }); var v = [], c = [], k = []; // Collect values and colors, and labels - $.each(data.querytypes, function(key , value) { + if(data.hasOwnProperty("querytypes")) + iter = data.querytypes; + else + iter = data; + $.each(iter, function(key , value) { v.push(value); c.push(colors.shift()); k.push(key); @@ -183,9 +187,13 @@ function updateForwardDestinations() { var colors = []; // Get colors from AdminLTE $.each($.AdminLTE.options.colors, function(key, value) { colors.push(value); }); - var v = [], c = [], k = []; + var v = [], c = [], k = [], iter; // Collect values and colors, immediately push individual labels - $.each(data.forward_destinations, function(key , value) { + if(data.hasOwnProperty("forward_destinations")) + iter = data.forward_destinations; + else + iter = data; + $.each(iter, function(key , value) { v.push(value); c.push(colors.shift()); if(key.indexOf("|") > -1) From 1975c261d05f04251772d3fa96891c298e4f8fcf Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 19 Feb 2017 14:52:28 +0100 Subject: [PATCH 12/30] Moved FTL API into own file as well --- api.php | 232 ++------------------------------------------------- api_FTL.php | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 227 deletions(-) create mode 100644 api_FTL.php diff --git a/api.php b/api.php index 128640f8..6683a2e7 100644 --- a/api.php +++ b/api.php @@ -9,234 +9,12 @@ $api = true; require("scripts/pi-hole/php/FTL.php"); -if(!testFTL()) +if(testFTL()) +{ + require("api_FTL.php"); +} +else { require("api_PHP.php"); - exit(); } - -require "scripts/pi-hole/php/password.php"; -require "scripts/pi-hole/php/auth.php"; - -check_cors(); -$socket = connectFTL("127.0.0.1"); -header('Content-type: application/json'); - -$data = []; - -if (isset($_GET['summary']) || isset($_GET['summaryRaw'])) -{ - sendRequestFTL("stats"); - $return = getResponseFTL(); - - $stats = []; - foreach($return as $line) - { - $tmp = explode(" ",$line); - - if(isset($_GET['summary'])) - { - if($tmp[0] !== "ads_percentage_today") - { - $stats[$tmp[0]] = number_format($tmp[1]); - } - else - { - $stats[$tmp[0]] = number_format($tmp[1], 1, '.', ''); - } - } - else - { - $stats[$tmp[0]] = $tmp[1]; - } - } - $data = array_merge($data,$stats); -} - -if (isset($_GET['overTimeData10mins'])) -{ - sendRequestFTL("overTime"); - $return = getResponseFTL(); - - $domains_over_time = array(); - $ads_over_time = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - $domains_over_time[$tmp[0]] = $tmp[1]; - $ads_over_time[$tmp[0]] = $tmp[2]; - } - $result = array('domains_over_time' => $domains_over_time, - 'ads_over_time' => $ads_over_time); - $data = array_merge($data, $result); -} - -if (isset($_GET['topItems']) && $auth) -{ - if(is_numeric($_GET['topItems'])) - { - sendRequestFTL("top-domains ".$_GET['topItems']); - } - else - { - sendRequestFTL("top-domains"); - } - - $return = getResponseFTL(); - $top_queries = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - $top_queries[$tmp[2]] = $tmp[1]; - } - - if(is_numeric($_GET['topItems'])) - { - sendRequestFTL("top-ads ".$_GET['topItems']); - } - else - { - sendRequestFTL("top-ads"); - } - - $return = getResponseFTL(); - $top_ads = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - $top_ads[$tmp[2]] = $tmp[1]; - } - - $result = array('top_queries' => $top_queries, - 'top_ads' => $top_ads); - - $data = array_merge($data, $result); -} - -if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth) -{ - sendRequestFTL("top-clients"); - $return = getResponseFTL(); - $top_clients = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - if(count($tmp) == 4) - { - $top_clients[$tmp[3]."|".$tmp[2]] = $tmp[1]; - } - else - { - $top_clients[$tmp[2]] = $tmp[1]; - } - } - - $result = array('top_sources' => $top_clients); - $data = array_merge($data, $result); -} - -if (isset($_GET['getForwardDestinations']) && $auth) -{ - sendRequestFTL("forward-dest"); - $return = getResponseFTL(); - $forward_dest = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - if(count($tmp) == 4) - { - $forward_dest[$tmp[3]."|".$tmp[2]] = $tmp[1]; - } - else - { - $forward_dest[$tmp[2]] = $tmp[1]; - } - } - - $result = array('forward_destinations' => $forward_dest); - $data = array_merge($data, $result); -} - -if (isset($_GET['getQueryTypes']) && $auth) -{ - sendRequestFTL("querytypes"); - $return = getResponseFTL(); - $querytypes = array(); - foreach($return as $ret) - { - $tmp = explode(": ",$ret); - $querytypes[$tmp[0]] = $tmp[1]; - } - - $result = array('querytypes' => $querytypes); - $data = array_merge($data, $result); -} - -if (isset($_GET['getAllQueries']) && $auth) -{ - if(isset($_GET['from']) && isset($_GET['until'])) - { - // Get limited time interval - sendRequestFTL("getallqueries-time ".$_GET['from']." ".$_GET['until']); - } - else if(isset($_GET['domain'])) - { - // Get specific domain only - sendRequestFTL("getallqueries-domain ".$_GET['domain']); - } - else if(isset($_GET['client'])) - { - // Get specific client only - sendRequestFTL("getallqueries-client ".$_GET['client']); - } - else - { - // Get all queries - sendRequestFTL("getallqueries"); - } - $return = getResponseFTL(); - $allQueries = array(); - foreach($return as $line) - { - $tmp = explode(" ",$line); - array_push($allQueries,$tmp); - } - - $result = array('data' => $allQueries); - $data = array_merge($data, $result); -} - -if (isset($_GET['enable'], $_GET['token']) && $auth) { - check_csrf($_GET['token']); - exec('sudo pihole enable'); - $data = array_merge($data, array("status" => "enabled")); -} -elseif (isset($_GET['disable'], $_GET['token']) && $auth) { - check_csrf($_GET['token']); - $disable = intval($_GET['disable']); - // intval returns the integer value on success, or 0 on failure - if($disable > 0) - { - exec("sudo pihole disable ".$disable."s"); - } - else - { - exec('sudo pihole disable'); - } - $data = array_merge($data, array("status" => "disabled")); -} - -if(isset($_GET["recentBlocked"])) -{ - sendRequestFTL("recentBlocked"); - echo getResponseFTL()[0]; - unset($data); -} - -if(isset($data)) -{ - echo json_encode($data); -} - -disconnectFTL(); ?> diff --git a/api_FTL.php b/api_FTL.php new file mode 100644 index 00000000..045c3978 --- /dev/null +++ b/api_FTL.php @@ -0,0 +1,233 @@ + $domains_over_time, + 'ads_over_time' => $ads_over_time); + $data = array_merge($data, $result); +} + +if (isset($_GET['topItems']) && $auth) +{ + if(is_numeric($_GET['topItems'])) + { + sendRequestFTL("top-domains ".$_GET['topItems']); + } + else + { + sendRequestFTL("top-domains"); + } + + $return = getResponseFTL(); + $top_queries = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + $top_queries[$tmp[2]] = $tmp[1]; + } + + if(is_numeric($_GET['topItems'])) + { + sendRequestFTL("top-ads ".$_GET['topItems']); + } + else + { + sendRequestFTL("top-ads"); + } + + $return = getResponseFTL(); + $top_ads = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + $top_ads[$tmp[2]] = $tmp[1]; + } + + $result = array('top_queries' => $top_queries, + 'top_ads' => $top_ads); + + $data = array_merge($data, $result); +} + +if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth) +{ + sendRequestFTL("top-clients"); + $return = getResponseFTL(); + $top_clients = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + if(count($tmp) == 4) + { + $top_clients[$tmp[3]."|".$tmp[2]] = $tmp[1]; + } + else + { + $top_clients[$tmp[2]] = $tmp[1]; + } + } + + $result = array('top_sources' => $top_clients); + $data = array_merge($data, $result); +} + +if (isset($_GET['getForwardDestinations']) && $auth) +{ + sendRequestFTL("forward-dest"); + $return = getResponseFTL(); + $forward_dest = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + if(count($tmp) == 4) + { + $forward_dest[$tmp[3]."|".$tmp[2]] = $tmp[1]; + } + else + { + $forward_dest[$tmp[2]] = $tmp[1]; + } + } + + $result = array('forward_destinations' => $forward_dest); + $data = array_merge($data, $result); +} + +if (isset($_GET['getQueryTypes']) && $auth) +{ + sendRequestFTL("querytypes"); + $return = getResponseFTL(); + $querytypes = array(); + foreach($return as $ret) + { + $tmp = explode(": ",$ret); + $querytypes[$tmp[0]] = $tmp[1]; + } + + $result = array('querytypes' => $querytypes); + $data = array_merge($data, $result); +} + +if (isset($_GET['getAllQueries']) && $auth) +{ + if(isset($_GET['from']) && isset($_GET['until'])) + { + // Get limited time interval + sendRequestFTL("getallqueries-time ".$_GET['from']." ".$_GET['until']); + } + else if(isset($_GET['domain'])) + { + // Get specific domain only + sendRequestFTL("getallqueries-domain ".$_GET['domain']); + } + else if(isset($_GET['client'])) + { + // Get specific client only + sendRequestFTL("getallqueries-client ".$_GET['client']); + } + else + { + // Get all queries + sendRequestFTL("getallqueries"); + } + $return = getResponseFTL(); + $allQueries = array(); + foreach($return as $line) + { + $tmp = explode(" ",$line); + array_push($allQueries,$tmp); + } + + $result = array('data' => $allQueries); + $data = array_merge($data, $result); +} + +if (isset($_GET['enable'], $_GET['token']) && $auth) { + check_csrf($_GET['token']); + exec('sudo pihole enable'); + $data = array_merge($data, array("status" => "enabled")); +} +elseif (isset($_GET['disable'], $_GET['token']) && $auth) { + check_csrf($_GET['token']); + $disable = intval($_GET['disable']); + // intval returns the integer value on success, or 0 on failure + if($disable > 0) + { + exec("sudo pihole disable ".$disable."s"); + } + else + { + exec('sudo pihole disable'); + } + $data = array_merge($data, array("status" => "disabled")); +} + +if(isset($_GET["recentBlocked"])) +{ + sendRequestFTL("recentBlocked"); + echo getResponseFTL()[0]; + unset($data); +} + +if(isset($data)) +{ + echo json_encode($data); +} + +disconnectFTL(); +?> From 91c563692e84650e64e7722d9fe480eb5ef4690e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 19 Feb 2017 14:58:20 +0100 Subject: [PATCH 13/30] Codacy --- scripts/pi-hole/js/index.js | 8 ++++++++ scripts/pi-hole/js/queries.js | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 9bd1401a..002fec42 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -107,9 +107,13 @@ function updateQueryTypes() { var v = [], c = [], k = []; // Collect values and colors, and labels if(data.hasOwnProperty("querytypes")) + { iter = data.querytypes; + } else + { iter = data; + } $.each(iter, function(key , value) { v.push(value); c.push(colors.shift()); @@ -190,9 +194,13 @@ function updateForwardDestinations() { var v = [], c = [], k = [], iter; // Collect values and colors, immediately push individual labels if(data.hasOwnProperty("forward_destinations")) + { iter = data.forward_destinations; + } else + { iter = data; + } $.each(iter, function(key , value) { v.push(value); c.push(colors.shift()); diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index f5abc74e..073ed50a 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -89,7 +89,7 @@ function handleAjaxError( xhr, textStatus, error ) { } else { - alert( "An unknown error occured while loading the data." ); + alert( "An unknown error occured while loading the data.\n"+xhr.responseText ); } $("#all-queries_processing").hide(); tableApi.clear(); From 8a0839f4398b057a20de499dba5afe683a8636fa Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 19 Feb 2017 19:05:06 +0100 Subject: [PATCH 14/30] Small security update to the indicidual API files --- api_FTL.php | 6 ++++++ api_PHP.php | 7 ++++++- scripts/pi-hole/js/index.js | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/api_FTL.php b/api_FTL.php index 045c3978..6711d545 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -6,6 +6,12 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license */ + +if(!isset($api)) +{ + die("Direct call to api_FTL.php is not allowed!"); +} + require "scripts/pi-hole/php/password.php"; require "scripts/pi-hole/php/auth.php"; diff --git a/api_PHP.php b/api_PHP.php index eea77080..f8f65876 100644 --- a/api_PHP.php +++ b/api_PHP.php @@ -6,7 +6,12 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ - $api = true; + + if(!isset($api)) + { + die("Direct call to api_PHP.php is not allowed!"); + } + require "scripts/pi-hole/php/password.php"; require "scripts/pi-hole/php/auth.php"; diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 002fec42..e9226731 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -104,7 +104,7 @@ function updateQueryTypes() { var colors = []; // Get colors from AdminLTE $.each($.AdminLTE.options.colors, function(key, value) { colors.push(value); }); - var v = [], c = [], k = []; + var v = [], c = [], k = [], iter; // Collect values and colors, and labels if(data.hasOwnProperty("querytypes")) { From dc61eb82d4b33df02ba68432e00a334453a80331 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 19 Feb 2017 19:10:49 +0100 Subject: [PATCH 15/30] Show summary if no arguments are passed to API --- api_FTL.php | 2 +- scripts/pi-hole/php/data.php | 3 +-- scripts/pi-hole/php/func.php | 4 +--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/api_FTL.php b/api_FTL.php index 6711d545..2fec16a0 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -21,7 +21,7 @@ header('Content-type: application/json'); $data = []; -if (isset($_GET['summary']) || isset($_GET['summaryRaw'])) +if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) { sendRequestFTL("stats"); $return = getResponseFTL(); diff --git a/scripts/pi-hole/php/data.php b/scripts/pi-hole/php/data.php index 289d3131..f7226698 100644 --- a/scripts/pi-hole/php/data.php +++ b/scripts/pi-hole/php/data.php @@ -4,9 +4,8 @@ * 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. */ ?> +* Please see LICENSE file for your rights under this license. */ - - - Date: Sun, 19 Feb 2017 19:25:04 +0100 Subject: [PATCH 16/30] Make FTL healtcheck box collapsed on settings page --- settings.php | 61 +++++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/settings.php b/settings.php index 99f96aaf..c911ba34 100644 --- a/settings.php +++ b/settings.php @@ -736,35 +736,6 @@
  • */ ?> - -
    -
    -

    FTL status (Running, PID: Not running)

    -
    -
    - Time FTL started:
    - User / Group: /
    - Total CPU utilization: %
    - Total CPU time:
    - Memory utilization: %
    - Used memory:
    - -
    -

    System Administration

    @@ -785,6 +756,38 @@ if($FTL)
    + +
    +
    +

    Pi-hole FTL (RunningNot running)

    +
    +
    +
    + Process identifier (PID):
    + Time FTL started:
    + User / Group: / +
    (WARNING: You should not use root as user for running FTL!)
    + Total CPU utilization: %
    + Total CPU time:
    + Memory utilization: %
    + Used memory:
    + +
    +

    Pi-hole Teleporter

    From c3047f76c9f01dd9ee9613bb537ce619d62818ae Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 20 Feb 2017 11:33:24 +0100 Subject: [PATCH 17/30] Add api.php?version --- api_FTL.php | 12 +++++++++++- api_PHP.php | 6 ++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/api_FTL.php b/api_FTL.php index 2fec16a0..f4d53bc9 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -21,6 +21,12 @@ header('Content-type: application/json'); $data = []; +if (isset($_GET['version'])) { + $version = array(); + $version["version"] = "FTL"; + $data = array_merge($data, $version); +} + if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) { sendRequestFTL("stats"); @@ -230,7 +236,11 @@ if(isset($_GET["recentBlocked"])) unset($data); } -if(isset($data)) +if(isset($_GET["jsonForceObject"])) +{ + echo json_encode($data, JSON_FORCE_OBJECT); +} +else { echo json_encode($data); } diff --git a/api_PHP.php b/api_PHP.php index f8f65876..a71e9401 100644 --- a/api_PHP.php +++ b/api_PHP.php @@ -24,6 +24,12 @@ // Non-Auth + if (isset($_GET['version'])) { + $version = array(); + $version["version"] = "PHP"; + $data = array_merge($data, $version); + } + if (isset($_GET['summaryRaw'])) { $data = array_merge($data, getSummaryData()); } From 3060279c68c5d4efe665e95dc2c5df06d621a124 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 20 Feb 2017 11:39:11 +0100 Subject: [PATCH 18/30] Extend README text --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6e7e1668..7675162a 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ A read-only API can be accessed at `/admin/api.php`. With either no parameters o There are many more parameters, such as `summaryRaw`, `overTimeData10mins`, `topItems`, ` topClients` or `getQuerySources`, `getQueryTypes`, `getForwardDestinations`, and finally `getAllQueries`. Together with a token it is also possible to enable and disable (also with a set timeout) blocking via the API +The API returns more information (in a slighly different format if `FTL` is running) - it supports a fall-back to the "old" PHP API if `FTL` is not running. Test the version by using the parameter `version`. +

    We use BrowserStack for multi-platform multi-browser testing. From e3d791abb65c1a4634a8b6eccc6af20753d4e0fa Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 20 Feb 2017 12:45:56 +0100 Subject: [PATCH 19/30] Add type / version --- api_FTL.php | 8 +++++--- api_PHP.php | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/api_FTL.php b/api_FTL.php index f4d53bc9..90a8af20 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -21,10 +21,12 @@ header('Content-type: application/json'); $data = []; +if (isset($_GET['type'])) { + $data["type"] = "FTL"; +} + if (isset($_GET['version'])) { - $version = array(); - $version["version"] = "FTL"; - $data = array_merge($data, $version); + $data["version"] = 2; } if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) diff --git a/api_PHP.php b/api_PHP.php index a71e9401..4f75a0f8 100644 --- a/api_PHP.php +++ b/api_PHP.php @@ -24,10 +24,12 @@ // Non-Auth + if (isset($_GET['type'])) { + $data["type"] = "PHP"; + } + if (isset($_GET['version'])) { - $version = array(); - $version["version"] = "PHP"; - $data = array_merge($data, $version); + $data["version"] = 1; } if (isset($_GET['summaryRaw'])) { From d82f2ef474787545296a3fd9708775889caf0e7b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 20 Feb 2017 12:46:38 +0100 Subject: [PATCH 20/30] Updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7675162a..e3f3b257 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ A read-only API can be accessed at `/admin/api.php`. With either no parameters o There are many more parameters, such as `summaryRaw`, `overTimeData10mins`, `topItems`, ` topClients` or `getQuerySources`, `getQueryTypes`, `getForwardDestinations`, and finally `getAllQueries`. Together with a token it is also possible to enable and disable (also with a set timeout) blocking via the API -The API returns more information (in a slighly different format if `FTL` is running) - it supports a fall-back to the "old" PHP API if `FTL` is not running. Test the version by using the parameter `version`. +The API returns more information (in a slighly different format if `FTL` is running) - it supports a fall-back to the "old" PHP API if `FTL` is not running. Test the type and/or version of the API by using the parameter `type` and `version`.

    From edd52d0de45253993007712ff3854bb32a683333 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 22 Feb 2017 11:09:08 +0100 Subject: [PATCH 21/30] Show FTL version on Settings page --- settings.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/settings.php b/settings.php index c911ba34..75ff671b 100644 --- a/settings.php +++ b/settings.php @@ -760,6 +760,7 @@ if($FTL) { $parts = preg_split('/\s+/',trim(exec("ps -p `cat /var/run/pihole-FTL.pid` -o pid,start,vsz,rss,euser,egroup,cputime,%cpu,%mem"))); + $FTLversion = exec("/usr/bin/pihole-FTL version"); // pid // time the command started. If the process was started less than 24 hours ago, the output format is "HH:MM:SS", else it is " Mmm dd" (where Mmm is a three-letter month name). // virtual memory size of the process in KiB @@ -777,7 +778,8 @@ if($FTL)
    - Process identifier (PID):
    + FTL version:
    + Process identifier (PID):
    Time FTL started:
    User / Group: /
    (WARNING: You should not use root as user for running FTL!)
    From d58439e0d9b50390bba7d44d9faf159ea2fa45c9 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 22 Feb 2017 11:41:25 +0100 Subject: [PATCH 22/30] Some cosmetics for fresh installed systems --- queries.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/queries.php b/queries.php index 7dd4d1ae..e26df9fe 100644 --- a/queries.php +++ b/queries.php @@ -19,21 +19,27 @@ if(isset($setupVars["API_QUERY_LOG_SHOW"])) { if($setupVars["API_QUERY_LOG_SHOW"] === "all") { - $showing = "(showing all queries"; + $showing = "showing all queries"; } elseif($setupVars["API_QUERY_LOG_SHOW"] === "permittedonly") { - $showing = "(showing permitted queries only"; + $showing = "showing permitted queries only"; } elseif($setupVars["API_QUERY_LOG_SHOW"] === "blockedonly") { - $showing = "(showing blocked queries only"; + $showing = "showing blocked queries only"; } elseif($setupVars["API_QUERY_LOG_SHOW"] === "nothing") { - $showing = "(showing no queries at all"; + $showing = "showing no queries at all"; } } +else +{ + // If filter variable is not set, we + // automatically show all queries + $showing = "showing all queries"; +} if(isset($_GET["all"])) { @@ -64,7 +70,11 @@ if(isset($setupVars["API_PRIVACY_MODE"])) $showing .= ", privacy mode enabled"; } } -$showing .= ")"; + +if(strlen($showing) > 0) +{ + $showing = "(".$showing.")"; +} ?> From bb9c669109d622f660edbeaadd72615ba1a05d74 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 23 Feb 2017 13:17:45 +0100 Subject: [PATCH 23/30] Adapt API versions --- api_FTL.php | 2 +- api_PHP.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api_FTL.php b/api_FTL.php index 90a8af20..3ec9b8cc 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -26,7 +26,7 @@ if (isset($_GET['type'])) { } if (isset($_GET['version'])) { - $data["version"] = 2; + $data["version"] = 3; } if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) diff --git a/api_PHP.php b/api_PHP.php index 4f75a0f8..2d9598b2 100644 --- a/api_PHP.php +++ b/api_PHP.php @@ -29,7 +29,7 @@ } if (isset($_GET['version'])) { - $data["version"] = 1; + $data["version"] = 2; } if (isset($_GET['summaryRaw'])) { From a6a4c944b0255a1361c1aec000201152912f8a7f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 23 Feb 2017 14:20:26 +0100 Subject: [PATCH 24/30] Several optimizations to the new FTL API --- api.php | 2 +- api_FTL.php | 46 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/api.php b/api.php index 6683a2e7..e2fe9d25 100644 --- a/api.php +++ b/api.php @@ -9,7 +9,7 @@ $api = true; require("scripts/pi-hole/php/FTL.php"); -if(testFTL()) +if(testFTL() && !isset($_GET["PHP"])) { require("api_FTL.php"); } diff --git a/api_FTL.php b/api_FTL.php index 3ec9b8cc..05baba7d 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -52,7 +52,7 @@ if (isset($_GET['summary']) || isset($_GET['summaryRaw']) || !count($_GET)) } else { - $stats[$tmp[0]] = $tmp[1]; + $stats[$tmp[0]] = intval($tmp[1]); } } $data = array_merge($data,$stats); @@ -68,8 +68,8 @@ if (isset($_GET['overTimeData10mins'])) foreach($return as $line) { $tmp = explode(" ",$line); - $domains_over_time[$tmp[0]] = $tmp[1]; - $ads_over_time[$tmp[0]] = $tmp[2]; + $domains_over_time[$tmp[0]] = intval($tmp[1]); + $ads_over_time[$tmp[0]] = intval($tmp[2]); } $result = array('domains_over_time' => $domains_over_time, 'ads_over_time' => $ads_over_time); @@ -80,7 +80,7 @@ if (isset($_GET['topItems']) && $auth) { if(is_numeric($_GET['topItems'])) { - sendRequestFTL("top-domains ".$_GET['topItems']); + sendRequestFTL("top-domains (".$_GET['topItems'].")"); } else { @@ -92,12 +92,12 @@ if (isset($_GET['topItems']) && $auth) foreach($return as $line) { $tmp = explode(" ",$line); - $top_queries[$tmp[2]] = $tmp[1]; + $top_queries[$tmp[2]] = intval($tmp[1]); } if(is_numeric($_GET['topItems'])) { - sendRequestFTL("top-ads ".$_GET['topItems']); + sendRequestFTL("top-ads (".$_GET['topItems'].")"); } else { @@ -109,7 +109,7 @@ if (isset($_GET['topItems']) && $auth) foreach($return as $line) { $tmp = explode(" ",$line); - $top_ads[$tmp[2]] = $tmp[1]; + $top_ads[$tmp[2]] = intval($tmp[1]); } $result = array('top_queries' => $top_queries, @@ -120,7 +120,25 @@ if (isset($_GET['topItems']) && $auth) if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth) { - sendRequestFTL("top-clients"); + + if(isset($_GET['topClients'])) + { + $number = $_GET['topClients']; + } + elseif(isset($_GET['getQuerySources'])) + { + $number = $_GET['getQuerySources']; + } + + if(is_numeric($number)) + { + sendRequestFTL("top-clients (".$number.")"); + } + else + { + sendRequestFTL("top-clients"); + } + $return = getResponseFTL(); $top_clients = array(); foreach($return as $line) @@ -128,11 +146,11 @@ if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth) $tmp = explode(" ",$line); if(count($tmp) == 4) { - $top_clients[$tmp[3]."|".$tmp[2]] = $tmp[1]; + $top_clients[$tmp[3]."|".$tmp[2]] = intval($tmp[1]); } else { - $top_clients[$tmp[2]] = $tmp[1]; + $top_clients[$tmp[2]] = intval($tmp[1]); } } @@ -150,11 +168,11 @@ if (isset($_GET['getForwardDestinations']) && $auth) $tmp = explode(" ",$line); if(count($tmp) == 4) { - $forward_dest[$tmp[3]."|".$tmp[2]] = $tmp[1]; + $forward_dest[$tmp[3]."|".$tmp[2]] = intval($tmp[1]); } else { - $forward_dest[$tmp[2]] = $tmp[1]; + $forward_dest[$tmp[2]] = intval($tmp[1]); } } @@ -170,7 +188,7 @@ if (isset($_GET['getQueryTypes']) && $auth) foreach($return as $ret) { $tmp = explode(": ",$ret); - $querytypes[$tmp[0]] = $tmp[1]; + $querytypes[$tmp[0]] = intval($tmp[1]); } $result = array('querytypes' => $querytypes); @@ -234,7 +252,7 @@ elseif (isset($_GET['disable'], $_GET['token']) && $auth) { if(isset($_GET["recentBlocked"])) { sendRequestFTL("recentBlocked"); - echo getResponseFTL()[0]; + die(getResponseFTL()[0]); unset($data); } From 874ec8c9db9b13fb9b0c2dd879dc634df40792ce Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 23 Feb 2017 15:03:53 +0100 Subject: [PATCH 25/30] Implemented tailLog into FTL API --- api_FTL.php | 25 +++++++++++++++++++++++++ scripts/pi-hole/js/taillog.js | 4 ++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/api_FTL.php b/api_FTL.php index 05baba7d..2597564b 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -249,6 +249,31 @@ elseif (isset($_GET['disable'], $_GET['token']) && $auth) { $data = array_merge($data, array("status" => "disabled")); } +if (isset($_GET['tailLog']) && $auth) { + // Not using SplFileObject here, since direct + // usage of f-streams will be much faster for + // files as large as the pihole.log + $file = fopen("/var/log/pihole.log","r"); + $offset = intval($_GET['tailLog']); + if($offset > 0) + { + // Seeks on the file pointer where we want to continue reading is known + fseek($file, $offset); + $lines = []; + while (!feof($file)) + array_push($lines,fgets($file)); + $data = array_merge($data, array("offset" => ftell($file), "lines" => $lines)); + } + else + { + // Locate the current position of the file read/write pointer + fseek($file, -1, SEEK_END); + // Add one to skip the very last "\n" in the log file + $data = array_merge($data, array("offset" => ftell($file)+1)); + } + fclose($file); +} + if(isset($_GET["recentBlocked"])) { sendRequestFTL("recentBlocked"); diff --git a/scripts/pi-hole/js/taillog.js b/scripts/pi-hole/js/taillog.js index 32272de9..ffe370fc 100644 --- a/scripts/pi-hole/js/taillog.js +++ b/scripts/pi-hole/js/taillog.js @@ -12,7 +12,7 @@ var interval = 200; // Function that asks the API for new data function reloadData(){ clearTimeout(timer); - $.getJSON("api_PHP.php?tailLog="+offset, function (data) + $.getJSON("api.php?tailLog="+offset, function (data) { offset = data["offset"]; pre.append(data["lines"]); @@ -27,7 +27,7 @@ function reloadData(){ $(function(){ // Get offset at first loading of page - $.getJSON("api_PHP.php?tailLog", function (data) + $.getJSON("api.php?tailLog", function (data) { offset = data["offset"]; }); From 9471594bdfa2c49756e9d07515adcec2ebf399c7 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 23 Feb 2017 15:12:23 +0100 Subject: [PATCH 26/30] Move tailLog function into own file --- api_FTL.php | 25 ----------------------- api_PHP.php | 4 ---- scripts/pi-hole/js/taillog.js | 4 ++-- scripts/pi-hole/php/tailLog.php | 35 +++++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 scripts/pi-hole/php/tailLog.php diff --git a/api_FTL.php b/api_FTL.php index 2597564b..05baba7d 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -249,31 +249,6 @@ elseif (isset($_GET['disable'], $_GET['token']) && $auth) { $data = array_merge($data, array("status" => "disabled")); } -if (isset($_GET['tailLog']) && $auth) { - // Not using SplFileObject here, since direct - // usage of f-streams will be much faster for - // files as large as the pihole.log - $file = fopen("/var/log/pihole.log","r"); - $offset = intval($_GET['tailLog']); - if($offset > 0) - { - // Seeks on the file pointer where we want to continue reading is known - fseek($file, $offset); - $lines = []; - while (!feof($file)) - array_push($lines,fgets($file)); - $data = array_merge($data, array("offset" => ftell($file), "lines" => $lines)); - } - else - { - // Locate the current position of the file read/write pointer - fseek($file, -1, SEEK_END); - // Add one to skip the very last "\n" in the log file - $data = array_merge($data, array("offset" => ftell($file)+1)); - } - fclose($file); -} - if(isset($_GET["recentBlocked"])) { sendRequestFTL("recentBlocked"); diff --git a/api_PHP.php b/api_PHP.php index 2d9598b2..f76d60ca 100644 --- a/api_PHP.php +++ b/api_PHP.php @@ -109,10 +109,6 @@ $data = array_merge($data, getGravity()); } - if (isset($_GET['tailLog']) && $auth) { - $data = array_merge($data, tailPiholeLog($_GET['tailLog'])); - } - function filterArray(&$inArray) { $outArray = array(); foreach ($inArray as $key=>$value) { diff --git a/scripts/pi-hole/js/taillog.js b/scripts/pi-hole/js/taillog.js index ffe370fc..7e3b2dc3 100644 --- a/scripts/pi-hole/js/taillog.js +++ b/scripts/pi-hole/js/taillog.js @@ -12,7 +12,7 @@ var interval = 200; // Function that asks the API for new data function reloadData(){ clearTimeout(timer); - $.getJSON("api.php?tailLog="+offset, function (data) + $.getJSON("scripts/pi-hole/php/tailLog.php?offset="+offset, function (data) { offset = data["offset"]; pre.append(data["lines"]); @@ -27,7 +27,7 @@ function reloadData(){ $(function(){ // Get offset at first loading of page - $.getJSON("api.php?tailLog", function (data) + $.getJSON("scripts/pi-hole/php/tailLog.php", function (data) { offset = data["offset"]; }); diff --git a/scripts/pi-hole/php/tailLog.php b/scripts/pi-hole/php/tailLog.php new file mode 100644 index 00000000..0ccc0eef --- /dev/null +++ b/scripts/pi-hole/php/tailLog.php @@ -0,0 +1,35 @@ + 0) + { + // Seeks on the file pointer where we want to continue reading is known + fseek($file, $offset); + $lines = []; + while (!feof($file)) + array_push($lines,fgets($file)); + die(json_encode(array("offset" => ftell($file), "lines" => $lines))); + } +} + +// Locate the current position of the file read/write pointer +fseek($file, -1, SEEK_END); +// Add one to skip the very last "\n" in the log file +die(json_encode(array("offset" => ftell($file)+1))); + +?> From 9fec6bb7fb1189fb501dfc591aa4ce46a42dc537 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 27 Feb 2017 15:42:50 +0100 Subject: [PATCH 27/30] Necessary changes doe to commit 85a7818e7996b44b15d51af26ecf165e672d616b --- scripts/pi-hole/js/index.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index e9226731..d942558f 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -71,11 +71,21 @@ function updateQueriesOverTime() { timeLineChart.data.labels = []; timeLineChart.data.datasets[0].data = []; timeLineChart.data.datasets[1].data = []; - // Add data for each hour that is available + + // Add data for each hour that is available for (var hour in data.ads_over_time[0]) { if ({}.hasOwnProperty.call(data.ads_over_time[0], hour)) { - var h = parseInt(data.domains_over_time[0][hour]); - var d = new Date().setHours(Math.floor(h / 6), 10 * (h % 6), 0, 0); + if(parseInt(data.ads_over_time[0][0]) < 1200) + { + // Fallback - old style + var h = parseInt(data.domains_over_time[0][hour]); + var d = new Date().setHours(Math.floor(h / 6), 10 * (h % 6), 0, 0); + } + else + { + // New style: Get Unix timestamps + var d = new Date(1000*parseInt(data.domains_over_time[0][hour])); + } timeLineChart.data.labels.push(d); timeLineChart.data.datasets[0].data.push(data.domains_over_time[1][hour]); From aacc2153946f98a9f50c1459d2de8f3b19c78865 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 27 Feb 2017 17:20:12 +0100 Subject: [PATCH 28/30] Some simplifications + center 10min interval data in the middle instead of the beginning of the corresponding interval --- api_FTL.php | 4 ++-- scripts/pi-hole/js/index.js | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/api_FTL.php b/api_FTL.php index 05baba7d..524d2d2d 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -68,8 +68,8 @@ if (isset($_GET['overTimeData10mins'])) foreach($return as $line) { $tmp = explode(" ",$line); - $domains_over_time[$tmp[0]] = intval($tmp[1]); - $ads_over_time[$tmp[0]] = intval($tmp[2]); + $domains_over_time[intval($tmp[0])] = intval($tmp[1]); + $ads_over_time[intval($tmp[0])] = intval($tmp[2]); } $result = array('domains_over_time' => $domains_over_time, 'ads_over_time' => $ads_over_time); diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index d942558f..bc963aa3 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -75,16 +75,17 @@ function updateQueriesOverTime() { // Add data for each hour that is available for (var hour in data.ads_over_time[0]) { if ({}.hasOwnProperty.call(data.ads_over_time[0], hour)) { + var d,h; + h = parseInt(data.domains_over_time[0][hour]); if(parseInt(data.ads_over_time[0][0]) < 1200) { // Fallback - old style - var h = parseInt(data.domains_over_time[0][hour]); - var d = new Date().setHours(Math.floor(h / 6), 10 * (h % 6), 0, 0); + d = new Date().setHours(Math.floor(h / 6), 10 * (h % 6), 0, 0); } else { // New style: Get Unix timestamps - var d = new Date(1000*parseInt(data.domains_over_time[0][hour])); + d = new Date(1000*h); } timeLineChart.data.labels.push(d); @@ -350,8 +351,8 @@ $(document).ready(function() { var time = label.match(/(\d?\d):?(\d?\d?)/); var h = parseInt(time[1], 10); var m = parseInt(time[2], 10) || 0; - var from = padNumber(h)+":"+padNumber(m)+":00"; - var to = padNumber(h)+":"+padNumber(m+9)+":59"; + var from = padNumber(h)+":"+padNumber(m-5)+":00"; + var to = padNumber(h)+":"+padNumber(m+4)+":59"; return "Queries from "+from+" to "+to; }, label: function(tooltipItems, data) { @@ -473,8 +474,8 @@ $(document).ready(function() { var label = timeLineChart.data.labels[clickedElementindex]; //get value by index - var from = label/1000; - var until = label/1000 + 600; + var from = label/1000 - 300; + var until = label/1000 + 300; window.location.href = "queries.php?from="+from+"&until="+until; } return false; From 43b60829dc9d42fd77f49de9e7eaee32ab12ad99 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 28 Feb 2017 14:49:43 +0100 Subject: [PATCH 29/30] Copy disable coutdown timer and new (simplified) API token authentification from PHP API to FTL API --- api_FTL.php | 60 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/api_FTL.php b/api_FTL.php index 524d2d2d..e48983a2 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -229,25 +229,47 @@ if (isset($_GET['getAllQueries']) && $auth) $data = array_merge($data, $result); } -if (isset($_GET['enable'], $_GET['token']) && $auth) { - check_csrf($_GET['token']); - exec('sudo pihole enable'); - $data = array_merge($data, array("status" => "enabled")); -} -elseif (isset($_GET['disable'], $_GET['token']) && $auth) { - check_csrf($_GET['token']); - $disable = intval($_GET['disable']); - // intval returns the integer value on success, or 0 on failure - if($disable > 0) - { - exec("sudo pihole disable ".$disable."s"); - } - else - { - exec('sudo pihole disable'); - } - $data = array_merge($data, array("status" => "disabled")); -} + if (isset($_GET['enable']) && $auth) { + if(isset($_GET["auth"])) + { + if($_GET["auth"] !== $pwhash) + die("Not authorized!"); + } + else + { + // Skip token validation if explicit auth string is given + check_csrf($_GET['token']); + } + exec('sudo pihole enable'); + $data = array_merge($data, array("status" => "enabled")); + unlink("../custom_disable_timer"); + } + elseif (isset($_GET['disable']) && $auth) { + if(isset($_GET["auth"])) + { + if($_GET["auth"] !== $pwhash) + die("Not authorized!"); + } + else + { + // Skip token validation if explicit auth string is given + check_csrf($_GET['token']); + } + $disable = intval($_GET['disable']); + // intval returns the integer value on success, or 0 on failure + if($disable > 0) + { + $timestamp = time(); + exec("sudo pihole disable ".$disable."s"); + file_put_contents("../custom_disable_timer",($timestamp+$disable)*1000); + } + else + { + exec('sudo pihole disable'); + unlink("../custom_disable_timer"); + } + $data = array_merge($data, array("status" => "disabled")); + } if(isset($_GET["recentBlocked"])) { From fb8f0a081e19cbfba03b5b4f6b23f202ef29d2cd Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 1 Mar 2017 14:05:38 +0100 Subject: [PATCH 30/30] Move API functions that are neither FTL nor PHP specific (enable, disable) to api.php --- api.php | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ api_FTL.php | 58 ------------------------------------------------- api_PHP.php | 59 -------------------------------------------------- 3 files changed, 62 insertions(+), 117 deletions(-) diff --git a/api.php b/api.php index eaed3c03..d75b55ff 100644 --- a/api.php +++ b/api.php @@ -7,8 +7,61 @@ * Please see LICENSE file for your rights under this license */ $api = true; +header('Content-type: application/json'); require("scripts/pi-hole/php/FTL.php"); +require("scripts/pi-hole/php/password.php"); +require("scripts/pi-hole/php/auth.php"); +check_cors(); + +$data = array(); + +// Common API functions +if (isset($_GET['enable']) && $auth) +{ + if(isset($_GET["auth"])) + { + if($_GET["auth"] !== $pwhash) + die("Not authorized!"); + } + else + { + // Skip token validation if explicit auth string is given + check_csrf($_GET['token']); + } + exec('sudo pihole enable'); + $data = array_merge($data, array("status" => "enabled")); + unlink("../custom_disable_timer"); +} +elseif (isset($_GET['disable']) && $auth) +{ + if(isset($_GET["auth"])) + { + if($_GET["auth"] !== $pwhash) + die("Not authorized!"); + } + else + { + // Skip token validation if explicit auth string is given + check_csrf($_GET['token']); + } + $disable = intval($_GET['disable']); + // intval returns the integer value on success, or 0 on failure + if($disable > 0) + { + $timestamp = time(); + exec("sudo pihole disable ".$disable."s"); + file_put_contents("../custom_disable_timer",($timestamp+$disable)*1000); + } + else + { + exec('sudo pihole disable'); + unlink("../custom_disable_timer"); + } + $data = array_merge($data, array("status" => "disabled")); +} + +// Other API functions if(testFTL() && !isset($_GET["PHP"])) { require("api_FTL.php"); @@ -17,4 +70,13 @@ else { require("api_PHP.php"); } + +if(isset($_GET["jsonForceObject"])) +{ + echo json_encode($data, JSON_FORCE_OBJECT); +} +else +{ + echo json_encode($data); +} ?> diff --git a/api_FTL.php b/api_FTL.php index e48983a2..23419a45 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -12,14 +12,7 @@ if(!isset($api)) die("Direct call to api_FTL.php is not allowed!"); } -require "scripts/pi-hole/php/password.php"; -require "scripts/pi-hole/php/auth.php"; - -check_cors(); $socket = connectFTL("127.0.0.1"); -header('Content-type: application/json'); - -$data = []; if (isset($_GET['type'])) { $data["type"] = "FTL"; @@ -229,48 +222,6 @@ if (isset($_GET['getAllQueries']) && $auth) $data = array_merge($data, $result); } - if (isset($_GET['enable']) && $auth) { - if(isset($_GET["auth"])) - { - if($_GET["auth"] !== $pwhash) - die("Not authorized!"); - } - else - { - // Skip token validation if explicit auth string is given - check_csrf($_GET['token']); - } - exec('sudo pihole enable'); - $data = array_merge($data, array("status" => "enabled")); - unlink("../custom_disable_timer"); - } - elseif (isset($_GET['disable']) && $auth) { - if(isset($_GET["auth"])) - { - if($_GET["auth"] !== $pwhash) - die("Not authorized!"); - } - else - { - // Skip token validation if explicit auth string is given - check_csrf($_GET['token']); - } - $disable = intval($_GET['disable']); - // intval returns the integer value on success, or 0 on failure - if($disable > 0) - { - $timestamp = time(); - exec("sudo pihole disable ".$disable."s"); - file_put_contents("../custom_disable_timer",($timestamp+$disable)*1000); - } - else - { - exec('sudo pihole disable'); - unlink("../custom_disable_timer"); - } - $data = array_merge($data, array("status" => "disabled")); - } - if(isset($_GET["recentBlocked"])) { sendRequestFTL("recentBlocked"); @@ -278,14 +229,5 @@ if(isset($_GET["recentBlocked"])) unset($data); } -if(isset($_GET["jsonForceObject"])) -{ - echo json_encode($data, JSON_FORCE_OBJECT); -} -else -{ - echo json_encode($data); -} - disconnectFTL(); ?> diff --git a/api_PHP.php b/api_PHP.php index c41024e6..dc7a1d2b 100644 --- a/api_PHP.php +++ b/api_PHP.php @@ -12,15 +12,7 @@ die("Direct call to api_PHP.php is not allowed!"); } - require "scripts/pi-hole/php/password.php"; - require "scripts/pi-hole/php/auth.php"; - - check_cors(); - include('scripts/pi-hole/php/data.php'); - header('Content-type: application/json'); - - $data = array(); // Non-Auth @@ -81,48 +73,6 @@ $data = array_merge($data, getAllQueries($_GET['getAllQueries'])); } - if (isset($_GET['enable']) && $auth) { - if(isset($_GET["auth"])) - { - if($_GET["auth"] !== $pwhash) - die("Not authorized!"); - } - else - { - // Skip token validation if explicit auth string is given - check_csrf($_GET['token']); - } - exec('sudo pihole enable'); - $data = array_merge($data, array("status" => "enabled")); - unlink("../custom_disable_timer"); - } - elseif (isset($_GET['disable']) && $auth) { - if(isset($_GET["auth"])) - { - if($_GET["auth"] !== $pwhash) - die("Not authorized!"); - } - else - { - // Skip token validation if explicit auth string is given - check_csrf($_GET['token']); - } - $disable = intval($_GET['disable']); - // intval returns the integer value on success, or 0 on failure - if($disable > 0) - { - $timestamp = time(); - exec("sudo pihole disable ".$disable."s"); - file_put_contents("../custom_disable_timer",($timestamp+$disable)*1000); - } - else - { - exec('sudo pihole disable'); - unlink("../custom_disable_timer"); - } - $data = array_merge($data, array("status" => "disabled")); - } - if (isset($_GET['getGravityDomains'])) { $data = array_merge($data, getGravity()); } @@ -140,13 +90,4 @@ } $data = filterArray($data); - - if(isset($_GET["jsonForceObject"])) - { - echo json_encode($data, JSON_FORCE_OBJECT); - } - else - { - echo json_encode($data); - } ?>