first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+330
View File
@@ -0,0 +1,330 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Manager class for antivirus integration.
*
* @package core_antivirus
* @copyright 2015 Ruslan Kabalin, Lancaster University.
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\antivirus;
defined('MOODLE_INTERNAL') || die();
/**
* Class used for various antivirus related stuff.
*
* @package core_antivirus
* @copyright 2015 Ruslan Kabalin, Lancaster University.
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class manager {
/**
* Returns list of enabled antiviruses.
*
* @return array Array ('antivirusname'=>stdClass antivirus object).
*/
private static function get_enabled() {
global $CFG;
$active = array();
if (empty($CFG->antiviruses)) {
return $active;
}
foreach (explode(',', $CFG->antiviruses) as $e) {
if ($antivirus = self::get_antivirus($e)) {
if ($antivirus->is_configured()) {
$active[$e] = $antivirus;
}
}
}
return $active;
}
/**
* Scan file using all enabled antiviruses, throws exception in case of infected file.
*
* @param string $file Full path to the file.
* @param string $filename Name of the file (could be different from physical file if temp file is used).
* @param bool $deleteinfected whether infected file needs to be deleted.
* @throws \core\antivirus\scanner_exception If file is infected.
* @return void
*/
public static function scan_file($file, $filename, $deleteinfected) {
global $USER;
$antiviruses = self::get_enabled();
$notifylevel = (int)get_config('antivirus', 'notifylevel');
foreach ($antiviruses as $antivirus) {
// Attempt to scan, catching internal exceptions.
try {
$result = $antivirus->scan_file($file, $filename);
} catch (\core\antivirus\scanner_exception $e) {
$notice = $antivirus->get_scanning_notice();
$incidentdetails = $antivirus->get_incident_details($file, $filename, $notice, false);
// Log scan error event.
$params = [
'context' => \context_system::instance(),
'relateduserid' => $USER->id,
'other' => ['filename' => $filename, 'incidentdetails' => $incidentdetails],
];
$event = \core\event\antivirus_scan_file_error::create($params);
$event->trigger();
// If there was a scanner exception (such as ClamAV denying
// upload), send messages (on error and above), and rethrow.
if ($notifylevel === $antivirus::SCAN_RESULT_ERROR) {
$notice = $antivirus->get_scanning_notice();
self::send_antivirus_messages($antivirus, $incidentdetails);
}
throw $e;
}
$notice = $antivirus->get_scanning_notice();
if ($result === $antivirus::SCAN_RESULT_FOUND) {
// Infection found, send notification.
$incidentdetails = $antivirus->get_incident_details($file, $filename, $notice);
self::send_antivirus_messages($antivirus, $incidentdetails);
// Move to quarantine folder.
$zipfile = \core\antivirus\quarantine::quarantine_file($file, $filename, $incidentdetails, $notice);
// If file not stored due to disabled quarantine, store a message.
if (empty($zipfile)) {
$zipfile = get_string('quarantinedisabled', 'antivirus');
}
// Log file infected event.
$params = [
'context' => \context_system::instance(),
'relateduserid' => $USER->id,
'other' => ['filename' => $filename, 'zipfile' => $zipfile, 'incidentdetails' => $incidentdetails],
];
$event = \core\event\virus_infected_file_detected::create($params);
$event->trigger();
if ($deleteinfected) {
unlink($file);
}
// Get custom message to display to user from antivirus engine.
$displaymessage = $antivirus->get_virus_found_message();
$placeholders = array_merge(['item' => $filename], $displaymessage['placeholders']);
throw new \core\antivirus\scanner_exception(
$displaymessage['string'],
'',
$placeholders,
null,
$displaymessage['component']
);
} else if ($result === $antivirus::SCAN_RESULT_ERROR) {
// Here we need to generate a different incident based on an error.
$incidentdetails = $antivirus->get_incident_details($file, $filename, $notice, false);
// Log scan error event.
$params = [
'context' => \context_system::instance(),
'relateduserid' => $USER->id,
'other' => ['filename' => $filename, 'incidentdetails' => $incidentdetails],
];
$event = \core\event\antivirus_scan_file_error::create($params);
$event->trigger();
// Send a notification if required (error or above).
if ($notifylevel === $antivirus::SCAN_RESULT_ERROR) {
self::send_antivirus_messages($antivirus, $incidentdetails);
}
}
}
}
/**
* Scan data steam using all enabled antiviruses, throws exception in case of infected data.
*
* @param string $data The variable containing the data to scan.
* @throws \core\antivirus\scanner_exception If data is infected.
* @return void
*/
public static function scan_data($data) {
global $USER;
$antiviruses = self::get_enabled();
$notifylevel = (int)get_config('antivirus', 'notifylevel');
foreach ($antiviruses as $antivirus) {
// Attempt to scan, catching internal exceptions.
try {
$result = $antivirus->scan_data($data);
} catch (\core\antivirus\scanner_exception $e) {
$notice = $antivirus->get_scanning_notice();
$incidentdetails = $antivirus->get_incident_details('', $filename, $notice, false);
// Log scan error event.
$params = [
'context' => \context_system::instance(),
'relateduserid' => $USER->id,
'other' => ['filename' => $filename, 'incidentdetails' => $incidentdetails],
];
$event = \core\event\antivirus_scan_file_error::create($params);
$event->trigger();
// If there was a scanner exception (such as ClamAV denying upload), send messages and rethrow.
if ($notifylevel === $antivirus::SCAN_RESULT_ERROR) {
$notice = $antivirus->get_scanning_notice();
$filename = get_string('datastream', 'antivirus');
self::send_antivirus_messages($antivirus, $incidentdetails);
}
throw $e;
}
$filename = get_string('datastream', 'antivirus');
$notice = $antivirus->get_scanning_notice();
if ($result === $antivirus::SCAN_RESULT_FOUND) {
// Infection found, send notification.
$incidentdetails = $antivirus->get_incident_details('', $filename, $notice);
self::send_antivirus_messages($antivirus, $incidentdetails);
// Copy data to quarantine folder.
$zipfile = \core\antivirus\quarantine::quarantine_data($data, $filename, $incidentdetails, $notice);
// If file not stored due to disabled quarantine, store a message.
if (empty($zipfile)) {
$zipfile = get_string('quarantinedisabled', 'antivirus');
}
// Log file infected event.
$params = [
'context' => \context_system::instance(),
'relateduserid' => $USER->id,
'other' => ['filename' => $filename, 'zipfile' => $zipfile, 'incidentdetails' => $incidentdetails],
];
$event = \core\event\virus_infected_data_detected::create($params);
$event->trigger();
// Get custom message to display to user from antivirus engine.
$displaymessage = $antivirus->get_virus_found_message();
$placeholders = array_merge(['item' => get_string('datastream', 'antivirus')], $displaymessage['placeholders']);
throw new \core\antivirus\scanner_exception(
$displaymessage['string'],
'',
$placeholders,
null,
$displaymessage['component']
);
} else if ($result === $antivirus::SCAN_RESULT_ERROR) {
// Here we need to generate a different incident based on an error.
$incidentdetails = $antivirus->get_incident_details('', $filename, $notice, false);
// Log scan error event.
$params = [
'context' => \context_system::instance(),
'relateduserid' => $USER->id,
'other' => ['filename' => $filename, 'incidentdetails' => $incidentdetails],
];
$event = \core\event\antivirus_scan_data_error::create($params);
$event->trigger();
// Send a notification if required (error or above).
if ($notifylevel === $antivirus::SCAN_RESULT_ERROR) {
self::send_antivirus_messages($antivirus, $incidentdetails);
}
}
}
}
/**
* Returns instance of antivirus.
*
* @param string $antivirusname name of antivirus.
* @return object|bool antivirus instance or false if does not exist.
*/
public static function get_antivirus($antivirusname) {
global $CFG;
$classname = '\\antivirus_' . $antivirusname . '\\scanner';
if (!class_exists($classname)) {
return false;
}
return new $classname();
}
/**
* Get the list of available antiviruses.
*
* @return array Array ('antivirusname'=>'localised antivirus name').
*/
public static function get_available() {
$antiviruses = array();
foreach (\core_component::get_plugin_list('antivirus') as $antivirusname => $dir) {
$antiviruses[$antivirusname] = get_string('pluginname', 'antivirus_'.$antivirusname);
}
return $antiviruses;
}
/**
* This function puts all relevant information into the messages required, and sends them.
*
* @param \core\antivirus\scanner $antivirus the scanner engine.
* @param string $incidentdetails details of the incident.
* @return void
*/
public static function send_antivirus_messages(\core\antivirus\scanner $antivirus, string $incidentdetails) {
$messages = $antivirus->get_messages();
// If there is no messages, and a virus is found, we should generate one, then send it.
if (empty($messages)) {
$antivirus->message_admins($antivirus->get_scanning_notice(), FORMAT_MOODLE, 'infected');
$messages = $antivirus->get_messages();
}
foreach ($messages as $message) {
// Check if the information is already in the current scanning notice.
if (!empty($antivirus->get_scanning_notice()) &&
strpos($antivirus->get_scanning_notice(), $message->fullmessage) === false) {
// This is some extra information. We should append this to the end of the incident details.
$incidentdetails .= \html_writer::tag('pre', $message->fullmessage);
}
// Now update the message to the detailed version, and format.
$message->name = 'infected';
$message->fullmessagehtml = $incidentdetails;
$message->fullmessageformat = FORMAT_MOODLE;
$message->fullmessage = format_text_email($incidentdetails, $message->fullmessageformat);
// Now we must check if message is going to a real account.
// It may be an email that needs to be sent to non-user address.
if ($message->userto->id === -1) {
// If this doesnt exist, send a regular email.
email_to_user(
$message->userto,
get_admin(),
$message->subject,
$message->fullmessage,
$message->fullmessagehtml
);
} else {
// And now we can send.
message_send($message);
}
}
}
}
+326
View File
@@ -0,0 +1,326 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Quarantine file
*
* @package core_antivirus
* @author Nathan Nguyen <nathannguyen@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\antivirus;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/filelib.php');
/**
* Quarantine file
*
* @package core_antivirus
* @author Nathan Nguyen <nathannguyen@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class quarantine {
/** Default quarantine folder */
const DEFAULT_QUARANTINE_FOLDER = 'antivirus_quarantine';
/** Zip infected file */
const FILE_ZIP_INFECTED = '_infected_file.zip';
/** Zip all infected file */
const FILE_ZIP_ALL_INFECTED = '_all_infected_files.zip';
/** Incident details file */
const FILE_HTML_DETAILS = '_details.html';
/** Incident details file */
const DEFAULT_QUARANTINE_TIME = DAYSECS * 28;
/** Date format in filename */
const FILE_NAME_DATE_FORMAT = '%Y%m%d%H%M%S';
/**
* Move the infected file to the quarantine folder.
*
* @param string $file infected file.
* @param string $filename infected file name.
* @param string $incidentdetails incident details.
* @param string $notice notice details.
* @return string|null the name of the newly created quarantined file.
* @throws \dml_exception
*/
public static function quarantine_file(string $file, string $filename, string $incidentdetails, string $notice): ?string {
if (!self::is_quarantine_enabled()) {
return null;
}
// Generate file names.
$date = userdate(time(), self::FILE_NAME_DATE_FORMAT) . "_" . rand();
$zipfilepath = self::get_quarantine_folder() . $date . self::FILE_ZIP_INFECTED;
$detailsfilename = $date . self::FILE_HTML_DETAILS;
// Create Zip file.
$ziparchive = new \zip_archive();
if ($ziparchive->open($zipfilepath, \file_archive::CREATE)) {
$ziparchive->add_file_from_string($detailsfilename, format_text($incidentdetails, FORMAT_MOODLE));
$ziparchive->add_file_from_pathname($filename, $file);
$ziparchive->close();
}
$zipfile = basename($zipfilepath);
self::create_infected_file_record($filename, $zipfile, $notice);
return $zipfile;
}
/**
* Move the infected file to the quarantine folder.
*
* @param string $data data which is infected.
* @param string $filename infected file name.
* @param string $incidentdetails incident details.
* @param string $notice notice details.
* @return string|null the name of the newly created quarantined file.
* @throws \dml_exception
*/
public static function quarantine_data(string $data, string $filename, string $incidentdetails, string $notice): ?string {
if (!self::is_quarantine_enabled()) {
return null;
}
// Generate file names.
$date = userdate(time(), self::FILE_NAME_DATE_FORMAT) . "_" . rand();
$zipfilepath = self::get_quarantine_folder() . $date . self::FILE_ZIP_INFECTED;
$detailsfilename = $date . self::FILE_HTML_DETAILS;
// Create Zip file.
$ziparchive = new \zip_archive();
if ($ziparchive->open($zipfilepath, \file_archive::CREATE)) {
$ziparchive->add_file_from_string($detailsfilename, format_text($incidentdetails, FORMAT_MOODLE));
$ziparchive->add_file_from_string($filename, $data);
$ziparchive->close();
}
$zipfile = basename($zipfilepath);
self::create_infected_file_record($filename, $zipfile, $notice);
return $zipfile;
}
/**
* Check if the virus quarantine is allowed
*
* @return bool
* @throws \dml_exception
*/
public static function is_quarantine_enabled(): bool {
return !empty(get_config("antivirus", "enablequarantine"));
}
/**
* Get quarantine folder
*
* @return string path of quarantine folder
*/
private static function get_quarantine_folder(): string {
global $CFG;
$quarantinefolder = $CFG->dataroot . DIRECTORY_SEPARATOR . self::DEFAULT_QUARANTINE_FOLDER;
if (!file_exists($quarantinefolder)) {
make_upload_directory(self::DEFAULT_QUARANTINE_FOLDER);
}
return $quarantinefolder . DIRECTORY_SEPARATOR;
}
/**
* Checks whether a file exists inside the antivirus quarantine folder.
*
* @param string $filename the filename to check.
* @return boolean whether file exists.
*/
public static function quarantined_file_exists(string $filename): bool {
$folder = self::get_quarantine_folder();
return file_exists($folder . $filename);
}
/**
* Download quarantined file.
*
* @param int $fileid the id of file to be downloaded.
*/
public static function download_quarantined_file(int $fileid) {
global $DB;
// Get the filename to be downloaded.
$filename = $DB->get_field('infected_files', 'quarantinedfile', ['id' => $fileid], IGNORE_MISSING);
// If file record isnt found, user might be doing something naughty in params, or a stale request.
if (empty($filename)) {
return;
}
$file = self::get_quarantine_folder() . $filename;
send_file($file, $filename);
}
/**
* Delete quarantined file.
*
* @param int $fileid id of file to be deleted.
*/
public static function delete_quarantined_file(int $fileid) {
global $DB;
// Get the filename to be deleted.
$filename = $DB->get_field('infected_files', 'quarantinedfile', ['id' => $fileid], IGNORE_MISSING);
// If file record isnt found, user might be doing something naughty in params, or a stale request.
if (empty($filename)) {
return;
}
// Delete the file from the folder.
$file = self::get_quarantine_folder() . $filename;
if (file_exists($file)) {
unlink($file);
}
// Now we are finished with the record, delete the quarantine information.
self::delete_infected_file_record($fileid);
}
/**
* Download all quarantined files.
*
* @return void
*/
public static function download_all_quarantined_files() {
$files = new \DirectoryIterator(self::get_quarantine_folder());
// Add all infected files to a zip file.
$date = userdate(time(), self::FILE_NAME_DATE_FORMAT);
$zipfilename = $date . self::FILE_ZIP_ALL_INFECTED;
$zipfilepath = self::get_quarantine_folder() . DIRECTORY_SEPARATOR . $zipfilename;
$tempfilestocleanup = [];
$ziparchive = new \zip_archive();
if ($ziparchive->open($zipfilepath, \file_archive::CREATE)) {
foreach ($files as $file) {
if (!$file->isDot()) {
// Only send the actual files.
$filename = $file->getFilename();
$filepath = $file->getPathname();
$ziparchive->add_file_from_pathname($filename, $filepath);
}
}
$ziparchive->close();
}
// Clean up temp files.
foreach ($tempfilestocleanup as $tempfile) {
if (file_exists($tempfile)) {
unlink($tempfile);
}
}
send_temp_file($zipfilepath, $zipfilename);
}
/**
* Return array of quarantined files.
*
* @return array list of quarantined files.
*/
public static function get_quarantined_files(): array {
$files = new \DirectoryIterator(self::get_quarantine_folder());
$filestosort = [];
// Grab all files that match the naming structure.
foreach ($files as $file) {
$filename = $file->getFilename();
if (!$file->isDot() && strpos($filename, self::FILE_ZIP_INFECTED) !== false) {
$filestosort[$filename] = $file->getPathname();
}
}
krsort($filestosort, SORT_NATURAL);
return $filestosort;
}
/**
* Clean up quarantine folder
*
* @param int $timetocleanup time to clean up
*/
public static function clean_up_quarantine_folder(int $timetocleanup) {
$files = new \DirectoryIterator(self::get_quarantine_folder());
// Clean up the folder.
foreach ($files as $file) {
$filename = $file->getFilename();
// Only delete files that match the correct name structure.
if (!$file->isDot() && strpos($filename, self::FILE_ZIP_INFECTED) !== false) {
$modifiedtime = $file->getMTime();
if ($modifiedtime <= $timetocleanup) {
unlink($file->getPathname());
}
}
}
// Lastly cleanup the infected files table as well.
self::clean_up_infected_records($timetocleanup);
}
/**
* This function removes any stale records from the infected files table.
*
* @param int $timetocleanup the time to cleanup from
* @return void
*/
private static function clean_up_infected_records(int $timetocleanup) {
global $DB;
$select = "timecreated <= ?";
$DB->delete_records_select('infected_files', $select, [$timetocleanup]);
}
/**
* Create an infected file record
*
* @param string $filename original file name
* @param string $zipfile quarantined file name
* @param string $reason failure reason
* @throws \dml_exception
*/
private static function create_infected_file_record(string $filename, string $zipfile, string $reason) {
global $DB, $USER;
$record = new \stdClass();
$record->filename = $filename;
$record->quarantinedfile = $zipfile;
$record->userid = $USER->id;
$record->reason = $reason;
$record->timecreated = time();
$DB->insert_record('infected_files', $record);
}
/**
* Delete the database record for an infected file.
*
* @param int $fileid quarantined file id
* @throws \dml_exception
*/
private static function delete_infected_file_record(int $fileid) {
global $DB;
$DB->delete_records('infected_files', ['id' => $fileid]);
}
}
+251
View File
@@ -0,0 +1,251 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Base class for antivirus integration.
*
* @package core_antivirus
* @copyright 2015 Ruslan Kabalin, Lancaster University.
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\antivirus;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/iplookup/lib.php');
/**
* Base abstract antivirus scanner class.
*
* @package core
* @subpackage antivirus
* @copyright 2015 Ruslan Kabalin, Lancaster University.
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class scanner {
/** Scanning result indicating no virus found. */
const SCAN_RESULT_OK = 0;
/** Scanning result indicating that virus is found. */
const SCAN_RESULT_FOUND = 1;
/** Scanning result indicating the error. */
const SCAN_RESULT_ERROR = 2;
/** @var \stdClass the config for antivirus */
protected $config;
/** @var string scanning notice */
protected $scanningnotice = '';
/** @var array any admin messages generated by a plugin. */
protected $messages = [];
/**
* Class constructor.
*
* @return void.
*/
public function __construct() {
// Populate config variable, inheriting class namespace is matching
// full plugin name, so we can use it directly to retrieve plugin
// configuration.
$ref = new \ReflectionClass(get_class($this));
$this->config = get_config($ref->getNamespaceName());
}
/**
* Config get method.
*
* @param string $property config property to get.
* @return mixed
* @throws \coding_exception
*/
public function get_config($property) {
if (property_exists($this->config, $property)) {
return $this->config->$property;
}
throw new \coding_exception('Config property "' . $property . '" doesn\'t exist');
}
/**
* Get scanning notice.
*
* @return string
*/
public function get_scanning_notice() {
return $this->scanningnotice;
}
/**
* Set scanning notice.
*
* @param string $notice notice to set.
* @return void
*/
protected function set_scanning_notice($notice) {
$this->scanningnotice = $notice;
}
/**
* Are the antivirus settings configured?
*
* @return bool True if plugin has been configured.
*/
abstract public function is_configured();
/**
* Scan file.
*
* @param string $file Full path to the file.
* @param string $filename Name of the file (could be different from physical file if temp file is used).
* @return int Scanning result constants.
*/
abstract public function scan_file($file, $filename);
/**
* Scan data.
*
* By default it saves data variable content to file and then scans it using
* scan_file method, however if antivirus plugin permits scanning data directly,
* the method can be overridden.
*
* @param string $data The variable containing the data to scan.
* @return int Scanning result constants.
*/
public function scan_data($data) {
// Prepare temp file.
$tempdir = make_request_directory();
$tempfile = $tempdir . DIRECTORY_SEPARATOR . rand();
file_put_contents($tempfile, $data);
// Perform a virus scan now.
return $this->scan_file($tempfile, get_string('datastream', 'antivirus'));
}
/**
* This function pushes given messages into the message queue, which will be sent by the antivirus manager.
*
* @param string $notice The body of the email to be sent.
* @param string $format The body format.
* @param string $eventname event name
* @return void
* @throws \coding_exception
* @throws \moodle_exception
*/
public function message_admins($notice, $format = FORMAT_PLAIN, $eventname = 'errors') {
$noticehtml = $format !== FORMAT_PLAIN ? format_text($notice, $format) : '';
$site = get_site();
$subject = get_string('emailsubject', 'antivirus', format_string($site->fullname));
$notifyemail = get_config('antivirus', 'notifyemail');
// If one email address is specified, construct a message to fake account.
if (!empty($notifyemail)) {
$user = new \stdClass();
$user->id = -1;
$user->email = $notifyemail;
$user->mailformat = 1;
$admins = [$user];
} else {
// Otherwise, we message all admins.
$admins = get_admins();
}
foreach ($admins as $admin) {
$eventdata = new \core\message\message();
$eventdata->courseid = SITEID;
$eventdata->component = 'moodle';
$eventdata->name = $eventname;
$eventdata->userfrom = get_admin();
$eventdata->userto = $admin;
$eventdata->subject = $subject;
$eventdata->fullmessage = $notice;
$eventdata->fullmessageformat = $format;
$eventdata->fullmessagehtml = $noticehtml;
$eventdata->smallmessage = '';
// Now add the message to an array to be sent by the antivirus manager.
$this->messages[] = $eventdata;
}
}
/**
* Return incident details
*
* @param string $file full path to the file
* @param string $filename original name of the file
* @param string $notice notice from antivirus
* @param string $virus if this template is due to a virus found.
* @return string the incident details
* @throws \coding_exception
*/
public function get_incident_details($file = '', $filename = '', $notice = '', $virus = true) {
global $OUTPUT, $USER;
if (empty($notice)) {
$notice = $this->get_scanning_notice();
}
$classname = get_class($this);
$component = explode('\\', $classname)[0];
$content = new \stdClass();
$unknown = get_string('unknown', 'antivirus');
$content->header = get_string('emailinfectedfiledetected', 'antivirus');
$content->filename = !empty($filename) ? $filename : $unknown;
$content->scanner = $component;
// Check for empty file, or file not uploaded.
if (!empty($file) && filesize($file) !== false) {
$content->filesize = display_size(filesize($file));
$content->contenthash = \file_storage::hash_from_path($file);
$content->contenttype = mime_content_type($file);
} else {
$content->filesize = $unknown;
$content->contenthash = $unknown;
$content->contenttype = $unknown;
}
$content->author = \core_user::is_real_user($USER->id) ? fullname($USER) . " ($USER->username)" : $unknown;
$content->ipaddress = getremoteaddr();
$geoinfo = iplookup_find_location(getremoteaddr());
$content->geoinfo = $geoinfo['city'] . ', ' . $geoinfo['country'];
$content->date = userdate(time(), get_string('strftimedatetimeshort'));
$content->referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : $unknown;
$content->notice = $notice;
$report = new \moodle_url('/report/infectedfiles/index.php');
$content->report = $report->out();
// If this is not due to a virus, we need to change the header line.
if (!$virus) {
$content->header = get_string('emailscannererrordetected', 'antivirus');
}
return $OUTPUT->render_from_template('core/infected_file_email', $content);
}
/**
* Getter method for messages queued by the antivirus scanner.
*
* @return array
*/
public function get_messages(): array {
return $this->messages;
}
/**
* Getter method for the antivirus message displayed in the exception.
*
* @return array array of string and component to pass to exception constructor.
*/
public function get_virus_found_message() {
// Base antivirus found string.
return ['string' => 'virusfound', 'component' => 'antivirus', 'placeholders' => []];
}
}
@@ -0,0 +1,50 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Exception for antivirus.
*
* @package core_antivirus
* @copyright 2015 Ruslan Kabalin, Lancaster University.
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\antivirus;
defined('MOODLE_INTERNAL') || die();
/**
* An antivirus scanner exception class.
*
* @package core
* @subpackage antivirus
* @copyright 2015 Ruslan Kabalin, Lancaster University.
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class scanner_exception extends \moodle_exception {
/**
* Constructs a new exception
*
* @param string $errorcode
* @param string $link
* @param mixed $a
* @param mixed $debuginfo
* @param string $module optional plugin name
*/
public function __construct($errorcode, $link = '', $a = null, $debuginfo = null, $module = 'antivirus') {
parent::__construct($errorcode, $module, $link, $a, $debuginfo);
}
}