<?php /* * @package bfNetwork * @copyright Copyright (C) 2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025 Blue Flame Digital Solutions Ltd. All rights reserved. * @license GNU General Public License version 3 or later * * @see https://mySites.guru/ * @see https://www.phil-taylor.com/ * * @author Phil Taylor / Blue Flame Digital Solutions Limited. * * bfNetwork is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * bfNetwork is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package. If not, see http://www.gnu.org/licenses/ * * If you have any questions regarding this code, please contact [email protected] */ use Akeeba\AdminTools\Admin\Model\AdminPassword; use Akeeba\Engine\Platform; use FOF30\Container\Container; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Schema\ChangeSet; use Joomla\CMS\Table\Table; use Joomla\CMS\Uri\Uri; use Joomla\CMS\User\User; use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper; use Joomla\Component\Actionlogs\Administrator\Model\ActionlogsModel; use Joomla\Component\Installer\Administrator\Model\DatabaseModel; use Joomla\Database\Exception\PrepareStatementFailureException; require 'bfEncrypt.php'; /** * If we have got here then we have already passed through decrypting the encrypted header and so we are sure we are now * secure and no one else cannot run the code below. * * I'M NOT PROUD OF THIS FILE - it has grown a lot over the years and there are a lot of workarounds so that we can be * fully compatible from Joomla 1.5.0 to the latest Joomla version, and on all the crazy configurations of webservers. */ final class bfTools { /** * We pass the command to run as a simple integer in our encrypted request this is mainly to speed up the decryption * process, plus its a single digit(or 2) rather than a huge string to remember :-). */ private $_methods = [ 1 => 'getCoreHashFailedFileList', 2 => 'downloadfile', 3 => 'restorefile', 4 => 'getSuspectContentFileList', 5 => 'deleteFile', 6 => 'checkFTPLayer', 7 => 'disableFTPLayer', 8 => 'checkNewDBCredentials', 9 => 'testDbCredentials', 10 => 'getFolderPermissions', 11 => 'setFolderPermissions', 12 => 'getHiddenFolders', 13 => 'deleteFolder', 14 => 'getInstallationFolders', 15 => 'getRecentlyModified', 16 => 'getFilePermissions', 17 => 'setFilePermissions', 18 => 'getErrorLogs', 19 => 'getEncrypted', 20 => 'getUser', 21 => 'setUser', 22 => 'setDbPrefix', 23 => 'setDbCredentials', 24 => 'getBakTables', 25 => 'deleteBakTables', 26 => 'getHtaccessFiles', 27 => 'setHtaccess', 28 => 'getUpdatesCount', 29 => 'getUpdatesDetail', 30 => 'getDotfiles', 31 => 'getArchivefiles', 32 => 'getLargefiles', 33 => 'fixDbSchema', 34 => 'getDbSchemaVersion', 35 => 'checkGoogleFile', 36 => 'toggleOnline', 37 => 'getOfflineStatus', 38 => 'getRobotsFile', 39 => 'saveRobotsFile', 40 => 'getTmpfiles', 41 => 'clearTmpFiles', 42 => 'getFlufffiles', 43 => 'clearFlufffiles', 44 => 'getRenamedToHide', 45 => 'getPhpinwrongplace', 46 => 'doExtensionUpgrade', 47 => 'toggleCache', 48 => 'getCacheStatus', 49 => 'checkAkeebaOutputDirectory', 50 => '', // @deprecated function "eolsecuritystatus" Not Needed in Joomla 4 51 => '', // @deprecated function "applyeolpatch" Not Needed in Joomla 4 52 => 'getMailerFileList', 53 => 'getUploaderFileList', 54 => 'getNonCoreFileList', 55 => 'saveFile', 56 => 'getZerobyteFiles', 57 => 'deleteZerobyteFiles', 58 => 'getMissingCoreFiles', 59 => 'restoreAllMissingFiles', 60 => 'getJoomlaLogTmpConfig', 61 => 'getActivityLog', 62 => 'getBFPluginStatus', 63 => 'getMD5PasswordUsers', 64 => 'getSessionGCStatus', 65 => 'setSessionGCStatus', 66 => 'get2FAPlugins', 67 => 'enable2FAPlugins', 68 => 'setLogTmpPaths', 69 => 'removeLiveSite', 70 => 'getConfiguredLiveSite', 71 => 'getSEFConfig', 72 => 'setSEFConfig', 73 => 'getAdminFilterFixed', 74 => 'setAdminFilterFixed', 75 => 'getPlaintextpasswords', 76 => 'setPlaintextpasswords', 77 => 'getUploadsettingsfixed', 78 => 'setUploadsettingsfixed', 79 => 'getMailtofrienddisabled', 80 => 'setMailtofrienddisabled', 81 => 'getDebugMode', 82 => 'setDebugMode', 83 => 'getErrorReporting', 84 => 'setErrorReporting', 85 => 'getTemplatePositionDisplay', 86 => 'setTemplatePositionDisplay', 87 => 'getCookieSettings', 88 => 'setCookieSettings', 89 => 'getSQLFiles', 90 => 'getCaptchaConfig', 91 => 'setCaptchaConfig', 92 => 'doExtensionInstallFromUrl', 93 => 'getSuperAdmins', 94 => 'getGroups', 95 => 'getUseractionlogenabled', 96 => 'setUseractionlogenabled', 97 => 'getPrivacyConsentPluginEnabled', 98 => 'setPrivacyConsentPluginEnabled', 99 => 'getUseractionlogiplogenabled', 100 => 'setUseractionlogiplogenabled', 101 => 'getSystemLogRotationEnabled', 102 => 'setSystemLogRotationEnabled', 103 => 'getPurge30Days', 104 => 'setPurge30Days', 105 => 'getGzip', 106 => 'setGzip', 107 => 'getSessionlifetime', 108 => 'setSessionlifetime', 109 => 'getPHPinifiles', 110 => 'getModifiedfilessincelastaudit', 111 => 'setAdminHtaccess', 112 => 'getAdminHtaccess', 113 => 'getUserRegistration', 114 => 'setUserRegistration', 115 => 'getPostInstallMessages', 116 => 'getUserFilterFixed', 117 => 'setUserFilterFixed', 118 => 'getHasrootuser', 119 => 'setHasrootuser', 120 => 'getDebuglanguage', 121 => 'setDebuglanguage', 122 => 'setPostInstallMessages', 123 => 'getSkipped', 124 => 'getSendcopytosubmitter', 125 => 'setSendcopytosubmitter', 126 => 'getGuidedTours', 127 => 'setGuidedTours', 128 => 'getFileinfoModuleEnabled', 129 => 'getThumbnails', 130 => 'setThumbnails', 131 => 'getNeedsCompact', 132 => 'getDefaultUpdateChannel', 133 => 'setDefaultUpdateChannel', 136 => 'getSendUpdateEmails', 137 => 'setSendUpdateEmails', 138 => 'getLogEverything', 139 => 'setLogEverything', 140 => 'getLogDeprecated', 141 => 'setLogDeprecated', 998 => 'setRealtimeActivate', 999 => 'getDebugLog', ]; private $fluffFiles = [ '/.drone.yml', '/robots.txt.dist', '/web.config.txt', '/joomla.xml', '/build.xml', '/LICENSE.txt', '/README.txt', '/htaccess.txt', '/LICENSES.php', '/configuration.php-dist', '/CHANGELOG.php', '/COPYRIGHT.php', '/CREDITS.php', '/INSTALL.php', '/LICENSE.php', '/CONTRIBUTING.md', '/phpunit.xml.dist', '/README.md', '/.travis.yml', '/travisci-phpunit.xml', '/images/banners/osmbanner1.png', '/images/banners/osmbanner2.png', '/images/banners/shop-ad-books.jpg', '/images/banners/shop-ad.jpg', '/images/banners/white.png', '/images/headers/blue-flower.jpg', '/images/headers/maple.jpg', '/images/headers/raindrops.jpg', '/images/headers/walden-pond.jpg', '/images/headers/windows.jpg', '/images/joomla_black.gif', '/images/joomla_black.png', '/images/joomla_green.gif', '/images/joomla_logo_black.jpg', '/images/powered_by.png', '/images/sampledata/fruitshop/apple.jpg', '/images/sampledata/fruitshop/bananas_2.jpg', '/images/sampledata/fruitshop/fruits.gif', '/images/sampledata/fruitshop/tamarind.jpg', '/images/sampledata/parks/animals/180px_koala_ag1.jpg', '/images/sampledata/parks/animals/180px_wobbegong.jpg', '/images/sampledata/parks/animals/200px_phyllopteryx_taeniolatus1.jpg', '/images/sampledata/parks/animals/220px_spottedquoll_2005_seanmcclean.jpg', '/images/sampledata/parks/animals/789px_spottedquoll_2005_seanmcclean.jpg', '/images/sampledata/parks/animals/800px_koala_ag1.jpg', '/images/sampledata/parks/animals/800px_phyllopteryx_taeniolatus1.jpg', '/images/sampledata/parks/animals/800px_wobbegong.jpg', '/images/sampledata/parks/banner_cradle.jpg', '/images/sampledata/parks/landscape/120px_pinnacles_western_australia.jpg', '/images/sampledata/parks/landscape/120px_rainforest_bluemountainsnsw.jpg', '/images/sampledata/parks/landscape/180px_ormiston_pound.jpg', '/images/sampledata/parks/landscape/250px_cradle_mountain_seen_from_barn_bluff.jpg', '/images/sampledata/parks/landscape/727px_rainforest_bluemountainsnsw.jpg', '/images/sampledata/parks/landscape/800px_cradle_mountain_seen_from_barn_bluff.jpg', '/images/sampledata/parks/landscape/800px_ormiston_pound.jpg', '/images/sampledata/parks/landscape/800px_pinnacles_western_australia.jpg', '/images/sampledata/parks/parks.gif', ]; /** * Pointer to the Joomla Database Object. * * @var \Joomla\Database\Mysqli\MysqliDriver */ private $_db; private $_container; private $_app; /** * Incoming decrypted vars from the request. * * @var stdClass */ private $_dataObj; /** * I inject the request to the object. * * @param stdClass $dataObj */ public function __construct($dataObj) { // init Joomla if (! defined('BF_JOOMLA_INIT_DONE')) { require_once 'bfInitJoomla.php'; } // Set the request vars $this->_dataObj = $dataObj; // set the db object $this->_container = Factory::getContainer(); $this->_app = Factory::getApplication(); $this->_db = $this->_container->get('DatabaseDriver'); } /** * I'm the controller - I run methods based on the request integer. */ public function run() { if (property_exists($this->_dataObj, 'c')) { $c = (int) $this->_dataObj->c; if (array_key_exists($c, $this->_methods)) { bfLog::log('Calling method ' . $this->_methods[$c]); // call the right method $this->{$this->_methods[$c]} (); } else { // Die if an unknown function bfEncrypt::reply('error', 'No Such method #err1 - ' . $c); } } else { // Die if an unknown function bfEncrypt::reply('error', 'No Such method #err2'); } } public function getDebugLog() { bfEncrypt::reply('success', [ 'data' => bfLog::getLog(), ]); } public function setThumbnails() { $this->_db->setQuery("SELECT params FROM `#__extensions` WHERE `name` = 'plg_filesystem_local'"); $params = $this->_db->LoadResult(); if ('{}' == $params || null == $params || '' == $params) { $params = '{"directories":{"directories0":{"directory":"images","thumbs":1}}}'; } $params = json_decode($params, JSON_OBJECT_AS_ARRAY); if (! array_key_exists('directories', $params)) { $params = json_decode('{"directories":{"directories0":{"directory":"images","thumbs":1}}}', JSON_OBJECT_AS_ARRAY); } foreach ($params['directories'] as $k => $directory) { $params['directories'][$k]['thumbs'] = ($this->_dataObj->s == 'true' ? 1 : 0); } $sql = sprintf("UPDATE `#__extensions` SET `params` = '%s' WHERE `name` = 'plg_filesystem_local'", json_encode($params)); $this->_db->setQuery($sql); $this->_db->execute(); bfEncrypt::reply('success', [ 'sql' => $sql, ]); return $this->getThumbnails(); } public function getThumbnails() { $this->_db->setQuery("SELECT params FROM `#__extensions` WHERE `name` = 'plg_filesystem_local'"); $params = $this->_db->LoadResult(); if ('{}' == $params || null == $params || '' == $params) { return; } $params = json_decode($params, JSON_OBJECT_AS_ARRAY); if (! array_key_exists('directories', $params)) { return; } $thumbsCount = 0; foreach ($params['directories'] as $directory) { if (! array_key_exists('thumbs', $directory)) { continue; } if ($directory['thumbs'] == 1) { $thumbsCount++; } } bfEncrypt::reply('success', [ 'enabled' => (int) $thumbsCount === \count($params['directories']), ]); } public function setSendcopytosubmitter() { // saving params to database $component = ComponentHelper::getComponent('com_contact'); $params = $component->getParams(); $params->set('show_email_copy', 'true' == $this->_dataObj->s ? 0 : 1); $table = Table::getInstance('extension'); $table->load($component->id); $table->bind([ 'params' => $params->toString(), ]); $table->store(); bfEncrypt::reply('success', $this->getSendcopytosubmitter()); } public function getSendcopytosubmitter() { $params = ComponentHelper::getComponent('com_contact')->getParams(); return (int) $params->get('show_email_copy', 0); } public function setPostInstallMessages() { $db = Factory::getDbo(); $query = $db->getQuery(true) ->update($db->quoteName('#__postinstall_messages')) ->set($db->quoteName('enabled') . ' = 0'); $db->setQuery($query); $db->execute(); bfEncrypt::reply('success', $this->getPostInstallMessages()); } public function getPostInstallMessages() { require_once 'bfInitJoomla.php'; require_once JPATH_BASE . '/administrator/components/com_postinstall/src/Helper/PostinstallHelper.php'; require_once JPATH_BASE . '/administrator/components/com_postinstall/src/Model/MessagesModel.php'; $model = Factory::getApplication() ->bootComponent('com_postinstall') ->getMVCFactory() ->createModel('Messages', 'Administrator', [ 'ignore_request' => true, ]); $messages = $model->getItems(); $data = []; foreach ($messages as $k => $item) { $data[] = [ 'title' => Text::_($item->title_key), 'desc' => Text::_($item->description_key), ]; } bfEncrypt::reply('success', $data); } /** * 114 Enable User Registration. */ public function setUserRegistration() { $this->_db->setQuery("SELECT params FROM `#__extensions` WHERE `name` = 'com_users'"); $params = json_decode($this->_db->LoadResult()); if ('true' == $this->_dataObj->s) {// true means, set to the OK value // enabled $params->allowUserRegistration = 0; } else { // disabled $params->allowUserRegistration = 1; } $this->_db->setQuery("UPDATE `#__extensions` set params = '" . json_encode($params) . "' WHERE `name` = 'com_users'"); $this->_db->execute(); return $this->getUserRegistration(); } /** * 113 Get User Registration Enable/Disable status. */ public function getUserRegistration() { bfEncrypt::reply('success', [ 'enabled' => (int) ComponentHelper::getParams('com_users')->get('allowUserRegistration'), ]); } /** * 111 Enable /administrator/.htaccess restriction on apache. */ public function setAdminHtaccess() { require 'lib/AdminTools/Model/AdminPassword/AdminPassword.php'; $p = new AdminPassword(); $p->username = $this->_dataObj->u; $p->password = $this->_dataObj->p; if (! $p->protect()) { bfEncrypt::reply('error', 'Could not enable administrator .htaccess for some unknown reason :-( '); } bfEncrypt::reply('success', [ 'enabled' => 1, 'username' => $this->_dataObj->u, 'password' => $this->_dataObj->p, ]); } /** * 112 Enable /administrator/.htaccess restriction on apache. */ public function getAdminHtaccess() { require 'lib/AdminTools/Model/AdminPassword/AdminPassword.php'; $obj = new AdminPassword(); bfEncrypt::reply('success', [ 'enabled' => $obj->isLocked(), ]); } /** * Get the value of $gzip from /configuration.php. */ public function getGzip() { bfEncrypt::reply('success', [ 'enabled' => (int) Factory::getApplication()->getConfig()->get('gzip', '0'), ]); } /** * set the value of $gzip in /configuration.php. */ public function setGzip() { if ('true' == $this->_dataObj->s) {// true means, set to the OK value return $this->_setConfigParam('gzip', true, 'bool'); } else { return $this->_setConfigParam('gzip', false, 'bool'); } } /** * Generic function for updating the configuration.php file. * * @param string $param * @param string|int $value */ private function _setConfigParam($param, $value, $type = 'int', $return = false) { // Require more complex methods for dealing with files require_once 'bfFilesystem.php'; if ('int' == $type && ! is_int($value)) { if ('true' == $value) { $value = 1; } elseif ('false' == $value) { $value = 0; } else { $value = 0; } } $config = Factory::getApplication()->getConfig(); $config->set($param, $value); $newConfig = $config->toString('PHP', [ 'class' => 'JConfig', ]); /** * On some occasions, Joomla! 1.6+ ignores the configuration and produces "class c". Let's fix this! */ $newConfig = str_replace('class c {', 'class JConfig {', $newConfig); $newConfig = str_replace('namespace c;', '', $newConfig); // Set the correct location of the file $filename = JPATH_ROOT . \DIRECTORY_SEPARATOR . 'configuration.php'; // Try to write out the configuration.php $result = Bf_Filesystem::_write($filename, $newConfig); if (true === $return) { return $result; } if (false !== $result) { bfEncrypt::reply('success', [ $param => $value, ]); } else { bfEncrypt::reply(bfReply::ERROR, [ 'msg' => 'Could Not Save Config value for ' . $param, ]); } } public function getFileinfoModuleEnabled() { bfEncrypt::reply('success', [ 'enabled' => \extension_loaded('fileinfo'), ]); } /** * Get the config for session time. */ public function getSessionlifetime() { bfEncrypt::reply('success', [ 'lifetime' => Factory::getApplication()->getConfig()->get('lifetime', 0), ]); } /** * set the session time to a sensibel recommend default. */ public function setSessionlifetime() { $this->_setConfigParam('lifetime', 15, 'int'); } /** * Get the number of days to delete logs after from the System - User Actions Log. * * @return int */ public function setPurge30Days() { $this->_db->setQuery("SELECT params FROM `#__extensions` WHERE `name` = 'PLG_SYSTEM_ACTIONLOGS'"); $params = json_decode($this->_db->LoadResult()); // enabled $params->logDeletePeriod = 30; $this->_db->setQuery("UPDATE `#__extensions` set params = '" . json_encode($params) . "' WHERE `name` = 'PLG_SYSTEM_ACTIONLOGS'"); $this->_db->execute(); return $this->getPurge30Days(); } /** * Get the number of days to delete logs after from the System - User Actions Log. * * @return int */ public function getPurge30Days() { if (version_compare(JVERSION, '3.9.0', '<')) { return false; } $this->_db->setQuery("SELECT params FROM `#__extensions` WHERE `name` = 'PLG_SYSTEM_ACTIONLOGS'"); $params = $this->_db->LoadResult(); if ('{}' == $params) { bfEncrypt::reply('success', [ 'days' => null, ]); } $params = json_decode($params); if (! $params) { return; } bfEncrypt::reply('success', [ 'days' => $params->logDeletePeriod, ]); } /** * Joomla 3.9.0+ enable system log rotation. */ public function setSystemLogRotationEnabled() { $this->_db->setQuery("UPDATE `#__extensions` set enabled = 1 WHERE `name` = 'plg_system_logrotation'"); $this->_db->execute(); return $this->getSystemLogRotationEnabled(); } /** * Joomla 3.9.0+ check for system log rotation. */ public function getSystemLogRotationEnabled() { $this->_db->setQuery("SELECT count(*) FROM `#__extensions` WHERE `name` = 'plg_system_logrotation' and enabled = 1"); bfEncrypt::reply('success', [ 'enabled' => $this->_db->LoadResult(), ]); } public function setGuidedTours() { // inverse. if ("true" === $this->_dataObj->s) { $enabled = 0; } else { $enabled = 1; } $sql = "UPDATE `#__extensions` set enabled = " . $enabled . " WHERE `name` = 'plg_system_guidedtours'"; $this->_db->setQuery($sql); $this->_db->execute(); bfEncrypt::reply('success', [ 'enabled' => $this->getGuidedTours(), ]); } public function getGuidedTours() { $this->_db->setQuery("SELECT count(*) FROM `#__extensions` WHERE `name` = 'plg_system_guidedtours' and enabled = 1"); bfEncrypt::reply('success', [ 'enabled' => $this->_db->LoadResult(), ]); } /** * Joomla 3.9.0+ enable IP logging in user action logging. */ public function setUseractionlogiplogenabled() { $this->_db->setQuery("SELECT params FROM `#__extensions` WHERE `name` = 'com_actionlogs'"); $params = json_decode($this->_db->LoadResult()); // enabled $params->ip_logging = 1; $this->_db->setQuery("UPDATE `#__extensions` set params = '" . json_encode($params) . "' WHERE `name` = 'com_actionlogs'"); $this->_db->execute(); return $this->getUseractionlogiplogenabled(); } /** * Joomla 3.9.0+ Check for plg_privacy_actionlogs enabled. */ public function getUseractionlogiplogenabled() { $this->_db->setQuery("SELECT params FROM `#__extensions` WHERE `name` = 'com_actionlogs'"); $params = json_decode($this->_db->LoadResult()); bfEncrypt::reply('success', [ 'enabled' => $params->ip_logging, ]); } /** * Joomla 3.9.0+ Check for plg_privacy_actionlogs enabled. */ public function setUseractionlogenabled() { $this->_db->setQuery("UPDATE `#__extensions` set enabled = 1 WHERE `name` = 'PLG_ACTIONLOG_JOOMLA'"); $this->_db->execute(); $this->_db->setQuery("UPDATE `#__extensions` set enabled = 1 WHERE `name` = 'PLG_SYSTEM_ACTIONLOGS'"); $this->_db->execute(); return $this->getUseractionlogenabled(); } /** * Joomla 3.9.0+ Check for plg_privacy_actionlogs enabled. */ public function getUseractionlogenabled() { $this->_db->setQuery( "SELECT count(*) FROM `#__extensions` WHERE (`name` = 'PLG_ACTIONLOG_JOOMLA' or `name` = 'PLG_SYSTEM_ACTIONLOGS') and enabled = 1" ); bfEncrypt::reply('success', [ 'enabled' => 2 == $this->_db->LoadResult() ? 1 : 0, ]); } /** * Joomla 3.9.0+ Check for plg_system_privacyconsent enabled. */ public function setPrivacyConsentPluginEnabled() { $this->_db->setQuery("UPDATE `#__extensions` set enabled = 1 WHERE `name` = 'plg_system_privacyconsent'"); $this->_db->execute(); return $this->getUseractionlogenabled(); } /** * Joomla 3.9.0+ Check for plg_system_privacyconsent enabled. */ public function getPrivacyConsentPluginEnabled() { $this->_db->setQuery("SELECT count(*) FROM `#__extensions` WHERE `name` = 'plg_system_privacyconsent' and enabled = 1"); bfEncrypt::reply('success', [ 'enabled' => $this->_db->LoadResult(), ]); } /** * Load Flash Upload Settings from params from com_media without using a helper. and then remove swf and * application/x-shockwave-flash. */ public function setUploadsettingsfixed() { $this->_db->setQuery("select params from #__extensions where element = 'com_media'"); $params = json_decode($this->_db->LoadResult()); $items = explode(',', $params->upload_extensions); if ('true' == $this->_dataObj->s) {// true means, set to the OK value foreach ($items as $k => $item) { if ('swf' == strtolower(trim($item))) { unset($items[$k]); } } } else { $items[] = 'swf'; } $params->upload_extensions = implode(',', $items); $items = explode(',', $params->upload_mime); if ('true' == $this->_dataObj->s) {// true means, set to the OK value foreach ($items as $k => $item) { if ('application/x-shockwave-flash' == strtolower(trim($item))) { unset($items[$k]); } } } else { $items[] = 'application/x-shockwave-flash'; } $params->upload_mime = implode(',', $items); $sql = sprintf("UPDATE #__extensions set `params` = '%s' WHERE `element` = 'com_media'", json_encode($params)); $this->_db->setQuery($sql); $this->_db->execute(); $this->getUploadsettingsfixed(); } /** * Load Flash Upload Settings from params from com_media without using a helper. */ public function getUploadsettingsfixed() { $this->_db->setQuery("select params from #__extensions where element = 'com_media'"); $params = json_decode($this->_db->LoadResult()); if ( ! preg_match('/swf/ism', $params->upload_extensions) && ! preg_match('/application\/x-shockwave-flash/ism', $params->upload_mime) ) { bfEncrypt::reply('success', [ 'uploadsettingsfixed' => 1, ]); } else { bfEncrypt::reply('success', [ 'uploadsettingsfixed' => 0, ]); } } public function setDefaultUpdateChannel() { $component = ComponentHelper::getComponent('com_joomlaupdate'); $params = $component->getParams(); $params->set('updatesource', 'true' === $this->_dataObj->s ? 'default' : 'next'); $table = Table::getInstance('extension'); $table->load($component->id); $table->bind([ 'params' => $params->toString(), ]); $table->store(); $this->getDefaultUpdateChannel(); } public function getDefaultUpdateChannel() { bfEncrypt::reply('success', ComponentHelper::getComponent('com_joomlaupdate') ->getParams() ->get('updatesource', 'default')); } /** * @param bool $internal * * @return array|mixed */ private function getSkipped($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = 'SELECT * FROM bf_files WHERE skipped = 1 ORDER BY filemtime DESC LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery('SELECT count(*) FROM bf_files WHERE skipped = 1'); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * 109 Gets php.ini and .user.ini files. */ private function getPHPinifiles() { // make sure we only retrieve a small dataset $limitstart = (int) $this->_dataObj->ls; $sort = $this->_dataObj->s; if (! $sort) { $sort = 'filewithpath'; } if (! in_array($sort, ['filewithpath', 'filemtime'])) { exit('Invalid Sort'); } if ('filemtime' == $sort) { $sort = 'filemtime DESC'; } $limit = (int) $this->_dataObj->limit; // Set the query $this->_db->setQuery('SELECT id, iscorefile, filewithpath, filemtime, fileperms, `size`, iscorefile from bf_files WHERE filewithpath LIKE "%php.ini%" OR filewithpath LIKE "%.user.ini%" ORDER BY ' . $sort . ' LIMIT ' . (int) $limitstart . ', ' . $limit); // Get an object list of files $files = $this->_db->loadObjectList(); // see how many files there are in total without a limit $this->_db->setQuery('SELECT count(*) from bf_files WHERE filewithpath LIKE "%php.ini%" OR filewithpath LIKE "%.user.ini%"'); $count = $this->_db->loadResult(); // Only show files that still exist on the hard drive $existingFiles = []; foreach ($files as $k => $file) { if (file_exists(JPATH_BASE . $file->filewithpath)) { $existingFiles[] = $file; } else { $this->_db->setQuery(sprintf('DELETE FROM bf_files WHERE filewithpath = "%s"', $file->filewithpath)); $this->_db->execute(); --$count; } } // return an encrypted reply bfEncrypt::reply('success', [ 'files' => $existingFiles, 'total' => $count, ]); } /** * Method to delete a named file when we know its id. */ private function deleteFile() { // Get the filewithpath based on the id $this->_db->setQuery('SELECT filewithpath from bf_files WHERE id = ' . (int) $this->_dataObj->file_id); $filewithpath = $this->_db->loadResult(); // check that the file we got form the database matches to the path we think it should be if ($this->_dataObj->filewithpath != $filewithpath) { bfEncrypt::reply('failure', [ 'msg' => 'File Not matching: ' . $this->_dataObj->filewithpath . ' !== ' . $filewithpath, ]); } // If the file doesnt exist then remove from cache and reply if (! file_exists(JPATH_BASE . $filewithpath)) { $this->_db->setQuery('DELETE FROM bf_files WHERE id = ' . (int) $this->_dataObj->file_id); $this->_db->execute(); bfEncrypt::reply('failure', [ 'msg' => 'File doesn\'t exist: ' . $filewithpath, ]); } // Attempt to force deletion if (! is_writable(JPATH_BASE . $filewithpath)) { @chmod(JPATH_BASE . $filewithpath, 0777); } // delete the file, making sure we prefix with a path if (@unlink(JPATH_BASE . $filewithpath)) { $this->_db->setQuery('DELETE FROM bf_files WHERE id = ' . (int) $this->_dataObj->file_id); $this->_db->execute(); // File deleted - say yes bfEncrypt::reply('success', [ 'msg' => 'File deleted: ' . $filewithpath, ]); } else { // File deleted - say no bfEncrypt::reply('failure', [ 'msg' => 'File Not Deleted: ' . $filewithpath, ]); } } /** * I delete a folder. */ private function deleteFolder() { // Require more complex methods for dealing with files require 'bfFilesystem.php'; // init our return msg $msg = []; // hidden or normal - needed for ALL deletes $type = $this->_dataObj->type; // switch on type if ('hidden' == $type) { // get the folders cache id $folder_id = $this->_dataObj->fid; // init $msgToReturn = []; $msgToReturn['deleted_files'] = 0; $msgToReturn['deleted_folders'] = 0; $msgToReturn['left'] = 0; // Do we want to delete all hidden folders? if ('ALL' == $folder_id) { // All meaning all hidden folders, not ALL folders in our db!! $this->_dataObj->ls = 0; $this->_dataObj->limit = 999999999; // get all the hidden folders $folders = $this->getHiddenFolders(true); bfLog::log('Deleting this many folders : ' . count($folders)); // foreach hidden folder, delete that hidden folder recursivly foreach ($folders as $folder) { // delete recursive bfLog::log('Deleting folder: ' . JPATH_BASE . $folder->folderwithpath); $msg = Bf_Filesystem::deleteRecursive(JPATH_BASE . $folder->folderwithpath, true, $msg); $this->_db->setQuery('DELETE FROM bf_folders WHERE folderwithpath LIKE "' . $folder->folderwithpath . '%"'); $this->_db->loadResult(); $this->_db->setQuery('DELETE FROM bf_files WHERE filewithpath LIKE "' . $folder->folderwithpath . '%"'); $this->_db->loadResult(); // oh dear we failed if ('failure' == $msg['result']) { $msgToReturn = []; $msgToReturn['deleted_files'] = count(@$msg['deleted_files']); $msgToReturn['deleted_folders'] = count(@$msg['deleted_folders']); $msgToReturn['left'] = $this->getHiddenFolders(true); // send back the error message bfEncrypt::reply('failure', [ 'msg' => 'Problem!: ' . json_encode($msgToReturn), ]); } } } else { // select the folder to delete $this->_db->setQuery('SELECT folderwithpath FROM bf_folders WHERE id = ' . (int) $folder_id); $folderwithpath = $this->_db->loadResult(); // if the folder is not there if (! $folderwithpath) { bfEncrypt::reply('failure', [ 'msg' => 'Folder Not Found #msg2#: ' . $folderwithpath, ]); } $msg = Bf_Filesystem::deleteRecursive(JPATH_BASE . $folderwithpath, true, $msg); } // if we deleted some folders if (count($msg['deleted_folders'])) { foreach ($msg['deleted_folders'] as $folder) { $fwp = str_replace('//', '/', str_replace(JPATH_BASE, '', $folder)); $sql = "DELETE FROM bf_folders where folderwithpath = '" . $fwp . "'"; $this->_db->setQuery($sql); $this->_db->execute(); } } // if we deleted some files if (count($msg['deleted_files'])) { foreach ($msg['deleted_files'] as $file) { $fwp = str_replace('//', '/', str_replace(JPATH_BASE, '', $file)); $sql = "DELETE FROM bf_files where filewithpath = '" . $fwp . "'"; $this->_db->setQuery($sql); $this->_db->execute(); } } // reply back with our warning or success message $msgToReturn = []; $msgToReturn['deleted_files'] = count($msg['deleted_files']); $msgToReturn['deleted_folders'] = count($msg['deleted_folders']); $msgToReturn['left'] = count($this->getHiddenFolders(true)); bfEncrypt::reply('success', [ 'msg' => json_encode($msgToReturn), ]); } if ('deleteinstallation' === $type) { $folders = $this->getFolders(JPATH_BASE); foreach ($folders as $folder) { if (preg_match( '/installation|installation.old|docs\/installation|install|installation.bak|installation.old|installation.backup|installation.delete/i', $folder )) { $installationFolders[] = $folder; } } foreach ($installationFolders as $folderwithpath) { bfLog::log('Deleting folder: ' . $folderwithpath); $msg = Bf_Filesystem::deleteRecursive(JPATH_BASE . $folderwithpath, true, $msg); } bfEncrypt::reply('success', [ 'msg' => 'ok', ]); } } /** * @param bool $internal * * @return array|mixed */ private function getHiddenFolders($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $this->_db->setQuery('SELECT * FROM bf_folders WHERE folderwithpath LIKE "%/.%" LIMIT ' . (int) $limitstart . ', ' . $limit); $folders = $this->_db->loadObjectList(); if (true === $internal) { return $folders; } $this->_db->setQuery('SELECT count(*) FROM bf_folders WHERE folderwithpath LIKE "%/.%"'); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $folders, 'total' => $count, ]); } /** * Function taken from Akeeba filesystem.php. * * Akeeba Engine The modular PHP5 site backup engine * * @copyright Copyright (c)2009 Nicholas K. Dionysopoulos * @license GNU GPL version 3 or, at your option, any later version * * @version Id: scanner.php 158 2010-06-10 08:46:49Z nikosdion */ private function getFolders($folder) { // Initialize variables $arr = []; $false = false; $folder = trim($folder); if (! is_dir($folder) && ! is_dir($folder . \DIRECTORY_SEPARATOR) || is_link($folder . \DIRECTORY_SEPARATOR) || is_link( $folder ) || ! $folder) { return $false; } if (@file_exists($folder . \DIRECTORY_SEPARATOR . '.myjoomla.ignore.folder')) { return []; } $handle = @opendir($folder); if (false === $handle) { $handle = @opendir($folder . \DIRECTORY_SEPARATOR); } // If directory is not accessible, just return FALSE if (false === $handle) { return $false; } while ((false !== ($file = @readdir($handle)))) { if (('.' != $file) && ('..' != $file) && (null != trim($file))) { $ds = ('' == $folder) || (\DIRECTORY_SEPARATOR == $folder) || (\DIRECTORY_SEPARATOR == @substr( $folder, -1 )) || (\DIRECTORY_SEPARATOR == @substr($folder, -1)) ? '' : \DIRECTORY_SEPARATOR; $dir = trim($folder . $ds . $file); $isDir = @is_dir($dir); if ($isDir) { $arr[] = $this->cleanupFileFolderName(str_replace(JPATH_BASE, '', $folder . \DIRECTORY_SEPARATOR . $file)); } } } @closedir($handle); return $arr; } /** * Clean up a string, a path name. * * @param string $str * * @return string */ private function cleanupFileFolderName($str) { $str = str_replace('////', '/', $str); $str = str_replace('///', '/', $str); $str = str_replace('//', '/', $str); $str = str_replace('\\/', '/', $str); $str = str_replace('\\t', '/t', $str); $str = str_replace("\/", '/', $str); return addslashes($str); } /** * I get the number of core files that failed the hash checking. */ private function getCoreHashFailedFileList() { // set up the limit and limit start for the SQL $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; $this->_db->setQuery( 'SELECT id, filewithpath, filemtime, fileperms FROM bf_files WHERE hashfailed = 1 LIMIT ' . $limitstart . ', ' . $limit ); // Get the files from the cache $files = $this->_db->loadObjectList(); // get the count as well, for pagination $this->_db->setQuery('SELECT count(*) from bf_files WHERE hashfailed = 1'); $count = $this->_db->loadResult(); // send back the totals bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * I get list of database tables that begin with bak_. */ private function deleteBakTables() { $tables = $this->getBakTables(true); // for all the bak tables foreach ($tables as $table) { // compose the sql query $this->_db->setQuery('DROP TABLE ' . $table[0]); // delete the bak_tables $this->_db->execute(); } $count = count($tables); // send back the totals bfEncrypt::reply('success', [ 'tables' => $tables, 'total' => $count, ]); } /** * I get list of database tables that begin with bak_. */ private function getBakTables($internal = false) { // Get the database name $config = JFactory::getApplication()->getConfig(); $dbname = $config->get('db', ''); // compose the sql query $this->_db->setQuery("SHOW TABLES WHERE `Tables_in_{$dbname}` like 'bak_%'"); // Get the bak_tables $tables = $this->_db->loadRowList(); // return array if we are internally calling this method if (true === $internal) { return $tables; } // count them $count = count($tables); // send back the totals bfEncrypt::reply('success', [ 'tables' => $tables, 'total' => $count, ]); } /** * get the value of the $live_site var from configuration.php. */ private function getConfiguredLiveSite() { // send back the totals bfEncrypt::reply('success', [ 'live_site' => Factory::getApplication()->getConfig()->get('live_site'), ]); } /** * Get a list of folders with 777 permissions. */ private function getFolderPermissions() { // set up the limit and the limitstart SQL $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; $this->_db->setQuery( 'SELECT `id`, `folderwithpath`, `folderinfo` from bf_folders WHERE folderinfo IN ("777", "351", "311") LIMIT ' . $limitstart . ', ' . $limit ); // get the files $files = $this->_db->loadObjectList(); // get the count for pagination $this->_db->setQuery('SELECT count(*) from bf_folders WHERE `folderinfo` IN ("777", "351", "311")'); $count = $this->_db->loadResult(); // send back the totals bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * Get a list of files with 777 permissions. */ private function getFilePermissions() { // set up the limit and the limitstart SQL $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; $this->_db->setQuery( 'SELECT id, filewithpath, fileperms from bf_files WHERE fileperms = "0777" OR fileperms = "777" LIMIT ' . (int) $limitstart . ', ' . $limit ); // get the files $files = $this->_db->loadObjectList(); // get the count for pagination $this->_db->setQuery('SELECT count(*) from bf_files WHERE fileperms = "0777" OR fileperms = "777"'); $count = $this->_db->loadResult(); // send back the totals bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * Set the permissions on files that have 777 perms to be 644. */ private function setFilePermissions() { $fixed = 0; $errors = 0; $this->_db->setQuery('SELECT id, filewithpath from bf_files WHERE fileperms = "0777" OR fileperms = "777"'); $files = $this->_db->loadObjectList(); foreach ($files as $file) { if (@chmod(JPATH_BASE . $file->filewithpath, 0644)) { ++$fixed; $this->_db->setQuery('UPDATE bf_files SET fileperms = "0644" WHERE id = "' . (int) $file->id . '"'); $this->_db->execute(); } else { ++$errors; } } $this->_db->setQuery('SELECT count(*) FROM bf_folders WHERE folderinfo LIKE "%777%"'); $folders_777 = $this->_db->LoadResult(); $res = new stdClass(); $res->errors = $errors; $res->fixed = $fixed; $res->leftover = $folders_777; bfEncrypt::reply('success', $res); } /** * Return the list of files that have been flagged as containing mail commands or text. */ private function getUploaderFileList() { // make sure we only retrieve a small dataset $limitstart = (int) $this->_dataObj->ls; $sort = $this->_dataObj->s; if (! $sort) { $sort = 'filewithpath'; } if (! in_array($sort, ['filewithpath', 'filemtime'])) { exit('Invalid Sort'); } if ('filemtime' == $sort) { $sort = 'filemtime DESC'; } $limit = (int) $this->_dataObj->limit; // Set the query $this->_db->setQuery('SELECT id, iscorefile, filewithpath, filemtime, fileperms, `size`, iscorefile from bf_files WHERE uploader = 1 ORDER BY ' . $sort . ' LIMIT ' . (int) $limitstart . ', ' . $limit); // Get an object list of files $files = $this->_db->loadObjectList(); // see how many files there are in total without a limit $this->_db->setQuery('SELECT count(*) from bf_files WHERE uploader = 1'); $count = $this->_db->loadResult(); // Only show files that still exist on the hard drive $existingFiles = []; foreach ($files as $k => $file) { if (file_exists(JPATH_BASE . $file->filewithpath)) { $existingFiles[] = $file; } else { $this->_db->setQuery(sprintf('DELETE FROM bf_files WHERE filewithpath = "%s"', $file->filewithpath)); $this->_db->execute(); --$count; } } // return an encrypted reply bfEncrypt::reply('success', [ 'files' => $existingFiles, 'total' => $count, ]); } /** * Return the list of files that have been flagged as containing mail commands or text. */ private function getMailerFileList() { $sort = null; if (property_exists($this->_dataObj, 's')) { $sort = $this->_dataObj->s; } // make sure we only retrieve a small dataset $limitstart = (int) $this->_dataObj->ls; if (! $sort) { $sort = 'filewithpath'; } if (! in_array($sort, ['filewithpath', 'filemtime'])) { exit('Invalid Sort'); } if ('filemtime' == $sort) { $sort = 'filemtime DESC'; } $limit = (int) $this->_dataObj->limit; // Set the query $this->_db->setQuery('SELECT id, iscorefile, filewithpath, filemtime, fileperms, `size`, iscorefile from bf_files WHERE mailer = 1 ORDER BY ' . $sort . ' LIMIT ' . (int) $limitstart . ', ' . $limit); // Get an object list of files $files = $this->_db->loadObjectList(); // see how many files there are in total without a limit $this->_db->setQuery('SELECT count(*) from bf_files WHERE mailer = 1'); $count = $this->_db->loadResult(); // Only show files that still exist on the hard drive $existingFiles = []; foreach ($files as $k => $file) { if (file_exists(JPATH_BASE . $file->filewithpath)) { $existingFiles[] = $file; } else { $this->_db->setQuery(sprintf('DELETE FROM bf_files WHERE filewithpath = "%s"', $file->filewithpath)); $this->_db->execute(); --$count; } } // return an encrypted reply bfEncrypt::reply('success', [ 'files' => $existingFiles, 'total' => $count, ]); } /** * Return the list of files that have been flagged as containing patterns that match our suspect patterns These * maybe false positives for suspect content, but might be examples of bad code standards like using ../../../ or * eval() method. */ private function getSuspectContentFileList() { // make sure we only retrieve a small dataset $limitstart = (int) $this->_dataObj->ls; $sort = $this->_dataObj->s; if (! $sort) { $sort = 'filewithpath'; } if (! in_array($sort, ['filewithpath', 'filemtime'])) { exit('Invalid Sort'); } if ('filemtime' == $sort) { $sort = 'filemtime DESC'; } $limit = (int) $this->_dataObj->limit; // Set the query $this->_db->setQuery( 'SELECT id, iscorefile, filewithpath, filemtime, fileperms, `size`, iscorefile, hacked, currenthash from bf_files WHERE suspectcontent = 1 OR hacked = 1 ORDER BY hacked desc, ' . $sort . ' LIMIT ' . (int) $limitstart . ', ' . $limit ); // Get an object list of files $files = $this->_db->loadObjectList(); // see how many files there are in total without a limit $this->_db->setQuery('SELECT count(*) from bf_files WHERE suspectcontent = 1 OR hacked = 1'); $count = $this->_db->loadResult(); // Only show files that still exist on the hard drive $existingFiles = []; foreach ($files as $k => $file) { if (file_exists(JPATH_BASE . $file->filewithpath)) { $existingFiles[] = $file; } else { $this->_db->setQuery(sprintf('DELETE FROM bf_files WHERE filewithpath = "%s"', $file->filewithpath)); $this->_db->execute(); --$count; } } // return an encrypted reply bfEncrypt::reply('success', [ 'files' => $existingFiles, 'total' => $count, ]); } private function getNeedsCompact() { // make sure we only retrieve a small dataset $limitstart = (int) $this->_dataObj->ls; $sort = $this->_dataObj->s; if (! $sort) { $sort = 'filewithpath'; } if (! in_array($sort, ['filewithpath', 'filemtime'])) { exit('Invalid Sort'); } if ('filemtime' === $sort) { $sort = 'filemtime DESC'; } $limit = (int) $this->_dataObj->limit; // Set the query $this->_db->setQuery( 'SELECT id, iscorefile, filewithpath, filemtime, fileperms, `size`, iscorefile, hacked, currenthash from bf_files WHERE needscompat = 1 ORDER BY ' . $sort . ' LIMIT ' . (int) $limitstart . ', ' . $limit ); // Get an object list of files $files = $this->_db->loadObjectList(); // see how many files there are in total without a limit $this->_db->setQuery('SELECT count(*) from bf_files WHERE needscompat = 1'); $count = $this->_db->loadResult(); // Only show files that still exist on the hard drive $existingFiles = []; foreach ($files as $k => $file) { if (file_exists(JPATH_BASE . $file->filewithpath)) { $existingFiles[] = $file; } else { $this->_db->setQuery(sprintf('DELETE FROM bf_files WHERE filewithpath = "%s"', $file->filewithpath)); $this->_db->execute(); --$count; } } // return an encrypted reply bfEncrypt::reply('success', [ 'files' => $existingFiles, 'total' => $count, ]); } /** * Get SQL files found. */ private function getSQLFiles() { // make sure we only retrieve a small dataset $limitstart = (int) $this->_dataObj->ls; $sort = $this->_dataObj->s; if (! $sort) { $sort = 'filewithpath'; } if (! in_array($sort, ['filewithpath', 'filemtime'])) { exit('Invalid Sort'); } if ('filemtime' == $sort) { $sort = 'filemtime DESC'; } $limit = (int) $this->_dataObj->limit; // Set the query $this->_db->setQuery('SELECT * FROM bf_files WHERE ( (filewithpath LIKE \'%.sql\' or filewithpath LIKE \'%sql/site.%\') and (iscorefile = 0 or iscorefile is null) ) ORDER BY ' . $sort . ' LIMIT ' . (int) $limitstart . ', ' . $limit); // Get an object list of files $files = $this->_db->loadObjectList(); // see how many files there are in total without a limit $this->_db->setQuery('SELECT count(*) FROM bf_files WHERE ( (filewithpath LIKE \'%.sql\' or filewithpath LIKE \'%sql/site.%\') and (iscorefile = 0 or iscorefile is null) )'); $count = $this->_db->loadResult(); // Only show files that still exist on the hard drive $existingFiles = []; foreach ($files as $k => $file) { if (file_exists(JPATH_BASE . $file->filewithpath)) { $existingFiles[] = $file; } else { $this->_db->setQuery(sprintf('DELETE FROM bf_files WHERE filewithpath = "%s"', $file->filewithpath)); $this->_db->execute(); --$count; } } // return an encrypted reply bfEncrypt::reply('success', [ 'files' => $existingFiles, 'total' => $count, ]); } /** * Return the list of files that have been flagged as containing patterns that match our suspect patterns These * maybe false positives for suspect content, but might be examples of bad code standards like using ../../../ or * eval() method. */ private function getNonCoreFileList() { // make sure we only retrieve a small dataset $limitstart = (int) $this->_dataObj->ls; $sort = $this->_dataObj->s; if (! $sort) { $sort = 'filewithpath'; } if (! in_array($sort, ['filewithpath', 'filemtime'])) { exit('Invalid Sort'); } if ('filemtime' == $sort) { $sort = 'filemtime DESC'; } $limit = (int) $this->_dataObj->limit; // Set the query $this->_db->setQuery('SELECT id, iscorefile, filewithpath, filemtime, fileperms, `size`, iscorefile from bf_files WHERE iscorefile IS NULL ORDER BY ' . $sort . ' LIMIT ' . (int) $limitstart . ', ' . $limit); // Get an object list of files $files = $this->_db->loadObjectList(); // see how many files there are in total without a limit $this->_db->setQuery('SELECT count(*) from bf_files WHERE iscorefile IS NULL'); $count = $this->_db->loadResult(); // Only show files that still exist on the hard drive $existingFiles = []; foreach ($files as $k => $file) { if (file_exists(JPATH_BASE . $file->filewithpath)) { $existingFiles[] = $file; } else { $this->_db->setQuery(sprintf('DELETE FROM bf_files WHERE filewithpath = "%s"', $file->filewithpath)); $this->_db->execute(); --$count; } } // return an encrypted reply bfEncrypt::reply('success', [ 'files' => $existingFiles, 'total' => $count, ]); } /** * @param bool $internal * * @return array|mixed */ private function getInstallationFolders($internal = false) { $installationFolders = []; $folders = $this->getFolders(JPATH_BASE); foreach ($folders as $folder) { if (preg_match( '/installation|installation.old|docs\/installation|install|installation.bak|installation.old|installation.backup|installation.delete/i', $folder )) { $installationFolders[] = $folder; } } bfEncrypt::reply('success', [ 'files' => $installationFolders, 'total' => count($installationFolders), ]); } /** * @param bool $internal * * @return array|mixed */ private function getRecentlyModified($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = "SELECT * FROM bf_files WHERE filemtime > '" . strtotime( '-3 days', time() ) . "' ORDER BY filemtime DESC LIMIT " . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery("SELECT count(*) FROM bf_files WHERE filemtime > '" . strtotime('-3 days', time()) . "'"); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * @param bool $internal * * @return array|mixed */ private function getHtaccessFiles($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = "SELECT * FROM bf_files WHERE filewithpath LIKE '%/.htaccess' ORDER BY filewithpath DESC LIMIT " . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery("SELECT count(*) FROM bf_files WHERE filewithpath LIKE '%/.htaccess'"); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * @param bool $internal * * @return array|mixed */ private function getLargefiles($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; $order = $this->_dataObj->orderby; if (! in_array($order, ['filewithpath', 'filemtime', 'size'])) { $order = 'filewithpath'; } if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = 'SELECT * FROM bf_files WHERE SIZE > 2097152 ORDER BY ' . $order . ' DESC LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery('SELECT COUNT(*) FROM bf_files WHERE SIZE > 2097152'); $count = (int) $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * @param bool $internal * * @return array|mixed */ private function getArchivefiles($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = 'SELECT * FROM bf_files WHERE filewithpath LIKE "%.zip" OR filewithpath LIKE "%.tar" OR filewithpath LIKE "%.tar.gz" OR filewithpath LIKE "%.bz2" OR filewithpath LIKE "%.gzip" OR filewithpath LIKE "%.bzip2" ORDER BY filemtime DESC LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery('SELECT count(*) FROM bf_files WHERE filewithpath LIKE "%.zip" OR filewithpath LIKE "%.tar" OR filewithpath LIKE "%.tar.gz" OR filewithpath LIKE "%.bz2" OR filewithpath LIKE "%.gzip" OR filewithpath LIKE "%.bzip2"'); $count = (int) $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * @param bool $internal * * @return array|mixed */ private function getPhpinwrongplace($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = 'SELECT * FROM bf_files AS b WHERE filewithpath REGEXP "^/images/.*\.php$" ORDER BY filemtime DESC LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $count = (int) count($files); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * @param bool $internal * * @return array|mixed */ private function getTmpfiles($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = 'SELECT * FROM bf_files WHERE filewithpath LIKE "/tmp%" AND filewithpath != "/tmp/index.html" ORDER BY filemtime DESC LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery('SELECT count(*) FROM bf_files WHERE filewithpath LIKE "/tmp%" AND filewithpath != "/tmp/index.html" ORDER BY filemtime'); $count = (int) $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } private function clearFluffFiles() { require 'bfFilesystem.php'; foreach ($this->fluffFiles as $file) { // ensure we are based correctly $fileWithPath = JPATH_BASE . $file; // Remove File. unlink($fileWithPath); } $this->getFlufffiles(true); } /** * @param bool $internal * * @return array|mixed */ private function getFlufffiles($internal = false) { $files = []; $files['present'] = []; $files['notpresent'] = []; foreach ($this->fluffFiles as $file) { // ensure we are based correctly $fileWithPath = JPATH_BASE . $file; // determine if the file is present or not if (@file_exists($fileWithPath)) { //@ to avoid any nasty warnings $files['present'][] = $file; } else { $files['notpresent'][] = $file; } } bfEncrypt::reply('success', [ 'total' => count($files['present']), 'files' => $files, ]); } /** * @param bool $internal * * @return array|mixed */ private function getRenamedToHide($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = 'SELECT * FROM bf_files WHERE filewithpath LIKE "%.backup%" OR filewithpath LIKE "%.bak%" OR filewithpath LIKE "%.old%" ORDER BY filemtime DESC LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery('SELECT count(*) FROM bf_files WHERE filewithpath LIKE "%.backup%" OR filewithpath LIKE "%.bak%" OR filewithpath LIKE "%.old%"'); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } private function clearTmpFiles() { require 'bfFilesystem.php'; $filesAndFolders = Bf_Filesystem::readDirectory(JPATH_ROOT . '/tmp', '.', true); foreach ($filesAndFolders as $pointer) { $pointer = JPATH_ROOT . '/tmp/' . $pointer; if (is_dir($pointer)) { bfLog::log('Deleting ' . $pointer); Bf_Filesystem::deleteRecursive($pointer, true); } else { bfLog::log('Deleting ' . $pointer); unlink($pointer); } } file_put_contents(JPATH_ROOT . '/tmp/index.html', '<html><body bgcolor="#FFFFFF"></body></html> '); $sql = 'DELETE FROM bf_files WHERE filewithpath LIKE "/tmp%" AND filewithpath != "/tmp/index.html"'; $this->_db->setQuery($sql); $this->_db->execute(); bfEncrypt::reply('success', [ 'res' => true, ]); } /** * @param bool $internal * * @return array|mixed */ private function getDotfiles($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = 'SELECT * FROM bf_files WHERE filewithpath LIKE "%/.%" ORDER BY filemtime DESC LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery('SELECT count(*) FROM bf_files WHERE filewithpath LIKE "%/.%"'); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * Find files which have zero bytes (no content) as they just litter the webspace and run up inode counts. Joomla * doesnt rely on zero byte files, we have seen "other hack cleanup companies" litter the webspace with zero byte * files and so this tool deletes those too. * * @param bool $internal */ private function getZerobyteFiles($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = 'SELECT * FROM bf_files WHERE size = 0 ORDER BY filemtime DESC LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery('SELECT count(*) FROM bf_files WHERE size = 0'); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * Restore core files from a trusted source. * * This source (corefiles.myjoomla.io) is checked hourly for integrity, if you are concerned about MITM Attacks, * well, if your server is compromised enough for a MITM Attack then you have bigger issues, plus this is how Joomla * updates happen anyway so no additional security issues are created with this code! */ private function restoreAllMissingFiles() { $url = 'https://corefiles.myjoomla.io/%s%s?raw'; $restored = 0; $notRestored = 0; // Crappy Servers Alert! @set_time_limit(3600); $files = $this->getMissingCoreFiles(true); foreach ($files as $file) { $downloadUrl = sprintf($url, JVERSION, $file->filewithpath); $restoreToFile = JPATH_BASE . $file->filewithpath; // check folder and path to folder exists $folder = dirname($restoreToFile); if (! file_exists($folder)) { @mkdir($folder, 0755, true); } $content = file_get_contents($downloadUrl); if ($content && file_exists($folder) && file_put_contents($restoreToFile, $content)) { // Set correct permissions @ for crappy servers @chmod($restoreToFile, 0644); // Update the cache database tables so we dont have to run a new audit right away $sql = "INSERT INTO `bf_files` (`id`, `filewithpath`, `fileperms`, `filemtime`, `toggler`, `currenthash`, `lasthash`, `iscorefile`, `hashfailed`, `hashchanged`, `hacked`, `suspectcontent`, `falsepositive`, `mailer`, `uploader`, `encrypted`, `queued`, `size`) VALUES (NULL, '%s', '0644', '%s', NULL, '%s', '%s', 1, NULL, NULL, NULL, 0, NULL, NULL, NULL, 0, 0, %s)"; $sql = sprintf( $sql, $file->filewithpath, time(), md5_file($restoreToFile), md5_file($restoreToFile), filesize($restoreToFile) ); $this->_db->setQuery($sql); $this->_db->execute(); ++$restored; } else { ++$notRestored; } } bfEncrypt::reply('success', [ 'total' => count($files), 'restored' => $restored, 'notrestored' => $notRestored, ]); } private function getMissingCoreFiles($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = " FROM `bf_core_hashes` WHERE filewithpath NOT IN ( SELECT filewithpath from bf_files ) AND filewithpath NOT LIKE '/installation/%' AND filewithpath != '/robots.txt.dist' AND filewithpath != '/administrator/manifests/packages/pkg_weblinks.xml' AND filewithpath != '/' AND filewithpath != '/robots.txt.dist' AND filewithpath != '/web.config.txt' AND filewithpath != '/joomla.xml' AND filewithpath != '/build.xml' AND filewithpath != '/LICENSE.txt' AND filewithpath != '/README.txt' AND filewithpath != '/htaccess.txt' AND filewithpath != '/LICENSES.php' AND filewithpath != '/configuration.php-dist' AND filewithpath != '/CHANGELOG.php' AND filewithpath != '/COPYRIGHT.php' AND filewithpath != '/CREDITS.php' AND filewithpath != '/INSTALL.php' AND filewithpath != '/LICENSE.php' AND filewithpath != '/CONTRIBUTING.md' AND filewithpath != '/phpunit.xml.dist' AND filewithpath != '/.drone.yml' AND filewithpath != '/README.md' AND filewithpath != '/.travis.yml' AND filewithpath != '/travisci-phpunit.xml' AND filewithpath != '/images/banners/osmbanner1.png' AND filewithpath != '/images/banners/osmbanner2.png' AND filewithpath != '/images/banners/shop-ad-books.jpg' AND filewithpath != '/images/banners/shop-ad.jpg' AND filewithpath != '/images/banners/white.png' AND filewithpath != '/images/headers/blue-flower.jpg' AND filewithpath != '/images/headers/maple.jpg' AND filewithpath != '/images/headers/raindrops.jpg' AND filewithpath != '/images/headers/walden-pond.jpg' AND filewithpath != '/images/headers/windows.jpg' AND filewithpath != '/images/joomla_black.gif' AND filewithpath != '/images/joomla_black.png' AND filewithpath != '/images/joomla_green.gif' AND filewithpath != '/images/joomla_logo_black.jpg' AND filewithpath != '/images/powered_by.png' AND filewithpath != '/images/sampledata/fruitshop/apple.jpg' AND filewithpath != '/images/sampledata/fruitshop/bananas_2.jpg' AND filewithpath != '/images/sampledata/fruitshop/fruits.gif' AND filewithpath != '/images/sampledata/fruitshop/tamarind.jpg' AND filewithpath != '/images/sampledata/parks/animals/180px_koala_ag1.jpg' AND filewithpath != '/images/sampledata/parks/animals/180px_wobbegong.jpg' AND filewithpath != '/images/sampledata/parks/animals/200px_phyllopteryx_taeniolatus1.jpg' AND filewithpath != '/images/sampledata/parks/animals/220px_spottedquoll_2005_seanmcclean.jpg' AND filewithpath != '/images/sampledata/parks/animals/789px_spottedquoll_2005_seanmcclean.jpg' AND filewithpath != '/images/sampledata/parks/animals/800px_koala_ag1.jpg' AND filewithpath != '/images/sampledata/parks/animals/800px_phyllopteryx_taeniolatus1.jpg' AND filewithpath != '/images/sampledata/parks/animals/800px_wobbegong.jpg' AND filewithpath != '/images/sampledata/parks/banner_cradle.jpg' AND filewithpath != '/images/sampledata/parks/landscape/120px_pinnacles_western_australia.jpg' AND filewithpath != '/images/sampledata/parks/landscape/120px_rainforest_bluemountainsnsw.jpg' AND filewithpath != '/images/sampledata/parks/landscape/180px_ormiston_pound.jpg' AND filewithpath != '/images/sampledata/parks/landscape/250px_cradle_mountain_seen_from_barn_bluff.jpg' AND filewithpath != '/images/sampledata/parks/landscape/727px_rainforest_bluemountainsnsw.jpg' AND filewithpath != '/images/sampledata/parks/landscape/800px_cradle_mountain_seen_from_barn_bluff.jpg' AND filewithpath != '/images/sampledata/parks/landscape/800px_ormiston_pound.jpg' AND filewithpath != '/images/sampledata/parks/landscape/800px_pinnacles_western_australia.jpg' AND filewithpath != '/images/sampledata/parks/parks.gif' ORDER BY filewithpath DESC "; $limitIt = 'LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery('SELECT * ' . $sql . $limitIt); $files = $this->_db->LoadObjectList(); foreach ($files as $k => $file) { if (file_exists(JPATH_BASE . $file->filewithpath)) { unset($files[$k]); } } if (true === $internal) { return $files; } $this->_db->setQuery('SELECT count(*) ' . $sql . $limitIt); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * Tool57 Delete files which have zero bytes (no content) as they just litter the webspace and run up inode counts. * Joomla doesnt rely on zero byte files, we have seen "other hack cleanup companies" litter the webspace with zero * byte files and so this tool deletes those too. */ private function deleteZerobyteFiles() { $sql = 'SELECT filewithpath FROM bf_files WHERE size = 0'; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); $filesDeleted = []; $count = 0; foreach ($files as $file) { $fullFilePath = JPATH_BASE . $file->filewithpath; if (@unlink($fullFilePath)) { ++$count; $filesDeleted[] = $file->filewithpath; $sql = sprintf('DELETE FROM bf_files WHERE filewithpath = " % s"', $file->filewithpath); $this->_db->setQuery($sql); $this->_db->execute(); } } bfEncrypt::reply('success', [ 'files' => $filesDeleted, 'total' => $count, ]); } /** * @param bool $internal * * @return array|mixed */ private function getEncrypted($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; } $sql = 'SELECT * FROM bf_files WHERE encrypted = 1 ORDER BY filemtime DESC LIMIT ' . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery('SELECT count(*) FROM bf_files WHERE encrypted = 1'); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * @param bool $internal * * @return JUser|mixed|object */ private function getUser($internal = false) { $row = null; switch ($this->_dataObj->searchfield) { case 'username': $sql = "SELECT * FROM #__users WHERE username = '%s'"; $sql = sprintf($sql, $this->_dataObj->searchvalue); $this->_db->setQuery($sql); $row = $this->_db->loadObject(); break; case 'id': $row = new User(); $row->load((int) $this->_dataObj->searchvalue); break; } if ($row->id) { // NEVER let the users password leave the remote site $row->password = '**REMOVED**'; } if (true === $internal) { return $row; } bfEncrypt::reply('success', [ 'user' => $row, ]); } /** * get $root_user from the configuration.php. */ private function getHasrootuser() { bfEncrypt::reply('success', [ 'root_user' => Factory::getApplication()->getConfig()->get('root_user', ''), ]); } /** * set debug_lang to false in the configuration.php. */ private function setHasrootuser() { $this->_setConfigParam('root_user', '', 'string', true); bfEncrypt::reply('success', [ 'root_user' => '', ]); } /** * get debug_lang from the configuration.php. */ private function getDebuglanguage() { bfEncrypt::reply('success', [ 'debug_lang' => Factory::getApplication()->getConfig()->get('debug_lang', ''), ]); } /** * remove $root_user from the configuration.php. */ private function setDebuglanguage() { $this->_setConfigParam('debug_lang', ('true' == $this->_dataObj->s ? false : true), 'bool', true); bfEncrypt::reply('success', [ 'debug_lang' => '', ]); } /** * remove $live_site from the configuration.php. */ private function removeLiveSite() { $this->_setConfigParam('live_site', '', 'string', true); bfEncrypt::reply('success', []); } /** * set the log_path and tmp_path to sane defaults. * * @throws exception Exception */ private function setLogTmpPaths() { // Require more complex methods for dealing with files require 'bfFilesystem.php'; try { // sane and recommended defaults $logpath = JPATH_ROOT . \DIRECTORY_SEPARATOR . 'administrator/logs'; $tmpath = JPATH_ROOT . \DIRECTORY_SEPARATOR . 'tmp'; // force creation and set sane permissions @mkdir($logpath); @mkdir($tmpath); @chmod($logpath, 0755); @chmod($tmpath, 0755); $config = Factory::getApplication()->getConfig(); if (version_compare(JVERSION, '3.0', 'ge')) { $config->set('log_path', $logpath); $config->set('tmp_path', $tmpath); } else { $config->setValue('config.log_path', $logpath); $config->setValue('config.tmp_path', $tmpath); } $newConfig = $config->toString('PHP', [ 'class' => 'JConfig', 'closingtag' => false, ]); // On some occasions, Joomla! 1.6 ignores the configuration and // produces "class c". Let's fix this! $newConfig = str_replace('class c {', 'class JConfig {', $newConfig); $newConfig = str_replace('namespace c;', '', $newConfig); // Try to write out the configuration.php $filename = JPATH_ROOT . \DIRECTORY_SEPARATOR . 'configuration.php'; $result = Bf_Filesystem::_write($filename, $newConfig); if (false !== $result) { bfEncrypt::reply('success', [ 'log_path' => $logpath, 'tmp_path' => $tmpath, 'config_file' => $filename, ]); } else { bfEncrypt::reply(bfReply::ERROR, [ 'msg' => 'Could Not Save Config', ]); } } catch (Exception $e) { bfEncrypt::reply(bfReply::ERROR, [ 'msg' => $e->getMessage(), ]); } } /** * Enable SEF and SEF Rewrite. */ private function setSEFConfig() { if ('true' == $this->_dataObj->s) {// true means, set to the OK value $this->_setConfigParam('sef', true, 'bool', true); $this->_setConfigParam('sef_rewrite', true, 'bool', true); $this->_setConfigParam('sef_suffix', false, 'bool', true); } else { $this->_setConfigParam('sef', false, 'bool', true); $this->_setConfigParam('sef_rewrite', false, 'bool', true); $this->_setConfigParam('sef_suffix', false, 'bool', true); } bfEncrypt::reply('success', $this->getSEFConfig()); } /** * Get the settings for the SEF from Joomla Global Config. * * public $sef = '1'; public $sef_rewrite = '0'; public $sef_suffix = '0'; */ private function getSEFConfig() { $config = Factory::getApplication()->getConfig(); $data = [ 'sef' => $config->get('sef'), 'sef_rewrite' => $config->get('sef_rewrite'), 'sef_suffix' => $config->get('sef_suffix'), ]; bfEncrypt::reply('success', $data); } /** * Set Cookie Settings right. */ private function setCookieSettings() { $this->_setConfigParam('cookie_domain', '', 'string', true); $this->_setConfigParam('cookie_path', '', 'string', true); bfEncrypt::reply('success', $this->getCookieSettings()); } /** * Get the settings for the cookie from config. * * public $cookie_domain public $cookie_path */ private function getCookieSettings() { $config = Factory::getApplication()->getConfig(); bfEncrypt::reply('success', [ 'cookie_domain' => $config->get('cookie_domain'), 'cookie_path' => $config->get('cookie_path'), ]); } /** * @throws exception Exception */ private function setDbPrefix() { try { $prefix = $this->_dataObj->prefix; $prefix = $this->_validateDbPrefix($prefix); $config = Factory::getApplication()->getConfig(); $oldprefix = $config->get('dbprefix', ''); $dbname = $config->get('db', ''); $db = $this->_db; $sql = "SHOW TABLES WHERE `Tables_in_{$dbname}` like '{$oldprefix}%'"; $db->setQuery($sql); $oldTables = $db->loadColumn(); if (empty($oldTables)) { throw new Exception('Could not find any tables with the old prefix to change to the new prefix'); } foreach ($oldTables as $table) { $newTable = $prefix . substr($table, strlen($oldprefix)); $sql = "RENAME TABLE `$table` TO `$newTable`"; $db->setQuery($sql); if (! $db->execute()) { // Something went wrong; I am pulling the plug and hope for // the best throw new Exception('Something went wrong; I am pulling the plug and hope for the best - Contact our support URGENTLY'); } } $this->_setConfigParam('prefix', $prefix, 'string', true); bfEncrypt::reply('success', [ 'prefix' => $prefix, ]); } catch (Exception $exception) { bfEncrypt::reply(bfReply::ERROR, [ 'msg' => $exception->getMessage(), ]); } } /** * Validates a prefix. The prefix must be 3-6 lowercase characters followed by an underscore and must not alrady * exist in the current database. It must also not be jos_ or bak_. * * @param string $prefix * The prefix to check * * @return string bool validated prefix or false if the prefix is invalid * * @throws exception * * @copyright Copyright (c)2010-2011 Nicholas K. Dionysopoulos */ private function _validateDbPrefix($prefix) { // Check that the prefix is not jos_ or bak_ if (('jos_' == $prefix) || ('bak_' == $prefix)) { throw new exception('Cannot be a standard prefix like jos_ or bak_'); } // Check that we're not trying to reuse the same prefix $config = Factory::getApplication()->getConfig(); $oldprefix = $config->get('dbprefix', ''); if ($prefix == $oldprefix) { throw new exception('Cannot be the same as existing prefix'); } // Check the length $pLen = strlen($prefix); if (($pLen < 4) || ($pLen > 6)) { throw new exception('Prefix must be between 4 and 6 chars'); } // Check that the prefix ends with an underscore if ('_' != substr($prefix, -1)) { throw new exception('Prefix must end with an underscore'); } // Check that the part before the underscore is lowercase letters $valid = preg_match('/[\w]_/i', $prefix); if (0 === $valid) { throw new exception('Prefix must be all lowercase'); } // Turn the prefix into lowercase $prefix = strtolower($prefix); // Check if the prefix already exists in the database $db = $this->_db; $dbname = $config->get('db', ''); $sql = "SHOW TABLES WHERE `Tables_in_{$dbname}` like '{$prefix}%'"; $db->setQuery($sql); if (version_compare(JVERSION, '3.0', 'ge')) { $existing_tables = $db->loadColumn(); } else { $existing_tables = $db->loadResultArray(); } if (count($existing_tables)) { // Sometimes we have false alerts, e.g. a prefix of dev_ will match // tables starting with dev15_ or dev16_ $realCount = 0; foreach ($existing_tables as $check) { if (substr($check, 0, $pLen) == $prefix) { ++$realCount; break; } } if ($realCount) { throw new exception('Prefix already exists in the database'); } } return $prefix; } /** * Update details of a user, including a hashed password. * * @todo Not sure this is ever called anymore (April 2018) */ private function setUser() { $email = $this->_dataObj->email; $pass = $this->_dataObj->password; $username = $this->_dataObj->username; $where = $this->_dataObj->where; if (! $email || ! $pass || ! $username || ! $where) { bfEncrypt::reply('failure', [ 'msg' => 'Not all required parts set', ]); } $sql = 'UPDATE #__users SET username="%s", password="%s", email ="%s" WHERE %s'; $sql = sprintf($sql, $username, $pass, $email, $where); $this->_db->setQuery($sql); $id = $this->_db->execute(); bfEncrypt::reply('success', [ 'usersaved' => $id, ]); } /** * @param bool $internal * * @return array|mixed */ private function getErrorLogs($internal = false) { $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '9999999999999999'; //pah } $sql = "SELECT * FROM bf_files WHERE filewithpath LIKE '%error_log' ORDER BY filemtime DESC LIMIT " . (int) $limitstart . ', ' . $limit; $this->_db->setQuery($sql); $files = $this->_db->LoadObjectList(); if (true === $internal) { return $files; } $this->_db->setQuery("SELECT count(*) FROM bf_files WHERE filewithpath LIKE '%error_log'"); $count = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'files' => $files, 'total' => $count, ]); } /** * Save the robots.txt file. */ private function saveRobotsFile() { if (file_put_contents(JPATH_BASE . '/robots.txt', base64_decode($this->_dataObj->filecontents))) { bfEncrypt::reply('success', [ 'msg' => 'File saved!', ]); } else { bfEncrypt::reply('error', [ 'msg' => 'File could not be saved!', ]); } } /** * ok ok I know this looks bad, it probably is, but this allows a subscriber to edit a file on mysites.guru and then * save the contents back to mysites.guru. * * In order to get to this method a lot of security jumps have to have gone through already * * Its not as insecure as first seen... promise :) */ private function saveFile() { require 'bfFilesystem.php'; if (! $this->_dataObj->filename || ! $this->_dataObj->filecontents) { bfEncrypt::reply('error', [ 'msg' => 'No file name or file contents were provided!', ]); } if (file_exists(JPATH_BASE . $this->_dataObj->filename) && ! is_writable(JPATH_BASE . $this->_dataObj->filename)) { bfEncrypt::reply('error', [ 'msg' => 'File not saved - as file is unwritable!', ]); } if (! file_exists(dirname(JPATH_BASE . $this->_dataObj->filename))) { if (! @mkdir(dirname(JPATH_BASE . $this->_dataObj->filename), 0755, true)) { bfEncrypt::reply('error', [ 'msg' => 'File not saved - could not create folder paths!', ]); } } $content = base64_decode($this->_dataObj->filecontents); if (! $content) { bfEncrypt::reply('error', [ 'msg' => 'File not saved - as no content sent to save into the file!', ]); } if (@Bf_Filesystem::_write(JPATH_BASE . $this->_dataObj->filename, $content)) { bfEncrypt::reply('success', [ 'msg' => 'File saved!', ]); } else { bfEncrypt::reply('error', [ 'msg' => 'No idea why, but file content could not be saved to ' . JPATH_BASE . $this->_dataObj->filename, ]); } } /** * get the contents of the robots.txt only if it exists in the cache tables. */ private function getRobotsFile() { $this->_db->setQuery('SELECT id from bf_files WHERE filewithpath = "/robots.txt"'); $id = $this->_db->loadResult(); if (! $id) { $obj = new stdclass(); $obj->filename = ''; $obj->filemd5 = md5(''); $obj->filewithpath = ''; $obj->filecontents = base64_encode( 'Could not load content for your own security, run a full audit before attempting to edit file content with mySites.guru' ); $obj->filesize = 0; $obj->basepath = JPATH_BASE; $obj->writeable = 0; bfEncrypt::reply('success', [ 'file' => $obj, ]); } $this->downloadfile($id); } /** * @param null $file_id */ private function downloadfile($file_id = null) { if (null === $file_id) { $file_id = (int) $this->_dataObj->f; } $this->_db->setQuery('SELECT filewithpath from bf_files WHERE id = ' . $file_id); $filename = $this->_db->loadResult(); $filewithpath = JPATH_BASE . $filename; if (file_exists($filewithpath)) { $contents = file_get_contents($filewithpath); $contentsbase64_encode = base64_encode($contents); $obj = new stdclass(); $obj->filename = $filename; $obj->filemd5 = md5($contents); $obj->filewithpath = $filewithpath; $obj->filecontents = $contentsbase64_encode; $obj->filesize = filesize($filewithpath); $obj->basepath = JPATH_BASE; $obj->writeable = is_writable($filewithpath); bfEncrypt::reply('success', [ 'file' => $obj, ]); } else { bfEncrypt::reply('error', [ 'msg' => 'File No Longer Exists!', ]); } } private function restorefile() { // Require more complex methods for dealing with files require 'bfFilesystem.php'; // get the cached data on the file $this->_db->setQuery('SELECT filewithpath FROM bf_files WHERE id = ' . $this->_dataObj->fileid); $file_to_restore_nopath = $this->_db->loadResult(); $file_to_restore = JPATH_BASE . $file_to_restore_nopath; $new_file_contents = base64_decode($this->_dataObj->filecontents); $new_md5 = md5($new_file_contents); if ($new_md5 !== $this->_dataObj->md5) { bfEncrypt::reply('failure', 'MD5 Check 1 Failed'); } $this->_db->setQuery('SELECT hash FROM bf_core_hashes WHERE filewithpath = "' . $file_to_restore_nopath . '"'); $core_md5 = $this->_db->loadResult(); if ($core_md5 !== $this->_dataObj->md5) { bfEncrypt::reply('failure', 'MD5 Check 2 Failed'); } $backup = file_get_contents($file_to_restore); Bf_Filesystem::_write($file_to_restore, $new_file_contents); if (md5_file($file_to_restore) !== $this->_dataObj->md5) { Bf_Filesystem::_write($file_to_restore, $backup); bfEncrypt::reply('failure', 'MD5 Check 3 Failed'); } $this->_db->setQuery( "UPDATE bf_files SET suspectcontent = 0 , hashfailed = 0 where filewithpath = '" . $file_to_restore_nopath . "'" ); $this->_db->execute(); bfEncrypt::reply('success', 'Restored OK'); } private function checkFTPLayer() { $config = Factory::getApplication()->getConfig(); $ftp_pass = $config->get('ftp_pass', ''); $ftp_user = $config->get('ftp_user', ''); $ftp_enable = $config->get('ftp_enable', ''); $ftp_port = $config->get('ftp_port', ''); $ftp_host = $config->get('ftp_host', ''); $ftp_root = $config->get('ftp_root', ''); if ($ftp_pass || $ftp_user || true === $ftp_enable || $ftp_host || $ftp_root || $ftp_port) { bfEncrypt::reply('success', 1); } else { bfEncrypt::reply('success', 0); } } private function disableFTPLayer() { $this->_setConfigParam('ftp_pass', '', 'string', true); $this->_setConfigParam('ftp_user', '', 'string', true); $this->_setConfigParam('ftp_host', '', 'string', true); $this->_setConfigParam('ftp_port', '', 'string', true); $this->_setConfigParam('ftp_root', '', 'string', true); $this->_setConfigParam('ftp_enable', false, 'string', true); bfEncrypt::reply('success', 1); } private function setFolderPermissions() { $fixed = 0; $errors = 0; $this->_db->setQuery('SELECT id, folderwithpath from bf_folders WHERE folderinfo = "777"'); $folders = $this->_db->loadObjectList(); foreach ($folders as $folder) { if (@chmod(JPATH_BASE . $folder->folderwithpath, 0755)) { ++$fixed; $this->_db->setQuery( 'UPDATE bf_folders SET folderinfo = "755" WHERE id = "' . (int) $folder->id . '" AND folderinfo = "777"' ); $this->_db->execute(); } else { ++$errors; } } $this->_db->setQuery('SELECT count(*) FROM bf_folders WHERE folderinfo LIKE "%777%"'); $folders_777 = $this->_db->LoadResult(); $res = new stdClass(); $res->errors = $errors; $res->fixed = $fixed; $res->leftover = $folders_777; bfEncrypt::reply('success', $res); } /** * I do some sanity checks then enable .htaccess. */ private function setHtaccess() { // Require more complex methods for dealing with files require 'bfFilesystem.php'; // init bfDatabase // To $htaccess = JPATH_BASE . \DIRECTORY_SEPARATOR . '.htaccess'; // From $htaccesstxt = JPATH_BASE . \DIRECTORY_SEPARATOR . 'htaccess.txt'; $res = new stdClass(); if (file_exists($htaccess)) { $res->result = 'ERROR'; $res->msg = '.htaccess file already exists!'; bfEncrypt::reply(bfReply::SUCCESS, $res); } if (! file_exists($htaccesstxt)) { $res->result = 'ERROR'; $res->msg = 'htaccess.txt file not found, cannot proceed'; bfEncrypt::reply(bfReply::SUCCESS, $res); } // Test we are on apache if (! preg_match('/Apache|LiteSpeed/i', $_SERVER['SERVER_SOFTWARE'])) { $res->result = 'ERROR'; $res->msg = 'Server reported its not running Apache/LiteSpeed, but is running ' . $_SERVER['SERVER_SOFTWARE']; bfEncrypt::reply(bfReply::SUCCESS, $res); } $didItWork = Bf_Filesystem::_write($htaccess, file_get_contents($htaccesstxt)); if (false == $didItWork) { $res->result = 'ERROR'; $res->msg = 'Could not copy htaccess.txt to .htaccess'; bfEncrypt::reply(bfReply::SUCCESS, $res); } $res->result = 'SUCCESS'; $res->msg = '.htaccess enabled! - Go and test your site!'; bfEncrypt::reply(bfReply::SUCCESS, $res); } /** * I set the new database credentials in /configuration.php after some testing. */ private function setDbCredentials() { // Require more complex methods for dealing with files require 'bfFilesystem.php'; $password = $this->_dataObj->p; $user = $this->_dataObj->u; $res = $this->testDbCredentials(true); if ('error' == $res->result) { bfEncrypt::reply(bfReply::ERROR, $res); } $this->_setConfigParam('user', $user, 'string', true); $this->_setConfigParam('password', $password, 'string', true); bfEncrypt::reply('success', [ 'msg' => 'Config saved!', 'dbs_visible' => 1, ]); } /** * @param bool $internal * * @return stdClass */ private function testDbCredentials($internal = false) { try { $config = Factory::getApplication()->getConfig(); $pass = $this->_dataObj->p; $user = $this->_dataObj->u; $host = $config->get('host', ''); $db = $config->get('db', ''); if (function_exists('mysql_connect')) { $link = @mysql_connect($host, $user, $pass); } else { $link = @mysqli_connect($host, $user, $pass); } $msg = new stdClass(); if (! $link) { if (function_exists('mysql_connect')) { $msg->msg = trim(mysql_error() . ' Could not connect to mysql server with supplied credentials'); } else { $msg->msg = trim(mysqli_error() . ' Could not connect to mysql server with supplied credentials'); } $msg->result = 'error'; if (true === $internal) { return $msg; } bfEncrypt::reply('success', $msg); } if (function_exists('mysql_connect')) { if (! @mysql_select_db($db, $link)) { $msg->msg = trim(mysql_error() . ' Mysql User exists, but has no access to the database'); $msg->result = 'error'; if (true === $internal) { return $msg; } bfEncrypt::reply('success', $msg); } } else { if (! @mysqli_select_db($link, $db)) { $msg->msg = trim(mysqli_error() . ' Mysql User exists, but has no access to the database'); $msg->result = 'error'; if (true === $internal) { return $msg; } bfEncrypt::reply('success', $msg); } } $msg->result = 'success'; if (true === $internal) { return $msg; } bfEncrypt::reply('success', $msg); } catch (Exception $e) { bfEncrypt::reply('error', 'exception: ' . $e->getMessage()); } } private function getUpdatesCount() { require 'bfUpdates.php'; $bfUpdates = new bfUpdates($this->_db); bfEncrypt::reply('success', [ 'count' => $bfUpdates->getupdates(true, $this->_dataObj->d), ]); } private function getUpdatesDetail() { @ob_start(); @set_time_limit(60); require 'bfUpdates.php'; $bfUpdates = new bfUpdates($this->_db); $updates = $bfUpdates->getupdates(false, $this->_dataObj->d); // remove any stray output echo ' '; // must have something to clean else warning occurs @ob_clean(); bfEncrypt::reply('success', [ 'current_joomla_version' => JVERSION, 'availableUpdates' => $updates['updates'], 'updateSites' => $updates['sites'], ]); } /** * Fix Db Schema version in the db. * * @since 20130929 */ private function fixDbSchema() { /** @var DatabaseModel $model */ $model = Factory::getApplication() ->bootComponent('com_installer') ->getMVCFactory() ->createModel('Database', 'Administrator', [ 'ignore_request' => true, ]); $model->fix([211]); /** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $updateModel */ $updateModel = Factory::getApplication()->bootComponent('com_joomlaupdate') ->getMVCFactory()->createModel('Update', 'Administrator', [ 'ignore_request' => true, ]); $updateModel->purge(); // Refresh versionable assets cache Factory::getApplication()->flushAssets(); $this->_db->setQuery('select extension_id from #__extensions where name = "files_joomla"'); $filesJoomlaId = $this->_db->loadResult(); $changeSet = new ChangeSet($this->_db, null); $model->fixSchemaVersion($changeSet, $filesJoomlaId); foreach ($model->getItems() as $item) { if ('com_admin' !== $item['extension']->element) { continue; } $version_latest = $item['extension']->version; $version_current = JVERSION; } bfEncrypt::reply('success', [ 'version_latest' => $version_latest, 'version_current' => $version_current, 'latest' => $changeSet->getSchema(), 'current' => $model->getSchemaVersion($filesJoomlaId), 'schema_errors' => $changeSet->check(), ]); } /** * Return the DB schema. * * @since 20130929 */ private function getDbSchemaVersion() { /** @var DatabaseModel $model */ $model = Factory::getApplication() ->bootComponent('com_installer') ->getMVCFactory() ->createModel('Database', 'Administrator', [ 'ignore_request' => true, ]); foreach ($model->getItems() as $item) { if ('com_admin' !== $item['extension']->element) { continue; } $version_latest = $item['extension']->version; $version_current = JVERSION; } $changeSet = new ChangeSet($this->_db, null); $this->_db->setQuery('select extension_id from #__extensions where name = "files_joomla"'); $filesJoomlaId = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'version_latest' => $version_latest, 'version_current' => $version_current, 'latest' => $changeSet->getSchema(), 'current' => $model->getSchemaVersion($filesJoomlaId), 'schema_errors' => $changeSet->check(), ]); } private function checkGoogleFile() { $found = false; $files = scandir(JPATH_BASE); foreach ($files as $file) { if (preg_match('/google.*\.html/', $file)) { $found = true; } } bfEncrypt::reply('success', [ 'found' => $found, ]); } /** * Toggles the @offline property of JConfig in /configuration.php. */ private function toggleOnline() { if ('true' == $this->_dataObj->s) { // false = set to online, true = set to offline return $this->_setConfigParam('offline', false, 'bool'); } else { return $this->_setConfigParam('offline', true, 'bool'); } } private function toggleCache() { if ('true' == $this->_dataObj->s) {// true means, set to the OK value return $this->_setConfigParam('caching', 1, 'bool'); } else { return $this->_setConfigParam('caching', 0, 'bool'); } } private function getOfflineStatus() { bfEncrypt::reply('success', [ 'offline' => Factory::getApplication()->getConfig()->get('offline'), ]); } private function getCacheStatus() { bfEncrypt::reply('success', [ 'caching' => Factory::getApplication()->getConfig()->get('caching'), ]); } /** * Install an extension from Url. */ private function doExtensionInstallFromUrl() { ob_start(); // Load up as much of Joomla as we need require 'bfExtensions.php'; $ext = new bfExtensions($this->_dataObj); $ext->installExtensionFromUrl(); } private function doExtensionUpgrade() { // Load up as much of Joomla as we need require 'bfExtensions.php'; // Support crappy extensions like OSMap that implement their own license manager via plugins PluginHelper::importPlugin('system'); // init reply to mysites.guru $result = []; $result['messages'] = []; // which row in the _updates table should we use $this->_db->setQuery('SELECT update_id from #__updates WHERE extension_id = "' . $this->_dataObj->eid . '"'); $extension_row_id = $this->_db->loadResult(); if (! $extension_row_id) { bfEncrypt::reply('error', [ 'result' => 'Could not find cached update to run, maybe its already been updated, check for updates before trying again', ]); } // Do the update $ext = new bfExtensions(); $result['result'] = $ext->doUpdate($extension_row_id); // Grab any error messages $result['messages'] = $this->_app->getMessageQueue(); // translate messages $lang = Factory::getLanguage(); $lang->load('com_installer', JPATH_ADMINISTRATOR, 'en-GB', true); $lang->load('lib_joomla', JPATH_ADMINISTRATOR, 'en-GB', true); if (count($result['messages'])) { foreach ($result['messages'] as &$msg) { $msg['message'] = Text::_($msg['message']); } } bfEncrypt::reply('success', [ 'result' => $result, ]); } private function checkAkeebaOutputDirectory() { try { // Check Akeeba Installed - Prerequisite if (! file_exists(JPATH_ADMINISTRATOR . '/components/com_akeeba/BackupEngine/Autoloader.php')) { bfEncrypt::reply('success', [ 'paths' => [], ]); } if (! defined('AKEEBAENGINE')) { define('AKEEBAENGINE', 1); } if (file_exists(JPATH_BASE . '/libraries/fof40/Autoloader/Autoloader.php')) { require JPATH_BASE . '/libraries/fof40/Autoloader/Autoloader.php'; } elseif (file_exists(JPATH_BASE . '/libraries/fof30/Autoloader/Autoloader.php')) { require JPATH_BASE . '/libraries/fof30/Autoloader/Autoloader.php'; } require JPATH_ADMINISTRATOR . '/components/com_akeeba/BackupEngine/Autoloader.php'; Platform::addPlatform('joomla3x', JPATH_ADMINISTRATOR . '/components/com_akeeba/BackupPlatform/Joomla3x'); if (class_exists('FOF30\Container\Container')) { $container = Container::getInstance('com_akeeba'); } else { $container = \FOF40\Container\Container::getInstance('com_akeeba'); } $profilesModel = $container->factory->model('Profiles')->tmpInstance(); $profiles = $profilesModel->get(true); $returnData = []; foreach ($profiles as $profile) { $config = json_decode(\Akeeba\Engine\Factory::getSecureSettings()->decryptSettings($profile->configuration)); $stockDirs = Platform::getInstance()->get_stock_directories(); $defaultOutputPath = $stockDirs['[DEFAULT_OUTPUT]']; $dir = $config->akeeba->basic->output_directory; if ('[DEFAULT_OUTPUT]' === $dir) { $dir = $defaultOutputPath; } $dir = realpath($dir); $returnData[] = [ 'path' => $dir, 'iswritable' => is_writable($dir), 'file_exists' => file_exists($dir), 'config' => $config, ]; } bfEncrypt::reply('success', [ 'paths' => $returnData, ]); } catch (Exception $e) { bfEncrypt::reply('error', [ 'msg' => $e->getMessage(), ]); } } /** * return a value from the config. */ private function getDebugMode() { bfEncrypt::reply('success', [ 'debug' => [ 'debug' => Factory::getApplication()->getConfig()->get('debug'), ], ]); } /** * set a value to the config. */ private function setDebugMode() { if ('true' == $this->_dataObj->s) {// true means, set to the OK value return $this->_setConfigParam('debug', false, 'bool'); } else { return $this->_setConfigParam('debug', true, 'bool'); } } /** * return a value from the config. */ private function getErrorReporting() { bfEncrypt::reply('success', [ 'error_reporting' => [ 'error_reporting' => JFactory::getApplication()->getConfig()->get('error_reporting'), ], ]); } /** * set a value to the config. */ private function setErrorReporting() { if ('true' == $this->_dataObj->s) {// true means, set to the OK value return $this->_setConfigParam('error_reporting', 'none', 'string'); } else { return $this->_setConfigParam('error_reporting', 'maximum', 'string'); } } /** * return the main configuration.php without sensitive information like passwords. */ private function getJoomlaLogTmpConfig() { $config = Factory::getApplication()->getConfig(); $data = [ 'log_path' => $config->get('log_path'), 'tmp_path' => $config->get('tmp_path'), 'base' => JPATH_BASE, ]; bfEncrypt::reply('success', [ 'paths' => $data, ]); } /** * Get the User Actions Log. * * Joomla 3.9.0 implemented a User Action Log which basically replicates what we used to do So now we load data from * their log and not ours :-) Saves duplicating our efforts! */ private function getActivityLog() { if (! class_exists('bfActivitylog')) { require_once 'bfActivitylog.php'; } $inst = bfActivitylog::getInstance(); $inst->ensureTableCreated(); $limitstart = (int) $this->_dataObj->ls; $limit = (int) $this->_dataObj->limit; if (! $limitstart) { $limitstart = 0; } if (! $limit) { $limit = '100'; } // Manipulate the base Uri in the Joomla Stack to provide compatibility with some 3pd extensions like ACL Manager! try { $uri = Uri::getInstance(); $reflection = new \ReflectionClass($uri); $baseProperty = $reflection->getProperty('base'); $baseProperty->setAccessible(true); $base = $baseProperty->getValue(); $base['prefix'] = $uri->toString(['scheme', 'host']); $base['path'] = '/'; $baseProperty->setValue($base); } catch (ReflectionException $e) { } /** @var ActionlogsModel $model */ $model = Factory::getApplication() ->bootComponent('com_actionlogs') ->getMVCFactory() ->createModel('Actionlogs', 'Administrator', [ 'ignore_request' => true, ]); // Set the Start and Limit $model->setState('list.start', $limitstart); $model->setState('list.limit', $limit); $model->setState('list.ordering', 'a.id'); $model->setState('list.direction', 'DESC'); $rows = $model->getItems(); // Load all language files needed ActionlogsHelper::loadActionLogPluginsLanguage(); $lang = Factory::getLanguage(); $lang->load('com_privacy', JPATH_ADMINISTRATOR, null, false, true); $lang->load('plg_system_actionlogs', JPATH_ADMINISTRATOR, null, false, true); $lang->load('plg_system_privacyconsent', JPATH_ADMINISTRATOR, null, false, true); // manipulate data to push to mysites.guru foreach ($rows as $row) { $row->what = ActionlogsHelper::getHumanReadableLogMessage($row); $row->ip = $row->ip_address; $row->when = $row->log_date; $row->who_id = $row->user_id; $row->source = 'core_user_action_log'; } bfEncrypt::reply('success', $rows ?: []); } /** * enable/disable and get status of our plugin. */ private function getBFPluginStatus() { if (property_exists($this->_dataObj, 'action')) { switch ($this->_dataObj->action) { case 'enable': $this->_db->setQuery("UPDATE `#__extensions` set enabled = 1 WHERE `name` = 'PLG_ACTIONLOG_JOOMLA'"); $this->_db->execute(); $this->_db->setQuery("UPDATE `#__extensions` set enabled = 1 WHERE `name` = 'PLG_SYSTEM_ACTIONLOGS'"); $this->_db->execute(); $this->_db->setQuery('UPDATE `#__extensions` SET enabled = 1 WHERE element = "bfnetwork"'); $this->_db->execute(); break; case 'disable': $this->_db->setQuery('UPDATE `#__extensions` SET enabled = 0 WHERE element = "bfnetwork"'); $this->_db->execute(); break; } } $this->_db->setQuery('SELECT enabled FROM #__extensions WHERE element = "bfnetwork"'); $result = $this->_db->loadResult(); bfEncrypt::reply('success', $result); } /** * get the list of users that have a 32 char password hash - e.g md5. */ private function getMD5PasswordUsers() { $this->_db->setQuery('SELECT id, username, name, password FROM #__users WHERE CHAR_LENGTH(password) = 32'); $result = $this->_db->loadObjectList(); bfEncrypt::reply('success', $result); } /** * Check the session gc plugin in Joomla 3. */ private function setSessionGCStatus() { if ('true' == $this->_dataObj->s) {// true means, set to the OK value $this->_db->setQuery("update #__extensions set enabled = 1 where name = 'plg_system_sessiongc'"); } else { $this->_db->setQuery("update #__extensions set enabled = 0 where name = 'plg_system_sessiongc'"); } $this->_db->execute(); bfEncrypt::reply('success', [ 'status' => $this->getSessionGCStatus(), ]); } /** * Check the session gc plugin in Joomla 3. */ private function getSessionGCStatus() { $res = 2; // Session GC $this->_db->setQuery("select count(*) from #__extensions where name = 'plg_system_sessiongc'"); $hasSessionGcPlugin = $this->_db->LoadResult(); if ($hasSessionGcPlugin) { $this->_db->setQuery("select enabled from #__extensions where name = 'plg_system_sessiongc'"); $res = $this->_db->LoadResult(); } bfEncrypt::reply('success', [ 'status' => $res, ]); } /** * Get the 2FA plugins. */ private function enable2FAPlugins() { if ('true' == $this->_dataObj->s) {// true means, set to the OK value $this->_db->setQuery("UPDATE `#__extensions` SET enabled = 1 WHERE `folder` = 'twofactorauth'"); } else { $this->_db->setQuery("UPDATE `#__extensions` SET enabled = 0 WHERE `folder` = 'twofactorauth'"); } $this->_db->LoadResult(); $this->get2FAPlugins(); } /** * Get the 2FA plugins. */ private function get2FAPlugins() { $this->_db->setQuery("SELECT * FROM `#__extensions` WHERE `folder` = 'twofactorauth'"); $res = $this->_db->loadObjectList(); bfEncrypt::reply('success', $res); } /** * set params from com_config without using a helper. */ private function setAdminFilterFixed() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE `element` = 'com_config'"); $params = json_decode($this->_db->LoadResult()); if ('true' == $this->_dataObj->s) {// true means, set to the OK value $params->filters->{7}->filter_type = 'BL'; } else { $params->filters->{7}->filter_type = 'NONE'; } $this->_db->setQuery(sprintf("UPDATE #__extensions set `params` = '%s' WHERE `element` = 'com_config'", json_encode($params))); $this->_db->execute(); return $this->getAdminFilterFixed(); } /** * Load params from com_config without using a helper. */ private function getAdminFilterFixed() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE element = 'com_config'"); $params = json_decode($this->_db->LoadResult()); bfEncrypt::reply('success', $params->filters->{7}); } /** * set params from com_config without using a helper. */ private function setUserFilterFixed() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE `element` = 'com_config'"); $params = json_decode($this->_db->LoadResult()); $params->filters->{1}->filter_type = 'NH'; $params->filters->{2}->filter_type = 'NH'; $params->filters->{9}->filter_type = 'NH'; $this->_db->setQuery(sprintf("UPDATE #__extensions set `params` = '%s' WHERE `element` = 'com_config'", json_encode($params))); $this->_db->execute(); return $this->getUserFilterFixed(); } /** * Load params from com_config without using a helper. */ private function getUserFilterFixed() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE element = 'com_config'"); $params = json_decode($this->_db->LoadResult()); $ret = 1; if ('NH' != $params->filters->{1}->filter_type) { $ret = 0; } if ('NH' != $params->filters->{2}->filter_type) { $ret = 0; } if ('NH' != $params->filters->{9}->filter_type) { $ret = 0; } bfEncrypt::reply('success', $ret); } /** * set params from com_config without using a helper. */ private function setPlaintextpasswords() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE `element` = 'com_users'"); $params = json_decode($this->_db->LoadResult()); if ('true' == $this->_dataObj->s) {// true means, set to the OK value $params->sendpassword = '0'; } else { $params->sendpassword = '1'; } $this->_db->setQuery(sprintf("UPDATE #__extensions set `params` = '%s' WHERE `element` = 'com_users'", json_encode($params))); $this->_db->execute(); $this->getPlaintextpasswords(); } /** * Load params from com_config without using a helper. */ private function getPlaintextpasswords() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE element = 'com_users'"); $params = json_decode($this->_db->LoadResult()); bfEncrypt::reply('success', [ 'sendpassword' => $params->sendpassword, ]); } /** * set params from com_content without using a helper. */ private function setMailtofrienddisabled() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE `element` = 'com_content'"); $params = json_decode($this->_db->LoadResult()); if ('true' == $this->_dataObj->s) {// true means, set to the OK value $params->show_email_icon = '0'; } else { $params->show_email_icon = '1'; } $this->_db->setQuery(sprintf("UPDATE #__extensions set `params` = '%s' WHERE `element` = 'com_content'", json_encode($params))); $this->_db->execute(); $this->getMailtofrienddisabled(); } /** * Load params from com_content without using a helper. */ private function getMailtofrienddisabled() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE element = 'com_content'"); $params = json_decode($this->_db->LoadResult()); bfEncrypt::reply('success', [ 'show_email_icon' => $params->show_email_icon, ]); } /** * set params from com_templates without using a helper. */ private function setTemplatePositionDisplay() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE `element` = 'com_templates'"); $params = json_decode($this->_db->LoadResult()); if ('true' == $this->_dataObj->s) {// true means, set to the OK value $params->template_positions_display = '0'; } else { $params->template_positions_display = '1'; } $this->_db->setQuery(sprintf("UPDATE #__extensions set `params` = '%s' WHERE `element` = 'com_templates'", json_encode($params))); $this->_db->execute(); $this->getTemplatePositionDisplay(); } /** * Load params from com_templates without using a helper. */ private function getTemplatePositionDisplay() { $this->_db->setQuery("SELECT `params` from #__extensions WHERE element = 'com_templates'"); $params = json_decode($this->_db->LoadResult()); bfEncrypt::reply('success', [ 'template_positions_display' => $params->template_positions_display, ]); } /** * Get the configuration of the google recaptcha plugin and global config. */ private function getCaptchaConfig() { $config = Factory::getApplication()->getConfig(); $this->_db->setQuery("SELECT enabled FROM #__extensions WHERE name ='plg_captcha_recaptcha'"); $enabled = $this->_db->loadResult(); $this->_db->setQuery("SELECT params FROM #__extensions WHERE name ='plg_captcha_recaptcha'"); $keyed = $this->_db->loadResult(); bfEncrypt::reply('success', [ 'enabled' => $enabled, 'configured' => $config->get('captcha', ''), 'keys' => json_decode($keyed), ]); } /** * Set the configuration of the google recaptcha plugin and global config. */ private function setCaptchaConfig() { $this->_db->setQuery(sprintf("UPDATE #__extensions SET enabled = 1, params = '{\"version\":\"2.0\",\"public_key\":\"%s\",\"private_key\":\"%s\",\"theme\":\"clean\",\"theme2\":\"light\",\"size\":\"normal\"}' WHERE name ='plg_captcha_recaptcha'", $this->_dataObj->site_key, $this->_dataObj->secret_key)); $this->_db->execute(); $this->_setConfigParam('captcha', 'recaptcha', 'string'); } /** * get the list of ACL Groups. */ private function getGroups() { $this->_db->setQuery('select id, title from #__usergroups'); bfEncrypt::reply('success', [ 'groups' => $this->_db->loadObjectList(), ]); } /** * get the list of super admins. */ private function getSuperAdmins() { $this->_db->setQuery('select id, name, username from #__users as u left join #__user_usergroup_map as m on u.id = m.user_id where m.group_id = ' . (int) $this->_dataObj->groupid); bfEncrypt::reply('success', [ 'users' => $this->_db->loadObjectList(), ]); } /** * 110 Identify Files That Existed In Last Audit, And Modified Before This Audit. */ private function getModifiedfilessincelastaudit() { $limitstart = (int) $this->_dataObj->ls; $sort = $this->_dataObj->s; if (! $sort) { $sort = 'filewithpath'; } if (! in_array($sort, ['filewithpath', 'filemtime'])) { exit('Invalid Sort'); } if ('filemtime' === $sort) { $sort = 'filemtime DESC'; } $limit = (int) $this->_dataObj->limit; // Set the query $this->_db->setQuery( 'SELECT new.id, new.iscorefile, new.filewithpath, new.filemtime, new.fileperms, new.`size`, new.iscorefile from bf_files as new LEFT JOIN bf_files_last as old ON old.filewithpath = new.filewithpath WHERE old.currenthash != new.currenthash ORDER BY ' . $sort . ' LIMIT ' . $limitstart . ', ' . $limit ); // Get an object list of files $files = $this->_db->loadObjectList(); // see how many files there are in total without a limit $sql = 'select count(*) from `bf_files` as new LEFT JOIN bf_files_last as old ON old.filewithpath = new.filewithpath WHERE old.currenthash != new.currenthash'; $this->_db->setQuery($sql); $count = $this->_db->loadResult(); // Only show files that still exist on the hard drive $existingFiles = []; foreach ($files as $k => $file) { if (file_exists(JPATH_BASE . $file->filewithpath)) { $existingFiles[] = $file; } else { $this->_db->setQuery(sprintf('DELETE FROM bf_files WHERE filewithpath = "%s"', $file->filewithpath)); $this->_db->execute(); --$count; } } // return an encrypted reply bfEncrypt::reply('success', [ 'files' => $existingFiles, 'total' => $count, ]); } /** * Activate realtime alerting. */ private function setRealtimeActivate() { $data = [ 'until' => $this->_dataObj->until, 'endpoint' => $this->_dataObj->endpoint, ]; bfEncrypt::reply('success', [ 'file_exists' => file_put_contents(__DIR__ . '/tmp/realtime.php', json_encode($data)), ]); } public function getSendUpdateEmails() { $sql = 'select count(*) from #__extensions where type = "plugin" and element = "updatenotification" and enabled = 1'; $this->_db->setQuery($sql); bfEncrypt::reply('success', $this->_db->LoadResult()); } public function setSendUpdateEmails() { $state = $this->_dataObj->s==='true' ? 0 : 1; $sql = 'UPDATE #__extensions SET enabled = ' . $state . ' WHERE type = "plugin" and element = "updatenotification"'; $this->_db->setQuery($sql); $this->_db->execute(); $this->getSendUpdateEmails(); } public function getLogEverything() { bfEncrypt::reply('success', [ 'enabled' => (int) Factory::getApplication()->getConfig()->get('log_everything', '0'), ]); } public function setLogEverything() { if ('true' == $this->_dataObj->s) {// true means, set to the OK value return $this->_setConfigParam('log_everything', 0, 'int'); } else { return $this->_setConfigParam('log_everything', 1, 'int'); } } public function getLogDeprecated() { bfEncrypt::reply('success', [ 'enabled' => (int) Factory::getApplication()->getConfig()->get('log_deprecated', '0'), ]); } public function setLogDeprecated() { if ('true' == $this->_dataObj->s) {// true means, set to the OK value return $this->_setConfigParam('log_deprecated', 0, 'int'); } else { return $this->_setConfigParam('log_deprecated', 1, 'int'); } } } try { // init this class $securityController = new bfTools($dataObj); // Run the tool method $securityController->run(); } catch (PrepareStatementFailureException $exception) { echo $exception->getMessage(); } catch (Exception $exception) { echo $exception->getMessage(); }