Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F1102229
upload_helper.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Aug 18, 8:13 AM
Size
11 KB
Mime Type
text/x-php
Expires
Wed, Aug 20, 8:13 AM (1 d, 6 h)
Engine
blob
Format
Raw Data
Handle
714019
Attached To
rINP In-Portal
upload_helper.php
View Options
<?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
);
$field_options
=
$this
->
getFieldOptions
(
$this
->
Application
->
GetVar
(
'field'
),
$event
);
$storage_format
=
isset
(
$field_options
[
'storage_format'
])
?
$field_options
[
'storage_format'
]
:
false
;
$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
);
}
if
(
getArrayValue
(
$field_options
,
'file_types'
)
&&
!
$this
->
fileHelper
->
extensionMatch
(
kUtil
::
removeTempExtension
(
$filename
),
$field_options
[
'file_types'
])
)
{
throw
new
kUploaderException
(
'File is not an allowed file type.'
,
415
);
}
if
(
filesize
(
$actual_file_path
)
>
$field_options
[
'max_size'
]
)
{
throw
new
kUploaderException
(
'File size exceeds allowed limit.'
,
413
);
}
$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
(
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
;
}
/**
* Returns field options.
*
* @param string $field Field.
* @param kEvent $event Event.
*
* @return array
*/
protected
function
getFieldOptions
(
$field
,
kEvent
$event
)
{
/** @var array $fields */
$fields
=
$this
->
Application
->
getUnitOption
(
$event
->
Prefix
,
'Fields'
);
/** @var array $virtual_fields */
$virtual_fields
=
$this
->
Application
->
getUnitOption
(
$event
->
Prefix
,
'VirtualFields'
);
return
array_key_exists
(
$field
,
$fields
)
?
$fields
[
$field
]
:
$virtual_fields
[
$field
];
}
/**
* 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
Log In to Comment