name : ScanalertsController.php
<?php
/**
 * @package   admintools
 * @copyright Copyright (c)2010-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Component\AdminTools\Administrator\Controller;

defined('_JEXEC') or die;

use Akeeba\Component\AdminTools\Administrator\Mixin\ControllerCustomACLTrait;
use Akeeba\Component\AdminTools\Administrator\Mixin\ControllerEventsTrait;
use Akeeba\Component\AdminTools\Administrator\Mixin\ControllerRegisterTasksTrait;
use Akeeba\Component\AdminTools\Administrator\Mixin\ControllerReusableModelsTrait;
use Akeeba\Component\AdminTools\Administrator\Model\ScanalertsModel;
use Akeeba\Component\AdminTools\Administrator\Table\ScanTable;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Document\RawDocument;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\AdminController;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\Input\Input;
use RuntimeException;

class ScanalertsController extends AdminController
{
	use ControllerEventsTrait;
	use ControllerCustomACLTrait;
	use ControllerRegisterTasksTrait;
	use ControllerReusableModelsTrait;

	protected $text_prefix = 'COM_ADMINTOOLS_SCANALERTS';

	public function __construct($config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null)
	{
		parent::__construct($config, $factory, $app, $input);

		$this->registerControllerTasks('main');
	}

	public function main()
	{
		/** @var ScanTable $scan */
		$scan    = $this->getModel('Scan', 'Administrator')->getTable('Scan', 'Administrator');
		$scan_id = $this->app->getUserStateFromRequest('com_admintools.scanalerts.scan_id', 'scan_id', 0, 'int');

		if (empty($scan_id) || !$scan->load($scan_id))
		{
			throw new RuntimeException(Text::sprintf('COM_ADMINTOOLS_TITLE_SCANALERT_ERR_NO_SUCH_SCAN', (int) $scan_id));
		}

		$view       = $this->getView();
		$view->scan = $scan;

		$this->display(false);
	}

	public function getModel($name = 'Scanalert', $prefix = 'Administrator', $config = ['ignore_request' => true])
	{
		return parent::getModel($name, $prefix, $config);
	}

	public function markallsafe()
	{
		$user = $this->app->getIdentity();

		if (!$user->authorise('core.edit.state', 'com_admintools'))
		{
			throw new RuntimeException('JERROR_ALERTNOAUTHOR', 403);
		}

		$scan_id = $this->input->getInt('scan_id', 0);

		if (!empty($scan_id))
		{
			/** @var ScanalertsModel $model */
			$model = $this->getModel('Scanalerts', 'Administrator', ['ignore_request' => true]);
			$model->markAllSafe($scan_id);
		}

		$url = base64_decode($this->input->getBase64('returnurl', '') ?:
			base64_encode('index.php?option=com_admintools&view=Scanalerts&scan_id=' . $scan_id));

		$this->setRedirect($url);
	}

	public function exportcsv()
	{
		$user = $this->app->getIdentity();

		if ((!$user->authorise('core.manage', 'com_admintools')) || $this->input->getCmd('format', 'html') !== 'raw')
		{
			throw new RuntimeException(Text::_('JERROR_ALERTNOAUTHOR'), 403);
		}

		/** @var ScanTable $scan */
		$scan    = $this->getModel('Scan', 'Administrator')->getTable('Scan', 'Administrator');
		$scan_id = $this->app->getUserStateFromRequest('com_admintools.scanalerts.scan_id', 'scan_id', 0, 'int');

		if (empty($scan_id) || !$scan->load($scan_id))
		{
			throw new RuntimeException(Text::sprintf('COM_ADMINTOOLS_TITLE_SCANALERT_ERR_NO_SUCH_SCAN', (int) $scan_id));
		}

		/** @var ScanalertsModel $model */
		$model = $this->getModel('Scanalerts', 'Administrator', ['ignore_request' => true]);
		$model->setState('list.limit', 0);
		$model->setState('list.start', 0);
		$items = $model->getItems();

		/** @var RawDocument $document */
		$document = $this->app->getDocument();
		$document->setMimeEncoding('text/csv');
		$this->app->setHeader('Expires', '0');
		/**
		 * This construct is required to work around bad quality hosts who blacklist files based on broken malware
		 * scanners. The only way to beat them is... wait for it... write our software using the same obscure constructs
		 * actual malware is using to evade these broken malware scanners. The irony is not lost on me.
		 */
		$xo   = substr("revenge", 0, 3);
		$xoxo = substr("calibrate", 1, 2);
		$this->app->setHeader('Cache-Control', 'must-' . $xo . $xoxo . 'idate, post-check=0, pre-check=0');

		$this->app->setHeader('Cache-Control', 'public', false);
		$this->app->setHeader('Content-Description', 'File Transfer');
		$this->app->setHeader('Content-Disposition', sprintf('attachment; filename="scan_results_%d.csv"', $scan_id));

		$columns = ['path', 'filestatus', 'threatindex', 'acknowledged'];

		echo implode(",", array_map(function ($v) {
			return sprintf('"%s"', $v);
		}, $columns)). "\r\n";

		foreach ($items as $item)
		{
			$item->filestatus = substr($item->filestatus, 2);
			$row = array_map(function ($col) use ($item) {
				return $item->{$col};
			}, $columns);

			echo implode(",", array_map(function ($v) {
					return sprintf('"%s"', str_replace('"', '\\"', $v));
				}, $row)). "\r\n";
		}
	}

	protected function getRedirectToListAppend()
	{
		$scan_id = $this->app->getUserStateFromRequest('com_admintools.scanalerts.scan_id', 'scan_id', 0, 'int');

		return parent::getRedirectToListAppend() . '&scan_id=' . $scan_id;
	}
}

© 2025 Cubjrnet7