Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sat, Feb 1, 6:10 PM

in-portal

Index: branches/5.2.x/core/units/helpers/upload_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/upload_helper.php (revision 16651)
+++ branches/5.2.x/core/units/helpers/upload_helper.php (revision 16652)
@@ -1,404 +1,404 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2012 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
class kUploadHelper extends kHelper
{
/**
* File helper reference
*
* @var FileHelper
*/
protected $fileHelper = null;
/**
* Creates kUploadHelper instance.
*/
public function __construct()
{
parent::__construct();
$this->fileHelper = $this->Application->recallObject('FileHelper');
// 5 minutes execution time
@set_time_limit(5 * 60);
}
/**
* Handles the upload.
*
* @param kEvent $event Event.
*
* @return string
* @throws kUploaderException When upload could not be handled properly.
*/
public function handle(kEvent $event)
{
$this->disableBrowserCache();
// Uncomment this one to fake upload time
// sleep(5);
if ( !$this->Application->HttpQuery->Post ) {
// Variables {field, id, flashsid} are always submitted through POST!
// When file size is larger, then "upload_max_filesize" (in php.ini),
// then these variables also are not submitted.
throw new kUploaderException('File size exceeds allowed limit.', 413);
}
if ( !$this->checkPermissions($event) ) {
// 403 Forbidden
throw new kUploaderException('You don\'t have permissions to upload.', 403);
}
$value = $this->Application->GetVar('file');
if ( !$value || ($value['error'] != UPLOAD_ERR_OK) ) {
// 413 Request Entity Too Large (file uploads disabled OR uploaded file was
// too large for web server to accept, see "upload_max_filesize" in php.ini)
throw new kUploaderException('File size exceeds allowed limit.', 413);
}
$value = $this->Application->unescapeRequestVariable($value);
$tmp_path = WRITEABLE . '/tmp/';
$filename = $this->getUploadedFilename() . '.tmp';
$id = $this->Application->GetVar('id');
if ( $id ) {
$filename = $id . '_' . $filename;
}
if ( !is_writable($tmp_path) ) {
// 500 Internal Server Error
// check both temp and live upload directory
throw new kUploaderException('Write permissions not set on the server, please contact server administrator.', 500);
}
$filename = $this->fileHelper->ensureUniqueFilename($tmp_path, $filename);
$storage_format = $this->getStorageFormat($this->Application->GetVar('field'), $event);
$file_path = $tmp_path . $filename;
$actual_file_path = $this->moveUploadedFile($file_path);
if ( $storage_format && $file_path == $actual_file_path ) {
$this->resizeUploadedFile($file_path, $storage_format);
}
$this->deleteTempFiles($tmp_path);
$thumbs_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $tmp_path, 1);
$thumbs_path = FULL_PATH . THUMBS_PATH . $thumbs_path;
if ( file_exists($thumbs_path) ) {
$this->deleteTempFiles($thumbs_path);
}
return preg_replace('/^' . preg_quote($id, '/') . '_/', '', basename($file_path));
}
/**
* Resizes uploaded file.
*
* @param string $file_path File path.
* @param string $format Format.
*
* @return boolean
*/
public function resizeUploadedFile(&$file_path, $format)
{
/** @var ImageHelper $image_helper */
$image_helper = $this->Application->recallObject('ImageHelper');
// Add extension, so that "ImageHelper::ResizeImage" can work.
- $resize_file_path = tempnam(sys_get_temp_dir(), 'uploaded_') . '.jpg';
+ $resize_file_path = tempnam(WRITEABLE . '/tmp', 'uploaded_') . '.jpg';
if ( rename($file_path, $resize_file_path) === false ) {
return false;
}
$resized_file_path = $this->fileHelper->urlToPath(
$image_helper->ResizeImage($resize_file_path, $format)
);
$file_path = $this->replaceFileExtension(
$file_path,
pathinfo($resized_file_path, PATHINFO_EXTENSION)
);
return rename($resized_file_path, $file_path);
}
/**
* Replace extension of uploaded file.
*
* @param string $file_path File path.
* @param string $new_file_extension New file extension.
*
* @return string
*/
protected function replaceFileExtension($file_path, $new_file_extension)
{
$file_path_without_temp_file_extension = kUtil::removeTempExtension($file_path);
$current_file_extension = pathinfo($file_path_without_temp_file_extension, PATHINFO_EXTENSION);
// Format of resized file wasn't changed.
if ( $current_file_extension === $new_file_extension ) {
return $file_path;
}
$ret = preg_replace(
'/\.' . preg_quote($current_file_extension, '/') . '$/',
'.' . $new_file_extension,
$file_path_without_temp_file_extension
);
// Add ".tmp" later, since it was removed.
if ( $file_path_without_temp_file_extension !== $file_path ) {
$ret .= '.tmp';
}
// After file extension change resulting filename might not be unique in that folder anymore.
$path = pathinfo($ret, PATHINFO_DIRNAME);
return $path . '/' . $this->fileHelper->ensureUniqueFilename($path, basename($ret));
}
/**
* Sends headers to ensure, that response is never cached.
*
* @return void
*/
protected function disableBrowserCache()
{
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
}
/**
* Checks, that flash uploader is allowed to perform upload
*
* @param kEvent $event
* @return bool
*/
protected function checkPermissions(kEvent $event)
{
// Flash uploader does NOT send correct cookies, so we need to make our own check
$cookie_name = 'adm_' . $this->Application->ConfigValue('SessionCookieName');
$this->Application->HttpQuery->Cookie['cookies_on'] = 1;
$this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid');
// this prevents session from auto-expiring when KeepSessionOnBrowserClose & FireFox is used
$this->Application->HttpQuery->Cookie[$cookie_name . '_live'] = $this->Application->GetVar('flashsid');
/** @var Session $admin_session */
$admin_session = $this->Application->recallObject('Session.admin');
if ( $this->Application->permissionCheckingDisabled($admin_session->RecallVar('user_id')) ) {
return true;
}
// copy some data from given session to current session
$backup_user_id = $this->Application->RecallVar('user_id');
$this->Application->StoreVar('user_id', $admin_session->RecallVar('user_id'));
$backup_user_groups = $this->Application->RecallVar('UserGroups');
$this->Application->StoreVar('UserGroups', $admin_session->RecallVar('UserGroups'));
// check permissions using event, that have "add|edit" rule
$check_event = new kEvent($event->getPrefixSpecial() . ':OnProcessSelected');
$check_event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
/** @var kEventHandler $event_handler */
$event_handler = $this->Application->recallObject($event->Prefix . '_EventHandler');
$allowed_to_upload = $event_handler->CheckPermission($check_event);
// restore changed data, so nothing gets saved to database
$this->Application->StoreVar('user_id', $backup_user_id);
$this->Application->StoreVar('UserGroups', $backup_user_groups);
return $allowed_to_upload;
}
/**
* Returns uploaded filename.
*
* @return string
*/
protected function getUploadedFilename()
{
if ( isset($_REQUEST['name']) ) {
$file_name = $_REQUEST['name'];
}
elseif ( !empty($_FILES) ) {
$file_name = $_FILES['file']['name'];
}
else {
$file_name = uniqid('file_');
}
return $file_name;
}
/**
* Gets storage format for a given field.
*
* @param string $field_name
* @param kEvent $event
* @return bool
*/
protected function getStorageFormat($field_name, kEvent $event)
{
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
$field_options = array_key_exists($field_name, $fields) ? $fields[$field_name] : $virtual_fields[$field_name];
return isset($field_options['storage_format']) ? $field_options['storage_format'] : false;
}
/**
* Moves uploaded file to given location.
*
* @param string $file_path File path.
*
* @return string
* @throws kUploaderException When upload could not be handled properly.
*/
protected function moveUploadedFile($file_path)
{
// Chunking might be enabled.
$chunk = (int)$this->Application->GetVar('chunk', 0);
$chunks = (int)$this->Application->GetVar('chunks', 0);
$actual_file_path = $file_path . '.part';
// Open temp file.
if ( !$out = @fopen($actual_file_path, $chunks ? 'ab' : 'wb') ) {
throw new kUploaderException('Failed to open output stream.', 102);
}
if ( !empty($_FILES) ) {
if ( $_FILES['file']['error'] || !is_uploaded_file($_FILES['file']['tmp_name']) ) {
throw new kUploaderException('Failed to move uploaded file.', 103);
}
// Read binary input stream and append it to temp file.
if ( !$in = @fopen($_FILES['file']['tmp_name'], 'rb') ) {
throw new kUploaderException('Failed to open input stream.', 101);
}
}
else {
if ( !$in = @fopen('php://input', 'rb') ) {
throw new kUploaderException('Failed to open input stream.', 101);
}
}
while ( $buff = fread($in, 4096) ) {
fwrite($out, $buff);
}
@fclose($out);
@fclose($in);
// Check if file has been uploaded.
if ( !$chunks || $chunk == $chunks - 1 ) {
// Strip the temp .part suffix off.
rename($actual_file_path, $file_path);
$actual_file_path = $file_path;
}
return $actual_file_path;
}
/**
* Delete temporary files, that won't be used for sure
*
* @param string $path
* @return void
*/
protected function deleteTempFiles($path)
{
$files = glob($path . '*.*');
$max_file_date = strtotime('-1 day');
foreach ( $files as $file ) {
if ( filemtime($file) < $max_file_date ) {
unlink($file);
}
}
}
/**
* Prepares object for operations with file on given field.
*
* @param kEvent $event Event.
* @param string $field Field.
*
* @return kDBItem
*/
public function prepareUploadedFile(kEvent $event, $field)
{
/** @var kDBItem $object */
$object = $event->getObject(Array ('skip_autoload' => true));
$filename = $this->getSafeFilename();
if ( !$filename ) {
$object->SetDBField($field, '');
return $object;
}
// set current uploaded file
if ( $this->Application->GetVar('tmp') ) {
$options = $object->GetFieldOptions($field);
$options['upload_dir'] = WRITEBALE_BASE . '/tmp/';
unset($options['include_path']);
$object->SetFieldOptions($field, $options);
$filename = $this->Application->GetVar('id') . '_' . $filename;
}
$object->SetDBField($field, $filename);
return $object;
}
/**
* Returns safe version of filename specified in url
*
* @return bool|string
* @access protected
*/
protected function getSafeFilename()
{
$filename = $this->Application->GetVar('file');
$filename = $this->Application->unescapeRequestVariable($filename);
if ( (strpos($filename, '../') !== false) || (trim($filename) !== $filename) ) {
// when relative paths or special chars are found template names from url, then it's hacking attempt
return false;
}
return $filename;
}
}
class kUploaderException extends Exception
{
}

Event Timeline