: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if (in_array($mime, array('application/x-empty', 'inode/x-empty'))) {
// finfo return this mime for empty files
} elseif ($mime == 'application/x-zip') {
// http://elrte.org/redmine/issues/163
$mime = 'application/zip';
return $mime ? $mime : 'unknown';
* Detect file type extension by local path
* @param object $volume elFinderVolumeDriver instance
* @param string $path Local path
* @param string $name Filename to save
* @return string file type extension with dot
protected function detectFileExtension($volume, $path, $name)
$mime = $this->detectMimeType($path);
if ($mime === 'unknown') {
$mime = 'application/octet-stream';
$ext = $volume->getExtentionByMime($volume->mimeTypeNormalize($mime, $name));
return $ext ? ('.' . $ext) : '';
* Get temporary directory path
* @param string $volumeTempPath
private function getTempDir($volumeTempPath = null)
if ($this->uploadTempPath) {
$testDirs[] = rtrim(realpath($this->uploadTempPath), DIRECTORY_SEPARATOR);
$testDirs[] = rtrim(realpath($volumeTempPath), DIRECTORY_SEPARATOR);
if (elFinder::$commonTempPath) {
$testDirs[] = elFinder::$commonTempPath;
foreach ($testDirs as $testDir) {
if (!$testDir || !is_dir($testDir)) continue;
if (is_writable($testDir)) {
foreach (glob($tempDir . DIRECTORY_SEPARATOR . 'ELF*') as $cf) {
if (filemtime($cf) < $gc) {
* @param array command arguments
* @throws elFinderAbortException
protected function chmod($args)
$targets = $args['targets'];
$mode = intval((string)$args['mode'], 8);
if (!is_array($targets)) {
$targets = array($targets);
if (($volume = $this->volume($targets[0])) == false) {
$result['error'] = $this->error(self::ERROR_CONF_NO_VOL);
$this->itemLock($targets);
foreach ($targets as $target) {
elFinder::checkAborted();
$file = $volume->chmod($target, $mode);
$files = array_merge($files, is_array($file) ? $file : array($file));
$errors = array_merge($errors, $volume->error());
$result['changed'] = $files;
$result['warning'] = $this->error($errors);
$result['error'] = $this->error($errors);
* Check chunked upload files
* @param string $tmpname uploaded temporary file path
* @param string $chunk uploaded chunk file name
* @param string $cid uploaded chunked file id
* @param string $tempDir temporary dirctroy path
* @throws elFinderAbortException
private function checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume = null)
/* @var elFinderVolumeDriver $volume */
if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
$encname = md5($cid . '_' . $fname);
$base = $tempDir . DIRECTORY_SEPARATOR . 'ELF' . $encname;
// chunked file upload fail
foreach (glob($base . '*') as $cf) {
ignore_user_abort(false);
$range = isset($_POST['range']) ? trim($_POST['range']) : '';
if ($range && preg_match('/^(\d+),(\d+),(\d+)$/', $range, $ranges)) {
$csize = filesize($tmpname);
$tmpExists = is_file($tmp);
$uploadMaxSize = $volume ? $volume->getUploadMaxSize() : 0;
if ($uploadMaxSize > 0 && $size > $uploadMaxSize) {
return array(self::ERROR_UPLOAD_FILE_SIZE, false);
if ($fp = fopen($tmp, 'wb')) {
$ok = ftruncate($fp, $size);
return array(self::ERROR_UPLOAD_TEMP, false);
// wait until makeing temp file (for anothor session)
$cnt = 1200; // Time limit 120 sec
while (!is_file($base) && --$cnt) {
usleep(100000); // wait 100ms
return array(self::ERROR_UPLOAD_TEMP, false);
if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) {
return array(self::ERROR_UPLOAD_TEMP, false);
$src = fopen($tmpname, 'rb');
$writelen = stream_copy_to_stream($src, $fp, $len);
// to check connection is aborted
elFinder::checkAborted();
} catch (elFinderAbortException $e) {
is_file($tmp) && unlink($tmp);
is_file($base) && unlink($base);
return array(self::ERROR_UPLOAD_TEMP, false);
file_put_contents($base, "\0", FILE_APPEND | LOCK_EX);
if (filesize($base) >= $clast + 1) {
return array($tmp, $fname);
if (move_uploaded_file($tmpname, $part)) {
if ($clast < count(glob($base . '*'))) {
for ($i = 0; $i <= $clast; $i++) {
$name = $base . '.' . $i . '_' . $clast;
if (is_readable($name)) {
if ($resfile = tempnam($tempDir, 'ELF')) {
$target = fopen($resfile, 'wb');
fwrite($target, fread($fp, 8192));
return array($resfile, $fname);
* @throws elFinderAbortException
* @author Dmitry (dio) Levashov
protected function upload($args)
$ngReg = '/[\/\\?*:|"<>]/';
$target = $args['target'];
$volume = $this->volume($target);
$files = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
$header = empty($args['html']) ? array() : array('header' => 'Content-Type: text/html; charset=utf-8');
$result = array_merge(array('added' => array()), $header);
$paths = $args['upload_path'] ? $args['upload_path'] : array();
$chunk = $args['chunk'] ? $args['chunk'] : '';
$cid = $args['cid'] ? (int)$args['cid'] : '';
$mtimes = $args['mtime'] ? $args['mtime'] : array();
return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#' . $target)), $header);
if (strpos($chunk, '/') !== false || strpos($chunk, '\\') !== false) {
return array('error' => $this->error(self::ERROR_UPLOAD));
if ($args['overwrite'] !== '') {
$volume->setUploadOverwrite($args['overwrite']);
$renames = $hashes = array();
if ($args['renames'] && is_array($args['renames'])) {
$renames = array_flip($args['renames']);
if (is_string($args['suffix']) && !preg_match($ngReg, $args['suffix'])) {
$suffix = $args['suffix'];
if ($args['hashes'] && is_array($args['hashes'])) {
$hashes = array_flip($args['hashes']);
$this->itemLock($target);
// file extentions table by MIME
$extTable = array_flip(array_unique($volume->getMimeTable()));
if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) {
foreach ($args['upload'] as $i => $url) {
// check chunked file upload commit
if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') {
$this->checkChunkedFile(null, $chunk, $cid, $tempDir);
if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], self::ERROR_UPLOAD_TEMP);
$tmpfname = $tempDir . '/' . $chunk;
$files['tmp_name'][$i] = $tmpfname;
$files['name'][$i] = $url;
$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
$tmpfname = $tempDir . DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url . microtime(true));
$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
if (substr($url, 0, 5) === 'data:') {
list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable, $args);
$fp = fopen($tmpfname, 'wb');
if ($data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp)) {
// to check connection is aborted
elFinder::checkAborted();
} catch(elFinderAbortException $e) {
if (strpos($url, '%') !== false) {
$url = rawurldecode($url);
if (is_callable('mb_convert_encoding') && is_callable('mb_detect_encoding')) {
$url = mb_convert_encoding($url, 'UTF-8', mb_detect_encoding($url));
$url = iconv('UTF-8', 'UTF-8//IGNORE', $url);
$_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', $url);
// Check `Content-Disposition` response header
if (($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) {
if (preg_match('/filename\*=(?:([a-zA-Z0-9_-]+?)\'\')"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) {
$_name = rawurldecode($m[2]);
if ($m[1] && strtoupper($m[1]) !== 'UTF-8' && function_exists('mb_convert_encoding')) {
$_name = mb_convert_encoding($_name, 'UTF-8', $m[1]);
} else if (preg_match('/filename="?([ a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) {
$_name = rawurldecode($m[1]);
if (isset($args['name'][$i])) {
$_name = $args['name'][$i];
if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) {
if ((is_resource($data) && fclose($data)) || file_put_contents($tmpfname, $data)) {
$GLOBALS['elFinderTempFiles'][$tmpfname] = true;
$_name = preg_replace($ngReg, '_', $_name);
list($_a, $_b) = array_pad(explode('.', $_name, 2), 2, '');
rename($tmpfname, $tmpfname . $_ext);
$tmpfname = $tmpfname . $_ext;
$_b = $this->detectFileExtension($volume, $tmpfname, $_name);
if (isset($names[$_name])) {
$_name = $_a . '_' . $names[$_name]++ . $_b;
$files['tmp_name'][$i] = $tmpfname;
$files['name'][$i] = $_name;
$volume->setUploadOverwrite(false);
return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES)), $header);
foreach ($files['name'] as $i => $name) {
if (($error = $files['error'][$i]) > 0) {
$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER, $error);
$this->uploadDebug = 'Upload error code: ' . $error;
$tmpname = $files['tmp_name'][$i];
$thash = ($paths && isset($paths[$i])) ? $paths[$i] : $target;
$mtime = isset($mtimes[$i]) ? $mtimes[$i] : 0;
if ($tempDir = $this->getTempDir($volume->getTempPath())) {
list($tmpname, $name) = $this->checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume);
preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m);
$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], $tmpname);
$result['_chunkfailure'] = true;
$this->uploadDebug = 'Upload error: ' . $tmpname;
$result['_chunkmerged'] = basename($tmpname);
$result['_name'] = $name;
$result['_mtime'] = $mtime;
$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $chunk, self::ERROR_UPLOAD_TEMP);
$this->uploadDebug = 'Upload error: unable open tmp file';
// for form clipboard with Google Chrome or Opera
// Set name if name eq 'image.png' and $args has 'name' array, e.g. clipboard data
if (strtolower(substr($name, 0, 5)) === 'image' && is_array($args['name']) && isset($args['name'][$i])) {
$type = $files['type'][$i];
$name = $args['name'][$i];
$ext = isset($extTable[$type]) ? '.' . $extTable[$type] : '';
$name = preg_replace('/\.[^.]*$/', '', $name);
// do hook function 'upload.presave'
$this->trigger('upload.presave', array(&$thash, &$name, $tmpname, $this, $volume), $errors);
} catch (elFinderTriggerException $e) {
if (!is_uploaded_file($tmpname) && unlink($tmpname) && $tmpfname) {
unset($GLOBALS['elFinderTempFiles'][$tmpfname]);
if ($mtime && is_file($tmpname)) {
// for keep timestamp option in the LocalFileSystem volume
if (!is_file($tmpname) || ($fp = fopen($tmpname, 'rb')) === false) {
$errors = array_merge($errors, array(self::ERROR_UPLOAD_FILE, $name, ($fp === false? self::ERROR_UPLOAD_TEMP : self::ERROR_UPLOAD_TRANSFER)));