2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 * file upload functions
8 if (! defined('PHPMYADMIN')) {
15 * @todo when uploading a file into a blob field, should we also consider using
16 * chunks like in import? UPDATE `table` SET `field` = `field` + [chunk]
23 * @var string the temporary file name
29 * @var string the content
35 * @var string the error message
38 var $_error_message = '';
41 * @var bool whether the file is temporary or not
44 var $_is_temp = false;
47 * @var string type of compression
50 var $_compression = null;
58 * @var integer size of chunk to read with every step
60 var $_chunk_size = 32768;
63 * @var resource file handle
68 * @var boolean whether to decompress content before returning
70 var $_decompress = false;
73 * @var string charset of file
80 * @param boolean|string $name file name or false
84 public function __construct($name = false)
87 $this->setName($name);
94 * @see PMA_File::cleanUp()
97 public function __destruct()
103 * deletes file if it is temporary, usually from a moved upload file
106 * @return boolean success
108 public function cleanUp()
110 if ($this->isTemp()) {
111 return $this->delete();
121 * @return boolean success
123 public function delete()
125 return unlink($this->getName());
129 * checks or sets the temp flag for this file
130 * file objects with temp flags are deleted with object destruction
132 * @param boolean $is_temp sets the temp flag
134 * @return boolean PMA_File::$_is_temp
137 public function isTemp($is_temp = null)
139 if (null !== $is_temp) {
140 $this->_is_temp
= (bool) $is_temp;
143 return $this->_is_temp
;
149 * @param string $name file name
154 public function setName($name)
156 $this->_name
= trim($name);
162 * @return string|false the binary file content as a string,
163 * or false if no content
167 public function getContent()
169 if (null === $this->_content
) {
170 if ($this->isUploaded() && ! $this->checkUploadedFile()) {
174 if (! $this->isReadable()) {
178 if (function_exists('file_get_contents')) {
179 $this->_content
= file_get_contents($this->getName());
180 } elseif ($size = filesize($this->getName())) {
181 $this->_content
= fread(fopen($this->getName(), 'rb'), $size);
185 return '0x' . bin2hex($this->_content
);
189 * Whether file is uploaded.
195 public function isUploaded()
197 return is_uploaded_file($this->getName());
204 * @return string PMA_File::$_name
206 public function getName()
212 * Initializes object from uploaded file.
214 * @param string $name name of file uploaded
216 * @return boolean success
219 public function setUploadedFile($name)
221 $this->setName($name);
223 if (! $this->isUploaded()) {
224 $this->setName(null);
225 $this->_error_message
= __('File was not an uploaded file.');
233 * Loads uploaded file from table change request.
235 * @param string $key the md5 hash of the column name
236 * @param string $rownumber number of row to process
238 * @return boolean success
241 public function setUploadedFromTblChangeRequest($key, $rownumber)
243 if (! isset($_FILES['fields_upload'])
244 ||
empty($_FILES['fields_upload']['name']['multi_edit'][$rownumber][$key])
248 $file = PMA_File
::fetchUploadedFromTblChangeRequestMultiple(
249 $_FILES['fields_upload'],
254 // check for file upload errors
255 switch ($file['error']) {
256 // we do not use the PHP constants here cause not all constants
257 // are defined in all versions of PHP - but the correct constants names
258 // are given as comment
259 case 0: //UPLOAD_ERR_OK:
260 return $this->setUploadedFile($file['tmp_name']);
261 case 4: //UPLOAD_ERR_NO_FILE:
263 case 1: //UPLOAD_ERR_INI_SIZE:
264 $this->_error_message
= __('The uploaded file exceeds the upload_max_filesize directive in php.ini.');
266 case 2: //UPLOAD_ERR_FORM_SIZE:
267 $this->_error_message
= __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.');
269 case 3: //UPLOAD_ERR_PARTIAL:
270 $this->_error_message
= __('The uploaded file was only partially uploaded.');
272 case 6: //UPLOAD_ERR_NO_TMP_DIR:
273 $this->_error_message
= __('Missing a temporary folder.');
275 case 7: //UPLOAD_ERR_CANT_WRITE:
276 $this->_error_message
= __('Failed to write file to disk.');
278 case 8: //UPLOAD_ERR_EXTENSION:
279 $this->_error_message
= __('File upload stopped by extension.');
282 $this->_error_message
= __('Unknown error in file upload.');
289 * strips some dimension from the multi-dimensional array from $_FILES
292 * $file['name']['multi_edit'][$rownumber][$key] = [value]
293 * $file['type']['multi_edit'][$rownumber][$key] = [value]
294 * $file['size']['multi_edit'][$rownumber][$key] = [value]
295 * $file['tmp_name']['multi_edit'][$rownumber][$key] = [value]
296 * $file['error']['multi_edit'][$rownumber][$key] = [value]
300 * $file['name'] = [value]
301 * $file['type'] = [value]
302 * $file['size'] = [value]
303 * $file['tmp_name'] = [value]
304 * $file['error'] = [value]
307 * @param array $file the array
308 * @param string $rownumber number of row to process
309 * @param string $key key to process
315 public function fetchUploadedFromTblChangeRequestMultiple(
316 $file, $rownumber, $key
319 'name' => $file['name']['multi_edit'][$rownumber][$key],
320 'type' => $file['type']['multi_edit'][$rownumber][$key],
321 'size' => $file['size']['multi_edit'][$rownumber][$key],
322 'tmp_name' => $file['tmp_name']['multi_edit'][$rownumber][$key],
323 'error' => $file['error']['multi_edit'][$rownumber][$key],
330 * sets the name if the file to the one selected in the tbl_change form
332 * @param string $key the md5 hash of the column name
333 * @param string $rownumber number of row to process
335 * @return boolean success
338 public function setSelectedFromTblChangeRequest($key, $rownumber = null)
340 if (! empty($_REQUEST['fields_uploadlocal']['multi_edit'][$rownumber][$key])
341 && is_string($_REQUEST['fields_uploadlocal']['multi_edit'][$rownumber][$key])
343 // ... whether with multiple rows ...
344 return $this->setLocalSelectedFile(
345 $_REQUEST['fields_uploadlocal']['multi_edit'][$rownumber][$key]
353 * Returns possible error message.
356 * @return string error message
358 public function getError()
360 return $this->_error_message
;
364 * Checks whether there was any error.
367 * @return boolean whether an error occurred or not
369 public function isError()
371 return ! empty($this->_error_message
);
375 * checks the superglobals provided if the tbl_change form is submitted
376 * and uses the submitted/selected file
378 * @param string $key the md5 hash of the column name
379 * @param string $rownumber number of row to process
381 * @return boolean success
384 public function checkTblChangeForm($key, $rownumber)
386 if ($this->setUploadedFromTblChangeRequest($key, $rownumber)) {
388 $this->_error_message
= '';
390 } elseif ($this->setSelectedFromTblChangeRequest($key, $rownumber)) {
392 $this->_error_message
= '';
395 // all failed, whether just no file uploaded/selected or an error
401 * Sets named file to be read from UploadDir.
403 * @param string $name file name
405 * @return boolean success
408 public function setLocalSelectedFile($name)
410 if (empty($GLOBALS['cfg']['UploadDir'])) {
415 PMA_Util
::userDir($GLOBALS['cfg']['UploadDir']) . PMA_securePath($name)
417 if (! $this->isReadable()) {
418 $this->_error_message
= __('File could not be read!');
419 $this->setName(null);
427 * Checks whether file can be read.
430 * @return boolean whether the file is readable or not
432 public function isReadable()
434 // suppress warnings from being displayed, but not from being logged
435 // any file access outside of open_basedir will issue a warning
437 $is_readable = is_readable($this->getName());
443 * If we are on a server with open_basedir, we must move the file
444 * before opening it. The FAQ 1.11 explains how to create the "./tmp"
445 * directory - if needed
447 * @todo move check of $cfg['TempDir'] into PMA_Config?
449 * @return boolean whether uploaded file is fine or not
451 public function checkUploadedFile()
453 if ($this->isReadable()) {
457 if (empty($GLOBALS['cfg']['TempDir'])
458 ||
! is_writable($GLOBALS['cfg']['TempDir'])
460 // cannot create directory or access, point user to FAQ 1.11
461 $this->_error_message
= __('Error moving the uploaded file, see [doc@faq1-11]FAQ 1.11[/doc].');
465 $new_file_to_upload = tempnam(
466 realpath($GLOBALS['cfg']['TempDir']),
467 basename($this->getName())
470 // suppress warnings from being displayed, but not from being logged
471 // any file access outside of open_basedir will issue a warning
473 $move_uploaded_file_result = move_uploaded_file(
478 if (! $move_uploaded_file_result) {
479 $this->_error_message
= __('Error while moving uploaded file.');
483 $this->setName($new_file_to_upload);
486 if (! $this->isReadable()) {
487 $this->_error_message
= __('Cannot read (moved) upload file.');
495 * Detects what compression the file uses
497 * @todo move file read part into readChunk() or getChunk()
498 * @todo add support for compression plugins
500 * @return string|false false on error, otherwise string MIME type of
501 * compression, none for none
503 protected function detectCompression()
505 // suppress warnings from being displayed, but not from being logged
506 // f.e. any file access outside of open_basedir will issue a warning
508 $file = fopen($this->getName(), 'rb');
512 $this->_error_message
= __('File could not be read!');
518 * get registered plugins for file compression
520 foreach (PMA_getPlugins($type = 'compression') as $plugin) {
521 if ($plugin['classname']::canHandle($this->getName())) {
522 $this->setCompressionPlugin($plugin);
528 $this->_compression
= PMA_Util
::getCompressionMimeType($file);
529 return $this->_compression
;
533 * Sets whether the content should be decompressed before returned
535 * @param boolean $decompress whether to decompress
539 public function setDecompressContent($decompress)
541 $this->_decompress
= (bool) $decompress;
545 * Returns the file handle
547 * @return resource file handle
549 public function getHandle()
551 if (null === $this->_handle
) {
554 return $this->_handle
;
558 * Sets the file handle
560 * @param object $handle file handle
564 public function setHandle($handle)
566 $this->_handle
= $handle;
571 * Sets error message for unsupported compression.
575 public function errorUnsupported()
577 $this->_error_message
= sprintf(
578 __('You attempted to load file with unsupported compression (%s). Either support for it is not implemented or disabled by your configuration.'),
579 $this->getCompression()
584 * Attempts to open the file.
588 public function open()
590 if (! $this->_decompress
) {
591 $this->_handle
= @fopen
($this->getName(), 'r');
594 switch ($this->getCompression()) {
597 case 'application/bzip2':
598 if ($GLOBALS['cfg']['BZipDump'] && @function_exists
('bzopen')) {
599 $this->_handle
= @bzopen
($this->getName(), 'r');
601 $this->errorUnsupported();
605 case 'application/gzip':
606 if ($GLOBALS['cfg']['GZipDump'] && @function_exists
('gzopen')) {
607 $this->_handle
= @gzopen
($this->getName(), 'r');
609 $this->errorUnsupported();
613 case 'application/zip':
614 if ($GLOBALS['cfg']['ZipDump'] && @function_exists
('zip_open')) {
615 include_once './libraries/zip_extension.lib.php';
616 $result = PMA_getZipContents($this->getName());
617 if (! empty($result['error'])) {
618 $this->_error_message
= PMA_Message
::rawError($result['error']);
623 $this->errorUnsupported();
628 $this->_handle
= @fopen
($this->getName(), 'r');
631 $this->errorUnsupported();
639 * Returns the character set of the file
641 * @return string character set of the file
643 public function getCharset()
645 return $this->_charset
;
649 * Sets the character set of the file
651 * @param string $charset character set of the file
655 public function setCharset($charset)
657 $this->_charset
= $charset;
661 * Returns compression used by file.
663 * @return string MIME type of compression, none for none
666 public function getCompression()
668 if (null === $this->_compression
) {
669 return $this->detectCompression();
672 return $this->_compression
;
678 * @return integer the offset
680 public function getOffset()
682 return $this->_offset
;
686 * Returns the chunk size
688 * @return integer the chunk size
690 public function getChunkSize()
692 return $this->_chunk_size
;
696 * Sets the chunk size
698 * @param integer $chunk_size the chunk size
702 public function setChunkSize($chunk_size)
704 $this->_chunk_size
= (int) $chunk_size;
708 * Returns the length of the content in the file
710 * @return integer the length of the file content
712 public function getContentLength()
714 return /*overload*/mb_strlen($this->_content
);
718 * Returns whether the end of the file has been reached
720 * @return boolean whether the end of the file has been reached
722 public function eof()
724 if ($this->getHandle()) {
725 return feof($this->getHandle());
727 return ($this->getOffset() >= $this->getContentLength());