public function display() { $task = JRequest::getCmd('task','default'); switch($task) { case 'showcomment': JToolBarHelper::title(JText::_('AKEEBA').': <small>'.JText::_('BUADMIN').'</small>','akeeba'); JToolBarHelper::back('AKEEBA_CONTROLPANEL', 'index.php?option='.JRequest::getCmd('option')); JToolBarHelper::save(); JToolBarHelper::cancel(); $document =& JFactory::getDocument(); $document->addStyleSheet(JURI::base().'../media/com_akeeba/theme/akeebaui.css?'.AKEEBAMEDIATAG); $id = JRequest::getInt('id',0); $record = AEPlatform::get_statistics($id); $this->assign('record', $record); $this->assign('record_id', $id); JRequest::setVar('tpl','comment'); break; default: $registry =& AEFactory::getConfiguration(); if($task == 'default') { JToolBarHelper::title(JText::_('AKEEBA').': <small>'.JText::_('BUADMIN').'</small>','akeeba'); } else { JToolBarHelper::title(JText::_('AKEEBA').': <small>'.JText::_('BUADMINSRP').'</small>','akeeba'); } JToolBarHelper::back('AKEEBA_CONTROLPANEL', 'index.php?option='.JRequest::getCmd('option')); JToolBarHelper::spacer(); JToolBarHelper::deleteList(); JToolBarHelper::custom( 'deletefiles', 'delete.png', 'delete_f2.png', JText::_('STATS_LABEL_DELETEFILES'), true ); // Add custom submenus JSubMenuHelper::addEntry( JText::_('BUADMIN_LABEL_BACKUPS'), JURI::base().'index.php?option=com_akeeba&view='.JRequest::getCmd('view').'&task=default', ($task == 'default') ); JSubMenuHelper::addEntry( JText::_('BUADMIN_LABEL_SRP'), JURI::base().'index.php?option=com_akeeba&view='.JRequest::getCmd('view').'&task=restorepoint', ($task == 'restorepoint') ); if(AKEEBA_PRO && ($task == 'default')) { $bar = & JToolBar::getInstance('toolbar'); $bar->appendButton( 'Link', 'restore', JText::_('DISCOVER'), 'index.php?option=com_akeeba&view=discover' ); JToolBarHelper::publish('restore', JText::_('STATS_LABEL_RESTORE')); } if(($task == 'default')) { JToolBarHelper::editList('showcomment', JText::_('STATS_LOG_EDITCOMMENT')); $pModel = JModel::getInstance('Profiles','AkeebaModel'); $enginesPerPprofile = $pModel->getPostProcessingEnginePerProfile(); $this->assign('enginesPerProfile', $enginesPerPprofile); } JToolBarHelper::spacer(); // "Show warning first" download button. Joomlantastic! $confirmationText = AkeebaHelperEscape::escapeJS( JText::_('STATS_LOG_DOWNLOAD_CONFIRM'), "'\n" ); $baseURI = JURI::base(); $js = <<<ENDSCRIPT function confirmDownloadButton() { var answer = confirm('$confirmationText'); if(answer) submitbutton('download'); } function confirmDownload(id, part) { var answer = confirm('$confirmationText'); var newURL = '$baseURI'; if(answer) { newURL += 'index.php?option=com_akeeba&view=buadmin&task=download&id='+id; if( part != '' ) newURL += '&part=' + part window.location = newURL; } } ENDSCRIPT; $document =& JFactory::getDocument(); $document->addScriptDeclaration($js); $document->addStyleSheet(JURI::base().'../media/com_akeeba/theme/akeebaui.css?'.AKEEBAMEDIATAG); $hash = 'akeebabuadmin'; // ...ordering $app = JFactory::getApplication(); $this->lists->set('order', $app->getUserStateFromRequest($hash.'filter_order', 'filter_order', 'backupstart')); $this->lists->set('order_Dir', $app->getUserStateFromRequest($hash.'filter_order_Dir', 'filter_order_Dir', 'DESC')); // ...filter state $this->lists->set('fltDescription', $app->getUserStateFromRequest($hash.'filter_description', 'description', null)); $this->lists->set('fltFrom', $app->getUserStateFromRequest($hash.'filter_from', 'from', null)); $this->lists->set('fltTo', $app->getUserStateFromRequest($hash.'filter_to', 'to', null)); $this->lists->set('fltOrigin', $app->getUserStateFromRequest($hash.'filter_origin', 'origin', null)); $this->lists->set('fltProfile', $app->getUserStateFromRequest($hash.'filter_profile', 'profile', null)); $filters = $this->_getFilters(); $ordering = $this->_getOrdering(); require_once JPATH_COMPONENT_ADMINISTRATOR.DS.'models'.DS.'statistics.php'; $model = new AkeebaModelStatistics(); $list =& $model->getStatisticsListWithMeta(false, $filters, $ordering); // Assign data to the view $this->assignRef( 'lists', $this->lists); // Filter lists $this->assignRef( 'list', $list); // Data $this->assignRef( 'pagination', $model->getPagination($filters)); // Pagination object break; } // Add live help AkeebaHelperIncludes::addHelp(); parent::display(JRequest::getVar('tpl')); }
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; }
/** * Save an edited backup record */ public function save() { // CSRF prevention if(!JRequest::getVar(JUtility::getToken(), false, 'POST')) { JError::raiseError('403', JText::_(version_compare(JVERSION, '1.6.0', 'ge') ? 'JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN' : 'Request Forbidden')); } $id = JRequest::getInt('id'); $description = JRequest::getString('description'); $comment = JRequest::getVar('comment',null,'default','string',4); $statistic = AEPlatform::get_statistics(JRequest::getInt('id')); $statistic['description'] = $description; $statistic['comment'] = $comment; AEPlatform::set_or_update_statistics(JRequest::getInt('id'),$statistic,$self); if( !$this->getError() ) { $message = JText::_('STATS_LOG_SAVEDOK'); $type = 'message'; } else { $message = JText::_('STATS_LOG_SAVEERROR'); $type = 'error'; } $session = JFactory::getSession(); $task = $session->get('buadmin.task', 'default', 'akeeba'); $this->setRedirect(JURI::base().'index.php?option=com_akeeba&view=buadmin&task='.$task, $message, $type); }
/** * Was the last backup a failed one? Used to apply magic settings as a means of * troubleshooting. * * @return bool */ public function isLastBackupFailed() { // Get the last backup record ID $list = AEPlatform::get_statistics_list(0,1); if(empty($list)) return false; $id = $list[0]; $statmodel->setId($id); $record = AEPlatform::get_statistics($id); return ($record['status'] == 'fail'); }
/** * Validates the data passed to the request. * @return mixed True if all is OK, an error string if something is wrong */ function validateRequest() { // Does the ID exist? $id = $this->getId(); if(empty($id)) { return JText::_('RESTORE_ERROR_INVALID_RECORD'); } // Is this a valid backup entry? $data = AEPlatform::get_statistics($id); if(empty($data)) { return JText::_('RESTORE_ERROR_INVALID_RECORD'); } // Is this a complete backup? if($data['status'] != 'complete') { return JText::_('RESTORE_ERROR_INVALID_RECORD'); } // Is it a restoration point backup? if($data['tag'] != 'restorepoint') { return JText::_('RESTORE_ERROR_NOT_AN_SRP'); } $rawDataParts = explode("\n", $data['comment']); $this->info = json_decode($rawDataParts[1]); $this->info->srpdate = $data['backupstart']; // Load the profile ID (so that we can find out the output directory) $profile_id = $data['profile_id']; AEPlatform::load_configuration($profile_id); $path = $data['absolute_path']; $exists = @file_exists($path); if(!$exists) { // Let's try figuring out an alternative path $config =& AEFactory::getConfiguration(); $path = $config->get('akeeba.basic.output_directory', '').DS.$data['archivename']; $exists = @file_exists($path); } if(!$exists) { return JText::_('RESTORE_ERROR_ARCHIVE_MISSING'); } $filename = basename($path); $lastdot = strrpos($filename, '.'); $extension = strtoupper( substr($filename, $lastdot+1) ); if( !in_array($extension, array('JPA','ZIP')) ) { return JText::_('RESTORE_ERROR_INVALID_TYPE'); } $this->data =& $data; $this->path = $path; $this->extension = $extension; return true; }
/** * Gets the stats record ID from the request and checks that it does exist * * @return bool|int False if an invalid ID is found, the numeric ID if it's valid */ private function getAndCheckId() { $id = JRequest::getInt('id',0); if($id <= 0) return false; $statObject = AEPlatform::get_statistics($id); if(empty($statObject) || !is_array($statObject)) return false; return $id; }
/** * 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::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::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->sub(new DateInterval('P'.$daysQuota.'D')); $killTS = $killDatetime->format('U'); foreach($allFiles as $file) { // 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(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::set_or_update_statistics($id, $data, $this); } // 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)) { $this->setWarning("Failed to remove old backup file ".$file ); } } } $this->apply_obsolete_quotas(); return true; }
private function _apiDownloadDirect($config) { $defConfig = array( 'backup_id' => 0, 'part_id' => 1 ); $config = array_merge($defConfig, $config); extract($config); $backup_stats = AEPlatform::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(); }