Your IP : 216.73.216.85


Current Path : /home/smartconb/www/armencom33/administrator/components/com_eyesite/helpers/
Upload File :
Current File : /home/smartconb/www/armencom33/administrator/components/com_eyesite/helpers/eyesite_scanner.php

<?php
/********************************************************************
Product		: Eyesite
Date		: 24 June 2024
Copyright	: Les Arbres Design 2009-2024
Contact		: https://www.lesarbresdesign.info
Licence		: GNU General Public License
*********************************************************************/
defined('_JEXEC') or die('Restricted Access');

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Language\Multilanguage;

if (class_exists("LAE_scanner"))
	return;

#[\AllowDynamicProperties]
class LAE_scanner
{

//-------------------------------------------------------------------------------
// Initialise and call the main scanning function
//
function scan()
{
	$this->app = Factory::getApplication();
	$jinput = Factory::getApplication()->input;
	$this->source = $jinput->get('source','', 'STRING');
	$this->entry = $jinput->get('entry','', 'STRING');
	$this->db_model = new LAE_model;
   
// check the entry requirements

    if (!$this->check_entry_requirements())
        return;
        
// handle any errors

	$this->history_model = new EyesiteModelHistory;             // handle_exception() and handle_error() use this
    set_exception_handler(array($this, 'handle_exception'));
    set_error_handler(array($this, 'handle_error'));
                     
// try to avoid time-outs

	ignore_user_abort(true);
	$time_limit = ini_get('max_execution_time'); 
	LAE_trace::trace('Scan started by '.$this->source.', working directory = '.getcwd().", max_execution_time = $time_limit",true);
    try {@set_time_limit(0);}
		catch (RuntimeException $e) {LAE_trace::trace("set_time_limit() is not allowed");}
    try {@ini_set('max_execution_time', 0);}
		catch (RuntimeException $e) {LAE_trace::trace("ini_set() is not allowed");}
	$time_limit = ini_get('max_execution_time'); 
	LAE_trace::trace("met: $time_limit");		// even if this reports 0, the server will probably ignore it

// more initialisation

	$this->sitename = $this->app->get('sitename');
	$this->error_count = 0;
	$this->change_count = 0;
	$this->config_model = new EyesiteModelConfig;
	$this->config_data = $this->config_model->getData();
	$this->extensions = LAE_helper::expand_cs_list($this->config_data->extensions);		// get array of included file extensions
	$incdirs = str_replace('%SITE_ROOT%', JPATH_ROOT, $this->config_data->incdirs);		// resolve %SITE_ROOT%
	$this->incdirs = LAE_helper::expand_cs_list($incdirs);								// get array of included directories
	$excdirs = str_replace('%SITE_ROOT%', JPATH_ROOT, $this->config_data->excdirs);		// resolve %SITE_ROOT%
	$this->excdirs = LAE_helper::expand_cs_list($excdirs);								// get array of excluded directories
	$excfiles = str_replace('%SITE_ROOT%', JPATH_ROOT, $this->config_data->excfiles);	// resolve %SITE_ROOT%
	$this->excfiles = LAE_helper::expand_cs_list($excfiles);							// get array of excluded files
	if ($this->config_data->case_sensitive)
		$this->query_binary = 'BINARY ';
	else
		$this->query_binary = '';
	$this->load_language($this->config_data->scan_language);
	$this->joomla_update = false;
	$this->new_extensions = [];
	$this->updated_extensions = [];
    $scan_start_time = microtime(true);

// purge the history table

	$this->db_model->ladb_execute("DELETE FROM `#__eye_site_history` WHERE DATEDIFF(CURDATE(),`datetime`) > ".$this->config_data->days_to_keep);

// accept previous changes and count how many files we are monitoring

	$data_model = new EyesiteModelData;
	$data_model->accept_all();
	$this->initial_count = $this->db_model->ladb_loadResult("SELECT Count(*) From `#__eye_site`");

// create a scan start history record

	if ($this->initial_count === false)
		{
		LAE_trace::trace($this->db_model->ladb_error_text);
		$this->add_history($this->db_model->ladb_error_text);
		$this->add_history(Text::_('COM_EYESITE_SCANNER_ABANDONING'));
		}
	if ($this->source == 'plugin')
		$started_by = Text::_('COM_EYESITE_STARTED_BY_PLUGIN');
	else
		$started_by = Text::_('COM_EYESITE_STARTED_BY_ADMIN');
	$total_files = Text::_('COM_EYESITE_TOTAL_FILES').': '.$this->initial_count;
	$this->history_summary = $started_by.'. '.$total_files;
	$this->history_model->store(LAE_HISTORY_SCAN_STARTED, $this->history_summary, '');
	if ($this->initial_count === false)
		return;
	$email_body = $started_by.' @ '.date(LAE_SQL_DATE_FORMAT).'. '.$total_files;
	$this->history_details = '';

// call the main scanner function

	if ($this->main_scan() == 2)    // database error
		{
		LAE_trace::trace("main_scan returned an error: ".$this->db_model->ladb_error_text);
		$this->add_history($this->db_model->ladb_error_text);
		$this->add_history(Text::_('COM_EYESITE_SCANNER_ABANDONING'));
		$this->error_count ++;
		}
    $scan_end_time = microtime(true);
	$this->scan_runtime = round(($scan_end_time - $scan_start_time),1);
	
// create history records

	$this->history_state = LAE_HISTORY_SCAN_NO_CHANGES;
	$this->history_summary = Text::_('COM_EYESITE_SCANNER_NO_NEW');
	if ($this->change_count > 0)
		{
		$this->history_state = LAE_HISTORY_SCAN_CHANGES;
		$this->history_summary = Text::sprintf('COM_EYESITE_X_NEW_CHANGES',$this->change_count);
		}		
	if ($this->error_count > 0)
		{
		$this->history_state = LAE_HISTORY_SCAN_ERROR;
		$this->history_summary = Text::_('COM_EYESITE_SCANNER_ERRORS');
		}
	$this->total_at_end = $this->db_model->ladb_loadResult("SELECT COUNT(*) FROM `#__eye_site` WHERE `state` != ".LAE_STATE_DELETED);
	if ($this->total_at_end > 0)
		$this->history_summary .= '. '.Text::_('COM_EYESITE_TOTAL_FILES').': '.$this->total_at_end;
	if ($this->joomla_update)
		{
		$joomla_now = Text::_('COM_EYESITE_JOOMLA_NOW').' '.JVERSION;
		$this->history_summary .= '. '.$joomla_now;
		$this->history_model->store(LAE_HISTORY_UPDATE_JOOMLA, $joomla_now, '');
		}
	if (!empty($this->new_extensions))
		{
		$extensions = self::name_version_list($this->new_extensions);
		$count = count($extensions);
		if ($count < 10)
			$summary = implode(', ', $extensions);
		else
			$summary = $count.' '.Text::_('COM_EYESITE_EXTENSIONS');
		$detail = serialize($this->new_extensions);
		$this->history_model->store(LAE_HISTORY_NEW_EXTENSION, $summary, $detail);
		}
	if (!empty($this->updated_extensions))
		{
		$extensions = self::name_version_list($this->updated_extensions);
		$count = count($extensions);
		if ($count < 10)
			$summary = implode(', ', $extensions);
		else
			$summary = $count.' '.Text::_('COM_EYESITE_EXTENSIONS');
		$detail = serialize($this->updated_extensions);
		$this->history_model->store(LAE_HISTORY_UPDATE_EXTENSION, $summary, $detail);
		}
	$history_with_runtime = $this->history_details.'<br>'.Text::sprintf('COM_EYESITE_SCAN_RUNTIME_X',$this->scan_runtime);
	$this->history_model->store($this->history_state, $this->history_summary, $history_with_runtime);

// send an email if we have new changes or any errors

	if (($this->config_data->emailto != '') && (($this->change_count > 0) || ($this->error_count > 0)))
		$this->send_email($email_body);
	else
		LAE_trace::trace("No email was sent: emailto=[".$this->config_data->emailto."] changes=".$this->change_count.", error_count=".$this->error_count);

	LAE_trace::trace('Scan complete, '.$this->change_count.' changes',true);
    $this->scan_unlock();
}

//-------------------------------------------------------------------------------
// Scan the configured directory structures
// Returns 0 if no errors, or 2 for a database error
//
function main_scan()
{
// set all states to RUNNING - any rows still in RUNNING state after the scan were not found in the file system

	$query = "UPDATE `#__eye_site` SET `state` = ".LAE_STATE_RUNNING;
	LAE_trace::trace($query);
	$result = $this->db_model->ladb_execute($query);
	if ($result === false)
		{
		LAE_trace::trace($this->db_model->ladb_error_text);
		return 2;
		}
	$rows_affected = $this->db_model->db->getAffectedRows();		
	LAE_trace::trace(" - affected $rows_affected rows");

// process all the directories
// this should set all the states to either OK or CHANGED
// and could create some with state NEW

	@clearstatcache();
	$numIncDirs = count($this->incdirs);
	for ($i = 0; $i < $numIncDirs; $i = $i+2)
		{
		LAE_trace::trace('Scanning directories, starting at: '.$this->incdirs[$i]);
		$filelist = $this->dirList($this->incdirs[$i],$this->incdirs[$i+1]);
		$this->db_model->ladb_execute("Start Transaction");
		$file_count = count($filelist);
		LAE_trace::trace("Directory scan found $file_count files. Starting to process...");
		foreach ($filelist as $file)
			{
			$ret = $this->process_file($file);
			if ($ret == 2)				// if we have a database problem, don't continue
				return 2;
			if ($ret == 1)				// if we have a file error, count it and continue - some files could be locked
				$this->error_count ++;
			}
		LAE_trace::trace("Finished processing $file_count files");
		$this->db_model->ladb_execute("Commit");
		}

// any records still in state "RUNNING" were not found in the file system and are newly deleted
// log the newly deleted files

	$query = "SELECT `filepath` FROM `#__eye_site` WHERE `state` = ".LAE_STATE_RUNNING;
	$rows = $this->db_model->ladb_loadObjectList($query);
	if ($rows === false)
		return 2;
	$numrows = count($rows);
	$this->change_count += $numrows;
	LAE_trace::trace("Found $numrows rows in state RUNNING after the scan");
	foreach ($rows as $row)
		$this->add_history(Text::_('COM_EYESITE_STATE_DELETED').': '.$row->filepath);

// set the newly deleted files to LAE_STATE_DELETED

	$query = "UPDATE `#__eye_site` SET `state` = ".LAE_STATE_DELETED.", `date_checked` = NOW() WHERE `state` = ".LAE_STATE_RUNNING;
	LAE_trace::trace("$query");
	$result = $this->db_model->ladb_execute($query);
	if ($result === false)
		{
		LAE_trace::trace($this->db_model->ladb_error_text);
		return 2;
		}
	$rows_affected = $this->db_model->db->getAffectedRows();		
	LAE_trace::trace(" - affected $rows_affected rows");
	return 0;
}

//-------------------------------------------------------------------------------
// Process one file
// Returns 0 if no errors, 1 for a file error, or 2 for a database error
//
function process_file($filepath)
{
	if (self::_in_arrayi($filepath, $this->excfiles))
		{
		LAE_trace::trace(" Excluding file: $filepath");
		return 0;							// this file is excluded
		}

	$quoted_filepath = $this->db_model->db->quote($filepath);

	if (!is_readable($filepath))
		{
		LAE_trace::trace(" Unreadable file: $filepath");
		$this->add_history(Text::_('COM_EYESITE_SCANNER_ERROR_READ').': '.$filepath);
		return 1;
		}

	$hash = md5_file($filepath); 
	if (!$hash)
		{
		LAE_trace::trace(" Unable to calculate md5 for: $filepath");
		$this->add_history(Text::_('COM_EYESITE_SCANNER_ERROR_MD5').': '.$filepath);
		return 1;
		}
		
	$query = "SELECT `md5`, `state`, UNIX_TIMESTAMP(`datetime`) AS unix_datetime, `filesize`
				FROM `#__eye_site` WHERE `filepath` = ".$this->query_binary.$quoted_filepath;
	$row = $this->db_model->ladb_loadObject($query);
	if ($row === false)
		return 2;

// if the file is not registered in the database, insert it in state NEW

	if (empty($row))
		{
		if (strrchr($filepath, '.') == '.xml')
			$this->check_xml_file($filepath, LAE_STATE_NEW);
		$this->add_history(Text::_('COM_EYESITE_STATE_NEW').":\t".$filepath);
		$filesize = filesize($filepath);		// allow zero length files
		$unix_filetime = filemtime($filepath);	// but not zero time files
		if (!$unix_filetime)
			{
			LAE_trace::trace(" Unable to get modification date for $filepath");
			$this->add_history(Text::_('COM_EYESITE_SCANNER_ERROR_DATE').': '.$filepath);
			return 1;
			}
		$sql_filetime = date(LAE_SQL_DATE_FORMAT, $unix_filetime);
		$query = "INSERT INTO `#__eye_site` (`filepath`, `md5`, `state`, `datetime`, `filesize`, `new_md5`, `new_datetime`, `new_filesize`, `date_added`, `date_checked`)
			VALUES ($quoted_filepath, '$hash', ".LAE_STATE_NEW.", '$sql_filetime', ".intval($filesize).", '', 0, 0, NOW(), 0)";
		LAE_trace::trace(' '.$query);
		$result = $this->db_model->ladb_execute($query);
		if ($result === false)
			return 2;
		$this->change_count ++;
		return 0;
		}
		
// here when the file is in the database
// if it already has a problem flagged, leave it alone

	if ($row->state != LAE_STATE_RUNNING)
		return 0;

// if the hash value matches, set the state to OK

	if ($row->md5 == $hash)
		{
		$query = "UPDATE `#__eye_site` SET `state` = ".LAE_STATE_OK.", `date_checked` = NOW() WHERE `filepath` = ".$this->db_model->db->quote($filepath);
		// LAE_trace::trace("$query");		// not normally needed and creates a huge trace file
		$result = $this->db_model->ladb_execute($query);
		if ($result === false)
			return 2;
		return 0;
		}
		
// the hash value doesn't match, so set the state to CHANGED and note the new file details

	if (strstr($filepath,'/libraries/src/Version.php'))		// if this file got changed, Joomla has been updated
		$this->joomla_update = true;
	if (strrchr($filepath, '.') == '.xml')
		$this->check_xml_file($filepath, LAE_STATE_CHANGED);
	$oldfilesize = $row->filesize;
	$newfilesize = filesize($filepath);
	$new_unix_filetime = filemtime($filepath);
	$new_sql_filetime = date(LAE_SQL_DATE_FORMAT, $new_unix_filetime);
	$change1 = Text::_('COM_EYESITE_STATE_CHANGED').' '.$filepath;
	$change2 = "&nbsp;&nbsp;&nbsp;".Text::_('COM_EYESITE_SCANNER_ORIG_DATE').' '.date(LAE_SQL_DATE_FORMAT, $row->unix_datetime).', '.Text::_('COM_EYESITE_SCANNER_SIZE').': '.$oldfilesize;
	$change3 = "&nbsp;&nbsp;&nbsp;".Text::_('COM_EYESITE_SCANNER_NEW_DATE').' '.date(LAE_SQL_DATE_FORMAT, $new_unix_filetime).', '.Text::_('COM_EYESITE_SCANNER_SIZE').': '.$newfilesize;
	$this->add_history($change1);
	$this->add_history($change2);
	$this->add_history($change3);
	$query = "UPDATE `#__eye_site` SET `state` = ".LAE_STATE_CHANGED.",	`new_md5` = '$hash', `new_datetime` = '$new_sql_filetime', `new_filesize` = ".intval($newfilesize)."
		WHERE `filepath` = $quoted_filepath";
	LAE_trace::trace(' '.$query);
	$result = $this->db_model->ladb_execute($query);
	if ($result === false)
		return 2;
	$this->change_count ++;
	return 0;
}

//-------------------------------------------------------------------------------
// list matching files in a directory and return as an array
//
function dirList($directory, $recurse)
{
static $depth = 0;
	if ($depth <= 2) 		// only trace two levels deep
		LAE_trace::trace(' '.$directory.', '.$recurse);
	$results = array();
	if (self::_in_arrayi($directory, $this->excdirs))
		{
		LAE_trace::trace("  Excluding directory: $directory");
		$depth --;
		return $results;							// this directory is excluded
		}
	if (!is_readable($directory))
        {
		$this->add_history(Text::_('COM_EYESITE_DIR_INC_ERROR3').' '.$directory);
		$this->error_count ++;
		$depth --;
        return $results;
        }

	if ($handle = opendir($directory)) 
		{
		while ($filename = readdir($handle))
			{
			if (($filename != '.') && ($filename != '..')) 
				{
				if (is_dir($directory."/".$filename)) 
					{
					if ($recurse == "S") 
						{
						$depth ++;
						$results = array_merge($results, $this->dirList($directory."/".$filename, $recurse));
						}
					} 
				else 
					{
					$ext = pathinfo($filename, PATHINFO_EXTENSION);// get the file extension
					if ((empty($this->extensions)) || (self::_in_arrayi($ext,$this->extensions)))
						{										// yes...
						$filename = $directory."/".$filename;	// make full pathname
						$results[] = $filename;					// and store in results array
						}
					}
				}
			}
		closedir($handle);
		}
	$depth --;
	return $results;
}

//-------------------------------------------------------------------------------
// Add text to the history buffer
//
function add_history($text)
{
	if (!empty($this->history_details))
		$this->history_details .= '<br>';
	$this->history_details .= $text;
}

//-------------------------------------------------------------------------------
// Case insensitive in_array
//
static function _in_arrayi($needle, $haystack) 
{
    foreach($haystack as $value)
        if (strtolower($value) == strtolower($needle)) 
			return true;
    return false;
}

//-------------------------------------------------------------------------------
// check if an xml file is an extension manifest
//
function check_xml_file($filepath, $state)
{
	if (strstr($filepath,'administrator/manifests'))		// don't check files here, they are copies
		return;
	$xml_array = Joomla\CMS\Installer\Installer::parseXMLInstallFile($filepath);
	if ($xml_array === false)
		return;
	$xml_array['filepath'] = $filepath;
	if ($state == LAE_STATE_NEW)
		$this->new_extensions[] = $xml_array;
	else
		$this->updated_extensions[] = $xml_array;
}

//-------------------------------------------------------------------------------
// make a list of extension names and their versions
//
static function name_version_list($extensions)
{
	$name_and_versions = [];
	foreach ($extensions as $extension)
		$name_and_versions[] = $extension['name'].' '.$extension['version'];
	return array_unique($name_and_versions);
}

//-------------------------------------------------------------------------------
// Check that the correct entry parameter was specified
// for admin scans it should be the site secret
// for plugin scans it must be the last runtime
// (the plugin Ajax path is potentially visible externally so the entry parameter must be hard to guess)
//
function check_entry_requirements()
{
    if (!$this::scan_lock())
		return;

    switch ($this->source)
        {
        case 'admin':
            if ($this->entry == $this->app->get('secret'))
                return true;
            LAE_trace::trace('Scanner stopping because admin entry parameter incorrect: '.$this->entry,true);
            return false;

        case 'plugin':
            $query = "SELECT `params` FROM `#__extensions` WHERE `type` = 'plugin' AND `folder` = 'system' AND `element` = 'eyesite'";
            $plugin_params = $this->db_model->ladb_loadResult($query);
            if ($plugin_params === false)
                {
                LAE_trace::trace("Scanner stopping because plugin entry parameter cannot be verified: ".$this->db_model->ladb_error_text);
                return false;
                }
            $plugin_params_object = json_decode($plugin_params);
            if (!empty($plugin_params_object->entry_code))
                $entry_code = $plugin_params_object->entry_code;
            else
                $entry_code = 0;
            if ($this->entry == $entry_code)
                {                                                           // ok, we are going to run so update the entry_code and last_run_time
                $plugin_params_object->last_run_time = time();
                $plugin_params_object->entry_code = uniqid(rand());
                $plugin_params = $this->db_model->db->quote(json_encode($plugin_params_object));
                $query = "UPDATE `#__extensions` SET `params` = $plugin_params WHERE `type` = 'plugin' AND `folder` = 'system' AND `element` = 'eyesite'";
                $this->db_model->ladb_execute($query);
                LAE_trace::trace("Scanner updating plugin last_run_time to ".$plugin_params_object->last_run_time." and entry_code to ".$plugin_params_object->entry_code,true);
                return true;
                }
            LAE_trace::trace('Scanner stopping because plugin entry parameter incorrect: '.$this->entry,true);
            return false;
        default:
            LAE_trace::trace('Scanner stopping because plugin source parameter incorrect: '.$this->source,true);
            return false;
        }
    return false;   // cannot get here
}

//-------------------------------------------------------------------------------
// load the scan language file
//
function load_language($scan_language)
{
	LAE_trace::trace("load_language ($scan_language)");
	$lang = Factory::getLanguage();
	if (!empty($this->config_data->scan_language))
		{
	    if (file_exists(JPATH_ADMINISTRATOR."/components/com_eyesite/language/$scan_language/$scan_language.com_eyesite.ini"))
			{
			LAE_trace::trace(" - loading: administrator/components/com_eyesite/language/$scan_language/$scan_language.com_eyesite.ini");
			$lang->load('com_eyesite', JPATH_ADMINISTRATOR.'/components/com_eyesite', $scan_language, true);
			return;
			}
		else
			LAE_trace::trace(" - no file: administrator/components/com_eyesite/language/$scan_language/$scan_language.com_eyesite.ini");
		}
	LAE_trace::trace(" - loading default language");
	$lang->load('com_eyesite', JPATH_ADMINISTRATOR.'/components/com_eyesite');
}

//-------------------------------------------------------------------------------
// send a scan report email
//
function send_email($email_body)
{
	$subject = Text::_('COM_EYESITE_EMAIL_SUBJECT_CHANGES').' ('.$this->sitename.')';
	if ($this->joomla_update)
		$subject .= ', '.Text::_('COM_EYESITE_JOOMLA_NOW').' '.JVERSION;
	$new_extensions = self::name_version_list($this->new_extensions);
	$updated_extensions = self::name_version_list($this->updated_extensions);
	$count_new = count($new_extensions);
	if ($count_new > 0)
		$subject .= ', '.Text::plural('COM_EYESITE_X_EXTENSIONS_NEW',$count_new);
	$count_updated = count($updated_extensions);
	if ($count_updated > 0)
		$subject .= ', '.Text::plural('COM_EYESITE_X_EXTENSIONS_UPDATED',$count_updated);
	if (!$this->joomla_update && ($count_new == 0) && ($count_updated == 0))
		$subject .= '. '.Text::_('COM_EYESITE_NO_UPDATES');
	if ($this->error_count > 0)
		$subject = Text::_('COM_EYESITE_EMAIL_SUBJECT_ERRORS').' ('.$this->sitename.')';
	$email_body .= '<br>'.Text::_('COM_EYESITE_TOTAL_CHANGES').': '.$this->change_count;

// new extensions added

	foreach ($new_extensions as $extension)
		$email_body .= '<br>'.Text::_('COM_EYESITE_ADDED').' '.$extension;

// updated extensions

	foreach ($updated_extensions as $extension)
		$email_body .= '<br>'.Text::_('COM_EYESITE_UPDATED').' '.$extension;

// runtime

	$email_body .= '<br>'.Text::sprintf('COM_EYESITE_SCAN_RUNTIME_X',$this->scan_runtime);

// file change details, if any

	if ($this->config_data->email_max_size > 0)
		{
		if (strlen($this->history_details) < $this->config_data->email_max_size)
			$email_body .= '<br><br>'.$this->history_details;
		else
			$email_body .= '<br><br>'.substr($this->history_details, 0, $this->config_data->email_max_size).'<br> -- '.Text::_('COM_EYESITE_TRUNCATED').' --';
		}

// send it and log the result

	LAE_trace::trace("Sending email to: ".$this->config_data->emailto.", Subject: $subject");
	$email_status = LAE_helper::send_email($this->config_data, $subject, $email_body);
	if ($email_status == '')
		{
		LAE_trace::trace("send_email ok");
		$this->history_model->store(LAE_HISTORY_EMAIL_OK, $subject, $email_body.'<br><br>'.Text::_('COM_EYESITE_EMAIL_ACCEPTED'));
		$this->add_history(Text::_('COM_EYESITE_EMAIL_SENT_TO').' '.$this->config_data->emailto);
		}
	else
		{
		LAE_trace::trace("send_email returned $email_status");
		$this->history_model->store(LAE_HISTORY_EMAIL_FAILED, $subject, $email_body.'<br><br>'.$email_status);
		$this->add_history($email_status);
		}
}

//-------------------------------------------------------------------------------
// returns true if we can acquire an exclusive lock on the lock file
// - the file handle cannot be local, because when the handle is destroyed, the file is unlocked
// returns true if the scan can run, false if not
//
function scan_lock()
{
	$app = Factory::getApplication();
	$tmp_path = $app->get('tmp_path');	// get the site temp directory from Joomla global configuration
	$lock_file_path = $tmp_path.'/eyesite.lock';
	if (!file_exists($lock_file_path))
        {
        if (@touch($lock_file_path) === false)
            {
            LAE_trace::trace("Unable to create lock file $lock_file_path. Scanner cannot run.");
            return false;
            }
        }
    $this->lock_handle = @fopen($lock_file_path, 'w');
    if ($this->lock_handle === false) 
        {
        LAE_trace::trace("Unable to open lock file $lock_file_path. Scanner cannot run.");
        return false;
        }
    $ret = flock($this->lock_handle, (LOCK_EX | LOCK_NB), $wouldblock); // returns true on success or false on failure
    LAE_trace::trace("flock returned ".($ret ? 'true':'false').', '.($wouldblock ? 'true':'false'));
    if (!$ret || $wouldblock)
		{
	    LAE_trace::trace("Unable to acquire lock. Scanner cannot run [$ret] [$wouldblock]",true);
        return false;
		}
    LAE_trace::trace("Scanner ok to run",true);
    return true;
}

//-------------------------------------------------------------------------------
// delete the scan lock file
//
function scan_unlock()
{
    flock ($this->lock_handle, LOCK_UN);
    fclose($this->lock_handle);
}

function handle_exception($e)
{
    $message = "Scanner exception: ".$e->getMessage();
    LAE_trace::trace($message,true);
	$this->history_model->store(LAE_HISTORY_SCAN_EXCEPTION, $message, '');
}

function handle_error($errno, $errstr, $errfile, $errline)
{
    if ($errno == 8192)     // don't report PHP deprecation error
        return;
	if ($errno == 16384)	// don't report Joomla deprecation errors
		return;
    $message = "Scanner Error $errno [$errstr] line $errline of $errfile";
    LAE_trace::trace($message,true);
	$this->history_model->store(LAE_HISTORY_SCAN_EXCEPTION, $message, '');
}

}