* @license https://files.froxlor.org/misc/COPYING.txt GPLv2 */ namespace Froxlor\System; use Exception; use Froxlor\Cron\TaskId; use Froxlor\Database\Database; use Froxlor\FroxlorLogger; use Froxlor\Settings; use PDO; class Cronjob { /** * Function checkLastGuid * * Checks if the system's last guid is not higher than the one saved * in froxlor's database. If it's higher, froxlor needs to * set its last guid to this one to avoid conflicts with libnss-users * * @return null */ public static function checkLastGuid() { $mylog = FroxlorLogger::getInstanceOf(); $group_lines = []; $group_guids = []; $update_to_guid = 0; $froxlor_guid = 0; $result_stmt = Database::query("SELECT MAX(`guid`) as `fguid` FROM `" . TABLE_PANEL_CUSTOMERS . "`"); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); $froxlor_guid = $result['fguid']; // possibly no customers yet or f*cked up lastguid settings if ($froxlor_guid < Settings::Get('system.lastguid')) { $froxlor_guid = Settings::Get('system.lastguid'); } $g_file = '/etc/group'; if (file_exists($g_file)) { if (is_readable($g_file)) { if (true == ($groups = file_get_contents($g_file))) { $group_lines = explode("\n", $groups); foreach ($group_lines as $group) { $group_guids[] = explode(":", $group); } foreach ($group_guids as $group) { /** * nogroup | nobody have very high guids * ignore them */ if ($group[0] == 'nogroup' || $group[0] == 'nobody') { continue; } $guid = isset($group[2]) ? (int)$group[2] : 0; if ($guid > $update_to_guid) { $update_to_guid = $guid; } } // if it's lower, then froxlor's highest guid is the last if ($update_to_guid < $froxlor_guid) { $update_to_guid = $froxlor_guid; } elseif ($update_to_guid == $froxlor_guid) { // if it's equal, that means we already have a collision // to ensure it won't happen again, increase the guid by one $update_to_guid = (int)$update_to_guid++; } // now check if it differs from our settings if ($update_to_guid != Settings::Get('system.lastguid')) { $mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Updating froxlor last guid to ' . $update_to_guid); Settings::Set('system.lastguid', $update_to_guid); } } else { $mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'File /etc/group not readable; cannot check for latest guid'); } } else { $mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'File /etc/group not readable; cannot check for latest guid'); } } else { $mylog->logAction(FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'File /etc/group does not exist; cannot check for latest guid'); } } /** * Inserts a task into the PANEL_TASKS-Table * * @param int $type Type of task * @param string $params Parameter (possible to pass multiple times) * * @throws Exception * @author Froxlor team (2010-) */ public static function inserttask(int $type, ...$params) { // prepare the insert-statement $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_TASKS . "` SET `type` = :type, `data` = :data "); if ($type == TaskId::REBUILD_VHOST || $type == TaskId::REBUILD_DNS || $type == TaskId::CREATE_FTP || $type == TaskId::CREATE_QUOTA || $type == TaskId::REBUILD_CRON) { // 4 = bind -> if bind disabled -> no task if ($type == TaskId::REBUILD_DNS && Settings::Get('system.bind_enable') == '0') { return; } // 10 = quota -> if quota disabled -> no task if ($type == TaskId::CREATE_QUOTA && Settings::Get('system.diskquota_enabled') == '0') { return; } // delete previously inserted tasks if they are the same as we only need ONE $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_TASKS . "` WHERE `type` = :type "); Database::pexecute($del_stmt, [ 'type' => $type ]); // insert the new task Database::pexecute($ins_stmt, [ 'type' => $type, 'data' => '' ]); } elseif ($type == TaskId::CREATE_HOME && count($params) == 4 && $params[0] != '' && $params[1] != '' && $params[2] != '' && ($params[3] == 0 || $params[3] == 1)) { $data = []; $data['loginname'] = $params[0]; $data['uid'] = $params[1]; $data['gid'] = $params[2]; $data['store_defaultindex'] = $params[3]; $data = json_encode($data); Database::pexecute($ins_stmt, [ 'type' => TaskId::CREATE_HOME, 'data' => $data ]); } elseif ($type == TaskId::DELETE_CUSTOMER_FILES && isset($params[0]) && $params[0] != '') { $data = []; $data['loginname'] = $params[0]; $data = json_encode($data); Database::pexecute($ins_stmt, [ 'type' => TaskId::DELETE_CUSTOMER_FILES, 'data' => $data ]); } elseif ($type == TaskId::DELETE_EMAIL_DATA && count($params) == 2 && $params[0] != '' && $params[1] != '') { $data = []; $data['loginname'] = $params[0]; $data['email'] = $params[1]; $data = json_encode($data); Database::pexecute($ins_stmt, [ 'type' => TaskId::DELETE_EMAIL_DATA, 'data' => $data ]); } elseif ($type == TaskId::DELETE_FTP_DATA && count($params) == 2 && $params[0] != '' && $params[1] != '') { $data = []; $data['loginname'] = $params[0]; $data['homedir'] = $params[1]; $data = json_encode($data); Database::pexecute($ins_stmt, [ 'type' => TaskId::DELETE_FTP_DATA, 'data' => $data ]); } elseif ($type == TaskId::DELETE_DOMAIN_PDNS && isset($params[0]) && $params[0] != '' && Settings::Get('system.bind_enable') == '1' && Settings::Get('system.dns_server') == 'PowerDNS') { // -> if bind disabled or dns-server not PowerDNS -> no task $data = []; $data['domain'] = $params[0]; $data = json_encode($data); Database::pexecute($ins_stmt, [ 'type' => TaskId::DELETE_DOMAIN_PDNS, 'data' => $data ]); } elseif ($type == TaskId::DELETE_DOMAIN_SSL && isset($params[0]) && $params[0] != '') { $data = []; $data['domain'] = $params[0]; $data = json_encode($data); Database::pexecute($ins_stmt, [ 'type' => TaskId::DELETE_DOMAIN_SSL, 'data' => $data ]); } elseif ($type == TaskId::CREATE_CUSTOMER_DATADUMP && isset($params[0]) && is_array($params[0])) { $data = json_encode($params[0]); Database::pexecute($ins_stmt, [ 'type' => TaskId::CREATE_CUSTOMER_DATADUMP, 'data' => $data ]); } } /** * returns an array of all cronjobs and when they last were executed * * @return array */ public static function getCronjobsLastRun(): array { $query = "SELECT `lastrun`, `desc_lng_key` FROM `" . TABLE_PANEL_CRONRUNS . "` WHERE `isactive` = '1' ORDER BY `cronfile` ASC"; $result = Database::query($query); $cronjobs_last_run = []; while ($row = $result->fetch(PDO::FETCH_ASSOC)) { $cronjobs_last_run[] = [ 'title' => lng('crondesc.' . $row['desc_lng_key']), 'lastrun' => $row['lastrun'] ]; } return $cronjobs_last_run; } /** * @param string $module * @param int $isactive * @return void * @throws Exception */ public static function toggleCronStatus(string $module, int $isactive = 0) { if ($isactive != 1) { $isactive = 0; } $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `isactive` = :active WHERE `module` = :module "); Database::pexecute($upd_stmt, [ 'active' => $isactive, 'module' => $module ]); } /** * returns an array of tasks that are queued to be run by the cronjob * * @return array */ public static function getOutstandingTasks(): array { $query = "SELECT * FROM `" . TABLE_PANEL_TASKS . "` ORDER BY `type` ASC"; $result = Database::query($query); $tasks = []; while ($row = $result->fetch(PDO::FETCH_ASSOC)) { if ($row['data'] != '') { $row['data'] = json_decode($row['data'], true); } $task_id = $row['type']; if (TaskId::isValid($task_id)) { $task_constname = TaskId::convertToConstant($task_id); $lngParams = []; if (is_array($row['data'])) { // task includes loginname if (isset($row['data']['loginname'])) { $lngParams = [$row['data']['loginname']]; } // task includes domain data if (isset($row['data']['domain'])) { $lngParams = [$row['data']['domain']]; } } $task = [ 'desc' => lng('tasks.' . $task_constname, $lngParams) ]; } else { // unknown $task = ['desc' => "ERROR: Unknown task type '" . $row['type'] . "'"]; } $tasks[] = $task; } if (empty($tasks)) { $tasks = [['desc' => lng('tasks.noneoutstanding')]]; } return $tasks; } /** * Send notification to system admin via email * * @param string $message * @param string $subject * * @return void */ public static function notifyMailToAdmin(string $message, string $subject = "[froxlor] Important notice") { $mail = new Mailer(true); $mailerror = false; $mailerr_msg = ""; try { $mail->Subject = $subject; $mail->AltBody = $message; $mail->MsgHTML(nl2br($message)); $mail->AddAddress(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname')); $mail->Send(); } catch (\PHPMailer\PHPMailer\Exception $e) { $mailerr_msg = $e->errorMessage(); $mailerror = true; } catch (Exception $e) { $mailerr_msg = $e->getMessage(); $mailerror = true; } $mail->ClearAddresses(); if ($mailerror) { echo 'Error sending mail: ' . $mailerr_msg . "\n"; } } /** * @param string $cronname * @return void * @throws Exception */ public static function updateLastRunOfCron(string $cronname) { $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = UNIX_TIMESTAMP() WHERE `cronfile` = :cron; "); Database::pexecute($upd_stmt, [ 'cron' => $cronname ]); } }