diff --git a/core/includes/file.inc b/core/includes/file.inc index 6cfff2f..102a9d4 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -1063,172 +1063,198 @@ function file_unmanaged_delete_recursive($path, $callback = NULL) { * - source: Path to the file before it is moved. * - destination: Path to the file after it is moved (same as 'uri'). */ -function file_save_upload($source, $delta = 0, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) { +function file_save_upload($source, $validators = array(), $destination = FALSE, $delta = FALSE, $replace = FILE_EXISTS_RENAME) { global $user; static $upload_cache; // Return cached objects without processing since the file will have // already been processed and the paths in _FILES will be invalid. - if (isset($upload_cache[$source][$delta])) { - return $upload_cache[$source][$delta]; + if (isset($upload_cache[$source])) { + if ($delta === FALSE) { + return $upload_cache[$source]; + } + else { + return $upload_cache[$source][$delta]; + } } // Make sure there's an upload to process. - if (empty($_FILES['files']['name'][$source][$delta])) { + if (empty($_FILES['files']['name'][$source][0])) { return NULL; } - // Check for file upload errors and return FALSE if a lower level system - // error occurred. For a complete list of errors: - // See http://php.net/manual/features.file-upload.errors.php. - switch ($_FILES['files']['error'][$source][$delta]) { - case UPLOAD_ERR_INI_SIZE: - case UPLOAD_ERR_FORM_SIZE: - drupal_set_message(t('The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $_FILES['files']['name'][$source][$delta], '%maxsize' => format_size(file_upload_max_size()))), 'error'); - return FALSE; + $files = array(); + foreach ($_FILES['files']['name'][$source] as $delta => $name) { + // Check for file upload errors and return FALSE for this file if a lower + // level system error occurred. For a complete list of errors: + // See http://php.net/manual/features.file-upload.errors.php. + switch ($_FILES['files']['error'][$source][$delta]) { + case UPLOAD_ERR_INI_SIZE: + case UPLOAD_ERR_FORM_SIZE: + drupal_set_message(t('The file %file could not be saved because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $name, '%maxsize' => format_size(file_upload_max_size()))), 'error'); + $files[$delta] = FALSE; + continue; - case UPLOAD_ERR_PARTIAL: - case UPLOAD_ERR_NO_FILE: - drupal_set_message(t('The file %file could not be saved because the upload did not complete.', array('%file' => $_FILES['files']['name'][$source][$delta])), 'error'); - return FALSE; + case UPLOAD_ERR_PARTIAL: + case UPLOAD_ERR_NO_FILE: + drupal_set_message(t('The file %file could not be saved because the upload did not complete.', array('%file' => $name)), 'error'); + $files[$delta] = FALSE; + continue; - case UPLOAD_ERR_OK: - // Final check that this is a valid upload, if it isn't, use the - // default error handler. - if (is_uploaded_file($_FILES['files']['tmp_name'][$source][$delta])) { - break; - } + case UPLOAD_ERR_OK: + // Final check that this is a valid upload, if it isn't, use the + // default error handler. + if (is_uploaded_file($_FILES['files']['tmp_name'][$source][$delta])) { + break; + } + + // Unknown error + default: + drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $name)), 'error'); + $files[$delta] = FALSE; + continue; - // Unknown error - default: - drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $_FILES['files']['name'][$source][$delta])), 'error'); - return FALSE; - } - // Begin building file entity. - $values = array( - 'uid' => $user->uid, - 'status' => 0, - 'filename' => trim(drupal_basename($_FILES['files']['name'][$source][$delta]), '.'), - 'uri' => $_FILES['files']['tmp_name'][$source], - 'filesize' => $_FILES['files']['size'][$source], - ); - $values['filemime'] = file_get_mimetype($values['filename']); - $file = entity_create('file', $values); - - $extensions = ''; - if (isset($validators['file_validate_extensions'])) { - if (isset($validators['file_validate_extensions'][0])) { - // Build the list of non-munged extensions if the caller provided them. - $extensions = $validators['file_validate_extensions'][0]; + } + // Begin building file entity. + $values = array( + 'uid' => $user->uid, + 'status' => 0, + 'filename' => trim(drupal_basename($name, '.')), + 'uri' => $_FILES['files']['tmp_name'][$source][$delta], + 'filesize' => $_FILES['files']['size'][$source][$delta], + ); + $values['filemime'] = file_get_mimetype($values['filename']); + $file = entity_create('file', $values); + + $extensions = ''; + if (isset($validators['file_validate_extensions'])) { + if (isset($validators['file_validate_extensions'][0])) { + // Build the list of non-munged extensions if the caller provided them. + $extensions = $validators['file_validate_extensions'][0]; + } + else { + // If 'file_validate_extensions' is set and the list is empty then the + // caller wants to allow any extension. In this case we have to remove the + // validator or else it will reject all extensions. + unset($validators['file_validate_extensions']); + } } else { - // If 'file_validate_extensions' is set and the list is empty then the - // caller wants to allow any extension. In this case we have to remove the - // validator or else it will reject all extensions. - unset($validators['file_validate_extensions']); + // No validator was provided, so add one using the default list. + // Build a default non-munged safe list for file_munge_filename(). + $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'; + $validators['file_validate_extensions'] = array(); + $validators['file_validate_extensions'][0] = $extensions; } - } - else { - // No validator was provided, so add one using the default list. - // Build a default non-munged safe list for file_munge_filename(). - $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'; - $validators['file_validate_extensions'] = array(); - $validators['file_validate_extensions'][0] = $extensions; - } - - if (!empty($extensions)) { - // Munge the filename to protect against possible malicious extension hiding - // within an unknown file type (ie: filename.html.foo). - $file->filename = file_munge_filename($file->filename, $extensions); - } - - // Rename potentially executable files, to help prevent exploits (i.e. will - // rename filename.php.foo and filename.php to filename.php.foo.txt and - // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads' - // evaluates to TRUE. - if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) { - $file->filemime = 'text/plain'; - $file->uri .= '.txt'; - $file->filename .= '.txt'; - // The .txt extension may not be in the allowed list of extensions. We have - // to add it here or else the file upload will fail. + if (!empty($extensions)) { - $validators['file_validate_extensions'][0] .= ' txt'; - drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->filename))); + // Munge the filename to protect against possible malicious extension hiding + // within an unknown file type (ie: filename.html.foo). + $file->filename = file_munge_filename($file->filename, $extensions); } - } - // If the destination is not provided, use the temporary directory. - if (empty($destination)) { - $destination = 'temporary://'; - } + // Rename potentially executable files, to help prevent exploits (i.e. will + // rename filename.php.foo and filename.php to filename.php.foo.txt and + // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads' + // evaluates to TRUE. + if (!variable_get('allow_insecure_uploads', 0) && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->filename) && (substr($file->filename, -4) != '.txt')) { + $file->filemime = 'text/plain'; + $file->uri .= '.txt'; + $file->filename .= '.txt'; + // The .txt extension may not be in the allowed list of extensions. We have + // to add it here or else the file upload will fail. + if (!empty($extensions)) { + $validators['file_validate_extensions'][0] .= ' txt'; + drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->filename))); + } + } - // Assert that the destination contains a valid stream. - $destination_scheme = file_uri_scheme($destination); - if (!file_stream_wrapper_valid_scheme($destination_scheme)) { - drupal_set_message(t('The file could not be uploaded because the destination %destination is invalid.', array('%destination' => $destination)), 'error'); - return FALSE; - } + // If the destination is not provided, use the temporary directory. + if (empty($destination)) { + $destination = 'temporary://'; + } - $file->source = $source; - // A URI may already have a trailing slash or look like "public://". - if (substr($destination, -1) != '/') { - $destination .= '/'; - } - $file->destination = file_destination($destination . $file->filename, $replace); - // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and - // there's an existing file so we need to bail. - if ($file->destination === FALSE) { - drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $source, '%directory' => $destination)), 'error'); - return FALSE; - } + // Assert that the destination contains a valid stream. + $destination_scheme = file_uri_scheme($destination); + if (!file_stream_wrapper_valid_scheme($destination_scheme)) { + drupal_set_message(t('The file could not be uploaded because the destination %destination is invalid.', array('%destination' => $destination)), 'error'); + $files[$delta] = FALSE; + continue; + } - // Add in our check of the the file name length. - $validators['file_validate_name_length'] = array(); + $file->source = $source; + // A URI may already have a trailing slash or look like "public://". + if (substr($destination, -1) != '/') { + $destination .= '/'; + } + $file->destination = file_destination($destination . $file->filename, $replace); + // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and + // there's an existing file so we need to bail. + if ($file->destination === FALSE) { + drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $source, '%directory' => $destination)), 'error'); + $files[$delta] = FALSE; + continue; + } + + // Add in our check of the the file name length. + $validators['file_validate_name_length'] = array(); - // Call the validation functions specified by this function's caller. - $errors = file_validate($file, $validators); + // Call the validation functions specified by this function's caller. + $errors = file_validate($file, $validators); - // Check for errors. - if (!empty($errors)) { - $message = t('The specified file %name could not be uploaded.', array('%name' => $file->filename)); - if (count($errors) > 1) { - $message .= theme('item_list', array('items' => $errors)); + // Check for errors. + if (!empty($errors)) { + $message = t('The specified file %name could not be uploaded.', array('%name' => $file->filename)); + if (count($errors) > 1) { + $message .= theme('item_list', array('items' => $errors)); + } + else { + $message .= ' ' . array_pop($errors); + } + form_set_error($source, $message); + $files[$delta] = FALSE; + continue; } - else { - $message .= ' ' . array_pop($errors); + + // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary + // directory. This overcomes open_basedir restrictions for future file + // operations. + $file->uri = $file->destination; + if (!drupal_move_uploaded_file($_FILES['files']['tmp_name'][$source][$delta], $file->uri)) { + form_set_error($source, t('File upload error. Could not move uploaded file.')); + watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->uri)); + $files[$delta] = FALSE; + continue; } - form_set_error($source, $message); - return FALSE; - } - // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary - // directory. This overcomes open_basedir restrictions for future file - // operations. - $file->uri = $file->destination; - if (!drupal_move_uploaded_file($_FILES['files']['tmp_name'][$source][$delta], $file->uri)) { - form_set_error($source, t('File upload error. Could not move uploaded file.')); - watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->uri)); - return FALSE; - } + // Set the permissions on the new file. + drupal_chmod($file->uri); - // Set the permissions on the new file. - drupal_chmod($file->uri); - - // If we are replacing an existing file re-use its database record. - if ($replace == FILE_EXISTS_REPLACE) { - $existing_files = entity_load_multiple_by_properties('file', array('uri' => $file->uri)); - if (count($existing_files)) { - $existing = reset($existing_files); - $file->fid = $existing->fid; + // If we are replacing an existing file re-use its database record. + if ($replace == FILE_EXISTS_REPLACE) { + $existing_files = entity_load_multiple_by_properties('file', array('uri' => $file->uri)); + if (count($existing_files)) { + $existing = reset($existing_files); + $file->fid = $existing->fid; + } } + + // If we made it this far it's safe to record this file in the database. + $file->save(); + $files[$delta] = $file; } - // If we made it this far it's safe to record this file in the database. - $file->save(); // Add file to the cache. - $upload_cache[$source][$delta] = $file; - return $file; + $upload_cache[$source] = $files; + + if ($delta === FALSE) { + return $files; + } + else { + return $files[$delta]; + } + + return $files; } /** diff --git a/core/modules/aggregator/aggregator.admin.inc b/core/modules/aggregator/aggregator.admin.inc index 3612e0c..ebe509f 100644 --- a/core/modules/aggregator/aggregator.admin.inc +++ b/core/modules/aggregator/aggregator.admin.inc @@ -358,7 +358,7 @@ function aggregator_form_opml_validate($form, &$form_state) { function aggregator_form_opml_submit($form, &$form_state) { $data = ''; $validators = array('file_validate_extensions' => array('opml xml')); - if ($file = file_save_upload('upload', $validators)) { + if ($file = file_save_upload('upload', $validators, FALSE, 0)) { $data = file_get_contents($file->uri); } else { diff --git a/core/modules/file/file.module b/core/modules/file/file.module index fa7c721..c4592ed 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -1097,7 +1097,7 @@ function file_managed_file_validate(&$element, &$form_state) { foreach ($fids as $fid) { if ($file = file_load($fid)) { if ($file->status == FILE_STATUS_PERMANENT) { - $references = file_ufile_usage()->listUsage($file); + $references = file_usage()->listUsage($file); if (empty($references)) { form_error($element, t('The file used in the !name field may not be referenced.', array('!name' => $element['#title']))); } @@ -1216,19 +1216,25 @@ function file_managed_file_save_upload($element) { return FALSE; } - // Loop through any attached files and save them to the database. - $files = array(); - $file_count = count(array_filter($_FILES['files']['name'][$upload_name])); - while ($file_count > 0) { - $file_count--; - if (!$file = file_save_upload($upload_name, $file_count, $element['#upload_validators'], $destination)) { + // Save attached files to the database. + if(count(array_filter($_FILES['files']['name'][$upload_name])) > 0) { + if (!$files = file_save_upload($upload_name, $element['#upload_validators'], $destination)) { watchdog('file', 'The file upload failed. %upload', array('%upload' => $upload_name)); - form_set_error($upload_name, t('The file in the !name field was unable to be uploaded.', array('!name' => $element['#title']))); + form_set_error($upload_name, t('Files in the !name field were unable to be uploaded.', array('!name' => $element['#title']))); + return array(); } - $files[$file->fid] = $file; + + // Value callback expects FIDs to be keys. + $uploads = array(); + $files = array_filter($files); + foreach ($files as $file) { + $uploads[$file->fid] = $file; + } + + return $uploads; } - return $files; + return array(); } /** diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module index caeb037..5ec0428 100644 --- a/core/modules/file/tests/file_test/file_test.module +++ b/core/modules/file/tests/file_test/file_test.module @@ -126,7 +126,7 @@ function _file_test_form_submit(&$form, &$form_state) { $validators['file_validate_extensions'] = array($form_state['values']['extensions']); } - $file = file_save_upload('file_test_upload', 0, $validators, $destination, $form_state['values']['file_test_replace']); + $file = file_save_upload('file_test_upload', 0, $validators, $destination, 0, $form_state['values']['file_test_replace']); if ($file) { $form_state['values']['file_test_upload'] = $file; drupal_set_message(t('File @filepath was uploaded.', array('@filepath' => $file->uri))); diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 5420ac9..981a447 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -108,7 +108,7 @@ function locale_translate_import_form($form, &$form_state) { */ function locale_translate_import_form_submit($form, &$form_state) { // Ensure we have the file uploaded. - if ($file = file_save_upload('file', $form['file']['#upload_validators'], 'translations://')) { + if ($file = file_save_upload('file', $form['file']['#upload_validators'], 'translations://', 0)) { // Add language, if not yet supported. $language = language_load($form_state['values']['langcode']); diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php index 5e5b270..5f5b1b9 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -1114,7 +1114,7 @@ function hook_delete(Drupal\node\Node $node) { */ function hook_prepare(Drupal\node\Node $node) { if ($file = file_check_upload($field_name)) { - $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE)); + $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE), FALSE, 0); if ($file) { if (!image_get_info($file->uri)) { form_set_error($field_name, t('Uploaded file is not a valid image')); diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 37a2bf2..5cb47d2 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -609,7 +609,7 @@ function system_theme_settings_validate($form, &$form_state) { $validators = array('file_validate_is_image' => array()); // Check for a new uploaded logo. - $file = file_save_upload('logo_upload', $validators); + $file = file_save_upload('logo_upload', $validators, FALSE, 0); if (isset($file)) { // File upload was attempted. if ($file) { @@ -625,7 +625,7 @@ function system_theme_settings_validate($form, &$form_state) { $validators = array('file_validate_extensions' => array('ico png gif jpg jpeg apng svg')); // Check for a new uploaded favicon. - $file = file_save_upload('favicon_upload', $validators); + $file = file_save_upload('favicon_upload', $validators, FALSE, 0); if (isset($file)) { // File upload was attempted. if ($file) { diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc index 0047a52..bc84058 100644 --- a/core/modules/update/update.manager.inc +++ b/core/modules/update/update.manager.inc @@ -645,7 +645,7 @@ function update_manager_install_form_submit($form, &$form_state) { elseif ($_FILES['files']['name']['project_upload']) { $validators = array('file_validate_extensions' => array(archiver_get_extensions())); $field = 'project_upload'; - if (!($finfo = file_save_upload($field, $validators, NULL, FILE_EXISTS_REPLACE))) { + if (!($finfo = file_save_upload($field, $validators, NULL, 0, FILE_EXISTS_REPLACE))) { // Failed to upload the file. file_save_upload() calls form_set_error() on // failure. return;