MDL-29670 cron cleanup and minor fixing

Auth and enrol is moved to the top because other plugin types depend on accurate user list and enrolments. Passwords and failed login messages are generated during every cron execution.  Contexts are build properly in each execution. Fixed deleting of unconfirmed users. And minor phpdocs and comments improvements.
This commit is contained in:
Petr Skoda
2011-10-22 14:31:18 +02:00
parent 3a81b37617
commit 50ed196e51
4 changed files with 383 additions and 369 deletions
+380 -250
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -24,6 +23,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Execute cron tasks
*/
function cron_run() {
global $DB, $CFG, $OUTPUT;
@@ -51,26 +53,195 @@ function cron_run() {
set_time_limit(0);
$starttime = microtime();
/// increase memory limit
// Increase memory limit
raise_memory_limit(MEMORY_EXTRA);
/// emulate normal session
// Emulate normal session - we use admin accoutn by default
cron_setup_user();
/// Start output log
// Start output log
$timenow = time();
mtrace("Server Time: ".date('r',$timenow)."\n\n");
/// Session gc
// Run cleanup core cron jobs, but not every time since they aren't too important.
// These don't have a timer to reduce load, so we'll use a random number
// to randomly choose the percentage of times we should run these jobs.
srand ((double) microtime() * 10000000);
$random100 = rand(0,100);
if ($random100 < 20) { // Approximately 20% of the time.
mtrace("Running clean-up tasks...");
mtrace("Cleaning up stale sessions");
// Delete users who haven't confirmed within required period
if (!empty($CFG->deleteunconfirmed)) {
$cuttime = $timenow - ($CFG->deleteunconfirmed * 3600);
$rs = $DB->get_recordset_sql ("SELECT *
FROM {user}
WHERE confirmed = 0 AND firstaccess > 0
AND firstaccess < ?", array($cuttime));
foreach ($rs as $user) {
delete_user($user); // we MUST delete user properly first
$DB->delete_records('user', array('id'=>$user->id)); // this is a bloody hack, but it might work
mtrace(" Deleted unconfirmed user for ".fullname($user, true)." ($user->id)");
}
$rs->close();
}
// Delete users who haven't completed profile within required period
if (!empty($CFG->deleteincompleteusers)) {
$cuttime = $timenow - ($CFG->deleteincompleteusers * 3600);
$rs = $DB->get_recordset_sql ("SELECT *
FROM {user}
WHERE confirmed = 1 AND lastaccess > 0
AND lastaccess < ? AND deleted = 0
AND (lastname = '' OR firstname = '' OR email = '')",
array($cuttime));
foreach ($rs as $user) {
delete_user($user);
mtrace(" Deleted not fully setup user $user->username ($user->id)");
}
$rs->close();
}
// Delete old logs to save space (this might need a timer to slow it down...)
if (!empty($CFG->loglifetime)) { // value in days
$loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
$DB->delete_records_select("log", "time < ?", array($loglifetime));
mtrace(" Deleted old log records");
}
// Delete old backup_controllers and logs
if (!empty($CFG->loglifetime)) { // value in days
$loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
// Delete child records from backup_logs
$DB->execute("DELETE FROM {backup_logs}
WHERE EXISTS (
SELECT 'x'
FROM {backup_controllers} bc
WHERE bc.backupid = {backup_logs}.backupid
AND bc.timecreated < ?)", array($loglifetime));
// Delete records from backup_controllers
$DB->execute("DELETE FROM {backup_controllers}
WHERE timecreated < ?", array($loglifetime));
mtrace(" Deleted old backup records");
}
// Delete old cached texts
if (!empty($CFG->cachetext)) { // Defined in config.php
$cachelifetime = time() - $CFG->cachetext - 60; // Add an extra minute to allow for really heavy sites
$DB->delete_records_select('cache_text', "timemodified < ?", array($cachelifetime));
mtrace(" Deleted old cache_text records");
}
if (!empty($CFG->usetags)) {
require_once($CFG->dirroot.'/tag/lib.php');
tag_cron();
mtrace(' Executed tag cron');
}
// Context maintenance stuff
context_helper::cleanup_instances();
mtrace(' Cleaned up context instances');
context_helper::build_all_paths(false);
// If you suspect that the context paths are somehow corrupt
// replace the line below with: context_helper::build_all_paths(true);
mtrace(' Built context paths');
// Remove expired cache flags
gc_cache_flags();
mtrace(' Cleaned cache flags');
// Cleanup messaging
if (!empty($CFG->messagingdeletereadnotificationsdelay)) {
$notificationdeletetime = time() - $CFG->messagingdeletereadnotificationsdelay;
$DB->delete_records_select('message_read', 'notification=1 AND timeread<:notificationdeletetime', array('notificationdeletetime'=>$notificationdeletetime));
mtrace(' Cleaned up read notifications');
}
mtrace("...finished clean-up tasks");
} // End of occasional clean-up tasks
// Send login failures notification - brute force protection in moodle is weak,
// we should at least send notices early in each cron execution
if (!empty($CFG->notifyloginfailures)) {
notify_login_failures();
mtrace(' Notified login failured');
}
// Make sure all context instances are properly created - they may be required in auth, enrol, etc.
context_helper::create_instances();
mtrace(' Created missing context instances');
// Session gc
session_gc();
mtrace("Cleaned up stale user sessions");
/// Run all cron jobs for each module
// Run the auth cron, if any before enrolments
// because it might add users that will be needed in enrol plugins
$auths = get_enabled_auth_plugins();
mtrace("Running auth crons if required...");
foreach ($auths as $auth) {
$authplugin = get_auth_plugin($auth);
if (method_exists($authplugin, 'cron')) {
mtrace("Running cron for auth/$auth...");
$authplugin->cron();
if (!empty($authplugin->log)) {
mtrace($authplugin->log);
}
}
unset($authplugin);
}
// Generate new password emails for users - ppl expect these generated asap
if ($DB->count_records('user_preferences', array('name'=>'create_password', 'value'=>'1'))) {
mtrace('Creating passwords for new users...');
$newusers = $DB->get_recordset_sql("SELECT u.id as id, u.email, u.firstname,
u.lastname, u.username,
p.id as prefid
FROM {user} u
JOIN {user_preferences} p ON u.id=p.userid
WHERE p.name='create_password' AND p.value='1' AND u.email !='' ");
foreach ($newusers as $newuser) {
if (setnew_password_and_mail($newuser)) {
unset_user_preference('create_password', $newuser);
set_user_preference('auth_forcepasswordchange', 1, $newuser);
} else {
trigger_error("Could not create and mail new user password!");
}
}
$newusers->close();
}
// It is very important to run enrol early
// because other plugins depend on correct enrolment info.
mtrace("Running enrol crons if required...");
$enrols = enrol_get_plugins(true);
foreach($enrols as $ename=>$enrol) {
// do this for all plugins, disabled plugins might want to cleanup stuff such as roles
if (!$enrol->is_cron_required()) {
continue;
}
mtrace("Running cron for enrol_$ename...");
$enrol->cron();
$enrol->set_config('lastcron', time());
}
// Run all cron jobs for each module
mtrace("Starting activity modules");
get_mailer('buffer');
if ($mods = $DB->get_records_select("modules", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
@@ -91,7 +262,7 @@ function cron_run() {
mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
}
/// Reset possible changes by modules to time_limit. MDL-11597
// Reset possible changes by modules to time_limit. MDL-11597
@set_time_limit(0);
mtrace("done.");
}
@@ -101,9 +272,10 @@ function cron_run() {
get_mailer('close');
mtrace("Finished activity modules");
mtrace("Starting blocks");
if ($blocks = $DB->get_records_select("block", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
// we will need the base class.
// We will need the base class.
require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
foreach ($blocks as $block) {
$blockfile = $CFG->dirroot.'/blocks/'.$block->name.'/block_'.$block->name.'.php';
@@ -116,7 +288,7 @@ function cron_run() {
if ($blockobj->cron()) {
$DB->set_field('block', 'lastcron', $timenow, array('id'=>$block->id));
}
/// Reset possible changes by blocks to time_limit. MDL-11597
// Reset possible changes by blocks to time_limit. MDL-11597
@set_time_limit(0);
mtrace('done.');
}
@@ -126,6 +298,8 @@ function cron_run() {
}
mtrace('Finished blocks');
//TODO: get rid of this bloody hardcoded quiz module stuff, this must be done from quiz_cron()!
mtrace("Starting quiz reports");
if ($reports = $DB->get_records_select('quiz_reports', "cron > 0 AND ((? - lastcron) > cron)", array($timenow))) {
foreach ($reports as $report) {
@@ -152,11 +326,13 @@ function cron_run() {
}
mtrace("Finished quiz reports");
mtrace('Starting admin reports');
cron_execute_plugin_type('report');
mtrace('Finished admin reports');
mtrace('Starting main gradebook job ...');
mtrace('Starting main gradebook job...');
grade_cron();
mtrace('done.');
@@ -183,189 +359,101 @@ function cron_run() {
mtrace('done');
}
//now do plagiarism checks
require_once($CFG->libdir.'/plagiarismlib.php');
plagiarism_cron();
/// Run all core cron jobs, but not every time since they aren't too important.
/// These don't have a timer to reduce load, so we'll use a random number
/// to randomly choose the percentage of times we should run these jobs.
srand ((double) microtime() * 10000000);
$random100 = rand(0,100);
mtrace('Starting course reports');
cron_execute_plugin_type('coursereport');
mtrace('Finished course reports');
if ($random100 < 20) { // Approximately 20% of the time.
mtrace("Running clean-up tasks...");
/// Delete users who haven't confirmed within required period
// run gradebook import/export/report cron
mtrace('Starting gradebook plugins');
cron_execute_plugin_type('gradeimport');
cron_execute_plugin_type('gradeexport');
cron_execute_plugin_type('gradereport');
mtrace('Finished gradebook plugins');
if (!empty($CFG->deleteunconfirmed)) {
$cuttime = $timenow - ($CFG->deleteunconfirmed * 3600);
$rs = $DB->get_recordset_sql ("SELECT id, firstname, lastname
FROM {user}
WHERE confirmed = 0 AND firstaccess > 0
AND firstaccess < ?", array($cuttime));
foreach ($rs as $user) {
if ($DB->delete_records('user', array('id'=>$user->id))) {
mtrace("Deleted unconfirmed user for ".fullname($user, true)." ($user->id)");
}
}
$rs->close();
// Run external blog cron if needed
if ($CFG->useexternalblogs) {
require_once($CFG->dirroot . '/blog/lib.php');
mtrace("Fetching external blog entries...", '');
$sql = "timefetched < ? OR timefetched = 0";
$externalblogs = $DB->get_records_select('blog_external', $sql, array(mktime() - $CFG->externalblogcrontime));
foreach ($externalblogs as $eb) {
blog_sync_external_entries($eb);
}
flush();
mtrace('done.');
}
// Run blog associations cleanup
if ($CFG->useblogassociations) {
require_once($CFG->dirroot . '/blog/lib.php');
// delete entries whose contextids no longer exists
mtrace("Deleting blog associations linked to non-existent contexts...", '');
$DB->delete_records_select('blog_association', 'contextid NOT IN (SELECT id FROM {context})');
mtrace('done.');
}
/// Delete users who haven't completed profile within required period
if (!empty($CFG->deleteincompleteusers)) {
$cuttime = $timenow - ($CFG->deleteincompleteusers * 3600);
$rs = $DB->get_recordset_sql ("SELECT *
FROM {user}
WHERE confirmed = 1 AND lastaccess > 0
AND lastaccess < ? AND deleted = 0
AND (lastname = '' OR firstname = '' OR email = '')",
array($cuttime));
foreach ($rs as $user) {
if (delete_user($user)) {
mtrace("Deleted not fully setup user $user->username ($user->id)");
}
}
$rs->close();
}
flush();
//Run registration updated cron
mtrace(get_string('siteupdatesstart', 'hub'));
require_once($CFG->dirroot . '/admin/registration/lib.php');
$registrationmanager = new registration_manager();
$registrationmanager->cron();
mtrace(get_string('siteupdatesend', 'hub'));
/// Delete old logs to save space (this might need a timer to slow it down...)
//cleanup old session linked tokens
//deletes the session linked tokens that are over a day old.
mtrace("Deleting session linked tokens more than one day old...", '');
$DB->delete_records_select('external_tokens', 'lastaccess < :onedayago AND tokentype = :tokentype',
array('onedayago' => time() - DAYSECS, 'tokentype' => EXTERNAL_TOKEN_EMBEDDED));
mtrace('done.');
if (!empty($CFG->loglifetime)) { // value in days
$loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
if ($DB->delete_records_select("log", "time < ?", array($loglifetime))) {
mtrace("Deleted old log records");
// all other plugins
cron_execute_plugin_type('message', 'message plugins');
cron_execute_plugin_type('filter', 'filters');
cron_execute_plugin_type('editor', 'editors');
cron_execute_plugin_type('format', 'course formats');
cron_execute_plugin_type('profilefield', 'profile fields');
cron_execute_plugin_type('webservice', 'webservices');
// TODO: Repository lib.php files are messed up (include many other files, etc), so it is
// currently not possible to implement repository plugin cron using this infrastructure
// cron_execute_plugin_type('repository', 'repository plugins');
cron_execute_plugin_type('qtype', 'question types');
cron_execute_plugin_type('plagiarism', 'plagiarism plugins');
cron_execute_plugin_type('theme', 'themes');
cron_execute_plugin_type('tool', 'admin tools');
// and finally run any local cronjobs, if any
if ($locals = get_plugin_list('local')) {
mtrace('Processing customized cron scripts ...', '');
// new cron functions in lib.php first
cron_execute_plugin_type('local');
// legacy cron files are executed directly
foreach ($locals as $local => $localdir) {
if (file_exists("$localdir/cron.php")) {
include("$localdir/cron.php");
}
}
flush();
// Delete old backup_controllers and logs
if (!empty($CFG->loglifetime)) { // value in days
$loglifetime = $timenow - ($CFG->loglifetime * 3600 * 24);
// Delete child records from backup_logs
$DB->execute("DELETE FROM {backup_logs}
WHERE EXISTS (
SELECT 'x'
FROM {backup_controllers} bc
WHERE bc.backupid = {backup_logs}.backupid
AND bc.timecreated < ?)", array($loglifetime));
// Delete records from backup_controllers
$DB->execute("DELETE FROM {backup_controllers}
WHERE timecreated < ?", array($loglifetime));
mtrace("Deleted old backup records");
}
flush();
mtrace('done.');
}
/// Delete old cached texts
if (!empty($CFG->cachetext)) { // Defined in config.php
$cachelifetime = time() - $CFG->cachetext - 60; // Add an extra minute to allow for really heavy sites
if ($DB->delete_records_select('cache_text', "timemodified < ?", array($cachelifetime))) {
mtrace("Deleted old cache_text records");
}
}
flush();
if (!empty($CFG->notifyloginfailures)) {
notify_login_failures();
mtrace('Notified login failured');
}
flush();
//
// generate new password emails for users
//
mtrace('checking for create_password');
if ($DB->count_records('user_preferences', array('name'=>'create_password', 'value'=>'1'))) {
mtrace('creating passwords for new users');
$newusers = $DB->get_recordset_sql("SELECT u.id as id, u.email, u.firstname,
u.lastname, u.username,
p.id as prefid
FROM {user} u
JOIN {user_preferences} p ON u.id=p.userid
WHERE p.name='create_password' AND p.value='1' AND u.email !='' ");
foreach ($newusers as $newuser) {
// email user
if (setnew_password_and_mail($newuser)) {
unset_user_preference('create_password', $newuser);
set_user_preference('auth_forcepasswordchange', 1, $newuser);
} else {
trigger_error("Could not create and mail new user password!");
}
}
$newusers->close();
}
if (!empty($CFG->usetags)) {
require_once($CFG->dirroot.'/tag/lib.php');
tag_cron();
mtrace ('Executed tag cron');
}
// Accesslib stuff
cleanup_contexts();
mtrace ('Cleaned up contexts');
gc_cache_flags();
mtrace ('Cleaned cache flags');
// If you suspect that the context paths are somehow corrupt
// replace the line below with: build_context_path(true);
build_context_path();
mtrace ('Built context paths');
if (!empty($CFG->messagingdeletereadnotificationsdelay)) {
$notificationdeletetime = time() - $CFG->messagingdeletereadnotificationsdelay;
$DB->delete_records_select('message_read', 'notification=1 AND timeread<:notificationdeletetime', array('notificationdeletetime'=>$notificationdeletetime));
mtrace('Cleaned up read notifications');
}
mtrace("Finished clean-up tasks...");
} // End of occasional clean-up tasks
// Run automated backups if required.
// Run automated backups if required - these may take a long time to execute
require_once($CFG->dirroot.'/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot.'/backup/util/helper/backup_cron_helper.class.php');
backup_cron_automated_helper::run_automated_backup();
/// Run the auth cron, if any
/// before enrolments because it might add users that will be needed in enrol plugins
$auths = get_enabled_auth_plugins();
mtrace("Running auth crons if required...");
foreach ($auths as $auth) {
$authplugin = get_auth_plugin($auth);
if (method_exists($authplugin, 'cron')) {
mtrace("Running cron for auth/$auth...");
$authplugin->cron();
if (!empty($authplugin->log)) {
mtrace($authplugin->log);
}
}
unset($authplugin);
}
mtrace("Running enrol crons if required...");
$enrols = enrol_get_plugins(true);
foreach($enrols as $ename=>$enrol) {
// do this for all plugins, disabled plugins might want to cleanup stuff such as roles
if (!$enrol->is_cron_required()) {
continue;
}
mtrace("Running cron for enrol_$ename...");
$enrol->cron();
$enrol->set_config('lastcron', time());
}
// Run stats as at the end because they are known to take very long time on large sites
if (!empty($CFG->enablestats) and empty($CFG->disablestatsprocessing)) {
require_once($CFG->dirroot.'/lib/statslib.php');
// check we're not before our runtime
@@ -388,85 +476,10 @@ function cron_run() {
}
mtrace('Starting course reports');
cron_execute_plugin_type('coursereport');
mtrace('Finished course reports');
// run gradebook import/export/report cron
mtrace('Starting gradebook plugins');
cron_execute_plugin_type('gradeimport');
cron_execute_plugin_type('gradeexport');
cron_execute_plugin_type('gradereport');
mtrace('Finished gradebook plugins');
// Run external blog cron if needed
if ($CFG->useexternalblogs) {
require_once($CFG->dirroot . '/blog/lib.php');
mtrace("Fetching external blog entries...", '');
$sql = "timefetched < ? OR timefetched = 0";
$externalblogs = $DB->get_records_select('blog_external', $sql, array(mktime() - $CFG->externalblogcrontime));
foreach ($externalblogs as $eb) {
blog_sync_external_entries($eb);
}
mtrace('done.');
}
// Run blog associations cleanup
if ($CFG->useblogassociations) {
require_once($CFG->dirroot . '/blog/lib.php');
// delete entries whose contextids no longer exists
mtrace("Deleting blog associations linked to non-existent contexts...", '');
$DB->delete_records_select('blog_association', 'contextid NOT IN (SELECT id FROM {context})');
mtrace('done.');
}
//Run registration updated cron
mtrace(get_string('siteupdatesstart', 'hub'));
require_once($CFG->dirroot . '/admin/registration/lib.php');
$registrationmanager = new registration_manager();
$registrationmanager->cron();
mtrace(get_string('siteupdatesend', 'hub'));
// cleanup file trash
// cleanup file trash - not very important
$fs = get_file_storage();
$fs->cron();
//cleanup old session linked tokens
//deletes the session linked tokens that are over a day old.
mtrace("Deleting session linked tokens more than one day old...", '');
$DB->delete_records_select('external_tokens', 'lastaccess < :onedayago AND tokentype = :tokentype',
array('onedayago' => time() - DAYSECS, 'tokentype' => EXTERNAL_TOKEN_EMBEDDED));
mtrace('done.');
// all other plugins
cron_execute_plugin_type('message', 'message plugins');
cron_execute_plugin_type('filter', 'filters');
cron_execute_plugin_type('editor', 'editors');
cron_execute_plugin_type('format', 'course formats');
cron_execute_plugin_type('profilefield', 'profile fields');
cron_execute_plugin_type('webservice', 'webservices');
// TODO: Repository lib.php files are messed up (include many other files, etc), so it is
// currently not possible to implement repository plugin cron using this infrastructure
// cron_execute_plugin_type('repository', 'repository plugins');
cron_execute_plugin_type('qtype', 'question types');
cron_execute_plugin_type('plagiarism', 'plagiarism plugins');
cron_execute_plugin_type('theme', 'themes');
cron_execute_plugin_type('tool', 'admin tools');
// and finally run any local cronjobs, if any
if ($locals = get_plugin_list('local')) {
mtrace('Processing customized cron scripts ...', '');
// new cron functions in lib.php first
cron_execute_plugin_type('local');
// legacy cron files are executed directly
foreach ($locals as $local => $localdir) {
if (file_exists("$localdir/cron.php")) {
include("$localdir/cron.php");
}
}
mtrace('done.');
}
mtrace("Cron script completed correctly");
@@ -476,6 +489,7 @@ function cron_run() {
/**
* Executes cron functions for a specific type of plugin.
*
* @param string $plugintype Plugin type (e.g. 'report')
* @param string $description If specified, will display 'Starting (whatever)'
* and 'Finished (whatever)' lines, otherwise does not display
@@ -541,6 +555,7 @@ function cron_execute_plugin_type($plugintype, $description = null) {
* Used to add in old-style cron functions within plugins that have not been converted to the
* new standard API. (The standard API is frankenstyle_name_cron() in lib.php; some types used
* cron.php and some used a different name.)
*
* @param string $plugintype Plugin type e.g. 'report'
* @param array $plugins Array from plugin name (e.g. 'report_frog') to function name (e.g.
* 'report_frog_cron') for plugin cron functions that were already found using the new API
@@ -595,4 +610,119 @@ function cron_bc_hack_plugin_functions($plugintype, $plugins) {
}
return $plugins;
}
}
/**
* Notify admin users or admin user of any failed logins (since last notification).
*
* Note that this function must be only executed from the cron script
* It uses the cache_flags system to store temporary records, deleting them
* by name before finishing
*/
function notify_login_failures() {
global $CFG, $DB, $OUTPUT;
$recip = get_users_from_config($CFG->notifyloginfailures, 'moodle/site:config');
if (empty($CFG->lastnotifyfailure)) {
$CFG->lastnotifyfailure=0;
}
// we need to deal with the threshold stuff first.
if (empty($CFG->notifyloginthreshold)) {
$CFG->notifyloginthreshold = 10; // default to something sensible.
}
// Get all the IPs with more than notifyloginthreshold failures since lastnotifyfailure
// and insert them into the cache_flags temp table
$sql = "SELECT ip, COUNT(*)
FROM {log}
WHERE module = 'login' AND action = 'error'
AND time > ?
GROUP BY ip
HAVING COUNT(*) >= ?";
$params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $iprec) {
if (!empty($iprec->ip)) {
set_cache_flag('login_failure_by_ip', $iprec->ip, '1', 0);
}
}
$rs->close();
// Get all the INFOs with more than notifyloginthreshold failures since lastnotifyfailure
// and insert them into the cache_flags temp table
$sql = "SELECT info, count(*)
FROM {log}
WHERE module = 'login' AND action = 'error'
AND time > ?
GROUP BY info
HAVING count(*) >= ?";
$params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $inforec) {
if (!empty($inforec->info)) {
set_cache_flag('login_failure_by_info', $inforec->info, '1', 0);
}
}
$rs->close();
// Now, select all the login error logged records belonging to the ips and infos
// since lastnotifyfailure, that we have stored in the cache_flags table
$sql = "SELECT l.*, u.firstname, u.lastname
FROM {log} l
JOIN {cache_flags} cf ON l.ip = cf.name
LEFT JOIN {user} u ON l.userid = u.id
WHERE l.module = 'login' AND l.action = 'error'
AND l.time > ?
AND cf.flagtype = 'login_failure_by_ip'
UNION ALL
SELECT l.*, u.firstname, u.lastname
FROM {log} l
JOIN {cache_flags} cf ON l.info = cf.name
LEFT JOIN {user} u ON l.userid = u.id
WHERE l.module = 'login' AND l.action = 'error'
AND l.time > ?
AND cf.flagtype = 'login_failure_by_info'
ORDER BY time DESC";
$params = array($CFG->lastnotifyfailure, $CFG->lastnotifyfailure);
// Init some variables
$count = 0;
$messages = '';
// Iterate over the logs recordset
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $log) {
$log->time = userdate($log->time);
$messages .= get_string('notifyloginfailuresmessage','',$log)."\n";
$count++;
}
$rs->close();
// If we haven't run in the last hour and
// we have something useful to report and we
// are actually supposed to be reporting to somebody
if ((time() - HOURSECS) > $CFG->lastnotifyfailure && $count > 0 && is_array($recip) && count($recip) > 0) {
$site = get_site();
$subject = get_string('notifyloginfailuressubject', '', format_string($site->fullname));
// Calculate the complete body of notification (start + messages + end)
$body = get_string('notifyloginfailuresmessagestart', '', $CFG->wwwroot) .
(($CFG->lastnotifyfailure != 0) ? '('.userdate($CFG->lastnotifyfailure).')' : '')."\n\n" .
$messages .
"\n\n".get_string('notifyloginfailuresmessageend','',$CFG->wwwroot)."\n\n";
// For each destination, send mail
mtrace('Emailing admins about '. $count .' failed login attempts');
foreach ($recip as $admin) {
//emailing the admins directly rather than putting these through the messaging system
email_to_user($admin,get_admin(), $subject, $body);
}
// Update lastnotifyfailure with current time
set_config('lastnotifyfailure', time());
}
// Finally, delete all the temp records we have created in cache_flags
$DB->delete_records_select('cache_flags', "flagtype IN ('login_failure_by_ip', 'login_failure_by_info')");
}
+1 -1
View File
@@ -126,7 +126,7 @@ function message_send($eventdata) {
} else {
//MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
//exist in the message_provider table (thus there is no default settings for them)
$preferrormsg = get_string('couldnotfindpreference', 'message', $preferencename);
$preferrormsg = get_string('couldnotfindpreference', 'message', $preferencename); //TODO: undefined $preferencename
throw new coding_exception($preferrormsg,'blah');
}
-118
View File
@@ -8320,124 +8320,6 @@ function upgrade_set_timeout($max_execution_time=300) {
/// MISCELLANEOUS ////////////////////////////////////////////////////////////////////
/**
* Notify admin users or admin user of any failed logins (since last notification).
*
* Note that this function must be only executed from the cron script
* It uses the cache_flags system to store temporary records, deleting them
* by name before finishing
*
* @global object
* @global object
* @uses HOURSECS
*/
function notify_login_failures() {
global $CFG, $DB, $OUTPUT;
$recip = get_users_from_config($CFG->notifyloginfailures, 'moodle/site:config');
if (empty($CFG->lastnotifyfailure)) {
$CFG->lastnotifyfailure=0;
}
// we need to deal with the threshold stuff first.
if (empty($CFG->notifyloginthreshold)) {
$CFG->notifyloginthreshold = 10; // default to something sensible.
}
/// Get all the IPs with more than notifyloginthreshold failures since lastnotifyfailure
/// and insert them into the cache_flags temp table
$sql = "SELECT ip, COUNT(*)
FROM {log}
WHERE module = 'login' AND action = 'error'
AND time > ?
GROUP BY ip
HAVING COUNT(*) >= ?";
$params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $iprec) {
if (!empty($iprec->ip)) {
set_cache_flag('login_failure_by_ip', $iprec->ip, '1', 0);
}
}
$rs->close();
/// Get all the INFOs with more than notifyloginthreshold failures since lastnotifyfailure
/// and insert them into the cache_flags temp table
$sql = "SELECT info, count(*)
FROM {log}
WHERE module = 'login' AND action = 'error'
AND time > ?
GROUP BY info
HAVING count(*) >= ?";
$params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $inforec) {
if (!empty($inforec->info)) {
set_cache_flag('login_failure_by_info', $inforec->info, '1', 0);
}
}
$rs->close();
/// Now, select all the login error logged records belonging to the ips and infos
/// since lastnotifyfailure, that we have stored in the cache_flags table
$sql = "SELECT l.*, u.firstname, u.lastname
FROM {log} l
JOIN {cache_flags} cf ON l.ip = cf.name
LEFT JOIN {user} u ON l.userid = u.id
WHERE l.module = 'login' AND l.action = 'error'
AND l.time > ?
AND cf.flagtype = 'login_failure_by_ip'
UNION ALL
SELECT l.*, u.firstname, u.lastname
FROM {log} l
JOIN {cache_flags} cf ON l.info = cf.name
LEFT JOIN {user} u ON l.userid = u.id
WHERE l.module = 'login' AND l.action = 'error'
AND l.time > ?
AND cf.flagtype = 'login_failure_by_info'
ORDER BY time DESC";
$params = array($CFG->lastnotifyfailure, $CFG->lastnotifyfailure);
/// Init some variables
$count = 0;
$messages = '';
/// Iterate over the logs recordset
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $log) {
$log->time = userdate($log->time);
$messages .= get_string('notifyloginfailuresmessage','',$log)."\n";
$count++;
}
$rs->close();
/// If we haven't run in the last hour and
/// we have something useful to report and we
/// are actually supposed to be reporting to somebody
if ((time() - HOURSECS) > $CFG->lastnotifyfailure && $count > 0 && is_array($recip) && count($recip) > 0) {
$site = get_site();
$subject = get_string('notifyloginfailuressubject', '', format_string($site->fullname));
/// Calculate the complete body of notification (start + messages + end)
$body = get_string('notifyloginfailuresmessagestart', '', $CFG->wwwroot) .
(($CFG->lastnotifyfailure != 0) ? '('.userdate($CFG->lastnotifyfailure).')' : '')."\n\n" .
$messages .
"\n\n".get_string('notifyloginfailuresmessageend','',$CFG->wwwroot)."\n\n";
/// For each destination, send mail
mtrace('Emailing admins about '. $count .' failed login attempts');
foreach ($recip as $admin) {
//emailing the admins directly rather than putting these through the messaging system
email_to_user($admin,get_admin(), $subject, $body);
}
/// Update lastnotifyfailure with current time
set_config('lastnotifyfailure', time());
}
/// Finally, delete all the temp records we have created in cache_flags
$DB->delete_records_select('cache_flags', "flagtype IN ('login_failure_by_ip', 'login_failure_by_info')");
}
/**
* Sets the system locale
*
+2
View File
@@ -29,6 +29,7 @@ define ('MESSAGE_SHORTLENGTH', 300);
//$PAGE isnt set if we're being loaded by cron which doesnt display popups anyway
if (isset($PAGE)) {
//TODO: this is a mega crazy hack - it is not acceptable to call anything when including lib!!! (skodak)
$PAGE->set_popup_notification_allowed(false); // We are in a message window (so don't pop up a new one)
}
@@ -77,6 +78,7 @@ define('MESSAGE_PERMITTED_MASK', 0x0c); // 1100
*/
define('MESSAGE_DEFAULT_PERMITTED', 'permitted');
//TODO: defaults must be initialised via settings - this is a bad hack! (skodak)
if (!isset($CFG->message_contacts_refresh)) { // Refresh the contacts list every 60 seconds
$CFG->message_contacts_refresh = 60;
}