diff --git a/administrator/components/com_jedchecker/libraries/rules/gpl.ini b/administrator/components/com_jedchecker/libraries/rules/gpl.ini index 2697061..5056d65 100644 --- a/administrator/components/com_jedchecker/libraries/rules/gpl.ini +++ b/administrator/components/com_jedchecker/libraries/rules/gpl.ini @@ -11,4 +11,4 @@ ; ; The valid constants to search for -constants="BSD" +constants="" diff --git a/administrator/components/com_jedchecker/libraries/rules/gpl.php b/administrator/components/com_jedchecker/libraries/rules/gpl.php index cbbe554..c7cf1d8 100644 --- a/administrator/components/com_jedchecker/libraries/rules/gpl.php +++ b/administrator/components/com_jedchecker/libraries/rules/gpl.php @@ -13,14 +13,13 @@ defined('_JEXEC') or die('Restricted access'); // Include the rule base class -require_once(JPATH_COMPONENT_ADMINISTRATOR . '/models/rule.php'); +require_once JPATH_COMPONENT_ADMINISTRATOR . '/models/rule.php'; /** * class JedcheckerRulesGpl * - * This class searches all files for the _JEXEC check - * which prevents direct file access. + * This class searches all files for the GPL/compatible licenses * * @since 1.0 */ @@ -47,6 +46,20 @@ class JedcheckerRulesGpl extends JEDcheckerRule */ protected $description = 'COM_JEDCHECKER_RULE_PH1_DESC'; + /** + * Regular expression to match GPL licenses. + * + * @var string + */ + protected $regexGPLLicenses; + + /** + * Regular expression to match GPL-compatible licenses. + * + * @var string + */ + protected $regexCompatLicenses; + /** * Initiates the file search and check * @@ -54,6 +67,9 @@ class JedcheckerRulesGpl extends JEDcheckerRule */ public function check() { + // Prepare regexp + $this->init(); + // Find all php files of the extension $files = JFolder::files($this->basedir, '\.php$', true, true); @@ -70,7 +86,99 @@ class JedcheckerRulesGpl extends JEDcheckerRule } /** - * Reads a file and searches for the _JEXEC statement + * Initialization (prepare regular expressions) + * + * @return void + */ + protected function init() + { + $GPLLicenses = (array) file(__DIR__ . '/gpl/gnu.txt'); + $this->regexGPLLicenses = $this->generateRegexp($GPLLicenses); + + $compatLicenses = (array) file(__DIR__ . '/gpl/compat.txt'); + + $extraLicenses = $this->params->get('constants'); + $extraLicenses = explode(',', $extraLicenses); + + $compatLicenses = array_merge($compatLicenses, $extraLicenses); + + $this->regexCompatLicenses = $this->generateRegexp($compatLicenses); + } + + /** + * Generate regular expression to match the given list of license names + * @param array $lines List of license names + * + * @return string + */ + protected function generateRegexp($lines) + { + $titles = array(); + $ids = array(); + + foreach ($lines as $line) + { + $line = trim($line); + + if ($line === '' || $line[0] === '#') + { + // Skip empty and commented lines + continue; + } + + $title = $line; + if (substr($line, -1, 1) === ')') + { + // Extract identifier + $pos = strrpos($line, '('); + + if ($pos !== false) + { + $title = trim(substr($line, 0, $pos)); + + $id = trim(substr($line, $pos + 1, -1)); + + if ($id !== '') + { + $id = preg_quote($id, '#'); + $ids[$id] = 1; + } + } + } + + if ($title !== '') + { + $title = preg_quote($title, '#'); + + // Expand vN.N to different version formats + $title = preg_replace('/(?<=\S)\s+v(?=\d)/', ',?\s+(?:v\.?\s*|version\s+)?', $title); + + $title = preg_replace('/\s+/', '\s+', $title); + + $titles[$title] = 1; + } + } + + if (count($titles) === 0) + { + return null; + } + + $titles = implode('|', array_keys($titles)); + + if (count($ids)) + { + $ids = implode('|', array_keys($ids)); + $titles .= + '|\blicense\b.+?(?:' . $ids . ')' . + '|\b(?:' . $ids . ')\s+license\b'; + } + + return '#^.*?(?:' . $titles . ').*?$#im'; + } + + /** + * Reads a file and searches for its license * * @param string $file - The path to the file * @@ -78,72 +186,46 @@ class JedcheckerRulesGpl extends JEDcheckerRule */ protected function find($file) { - $content = (array) file($file); + $content = php_strip_whitespace($file); - // Get the constants to look for - $licenses = $this->params->get('constants'); - $licenses = explode(',', $licenses); - - $hascode = 0; - - foreach ($content AS $key => $line) + // Check the file is empty, comments-only, or nonexecutable + if (empty($content) || preg_match('#^<\?php\s+(?:$|(?:die|exit)(?:\(\))?;)#', $content)) { - $tline = trim($line); - - if ($tline == '' || $tline == '') - { - continue; - } - - if ($tline['0'] != '/' && $tline['0'] != '*') - { - $hascode = 1; - } - - // Search for GPL license - $gpl = stripos($line, 'GPL'); - $gnu = stripos($line, 'GNU'); - $gpl_long = stripos($line, 'general public license'); - - if ($gpl || $gnu || $gpl_long) - { - $this->report->addInfo( - $file, - JText::_('COM_JEDCHECKER_PH1_LICENSE_FOUND') . ':' . '' . $line . '', - $key - ); - - return true; - } - - // Search for the constant name - foreach ($licenses AS $license) - { - $license = trim($license); - - // Search for the license - $found = strpos($line, $license); - - // Skip the line if the license is not found - if ($found === false) - { - continue; - } - else - { - $this->report->addInfo( - $file, - JText::_('COM_JEDCHECKER_GPL_COMPATIBLE_LICENSE_WAS_FOUND') . ':' . '' . $line . '', - $key - ); - - return true; - } - } + return true; } - unset($content); + // Reload file to preserve comments and line numbers + $content = file_get_contents($file); - return $hascode ? false : true; + // Remove leading "*" characters from phpDoc-like comments + $content = preg_replace('/^\s*\*/m', '', $content); + + if (preg_match($this->regexGPLLicenses, $content, $match, PREG_OFFSET_CAPTURE)) + { + $lineno = substr_count($content, "\n", 0, $match[0][1]) + 1; + $this->report->addInfo( + $file, + JText::_('COM_JEDCHECKER_PH1_LICENSE_FOUND'), + $lineno, + $match[0][0] + ); + + return true; + } + + if (preg_match($this->regexCompatLicenses, $content, $match, PREG_OFFSET_CAPTURE)) + { + $lineno = substr_count($content, "\n", 0, $match[0][1]) + 1; + $this->report->addWarning( + $file, + JText::_('COM_JEDCHECKER_GPL_COMPATIBLE_LICENSE_WAS_FOUND'), + $lineno, + $match[0][0] + ); + + return true; + } + + return false; } } diff --git a/administrator/components/com_jedchecker/libraries/rules/gpl/compat.txt b/administrator/components/com_jedchecker/libraries/rules/gpl/compat.txt new file mode 100644 index 0000000..add378f --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/gpl/compat.txt @@ -0,0 +1,81 @@ +# Based on: +# https://www.gnu.org/licenses/license-list.en.html +# https://opensource.org/licenses/alphabetical +# https://spdx.org/licenses/ + +# Comments are marked with the "#" character in the first position of the line + +# Each line contains the full name of the license with the optional abbreviation in parenthesis + +# The version of the license (if presented) should be written in the form +# [space][the letter "v"][digit(s)] +# (see examples below) + + +# BSD Licenses +0-clause BSD License (0BSD) +BSD Zero Clause License +Zero-Clause BSD / Free Public License v1.0.0 +1-clause BSD License (BSD-1-Clause) +BSD 1-Clause License +2-clause BSD License (BSD-2-Clause) +BSD v2 (BSDv2) +BSD 2-Clause "Simplified" License +BSD-2-Clause Plus Patent License (BSD-2-Clause-Patent) +FreeBSD License +3-clause BSD License (BSD-3-Clause) +BSD v3 (BSDv3) +BSD 3-Clause "New" or "Revised" License +Modified BSD License +Clear BSD License + +# Other GPL-compatible Licenses +Apache License v2.0 (Apache-2.0) +Artistic License v2.0 (Artistic-2.0) +Berkeley Database License +Boost Software License (BSL-1.0) +CeCILL v2 +CeCILL License v2.1 (CECILL-2.1) +CeCILL Free Software License Agreement v2.1 +Clarified Artistic License +Cryptix General License +eCos License v2.0 (eCos-2.0) +Educational Community License v2.0 (ECL-2.0) +Eiffel Forum License v2.0 (EFL-2.0) +EU DataGrid Software License (EUDatagrid) +Expat License +Freetype Project License +Historical Permission Notice and Disclaimer (HPND) +Independent JPEG Group License +Intel Open Source License (Intel) +ISC License (ISC) +MIT License (MIT) +Mozilla Public License v2.0 (MPL-2.0) +OpenLDAP License v2.7 +OpenLDAP Public License v2.8 (OLDAP-2.8) +Open LDAP Public License v2.8 +Public Domain +Python License (Python-2.0) +SGI Free Software License B v2.0 +Sleepycat License (Sleepycat) +Sleepycat Software Product License +Standard ML of New Jersey Copyright License +Unicode Data Files and Software License (Unicode-DFS-2016) +Unicode License Agreement - Data Files and Software +Unicode, Inc. License Agreement for Data Files and Software +Universal Permissive License (UPL) +Universal Permissive License v1.0 (UPL-1.0) +University of Illinois/NCSA Open Source License (NCSA) +NCSA/University of Illinois Open Source License +Unlicense +W3C License +W3C Software Notice and License +WTFPL v2 +WxWidgets Library License +wxWindows Library License (WXwindows) +X11 License +XFree86 v1.1 License +Zope Public License v2.0 (ZPL-2.0) +Zope Public License v2.1 +zlib/libpng License (Zlib) +zlib License diff --git a/administrator/components/com_jedchecker/libraries/rules/gpl/gnu.txt b/administrator/components/com_jedchecker/libraries/rules/gpl/gnu.txt new file mode 100644 index 0000000..8e2979f --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/gpl/gnu.txt @@ -0,0 +1,38 @@ +# GPL Licenses + +# Based on: +# https://www.gnu.org/licenses/license-list.en.html +# https://opensource.org/licenses/alphabetical +# https://spdx.org/licenses/ + +# Comments are marked with the "#" character in the first position of the line + +# Each line contains the full name of the license with the optional abbreviation in parenthesis + +# The version of the license (if presented) should be written in the form +# [space][the letter "v"][digit(s)] +# (see examples below) + + +GNU General Public License +GNU GPL +GNU/GPL +GNU Lesser General Public License +GNU LGPL +GNU/LGPL + +GNU General Public License v2 (GPL-2.0) +//www.gnu.org/licenses/gpl-2.0.html + +GNU General Public License v3 (GPL-3.0) +//www.gnu.org/licenses/gpl-3.0.html + +GNU Affero General Public License v3 (AGPL-3.0) +GNU Affero General Public License (AGPL) v3 +GNU Library General Public License v2 (LGPL-2.0) +GNU Lesser General Public License v2.1 (LGPL-2.1) +GNU Lesser General Public License (LGPL) v2.1 +GNU Lesser General Public License v3 (LGPL-3.0) +GNU Lesser General Public License (LGPL) v3 + +GNU All-Permissive License