private function _listS3Contents($path = null) { static $lastDirectory = null; static $lastListing = array(); $directory = substr($path, 5); if ($lastDirectory != $directory) { if ($directory == '/') { $directory = null; } else { $directory = trim($directory, '/') . '/'; } $s3 = AmazonS3::getInstance(); $lastListing = $s3->getBucket('', $directory, null, null, '/', true); } return $lastListing; }
public function check() { $this->assertNotEmpty($this->title, 'COM_ARS_CATEGORY_ERR_NEEDS_TITLE'); // If the alias is missing, auto-create a new one if (!$this->alias) { \JLoader::import('joomla.filter.input'); $alias = str_replace(' ', '-', strtolower($this->title)); $this->alias = (string) preg_replace('/[^A-Z0-9_-]/i', '', $alias); } // If no alias could be auto-generated, fail $this->assertNotEmpty($this->alias, 'COM_ARS_CATEGORY_ERR_NEEDS_SLUG'); // Check alias for uniqueness $db = $this->getDBO(); $query = $db->getQuery(true)->select($db->qn('alias'))->from($db->qn('#__ars_categories')); if ($this->id) { $query->where('NOT(' . $db->qn('id') . ' = ' . $db->q($this->id) . ')'); } $db->setQuery($query); $aliases = $db->loadColumn(); $this->assertNotInArray($this->alias, $aliases, 'COM_ARS_CATEGORY_ERR_NEEDS_UNIQUE_SLUG'); // Check directory \JLoader::import('joomla.filesystem.folder'); $this->directory = rtrim($this->directory, '/'); if ($this->directory == 's3:') { $this->directory = 's3://'; } $check = trim($this->directory); $this->assertNotEmpty($check, 'COM_ARS_CATEGORY_ERR_NEEDS_DIRECTORY'); $potentialPrefix = substr($check, 0, 5); $potentialPrefix = strtolower($potentialPrefix); if ($potentialPrefix == 's3://') { $check = substr($check, 5); if (!empty($check)) { $check .= '/'; } $s3 = AmazonS3::getInstance(); $items = $s3->getBucket('', $check); $this->assertNotEmpty($items, 'COM_ARS_CATEGORY_ERR_S3_DIRECTORY_NOT_EXISTS'); } else { if (!\JFolder::exists($this->directory)) { $directory = JPATH_SITE . '/' . $this->directory; $this->assert(\JFolder::exists($directory), 'COM_ARS_CATEGORY_ERR_DIRECTORY_NOT_EXISTS'); } } // Automaticaly fix the type if (!in_array($this->type, array('normal', 'bleedingedge'))) { $this->type = 'normal'; } // Set the access to registered if there are subscriptions groups defined if (empty($this->access)) { $this->access = 1; } if (!empty($this->groups) && $this->access == 1) { $this->access = 2; } if (empty($this->published) && $this->published !== 0) { $this->published = 0; } parent::check(); return $this; }
function delete() { $folder = $this->getCategoryFolder(); $file = $this->getState('file', ''); if (empty($file)) { return ''; } $potentialPrefix = substr($folder, 0, 5); $potentialPrefix = strtolower($potentialPrefix); $useS3 = $potentialPrefix == 's3://'; if ($useS3) { $folder = trim(substr($folder, 5), '/'); if (!empty($folder)) { $folder .= '/'; } $filepath = $folder . $file; } else { $filepath = $folder . '/' . $file; } if (!$useS3) { \JLoader::import('joomla.filesystem.file'); if (!\JFile::exists($filepath)) { return false; } } /** @var Items $itemsModel */ $itemsModel = $this->container->factory->model('Items')->tmpInstance(); $files = $itemsModel->category($this->getState('category', 0))->filename($this->getState('file', ''))->get(true); if ($files->count()) { // Unpublish entries /** @var Items $item */ foreach ($files as $item) { $item->unpublish(); } } if ($useS3) { $s3 = AmazonS3::getInstance(); return $s3->deleteObject($filepath); } else { return \JFile::delete($filepath); } }
public function doDownload(Items $item) { // If it's a link we just have to redirect users if ($item->type == 'link') { if (@ob_get_length() !== false) { @ob_end_clean(); } $this->logoutUser(); \JFactory::getApplication()->redirect($item->url, false); return; } \JLoader::import('joomla.filesystem.folder'); \JLoader::import('joomla.filesystem.file'); $folder = $item->release->category->directory; $potentialPrefix = substr($folder, 0, 5); $potentialPrefix = strtolower($potentialPrefix); $useS3 = $potentialPrefix == 's3://'; if ($useS3) { $filename = substr($folder, 5) . '/' . $item->filename; $s3 = AmazonS3::getInstance(); $url = $s3->getAuthenticatedURL($filename); if (@ob_get_length() !== false) { @ob_end_clean(); } $this->logoutUser(); \JFactory::getApplication()->redirect($url, false); return; } if (!\JFolder::exists($folder)) { $folder = JPATH_ROOT . '/' . $folder; if (!\JFolder::exists($folder)) { $this->logoutUser(); throw new \RuntimeException('Not found', 404); } } $filename = $folder . '/' . $item->filename; if (!\JFile::exists($filename)) { $this->logoutUser(); throw new \RuntimeException('Not found', 404); } $basename = @basename($filename); $filesize = @filesize($filename); $mime_type = null; if (class_exists('finfo')) { $fInfo = new \finfo(FILEINFO_MIME_TYPE); $mime_type = $fInfo->file($filename); } if (empty($mime_type)) { $mime_type = $this->get_mime_type($filename); } if (empty($mime_type)) { $mime_type = 'application/octet-stream'; } // Clear cache while (@ob_end_clean()) { // Make sure no junk will come before out content – to the extent we have a say on this... } // Fix IE bugs if (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) { $header_file = preg_replace('/\\./', '%2e', $basename, substr_count($basename, '.') - 1); if (ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); } } else { $header_file = $basename; } // Import ARS plugins \JLoader::import('joomla.plugin.helper'); \JPluginHelper::importPlugin('ars'); // Call any plugins to post-process the download file parameters $object = array('rawentry' => $item, 'filename' => $filename, 'basename' => $basename, 'header_file' => $header_file, 'mimetype' => $mime_type, 'filesize' => $filesize); $app = \JFactory::getApplication(); $retArray = $app->triggerEvent('onARSBeforeSendFile', array($object)); if (!empty($retArray)) { foreach ($retArray as $ret) { if (empty($ret) || !is_array($ret)) { continue; } $ret = (object) $ret; $filename = $ret->filename; $basename = $ret->basename; $header_file = $ret->header_file; $mime_type = $ret->mimetype; $filesize = $ret->filesize; } } @clearstatcache(); // Disable caching header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: public", false); // Send MIME headers header("Content-Description: File Transfer"); header('Content-Type: ' . $mime_type); header("Accept-Ranges: bytes"); header('Content-Disposition: attachment; filename="' . $header_file . '"'); header('Content-Transfer-Encoding: binary'); header('Connection: close'); error_reporting(0); if (!ini_get('safe_mode')) { set_time_limit(0); } // Support resumable downloads $isResumable = false; $seek_start = 0; $seek_end = $filesize - 1; if (isset($_SERVER['HTTP_RANGE'])) { list($size_unit, $range_orig) = explode('=', $_SERVER['HTTP_RANGE'], 2); if ($size_unit == 'bytes') { //multiple ranges could be specified at the same time, but for simplicity only serve the first range //http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt list($range, $extra_ranges) = explode(',', $range_orig, 2); } else { $range = ''; } } else { $range = ''; } if ($range) { // Figure out download piece from range (if set) list($seek_start, $seek_end) = explode('-', $range, 2); // Set start and end based on range (if set), else set defaults. Also checks for invalid ranges. $seek_end = empty($seek_end) ? $filesize - 1 : min(abs(intval($seek_end)), $filesize - 1); $seek_start = empty($seek_start) || $seek_end < abs(intval($seek_start)) ? 0 : max(abs(intval($seek_start)), 0); $isResumable = true; } // Use 1M chunks for echoing the data to the browser $chunksize = 1024 * 1024; //1M chunks $handle = @fopen($filename, 'rb'); if ($handle !== false) { if ($isResumable) { //Only send partial content header if downloading a piece of the file (IE workaround) if ($seek_start > 0 || $seek_end < $filesize - 1) { header('HTTP/1.1 206 Partial Content'); } // Necessary headers $totalLength = $seek_end - $seek_start + 1; header('Content-Range: bytes ' . $seek_start . '-' . $seek_end . '/' . $filesize); header('Content-Length: ' . $totalLength); // Seek to start fseek($handle, $seek_start); } else { $isResumable = false; // Notify of filesize, if this info is available if ($filesize > 0) { header('Content-Length: ' . (int) $filesize); } } $read = 0; while (!feof($handle) && $chunksize > 0) { if ($isResumable) { if ($totalLength - $read < $chunksize) { $chunksize = $totalLength - $read; if ($chunksize < 0) { continue; } } } $buffer = fread($handle, $chunksize); if ($isResumable) { $read += strlen($buffer); } echo $buffer; @ob_flush(); flush(); } @fclose($handle); } else { // Notify of filesize, if this info is available if ($filesize > 0) { header('Content-Length: ' . (int) $filesize); } @readfile($filename); } // Call any plugins to post-process the file download $object = array('rawentry' => $item, 'filename' => $filename, 'basename' => $basename, 'header_file' => $header_file, 'mimetype' => $mime_type, 'filesize' => $filesize, 'resumable' => $isResumable, 'range_start' => $seek_start, 'range_end' => $seek_end); $ret = $app->triggerEvent('onARSAfterSendFile', array($object)); if (!empty($ret)) { foreach ($ret as $r) { if (!empty($r)) { echo $r; } } } $this->logoutUser(); }
/** * Returns a list of select options which will let the user pick a file for a release. Files already used in other * items of the same category will not be listed to prevent the list getting too long. * * @param int $release_id The numeric ID of the release selected by the user * @param int $item_id The numeric ID of the current item. Leave 0 if it's a new item. * * @return array Array of JHtml options. */ public function getFilesOptions($release_id, $item_id = 0) { $options = array(); $options[] = JHTML::_('select.option', '', '- ' . JText::_('LBL_ITEMS_FILENAME_SELECT') . ' -'); // Try to figure out a directory $directory = null; if (empty($release_id)) { return $options; } /** @var Releases $releaseModel */ $releaseModel = $this->container->factory->model('Releases')->tmpInstance(); // Get the release $release = $releaseModel->find((int) $release_id); // Get which directory to use $directory = $release->category->directory; $potentialPrefix = substr($directory, 0, 5); $potentialPrefix = strtolower($potentialPrefix); $useS3 = $potentialPrefix == 's3://'; if ($useS3) { $directory = substr($directory, 5); if ($directory === false) { $directory = ''; } $s3 = AmazonS3::getInstance(); $items = $s3->getBucket('', $directory . '/'); if (empty($items)) { $directory = null; } if (empty($directory)) { $directory = '/'; } } else { \JLoader::import('joomla.filesystem.folder'); if (!\JFolder::exists($directory)) { $directory = JPATH_ROOT . '/' . $directory; if (!\JFolder::exists($directory)) { $directory = null; } } } if (empty($directory)) { return $options; } // Get a list of files already used in this category (so as not to show them again, he he!) $files = array(); $itemsModel = $this->tmpInstance(); $items = $itemsModel->category($release->category_id)->release('false')->get(true); if (!empty($items)) { // Walk through the list and find the currently selected filename $currentFilename = ''; foreach ($items as $item) { if ($item->id == $item_id) { $currentFilename = $item->filename; break; } } // Remove already used filenames except the currently selected filename reset($items); foreach ($items as $item) { if ($item->filename != $currentFilename && !empty($item->filename)) { $files[] = $item->filename; } } $files = array_unique($files); } // Produce a list of files and remove the items in the $files array $useFiles = array(); if ($useS3) { $s3 = Amazons3::getInstance(); $allFiles = $s3->getBucket('', $directory, null, null, null, true); if (!empty($allFiles)) { foreach ($allFiles as $aFile => $info) { $aFile = ltrim(substr($aFile, strlen($directory)), '/'); if (in_array($aFile, $files)) { continue; } $useFiles[] = $aFile; } } } else { $allFiles = \JFolder::files($directory, '.', 3, true); $root = str_replace('\\', '/', $directory); if (!empty($allFiles)) { foreach ($allFiles as $aFile) { $aFile = str_replace('\\', '/', $aFile); $aFile = ltrim(substr($aFile, strlen($root)), '/'); if (in_array($aFile, $files)) { continue; } $useFiles[] = $aFile; } } } if (empty($useFiles)) { return $options; } foreach ($useFiles as $file) { $options[] = JHTML::_('select.option', $file, $file); } return $options; }
/** * Create a new folder */ public function newFolder() { if (!$this->checkACL('core.create')) { throw new \RuntimeException(\JText::_('JERROR_ALERTNOAUTHOR'), 403); } $this->csrfProtection(); $categoryId = $this->input->getInt('id', 0); $folder = $this->input->getString('folder', ''); $file = $this->input->getString('file', ''); /** @var \Akeeba\ReleaseSystem\Admin\Model\Upload $model */ $model = $this->getModel(); $model->setState('category', (int) $categoryId); $model->setState('folder', $folder); $model->setState('file', $file); $parent = $model->getCategoryFolder(); $potentialPrefix = substr($parent, 0, 5); $potentialPrefix = strtolower($potentialPrefix); $useS3 = $potentialPrefix == 's3://'; if ($useS3) { $trimmedParent = $parent; if (substr($parent, 0, 5) == 's3://') { $trimmedParent = substr($parent, 5); } $newFolder = $trimmedParent . '/' . $file; $newFolder = trim($newFolder, '/') . '/'; $s3 = AmazonS3::getInstance(); $status = $s3->putObject('$folder$', $newFolder, true); } else { \JLoader::import('joomla.filesystem.folder'); $newFolder = $parent . '/' . \JFolder::makeSafe($file); $status = \JFolder::create($newFolder); } $url = 'index.php?option=com_ars&view=upload&task=category&id=' . (int) $categoryId . '&folder=' . urlencode($this->input->getString('folder')) . '&' . \JFactory::getSession()->getFormToken(true) . '=1'; if ($status) { $this->setRedirect($url, \JText::_('MSG_FOLDER_CREATED')); return; } $this->setRedirect($url, JText::_('MSG_FOLDER_NOT_CREATED'), 'error'); }