/** * Delete the backup file of the stats record whose ID is set in the model * @return bool True on success */ public function deleteFile() { $db = $this->getDBO(); $id = $this->getState('id', 0); if (!is_numeric($id) || $id <= 0) { $this->setError(JText::_('STATS_ERROR_INVALIDID')); return false; } $stat = AEPlatform::getInstance()->get_statistics($id); $allFiles = AEUtilStatistics::get_all_filenames($stat, false); $aeconfig = AEFactory::getConfiguration(); $status = true; JLoader::import('joomla.filesystem.file'); foreach ($allFiles as $filename) { $new_status = JFile::delete($filename); $status = $status ? $new_status : false; } return $status; }
/** * Downloads the backup file of a specific backup attempt, * if it's available * */ public function download() { $model = $this->getThisModel(); $id = $model->getId(); $part = $this->input->get('part', -1, 'int'); if ($this->input instanceof F0FInput) { $cid = $this->input->get('cid', array(), 'array'); } else { $cid = $this->input->get('cid', array(), 'array'); } if (empty($id)) { if (is_array($cid) && !empty($cid)) { $id = $cid[0]; } else { $id = -1; } } if ($id <= 0) { $session = JFactory::getSession(); $task = $session->get('buadmin.task', 'browse', 'akeeba'); $this->setRedirect(JURI::base() . 'index.php?option=com_akeeba&view=buadmin&task=' . $task, JText::_('STATS_ERROR_INVALIDID'), 'error'); parent::display(); return true; } $stat = AEPlatform::getInstance()->get_statistics($id); $allFilenames = AEUtilStatistics::get_all_filenames($stat); // Check single part files if (count($allFilenames) == 1 && $part == -1) { $filename = array_shift($allFilenames); } elseif (count($allFilenames) > 0 && count($allFilenames) > $part && $part >= 0) { $filename = $allFilenames[$part]; } else { $filename = null; } if (is_null($filename) || empty($filename) || !@file_exists($filename)) { $session = JFactory::getSession(); $task = $session->get('buadmin.task', 'browse', 'akeeba'); $this->setRedirect(JURI::base() . 'index.php?option=com_akeeba&view=buadmin&task=' . $task, JText::_('STATS_ERROR_INVALIDDOWNLOAD'), 'error'); parent::display(); return true; } else { // For a certain unmentionable browser -- Thank you, Nooku, for the tip if (function_exists('ini_get') && function_exists('ini_set')) { if (ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); } } // Remove php's time limit -- Thank you, Nooku, for the tip if (function_exists('ini_get') && function_exists('set_time_limit')) { if (!ini_get('safe_mode')) { @set_time_limit(0); } } $basename = @basename($filename); $filesize = @filesize($filename); $extension = strtolower(str_replace(".", "", strrchr($filename, "."))); while (@ob_end_clean()) { } @clearstatcache(); // Send MIME headers header('MIME-Version: 1.0'); header('Content-Disposition: attachment; filename="' . $basename . '"'); header('Content-Transfer-Encoding: binary'); header('Accept-Ranges: bytes'); switch ($extension) { case 'zip': // ZIP MIME type header('Content-Type: application/zip'); break; default: // Generic binary data MIME type header('Content-Type: application/octet-stream'); break; } // Notify of filesize, if this info is available if ($filesize > 0) { header('Content-Length: ' . @filesize($filename)); } // Disable caching header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Expires: 0"); header('Pragma: no-cache'); flush(); if ($filesize > 0) { // If the filesize is reported, use 1M chunks for echoing the data to the browser $blocksize = 1048756; //1M chunks $handle = @fopen($filename, "r"); // Now we need to loop through the file and echo out chunks of file data if ($handle !== false) { while (!@feof($handle)) { echo @fread($handle, $blocksize); @ob_flush(); flush(); } } if ($handle !== false) { @fclose($handle); } } else { // If the filesize is not reported, hope that readfile works @readfile($filename); } exit(0); } }
/** * Resets the Kettenrad state, wipping out any pending backups and/or stale * temporary data. * * @param array $config Configuration parameters for the reset operation */ public static function reset($config = array()) { $default_config = array('global' => true, 'log' => false, 'maxrun' => 0); $config = (object) array_merge($default_config, $config); // Pause logging if so desired if (!$config->log) { AEUtilLogger::WriteLog(false, ''); } $tag = null; if (!$config->global) { // If we're not resetting globally, get a list of running backups per tag $tag = AEPlatform::getInstance()->get_backup_origin(); } // Cache the factory before proceeding $factory = AEFactory::serialize(); $runningList = AEPlatform::getInstance()->get_running_backups($tag); // Origins we have to clean $origins = array(AEPlatform::getInstance()->get_backup_origin()); // 1. Detect failed backups if (is_array($runningList) && !empty($runningList)) { // The current timestamp $now = time(); // Mark running backups as failed foreach ($runningList as $running) { if (empty($tag)) { // Check the timestamp of the log file to decide if it's stuck, // but only if a tag is not set $tstamp = @filemtime(AEUtilLogger::logName($running['origin'])); if ($tstamp !== false) { // We can only check the timestamp if it's returned. If not, we assume the backup is stale $difference = abs($now - $tstamp); // Backups less than 3 minutes old are not considered stale if ($difference < $config->maxrun) { continue; } } } $filenames = AEUtilStatistics::get_all_filenames($running, false); // Process if there are files to delete... if (!is_null($filenames)) { // Delete the failed backup's archive, if exists foreach ($filenames as $failedArchive) { AEPlatform::getInstance()->unlink($failedArchive); } } // Mark the backup failed $running['status'] = 'fail'; $running['multipart'] = 0; $dummy = null; AEPlatform::getInstance()->set_or_update_statistics($running['id'], $running, $dummy); $origins[] = $running['origin']; } } if (!empty($origins)) { $origins = array_unique($origins); foreach ($origins as $tag) { AECoreKettenrad::load($tag); // Remove temporary files AEUtilTempfiles::deleteTempFiles(); // Delete any stale temporary data AEUtilTempvars::reset($tag); } } // Reload the factory AEFactory::unserialize($factory); unset($factory); // Unpause logging if it was previously paused if (!$config->log) { AEUtilLogger::WriteLog(true, ''); } }
/** * Delete the backup file of the stats record whose ID is set in the model * @return bool True on success */ public function deleteFile() { JLoader::import('joomla.filesystem.file'); $id = $this->getState('id', 0); if (!is_numeric($id) || $id <= 0) { $this->setError(JText::_('STATS_ERROR_INVALIDID')); return false; } // Get the backup statistics record and the files to delete $stat = AEPlatform::getInstance()->get_statistics($id); $allFiles = AEUtilStatistics::get_all_filenames($stat, false); // Remove the custom log file if necessary $this->_deleteLogs($stat); // No files? Nothing to do. if (empty($allFiles)) { return true; } $status = true; foreach ($allFiles as $filename) { if (!@file_exists($filename)) { continue; } $new_status = @unlink($filename); if (!$new_status) { $new_status = JFile::delete($filename); } $status = $status ? $new_status : false; } return $status; }
/** * Applies the size and count quotas * * @return bool */ private function apply_quotas() { $this->setStep('Applying quotas'); $this->setSubstep(''); // If no quota settings are enabled, quit $registry = AEFactory::getConfiguration(); $useDayQuotas = $registry->get('akeeba.quota.maxage.enable'); $useCountQuotas = $registry->get('akeeba.quota.enable_count_quota'); $useSizeQuotas = $registry->get('akeeba.quota.enable_size_quota'); if (!($useDayQuotas || $useCountQuotas || $useSizeQuotas)) { $this->apply_obsolete_quotas(); AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "No quotas were defined; old backup files will be kept intact"); return true; // No quota limits were requested } // Try to find the files to be deleted due to quota settings $statistics = AEFactory::getStatistics(); $latestBackupId = $statistics->getId(); // Get quota values $countQuota = $registry->get('akeeba.quota.count_quota'); $sizeQuota = $registry->get('akeeba.quota.size_quota'); $daysQuota = $registry->get('akeeba.quota.maxage.maxdays'); $preserveDay = $registry->get('akeeba.quota.maxage.keepday'); // Get valid-looking backup ID's $validIDs = AEPlatform::getInstance()->get_valid_backup_records(true, array('NOT', 'restorepoint')); // Create a list of valid files $allFiles = array(); if (count($validIDs)) { foreach ($validIDs as $id) { $stat = AEPlatform::getInstance()->get_statistics($id); try { $backupstart = new DateTime($stat['backupstart']); $backupTS = $backupstart->format('U'); $backupDay = $backupstart->format('d'); } catch (Exception $e) { $backupTS = 0; $backupDay = 0; } // Multipart processing $filenames = AEUtilStatistics::get_all_filenames($stat, true); if (!is_null($filenames)) { // Only process existing files $filesize = 0; foreach ($filenames as $filename) { $filesize += @filesize($filename); } $allFiles[] = array('id' => $id, 'filenames' => $filenames, 'size' => $filesize, 'backupstart' => $backupTS, 'day' => $backupDay); } } } unset($validIDs); // If there are no files, exit early if (count($allFiles) == 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "There were no old backup files to apply quotas on"); return true; } // Init arrays $killids = array(); $ret = array(); $leftover = array(); // Do we need to apply maximum backup age quotas? if ($useDayQuotas) { $killDatetime = new DateTime(); $killDatetime->modify('-' . $daysQuota . ($daysQuota == 1 ? ' day' : ' days')); $killTS = $killDatetime->format('U'); foreach ($allFiles as $file) { if ($file['id'] == $latestBackupId) { continue; } // Is this on a preserve day? if ($preserveDay > 0) { if ($preserveDay == $file['day']) { $leftover[] = $file; continue; } } // Otherwise, check the timestamp if ($file['backupstart'] < $killTS) { $ret[] = $file['filenames']; $killids[] = $file['id']; } else { $leftover[] = $file; } } } // Do we need to apply count quotas? if ($useCountQuotas && is_numeric($countQuota) && !($countQuota <= 0) && !$useDayQuotas) { // Are there more files than the quota limit? if (!(count($allFiles) > $countQuota)) { // No, effectively skip the quota checking $leftover = $allFiles; } else { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Processing count quotas"); // Yes, aply the quota setting. Add to $ret all entries minus the last // $countQuota ones. $totalRecords = count($allFiles); $checkLimit = $totalRecords - $countQuota; // Only process if at least one file (current backup!) is to be left for ($count = 0; $count < $totalRecords; $count++) { $def = array_pop($allFiles); if ($def['id'] == $latestBackupId) { array_push($allFiles, $def); continue; } if (count($ret) < $checkLimit) { if ($latestBackupId != $def['id']) { $ret[] = $def['filenames']; $killids[] = $def['id']; } } else { $leftover[] = $def; } } unset($allFiles); } } else { // No count quotas are applied $leftover = $allFiles; } // Do we need to apply size quotas? if ($useSizeQuotas && is_numeric($sizeQuota) && !($sizeQuota <= 0) && count($leftover) > 0 && !$useDayQuotas) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Processing size quotas"); // OK, let's start counting bytes! $runningSize = 0; while (count($leftover) > 0) { // Each time, remove the last element of the backup array and calculate // running size. If it's over the limit, add the archive to the return array. $def = array_pop($leftover); $runningSize += $def['size']; if ($runningSize >= $sizeQuota) { if ($latestBackupId == $def['id']) { $runningSize -= $def['size']; } else { $ret[] = $def['filenames']; $killids[] = $def['filenames']; } } } } // Convert the $ret 2-dimensional array to single dimensional $quotaFiles = array(); foreach ($ret as $temp) { foreach ($temp as $filename) { $quotaFiles[] = $filename; } } // Update the statistics record with the removed remote files if (!empty($killids)) { foreach ($killids as $id) { $data = array('filesexist' => '0'); AEPlatform::getInstance()->set_or_update_statistics($id, $data, $this); } } // Apply quotas if (count($quotaFiles) > 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Applying quotas"); JLoader::import('joomla.filesystem.file'); foreach ($quotaFiles as $file) { if (!@AEPlatform::getInstance()->unlink($file)) { $this->setWarning("Failed to remove old backup file " . $file); } } } $this->apply_obsolete_quotas(); return true; }
public function apply_srp_quotas($parent) { $parent->relayStep('Applying quotas'); $parent->relaySubstep(''); // If no quota settings are enabled, quit $registry =& AEFactory::getConfiguration(); $srpQuotas = $registry->get('akeeba.quota.srp_size_quota'); if($srpQuotas <= 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "No restore point quotas were defined; old restore point files will be kept intact" ); return true; // No quota limits were requested } // Get valid-looking backup ID's $validIDs =& AEPlatform::get_valid_backup_records(true, array('restorepoint')); $statistics =& AEFactory::getStatistics(); $latestBackupId = $statistics->getId(); // Create a list of valid files $allFiles = array(); if(count($validIDs)) { foreach($validIDs as $id) { $stat = AEPlatform::get_statistics($id); // Multipart processing $filenames = AEUtilStatistics::get_all_filenames($stat, true); if(!is_null($filenames)) { // Only process existing files $filesize = 0; foreach($filenames as $filename) { $filesize += @filesize($filename); } $allFiles[] = array('id' => $id, 'filenames' => $filenames, 'size' => $filesize); } } } unset($validIDs); // If there are no files, exit early if(count($allFiles) == 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "There were no old restore points to apply quotas on" ); return true; } // Init arrays $killids = array(); $ret = array(); $leftover = array(); // Do we need to apply size quotas? AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Processing restore point size quotas" ); // OK, let's start counting bytes! $runningSize = 0; while(count($allFiles) > 0) { // Each time, remove the last element of the backup array and calculate // running size. If it's over the limit, add the archive to the return array. $def = array_pop($allFiles); $runningSize += $def['size']; if($runningSize >= $srpQuotas) { if($latestBackupId == $def['id']) { $runningSize -= $def['size']; } else { $ret[] = $def['filenames']; $killids[] = $def['filenames']; } } } // Convert the $ret 2-dimensional array to single dimensional $quotaFiles = array(); foreach($ret as $temp) { foreach($temp as $filename) { $quotaFiles[] = $filename; } } // Update the statistics record with the removed remote files if(!empty($killids)) foreach($killids as $id) { $data = array('filesexist' => '0'); AEPlatform::set_or_update_statistics($id, $data, $parent); } // Apply quotas if(count($quotaFiles) > 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Applying quotas" ); jimport('joomla.filesystem.file'); foreach($quotaFiles as $file) { if(!@AEPlatform::unlink($file)) { $parent->setWarning("Failed to remove old system restore point file ".$file ); } } } return true; }
private function _apiDownloadDirect($config) { $defConfig = array('backup_id' => 0, 'part_id' => 1); $config = array_merge($defConfig, $config); extract($config); $backup_stats = AEPlatform::getInstance()->get_statistics($backup_id); if (empty($backup_stats)) { // Backup record doesn't exist $this->status = self::STATUS_NOT_FOUND; $this->encapsulation = self::ENCAPSULATION_RAW; @ob_end_clean(); header('HTTP/1.1 500 Invalid backup record identifier'); flush(); JFactory::getApplication()->close(); } $files = AEUtilStatistics::get_all_filenames($backup_stats); if (count($files) < $part_id || $part_id <= 0) { // Invalid part $this->status = self::STATUS_NOT_FOUND; $this->encapsulation = self::ENCAPSULATION_RAW; @ob_end_clean(); header('HTTP/1.1 500 Invalid backup part'); flush(); JFactory::getApplication()->close(); } $filename = $files[$part_id - 1]; @clearstatcache(); // For a certain unmentionable browser -- Thank you, Nooku, for the tip if (function_exists('ini_get') && function_exists('ini_set')) { if (ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); } } // Remove php's time limit -- Thank you, Nooku, for the tip if (function_exists('ini_get') && function_exists('set_time_limit')) { if (!ini_get('safe_mode')) { @set_time_limit(0); } } $basename = @basename($filename); $filesize = @filesize($filename); $extension = strtolower(str_replace(".", "", strrchr($filename, "."))); while (@ob_end_clean()) { } @clearstatcache(); // Send MIME headers header('MIME-Version: 1.0'); header('Content-Disposition: attachment; filename="' . $basename . '"'); header('Content-Transfer-Encoding: binary'); header('Accept-Ranges: bytes'); switch ($extension) { case 'zip': // ZIP MIME type header('Content-Type: application/zip'); break; default: // Generic binary data MIME type header('Content-Type: application/octet-stream'); break; } // Notify of filesize, if this info is available if ($filesize > 0) { header('Content-Length: ' . @filesize($filename)); } // Disable caching header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Expires: 0"); header('Pragma: no-cache'); flush(); if ($filesize > 0) { // If the filesize is reported, use 1M chunks for echoing the data to the browser $blocksize = 1048756; //1M chunks $handle = @fopen($filename, "r"); // Now we need to loop through the file and echo out chunks of file data if ($handle !== false) { while (!@feof($handle)) { echo @fread($handle, $blocksize); @ob_flush(); flush(); } } if ($handle !== false) { @fclose($handle); } } else { // If the filesize is not reported, hope that readfile works @readfile($filename); } flush(); JFactory::getApplication()->close(); }