diff --git a/ChangeLog b/ChangeLog
index 716051139..d6bf615a1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,7 +5,7 @@ phpMyAdmin - ChangeLog
$Id$
$HeadURL$
-- bug #1615530 [data] added more specific error message if field upload fails
+- bug #1615530 [upload] added more specific error message if field upload fails
- [gui] avoid displaying a wide selector in server selection
+ [core] added PMA_fatalError() and made use of it
. [i18n] use generic $strOptions
@@ -17,6 +17,8 @@ $HeadURL$
+ [gui] dropped css/ folder and moved into root of PMA
+ [l10n] new: sinhala
+ [export] YAML export (see yaml.org), thanks to Bryce Thornton
++ [upload] moved file upload functionality into own class
++ [upload] make use of $cfg['TempDir'] for file uploads
2.10.1.0 (not released yet)
=====================
diff --git a/libraries/PMA_File.class.php b/libraries/PMA_File.class.php
new file mode 100644
index 000000000..6c212da80
--- /dev/null
+++ b/libraries/PMA_File.class.php
@@ -0,0 +1,507 @@
+__construct($name);
+ }
+
+ /**
+ * constructor
+ *
+ * @access public
+ * @uses PMA_File::setName()
+ * @param string $name file name
+ */
+ function __construct($name = false)
+ {
+ if ($name) {
+ $this->setName($name);
+ }
+ }
+
+ /**
+ * destructor
+ *
+ * @see PMA_File::cleanUp()
+ * @uses PMA_File::cleanUp()
+ */
+ function __destruct()
+ {
+ $this->cleanUp();
+ }
+
+ /**
+ * deletes temp file from upload
+ */
+ function cleanUp()
+ {
+ if ($this->isTemp()) {
+ return $this->delete();
+ }
+
+ return true;
+ }
+
+ /**
+ * deletes the file
+ *
+ * @uses PMA_File::getName()
+ * @uses unlink()
+ * @return boolean success
+ */
+ function delete()
+ {
+ return unlink($this->getName());
+ }
+
+ /**
+ * checks or sets the temp flag for this file
+ * file objects with temp flags are deleted with object destruction
+ *
+ * @uses PMA_File::$_is_temp to set and read it
+ * @param boolean sets the temp flag
+ * @return boolean PMA_File::$_is_temp
+ */
+ function isTemp($is_temp = null)
+ {
+ if (null !== $is_temp) {
+ $this->_is_temp = (bool) $is_temp;
+ }
+
+ return $this->_is_temp;
+ }
+
+ /**
+ * accessor
+ *
+ * @uses PMA_File::$_name
+ * @param string $name file name
+ * @access public
+ */
+ function setName($name)
+ {
+ $this->_name = trim($name);
+ }
+
+ /**
+ * @access public
+ * @uses PMA_File::getName()
+ * @uses PMA_File::isUploaded()
+ * @uses PMA_File::checkUploadedFile()
+ * @uses PMA_File::isReadable()
+ * @uses PMA_File::$_content
+ * @uses function_exists()
+ * @uses file_get_contents()
+ * @uses filesize()
+ * @uses fread()
+ * @uses fopen()
+ * @uses bin2hex()
+ * @return string binary file content
+ */
+ function getContent()
+ {
+ if (null !== $this->_content) {
+ return $this->_content;
+ }
+
+ if ($this->isUploaded() && ! $this->checkUploadedFile()) {
+ return false;
+ }
+
+ if (! $this->isReadable()) {
+ return false;
+ }
+
+ // check if file is not empty
+ if (function_exists('file_get_contents')) {
+ $this->_content = file_get_contents($this->getName());
+ } elseif ($size = filesize($this->getName())) {
+ $this->_content = fread(fopen($this->getName(), 'rb'), $size);
+ }
+
+ if (! empty($this->_content)) {
+ $this->_content = '0x' . bin2hex($this->_content);
+ }
+
+ return $this->_content;
+ }
+
+ /**
+ * @uses PMA_File::getName()
+ * @uses is_uploaded_file()
+ */
+ function isUploaded()
+ {
+ return is_uploaded_file($this->getName());
+ }
+
+ /**
+ * accessor
+ *
+ * @uses PMA_File::$name as return value
+ * @return string PMA_File::$_name
+ */
+ function getName()
+ {
+ return $this->_name;
+ }
+
+ /**
+ * @todo replace error message with localized string
+ * @uses PMA_File::isUploaded()
+ * @uses PMA_File::setName()
+ * @uses PMA_File::$_error_message
+ * @param string name of file uploaded
+ * @return boolean success
+ */
+ function setUploadedFile($name)
+ {
+ $this->setName($name);
+
+ if (! $this->isUploaded()) {
+ $this->setName(null);
+ $this->_error_message = 'not an uploaded file';
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @uses PMA_File::fetchUploadedFromTblChangeRequestMultiple()
+ * @uses PMA_File::setUploadedFile()
+ * @uses PMA_File::$_error_message
+ * @uses $GLOBALS['strUploadErrorIniSize']
+ * @uses $GLOBALS['strUploadErrorFormSize']
+ * @uses $GLOBALS['strUploadErrorPartial']
+ * @uses $GLOBALS['strUploadErrorNoTempDir']
+ * @uses $GLOBALS['strUploadErrorCantWrite']
+ * @uses $GLOBALS['strUploadErrorExtension']
+ * @uses $GLOBALS['strUploadErrorUnknown']
+ * @uses $_FILES
+ * @param string $key a numeric key used to identify the different rows
+ * @param string $primary_key
+ * @return boolean success
+ */
+ function setUploadedFromTblChangeRequest($key, $primary = null)
+ {
+ if (! isset($_FILES['fields_upload_' . $key])) {
+ return false;
+ }
+
+ $file = $_FILES['fields_upload_' . $key];
+ if (null !== $primary) {
+ $file = PMA_File::fetchUploadedFromTblChangeRequestMultiple($file, $primary);
+ }
+
+ // check for file upload errors
+ switch ($file['error']) {
+ // cybot_tm: we do not use the PHP constants here cause not all constants
+ // are defined in all versions of PHP - but the correct constants names
+ // are given as comment
+ case 0: //UPLOAD_ERR_OK:
+ return $this->setUploadedFile($file['tmp_name']);
+ break;
+ case 4: //UPLOAD_ERR_NO_FILE:
+ break;
+ case 1: //UPLOAD_ERR_INI_SIZE:
+ $this->_error_message = $GLOBALS['strUploadErrorIniSize'];
+ break;
+ case 2: //UPLOAD_ERR_FORM_SIZE:
+ $this->_error_message = $GLOBALS['strUploadErrorFormSize'];
+ break;
+ case 3: //UPLOAD_ERR_PARTIAL:
+ $this->_error_message = $GLOBALS['strUploadErrorPartial'];
+ break;
+ case 6: //UPLOAD_ERR_NO_TMP_DIR:
+ $this->_error_message = $GLOBALS['strUploadErrorNoTempDir'];
+ break;
+ case 7: //UPLOAD_ERR_CANT_WRITE:
+ $this->_error_message = $GLOBALS['strUploadErrorCantWrite'];
+ break;
+ case 8: //UPLOAD_ERR_EXTENSION:
+ $this->_error_message = $GLOBALS['strUploadErrorExtension'];
+ break;
+ default:
+ $this->_error_message = $GLOBALS['strUploadErrorUnknown'];
+ } // end switch
+
+ return false;
+ }
+
+ /**
+ * strips some dimension from the multi-dimensional array from $_FILES
+ *
+ *
+ * $file['name']['multi_edit'][$primary] = [value]
+ * $file['type']['multi_edit'][$primary] = [value]
+ * $file['size']['multi_edit'][$primary] = [value]
+ * $file['tmp_name']['multi_edit'][$primary] = [value]
+ * $file['error']['multi_edit'][$primary] = [value]
+ *
+ * // becomes:
+ *
+ * $file['name'] = [value]
+ * $file['type'] = [value]
+ * $file['size'] = [value]
+ * $file['tmp_name'] = [value]
+ * $file['error'] = [value]
+ *
+ *
+ * @todo re-check if requirements changes to PHP >= 4.2.0
+ * @param array $file the array
+ * @param string $primary
+ * @return array
+ */
+ function fetchUploadedFromTblChangeRequestMultiple($file, $primary)
+ {
+ $new_file = array(
+ 'name' => $file['name']['multi_edit'][$primary],
+ 'type' => $file['type']['multi_edit'][$primary],
+ 'size' => $file['size']['multi_edit'][$primary],
+ 'tmp_name' => $file['tmp_name']['multi_edit'][$primary],
+ //'error' => $file['error']['multi_edit'][$primary],
+ );
+
+ // ['error'] exists since PHP 4.2.0
+ if (isset($file['error'])) {
+ $new_file['error'] = $file['error']['multi_edit'][$primary];
+ }
+
+ return $new_file;
+ }
+
+ /**
+ * sets the name if the file to the one selected in the tbl_change form
+ *
+ * @uses $_REQUEST
+ * @uses PMA_File::setLocalSelectedFile()
+ * @uses is_string()
+ * @param string $key a numeric key used to identify the different rows
+ * @param string $primary_key
+ * @return boolean success
+ */
+ function setSelectedFromTblChangeRequest($key, $primary = null)
+ {
+ if (null !== $primary) {
+ if (! empty($_REQUEST['fields_uploadlocal_' . $key]['multi_edit'][$primary])
+ && is_string($_REQUEST['fields_uploadlocal_' . $key]['multi_edit'][$primary])) {
+ // ... whether with multiple rows ...
+ return $this->setLocalSelectedFile($_REQUEST['fields_uploadlocal_' . $key]['multi_edit'][$primary]);
+ } else {
+ return false;
+ }
+ } elseif (! empty($_REQUEST['fields_uploadlocal_' . $key])
+ && is_string($_REQUEST['fields_uploadlocal_' . $key])) {
+ return $this->setLocalSelectedFile($_REQUEST['fields_uploadlocal_' . $key]);
+ }
+
+ return false;
+ }
+
+ /**
+ * @uses PMA_File->$_error_message as return value
+ * @return string error message
+ */
+ function getError()
+ {
+ return $this->_error_message;
+ }
+
+ /**
+ * @uses PMA_File->$_error_message to check it
+ * @return boolean whether an error occured or not
+ */
+ function isError()
+ {
+ return ! empty($this->_error_message);
+ }
+
+ /**
+ * chacks the supergloabls provided if the tbl_change form is submitted
+ * and uses the submitted/selected file
+ *
+ * @uses PMA_File::setUploadedFromTblChangeRequest()
+ * @uses PMA_File::setSelectedFromTblChangeRequest()
+ * @param string $key a numeric key used to identify the different rows
+ * @param string $primary_key
+ * @return boolean success
+ */
+ function checkTblChangeForm($key, $primary_key)
+ {
+ if ($this->setUploadedFromTblChangeRequest($key, $primary_key)) {
+ // well done ...
+ return true;
+ } elseif ($this->setUploadedFromTblChangeRequest($key)) {
+ // well done ...
+ return true;
+ } elseif ($this->setSelectedFromTblChangeRequest($key, $primary_key)) {
+ // well done ...
+ return true;
+ } elseif ($this->setSelectedFromTblChangeRequest($key)) {
+ // well done ...
+ return true;
+ }
+ // all failed, whether just no file uploaded/selected or an error
+
+ return false;
+ }
+
+ /**
+ *
+ * @uses PMA_File::setName()
+ * @uses preg_replace()
+ * @uses PMA_userDir()
+ * @uses $GLOBALS['cfg']['UploadDir']
+ * @param string $name
+ */
+ function setLocalSelectedFile($name)
+ {
+ $this->setName(PMA_userDir($GLOBALS['cfg']['UploadDir']) . preg_replace('@\.\.*@', '.', $name));
+ }
+
+ /**
+ * @uses PMA_File::getName()
+ * @uses is_readable()
+ * @uses ob_start()
+ * @uses ob_end_clean()
+ * @return boolean whether the file is readable or not
+ */
+ function isReadable()
+ {
+ // surprees warnings form beeing displayed, but not from beeing logged
+ // any file access outside of open_basedir will issue a warning
+ ob_start();
+ $is_readable = is_readable($this->getName());
+ ob_end_clean();
+ return $is_readable;
+ }
+
+ /**
+ * If we are on a server with open_basedir, we must move the file
+ * before opening it. The FAQ 1.11 explains how to create the "./tmp"
+ * directory - if needed
+ *
+ * @todo replace error message with localized string
+ * @todo move check of $cfg['TempDir'] into PMA_Config?
+ * @uses $cfg['TempDir']
+ * @uses $GLOBALS['strFieldInsertFromFileTempDirNotExists']
+ * @uses PMA_File::isReadable()
+ * @uses PMA_File::getName()
+ * @uses PMA_File::setName()
+ * @uses PMA_File::isTemp()
+ * @uses PMA_File::$_error_message
+ * @uses is_dir()
+ * @uses mkdir()
+ * @uses chmod()
+ * @uses is_writable()
+ * @uses basename()
+ * @uses move_uploaded_file()
+ * @uses ob_start()
+ * @uses ob_end_clean()
+ * @return boolean whether uploaded fiel is fine or not
+ */
+ function checkUploadedFile()
+ {
+ if ($this->isReadable()) {
+ return true;
+ }
+
+ /**
+ * it is not important if open_basedir is set - we just cannot read the file
+ * so we try to move it
+ if ('' != ini_get('open_basedir')) {
+ */
+
+ // check tmp dir config
+ if (empty($GLOBALS['cfg']['TempDir'])) {
+ $GLOBALS['cfg']['TempDir'] = 'tmp/';
+ }
+
+ // surprees warnings form beeing displayed, but not from beeing logged
+ ob_start();
+ // check tmp dir
+ if (! is_dir($GLOBALS['cfg']['TempDir'])) {
+ // try to create the tmp directory
+ if (@mkdir($GLOBALS['cfg']['TempDir'], 0777)) {
+ chmod($GLOBALS['cfg']['TempDir'], 0777);
+ } else {
+ // create tmp dir failed
+ $this->_error_message = $GLOBALS['strFieldInsertFromFileTempDirNotExists'];
+ ob_end_clean();
+ return false;
+ }
+ }
+ ob_end_clean();
+
+ if (! is_writable($GLOBALS['cfg']['TempDir'])) {
+ // cannot create directory or access, point user to FAQ 1.11
+ $this->_error_message = $GLOBALS['strFieldInsertFromFileTempDirNotExists'];
+ return false;
+ }
+
+ $new_file_to_upload = $GLOBALS['cfg']['TempDir'] . '/' . basename($this->getName());
+
+ // surprees warnings form beeing displayed, but not from beeing logged
+ // any file access outside of open_basedir will issue a warning
+ ob_start();
+ $move_uploaded_file_result = move_uploaded_file($this->getName(), $new_file_to_upload);
+ ob_end_clean();
+ if (! $move_uploaded_file_result) {
+ $this->_error_message = 'error while moving uploaded file';
+ return false;
+ }
+
+ $this->setName($new_file_to_upload);
+ $this->isTemp(true);
+
+ if (! $this->isReadable()) {
+ $this->_error_message = 'cannot read (moved) upload file';
+ return false;
+ }
+
+ return true;
+ }
+}
+?>
\ No newline at end of file
diff --git a/libraries/tbl_replace_fields.inc.php b/libraries/tbl_replace_fields.inc.php
index d8b882c28..448a0ff42 100644
--- a/libraries/tbl_replace_fields.inc.php
+++ b/libraries/tbl_replace_fields.inc.php
@@ -20,35 +20,15 @@
* @version $Id$
* vim: expandtab sw=4 ts=4 sts=4:
*
- * @uses $GLOBALS['cfg']['UploadDir']
- * @uses $_FILES
* @uses $_REQUEST
* @uses defined()
* @uses define()
- * @uses is_uploaded_file()
- * @uses ini_get()
- * @uses is_dir()
- * @uses mkdir()
- * @uses chmod()
- * @uses is_writable()
- * @uses is_readable()
- * @uses move_uploaded_file()
- * @uses basename()
- * @uses preg_replace()
* @uses bin2hex()
- * @uses fread()
- * @uses fopen()
- * @uses filesize()
- * @uses unlink()
* @uses strlen()
* @uses md5()
* @uses implode()
- * @uses PMA_IS_WINDOWS
* @uses PMA_NO_VARIABLES_IMPORT
- * @uses PMA_checkParameters()
* @uses PMA_sqlAddslashes()
- * @uses PMA_userDir()
- * @todo there are also file uploads in the import dialog - possible we can merge this
*/
/**
@@ -61,129 +41,21 @@ if (! defined('PMA_NO_VARIABLES_IMPORT')) {
* Gets some core libraries
*/
require_once './libraries/common.lib.php';
+require_once './libraries/PMA_File.class.php';
-$valid_file_was_uploaded = false;
+$file_to_insert = new PMA_File();
+$file_to_insert->checkTblChangeForm($key, $primary_key);
-// Check if a multi-edit row was found
-$me_fields_upload =
- (isset($_FILES['fields_upload_' . $key]['tmp_name']['multi_edit'][$primary_key])
- ? $_FILES['fields_upload_' . $key]['tmp_name']['multi_edit'][$primary_key]
- : (isset($_FILES['fields_upload_' . $key]['tmp_name'])
- ? $_FILES['fields_upload_' . $key]['tmp_name']
- : 'none'));
+$val = $file_to_insert->getContent();
-$me_fields_uploadlocal =
- (isset($_REQUEST['fields_uploadlocal_' . $key]['multi_edit'])
- ? $_REQUEST['fields_uploadlocal_' . $key]['multi_edit'][$primary_key]
- : (isset($_REQUEST['fields_uploadlocal_' . $key])
- ? $_REQUEST['fields_uploadlocal_' . $key]
- : null));
+if ($file_to_insert->isError()) {
+ $message .= $file_to_insert->getError();
+}
+$file_to_insert->cleanUp();
-if ($me_fields_upload != 'none') {
- // garvin: This fields content is a blob-file upload.
-
- $file_to_insert = false;
- $unlink = false;
-
- if (is_uploaded_file($me_fields_upload)) {
- // whether we insert form uploaded file ...
-
- $file_to_insert = $me_fields_upload;
-
- // If we are on a server with open_basedir, we must move the file
- // before opening it. The FAQ 1.11 explains how to create the "./tmp"
- // directory - if needed
- if ('' != ini_get('open_basedir')) {
- $tmp_subdir = (PMA_IS_WINDOWS ? 'tmp' : 'tmp');
-
- if (! is_dir($tmp_subdir)) {
- // try to create the tmp directory if not exists
- if (@mkdir($tmp_subdir, 0777)) {
- chmod($tmp_subdir, 0777);
- }
- }
-
- if (! is_writable($tmp_subdir)) {
- // cannot create directory or access, point user to FAQ 1.11
- $message .= $GLOBALS['strFieldInsertFromFileTempDirNotExists'] . '
';
- // if we cannot move the file don't change blob fields
- $file_to_insert = false;
- } else {
- $new_file_to_upload = $tmp_subdir . basename($file_to_insert);
-
- move_uploaded_file($file_to_insert, $new_file_to_upload);
-
- $file_to_insert = $new_file_to_upload;
- $unlink = true;
- unset($new_file_to_upload);
- }
- unset($tmp_subdir);
- }
- } elseif (! empty($me_fields_uploadlocal)) {
- // ... or selected file from $cfg['UploadDir']
-
- $file_to_insert = PMA_userDir($GLOBALS['cfg']['UploadDir']) . preg_replace('@\.\.*@', '.', $me_fields_uploadlocal);
-
- if (! is_readable($file_to_insert)) {
- $file_to_insert = false;
- }
- }
- // garvin: else: Post-field contains no data. Blob-fields are preserved, see below. ($protected$)
-
- if ($file_to_insert) {
- $val = '';
- // check if file is not empty
- if (function_exists('file_get_contents')) {
- $val = file_get_contents($file_to_insert);
- } elseif ($file_to_insert_size = filesize($file_to_insert)) {
- $val = fread(fopen($file_to_insert, 'rb'), $file_to_insert_size);
- }
-
- if (! empty($val)) {
- $val = '0x' . bin2hex($val);
- $seen_binary = true;
- $valid_file_was_uploaded = true;
- }
-
- if ($unlink == true) {
- unlink($file_to_insert);
- }
- }
-
- unset($file_to_insert, $file_to_insert_size, $unlink);
-} elseif (isset($_FILES['fields_upload_' . $key]['error']['multi_edit'][$primary_key])) {
- // check for file upload errors
- switch ($_FILES['fields_upload_' . $key]['error']['multi_edit'][$primary_key]) {
- // cybot_tm: we do not use the PHP constants here cause not all constants
- // are defined in all versions of PHP - but the correct constants names
- // are given as comment
- case 0: //UPLOAD_ERR_OK:
- case 4: //UPLOAD_ERR_NO_FILE:
- break;
- case 1: //UPLOAD_ERR_INI_SIZE:
- $message .= $GLOBALS['strUploadErrorIniSize'] . '
';
- break;
- case 2: //UPLOAD_ERR_FORM_SIZE:
- $message .= $GLOBALS['strUploadErrorFormSize'] . '
';
- break;
- case 3: //UPLOAD_ERR_PARTIAL:
- $message .= $GLOBALS['strUploadErrorPartial'] . '
';
- break;
- case 6: //UPLOAD_ERR_NO_TMP_DIR:
- $message .= $GLOBALS['strUploadErrorNoTempDir'] . '
';
- break;
- case 7: //UPLOAD_ERR_CANT_WRITE:
- $message .= $GLOBALS['strUploadErrorCantWrite'] . '
';
- break;
- case 8: //UPLOAD_ERR_EXTENSION:
- $message .= $GLOBALS['strUploadErrorExtension'] . '
';
- break;
- default:
- $message .= $GLOBALS['strUploadErrorUnknown'] . '
';
- } // end switch
-} // end else
-
-if (false === $valid_file_was_uploaded) {
+if (false !== $val) {
+ $seen_binary = true;
+} else {
// f i e l d v a l u e i n t h e f o r m
@@ -250,5 +122,5 @@ if (false === $valid_file_was_uploaded) {
$val = "''";
}
} // end else (field value in the form)
-unset($valid_file_was_uploaded, $me_fields_upload, $me_fields_uploadlocal, $type, $f);
+unset($type, $f);
?>