2012-12-13 10:33:17 +00:00
|
|
|
<?php
|
|
|
|
/**
|
2019-03-09 19:44:14 +00:00
|
|
|
* @package Joomla.JEDChecker
|
|
|
|
*
|
2019-03-10 16:09:42 +00:00
|
|
|
* @copyright Copyright (C) 2017 - 2019 Open Source Matters, Inc. All rights reserved.
|
|
|
|
* Copyright (C) 2008 - 2016 compojoom.com . All rights reserved.
|
2019-03-10 08:49:52 +00:00
|
|
|
* @author Daniel Dimitrov <daniel@compojoom.com>
|
|
|
|
* eaxs <support@projectfork.net>
|
|
|
|
*
|
2019-03-09 19:44:14 +00:00
|
|
|
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
2012-12-13 10:33:17 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
defined('_JEXEC') or die('Restricted access');
|
|
|
|
|
|
|
|
|
|
|
|
// Include the rule base class
|
2013-11-05 20:17:39 +00:00
|
|
|
require_once JPATH_COMPONENT_ADMINISTRATOR . '/models/rule.php';
|
2012-12-13 10:33:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-11-05 20:17:39 +00:00
|
|
|
* class JedcheckerRulesXMLinfo
|
2012-12-13 10:33:17 +00:00
|
|
|
*
|
2021-04-04 10:50:33 +00:00
|
|
|
* This class searches all xml manifests for specific tags
|
2013-11-05 20:17:39 +00:00
|
|
|
*
|
|
|
|
* @since 1.0
|
2012-12-13 10:33:17 +00:00
|
|
|
*/
|
2013-11-05 20:17:39 +00:00
|
|
|
class JedcheckerRulesXMLinfo extends JEDcheckerRule
|
2012-12-13 10:33:17 +00:00
|
|
|
{
|
2013-11-05 20:17:39 +00:00
|
|
|
/**
|
|
|
|
* The formal ID of this rule. For example: SE1.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $id = 'INFO_XML';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The title or caption of this rule.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $title = 'COM_JEDCHECKER_INFO_XML';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The description of this rule.
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $description = 'COM_JEDCHECKER_INFO_XML_DESC';
|
|
|
|
|
2021-02-23 21:20:09 +00:00
|
|
|
/**
|
|
|
|
* Mapping of the plugin title prefix to the plugin group
|
|
|
|
*
|
|
|
|
* @var string[]
|
|
|
|
*/
|
|
|
|
protected $pluginsGroupMap = array(
|
|
|
|
'button' => 'editors-xtd',
|
|
|
|
'editor' => 'editors',
|
|
|
|
'smartsearch' => 'finder',
|
|
|
|
'twofactorauthentication' => 'twofactorauth'
|
|
|
|
);
|
|
|
|
|
2013-11-05 20:17:39 +00:00
|
|
|
/**
|
|
|
|
* Initiates the search and check
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function check()
|
|
|
|
{
|
|
|
|
// Find all XML files of the extension
|
2021-02-13 20:12:08 +00:00
|
|
|
$files = JFolder::files($this->basedir, '\.xml$', true, true);
|
2013-11-05 20:17:39 +00:00
|
|
|
|
2021-02-02 11:56:56 +00:00
|
|
|
$manifestFound = false;
|
|
|
|
|
2013-11-05 20:17:39 +00:00
|
|
|
// Iterate through all the xml files
|
|
|
|
foreach ($files as $file)
|
|
|
|
{
|
|
|
|
// Try to find the license
|
2021-02-02 11:56:56 +00:00
|
|
|
if ($this->find($file))
|
|
|
|
{
|
|
|
|
$manifestFound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$manifestFound)
|
|
|
|
{
|
|
|
|
$this->report->addError('', JText::_('COM_JEDCHECKER_INFO_XML_NO_MANIFEST'));
|
2013-11-05 20:17:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads a file and searches for the license
|
|
|
|
*
|
|
|
|
* @param string $file - The path to the file
|
|
|
|
*
|
2021-02-02 11:56:56 +00:00
|
|
|
* @return boolean True if the manifest file was found, otherwise False.
|
2013-11-05 20:17:39 +00:00
|
|
|
*/
|
|
|
|
protected function find($file)
|
|
|
|
{
|
2016-05-25 16:19:19 +00:00
|
|
|
$xml = JFactory::getXml($file);
|
2013-11-05 20:17:39 +00:00
|
|
|
|
2021-02-02 11:56:56 +00:00
|
|
|
// Failed to parse the xml file.
|
|
|
|
// Assume that this is not a extension manifest
|
|
|
|
if (!$xml)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2019-03-09 19:44:14 +00:00
|
|
|
|
2021-02-02 11:56:56 +00:00
|
|
|
// Check if this is an extension manifest
|
|
|
|
// 1.5 uses 'install', 1.6+ uses 'extension'
|
2021-04-04 13:06:01 +00:00
|
|
|
if ($xml->getName() === 'install')
|
|
|
|
{
|
|
|
|
$this->report->addWarning($file, JText::sprintf('COM_JEDCHECKER_INFO_XML_MANIFEST_OUTDATED'));
|
|
|
|
}
|
|
|
|
|
2021-02-02 11:56:56 +00:00
|
|
|
if ($xml->getName() !== 'extension')
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2019-03-09 19:44:14 +00:00
|
|
|
|
2021-03-10 22:56:04 +00:00
|
|
|
// Get extension type
|
2021-02-02 11:56:56 +00:00
|
|
|
$type = (string) $xml['type'];
|
2021-03-10 22:56:04 +00:00
|
|
|
|
2021-05-11 18:19:44 +00:00
|
|
|
// Load the language of the extension (if any)
|
|
|
|
$this->loadExtensionLanguage($xml, dirname($file));
|
|
|
|
|
|
|
|
// Get the real extension's name now that the language has been loaded
|
|
|
|
$lang = JFactory::getLanguage();
|
|
|
|
$extensionName = $lang->_((string) $xml->name);
|
|
|
|
|
|
|
|
$info[] = JText::sprintf('COM_JEDCHECKER_INFO_XML_NAME_XML', $extensionName);
|
|
|
|
$info[] = JText::sprintf('COM_JEDCHECKER_INFO_XML_VERSION_XML', (string) $xml->version);
|
|
|
|
$info[] = JText::sprintf('COM_JEDCHECKER_INFO_XML_CREATIONDATE_XML', (string) $xml->creationDate);
|
|
|
|
|
|
|
|
$this->report->addInfo($file, implode('<br />', $info));
|
|
|
|
|
|
|
|
// NM3 - Listing name contains “module” or “plugin”
|
|
|
|
if (preg_match('/module|plugin/i', $extensionName))
|
|
|
|
{
|
|
|
|
$this->report->addError($file, JText::sprintf('COM_JEDCHECKER_INFO_XML_NAME_MODULE_PLUGIN', $extensionName));
|
|
|
|
}
|
|
|
|
|
|
|
|
// The "template" is reserved keyword
|
|
|
|
if (stripos($extensionName, 'template') !== false)
|
|
|
|
{
|
|
|
|
$this->report->addWarning($file, JText::sprintf('COM_JEDCHECKER_INFO_XML_NAME_RESERVED_KEYWORDS', $extensionName));
|
|
|
|
}
|
|
|
|
|
|
|
|
// NM5 - Version in name/title
|
|
|
|
if (preg_match('/(?:\bversion\b|\d\.\d)/i', $extensionName))
|
|
|
|
{
|
|
|
|
$this->report->addError($file, JText::sprintf('COM_JEDCHECKER_INFO_XML_NAME_VERSION', $extensionName));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stripos($extensionName, 'joomla') === 0)
|
|
|
|
{
|
|
|
|
// An extension name can't start with the word "Joomla"
|
|
|
|
$this->report->addError($file, JText::sprintf('COM_JEDCHECKER_INFO_XML_NAME_JOOMLA', $extensionName));
|
|
|
|
}
|
|
|
|
elseif (stripos($extensionName, 'joom') !== false)
|
|
|
|
{
|
|
|
|
// Extensions that use "Joomla" or a derivative of Joomla in the extension name need to be licensed by OSM
|
|
|
|
$this->report->addWarning($file, JText::sprintf('COM_JEDCHECKER_INFO_XML_NAME_JOOMLA_DERIVATIVE', $extensionName));
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->validateDomain($file, (string) $xml->authorUrl);
|
|
|
|
|
|
|
|
if ($type === 'package' && (string) $xml->packagerurl !== (string) $xml->authorUrl)
|
|
|
|
{
|
|
|
|
$this->validateDomain($file, (string) $xml->packagerurl);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($type === 'component' && isset($xml->administration->menu))
|
|
|
|
{
|
|
|
|
$menuName = $lang->_((string) $xml->administration->menu);
|
|
|
|
// Do name the Component's admin menu the same as the extension name
|
|
|
|
if ($extensionName !== $menuName)
|
|
|
|
{
|
|
|
|
$this->report->addWarning($file, JText::sprintf('COM_JEDCHECKER_INFO_XML_NAME_ADMIN_MENU', $menuName, $extensionName));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($type === 'plugin')
|
|
|
|
{
|
|
|
|
// The name of your plugin must comply with the JED naming conventions - plugins in the form “{Type} - {Extension Name}”.
|
|
|
|
$parts = explode(' - ', $extensionName, 2);
|
|
|
|
$extensionNameGroup = isset($parts[1]) ? strtolower(preg_replace('/\s/', '', $parts[0])) : false;
|
|
|
|
$group = (string) $xml['group'];
|
|
|
|
|
|
|
|
if ($extensionNameGroup !== $group && $extensionNameGroup !== str_replace('-', '', $group)
|
|
|
|
&& !(isset($this->pluginsGroupMap[$extensionNameGroup]) && $this->pluginsGroupMap[$extensionNameGroup] === $group)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
$this->report->addWarning($file, JText::sprintf('COM_JEDCHECKER_INFO_XML_NAME_PLUGIN_FORMAT', $extensionName));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// All checks passed. Return true
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Locate and load extension's .sys.ini translation file
|
|
|
|
*
|
|
|
|
* @param SimpleXMLElement $xml Extension's XML manifest
|
|
|
|
* @param string $langDir The basepath
|
|
|
|
* @param string $langTag The language to load
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
* @since 3.0
|
|
|
|
*/
|
|
|
|
protected function loadExtensionLanguage($xml, $langDir, $langTag = 'en-GB')
|
|
|
|
{
|
|
|
|
$type = (string) $xml['type'];
|
|
|
|
|
2021-03-10 22:56:04 +00:00
|
|
|
// Get extension's element name (simulates work of Joomla's installer)
|
2019-03-09 19:44:14 +00:00
|
|
|
|
2021-03-10 22:56:04 +00:00
|
|
|
// Firstly, check for <element> node
|
2021-02-02 11:56:56 +00:00
|
|
|
if (isset($xml->element))
|
|
|
|
{
|
|
|
|
$extension = (string) $xml->element;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-03-10 22:56:04 +00:00
|
|
|
// Otherwise, use <name> node or plugin/module attribute in the <files> section
|
2021-02-02 11:56:56 +00:00
|
|
|
$extension = (string) $xml->name;
|
2019-03-09 19:44:14 +00:00
|
|
|
|
2021-02-23 21:06:02 +00:00
|
|
|
if (isset($xml->files))
|
2021-02-02 11:56:56 +00:00
|
|
|
{
|
2021-02-23 21:06:02 +00:00
|
|
|
foreach ($xml->files->children() as $child)
|
2021-02-02 11:56:56 +00:00
|
|
|
{
|
2021-02-23 21:06:02 +00:00
|
|
|
if (isset($child[$type]))
|
|
|
|
{
|
|
|
|
$extension = (string) $child[$type];
|
|
|
|
}
|
2021-02-02 11:56:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-10 22:56:04 +00:00
|
|
|
|
|
|
|
// Filter extension's element name
|
2021-02-02 11:56:56 +00:00
|
|
|
$extension = strtolower(JFilterInput::getInstance()->clean($extension, 'cmd'));
|
2021-03-10 22:56:04 +00:00
|
|
|
|
|
|
|
// Component's element name starts with com_
|
2021-02-02 11:56:56 +00:00
|
|
|
if ($type === 'component' && strpos($extension, 'com_') !== 0)
|
|
|
|
{
|
|
|
|
$extension = 'com_' . $extension;
|
2019-03-09 19:44:14 +00:00
|
|
|
}
|
|
|
|
|
2021-03-24 12:11:59 +00:00
|
|
|
// Plugin's element name starts with plg_
|
2021-03-10 22:56:04 +00:00
|
|
|
if ($type === 'plugin' && isset($xml['group']) && strpos($extension, 'plg_') !== 0)
|
2021-02-23 21:16:31 +00:00
|
|
|
{
|
|
|
|
$extension = 'plg_' . $xml['group'] . '_' . $extension;
|
2019-03-09 19:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load the language of the extension (if any)
|
|
|
|
$lang = JFactory::getLanguage();
|
|
|
|
|
2021-03-10 22:56:04 +00:00
|
|
|
// Populate list of directories to look for
|
2021-04-04 10:50:33 +00:00
|
|
|
$lookupLangDirs = array();
|
2021-02-23 21:16:31 +00:00
|
|
|
|
2021-02-02 11:56:56 +00:00
|
|
|
if (isset($xml->administration->files['folder']))
|
2013-11-05 20:17:39 +00:00
|
|
|
{
|
2021-04-04 10:50:33 +00:00
|
|
|
$lookupLangDirs[] = trim($xml->administration->files['folder'], '/') . '/language/' . $langTag;
|
2013-11-05 20:17:39 +00:00
|
|
|
}
|
2012-12-13 10:33:17 +00:00
|
|
|
|
2021-02-02 11:56:56 +00:00
|
|
|
if (isset($xml->files['folder']))
|
2013-11-05 20:17:39 +00:00
|
|
|
{
|
2021-04-04 10:50:33 +00:00
|
|
|
$lookupLangDirs[] = trim($xml->files['folder'], '/') . '/language/' . $langTag;
|
2013-11-05 20:17:39 +00:00
|
|
|
}
|
2021-02-23 21:16:31 +00:00
|
|
|
|
2021-04-04 10:50:33 +00:00
|
|
|
$lookupLangDirs[] = 'language/' . $langTag;
|
2021-02-23 21:16:31 +00:00
|
|
|
|
|
|
|
if (isset($xml->administration->languages))
|
2021-02-02 11:56:56 +00:00
|
|
|
{
|
2021-02-23 21:16:31 +00:00
|
|
|
$folder = trim($xml->administration->languages['folder'], '/');
|
|
|
|
|
|
|
|
foreach ($xml->administration->languages->language as $language)
|
|
|
|
{
|
2021-04-04 10:50:33 +00:00
|
|
|
if (trim($language['tag']) === $langTag)
|
2021-02-23 21:16:31 +00:00
|
|
|
{
|
2021-04-04 10:50:33 +00:00
|
|
|
$lookupLangDirs[] = trim($folder . '/' . dirname($language), '/');
|
2021-02-23 21:16:31 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-02 11:56:56 +00:00
|
|
|
}
|
2021-02-23 21:16:31 +00:00
|
|
|
|
|
|
|
if (isset($xml->languages))
|
2012-12-13 10:33:17 +00:00
|
|
|
{
|
2021-02-23 21:16:31 +00:00
|
|
|
$folder = trim($xml->languages['folder'], '/');
|
|
|
|
|
|
|
|
foreach ($xml->languages->language as $language)
|
|
|
|
{
|
2021-04-04 10:50:33 +00:00
|
|
|
if (trim($language['tag']) === $langTag)
|
2021-02-23 21:16:31 +00:00
|
|
|
{
|
2021-04-04 10:50:33 +00:00
|
|
|
$lookupLangDirs[] = trim($folder . '/' . dirname($language), '/');
|
2021-02-23 21:16:31 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-02 11:56:56 +00:00
|
|
|
}
|
2021-02-23 21:16:31 +00:00
|
|
|
|
2021-04-04 10:50:33 +00:00
|
|
|
$lookupLangDirs[] = '';
|
2012-12-13 10:33:17 +00:00
|
|
|
|
2021-04-04 10:50:33 +00:00
|
|
|
$lookupLangDirs = array_unique($lookupLangDirs);
|
2021-02-23 21:16:31 +00:00
|
|
|
|
2021-03-10 22:01:14 +00:00
|
|
|
// Looking for language file in specified directories
|
2021-04-04 10:50:33 +00:00
|
|
|
foreach ($lookupLangDirs as $dir)
|
2012-12-13 10:33:17 +00:00
|
|
|
{
|
2021-04-04 10:50:33 +00:00
|
|
|
$langSysFile =
|
|
|
|
$langDir . '/' .
|
2021-02-18 12:13:51 +00:00
|
|
|
($dir === '' ? '' : $dir . '/') .
|
2021-04-04 10:50:33 +00:00
|
|
|
$langTag. '.' . $extension . '.sys.ini';
|
|
|
|
if (is_file($langSysFile))
|
2021-02-02 11:56:56 +00:00
|
|
|
{
|
|
|
|
$loadLanguage = new ReflectionMethod($lang, 'loadLanguage');
|
|
|
|
$loadLanguage->setAccessible(true);
|
2021-04-04 10:50:33 +00:00
|
|
|
$loadLanguage->invoke($lang, $langSysFile, $extension);
|
2021-02-02 11:56:56 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-12-13 10:33:17 +00:00
|
|
|
}
|
2013-11-05 20:17:39 +00:00
|
|
|
}
|
2021-04-04 11:13:19 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check domain name contains "Joomla"/derivative
|
|
|
|
*
|
|
|
|
* @param string $file Current file name
|
|
|
|
* @param string $url URL to validate
|
2021-04-04 12:08:43 +00:00
|
|
|
*
|
|
|
|
* @return void
|
2021-04-04 11:13:19 +00:00
|
|
|
*/
|
|
|
|
protected function validateDomain($file, $url)
|
|
|
|
{
|
|
|
|
$domain = (strpos($url, '//') === false) ? $url : parse_url(trim($url), PHP_URL_HOST);
|
|
|
|
|
2021-05-08 17:30:05 +00:00
|
|
|
if (stripos($domain, 'joomla') !== false)
|
2021-04-04 11:13:19 +00:00
|
|
|
{
|
|
|
|
// Extensions that use "Joomla" or a derivative of Joomla in the domain name need to be licensed by OSM
|
|
|
|
$this->report->addError($file, JText::sprintf('COM_JEDCHECKER_INFO_XML_URL_JOOMLA_DERIVATIVE', $url, 'https://tm.joomla.org/approved-domains.html'));
|
|
|
|
}
|
|
|
|
}
|
2012-12-13 10:33:17 +00:00
|
|
|
}
|