/** * Copy an existing image file. * * @since 0.0.1 * @access private * * @param string $attachment_id Attachment ID. * @return string|false New file path on success, false on failure. */ function _copy_image_file($attachment_id) { $dst_file = $src_file = get_attached_file($attachment_id); if (!file_exists($src_file)) { $src_file = _load_image_to_edit_path($attachment_id); } if ($src_file) { $dst_file = str_replace(basename($dst_file), 'copy-' . basename($dst_file), $dst_file); $dst_file = dirname($dst_file) . '/' . hq_unique_filename(dirname($dst_file), basename($dst_file)); /* * The directory containing the original file may no longer * exist when using a replication plugin. */ hq_mkdir_p(dirname($dst_file)); if (!@copy($src_file, $dst_file)) { $dst_file = false; } } else { $dst_file = false; } return $dst_file; }
/** * Create a file in the upload folder with given content. * * If there is an error, then the key 'error' will exist with the error message. * If success, then the key 'file' will have the unique file path, the 'url' key * will have the link to the new file. and the 'error' key will be set to false. * * This function will not move an uploaded file to the upload folder. It will * create a new file with the content in $bits parameter. If you move the upload * file, read the content of the uploaded file, and then you can give the * filename and content to this function, which will add it to the upload * folder. * * The permissions will be set on the new file automatically by this function. * * @since 0.0.1 * * @param string $name Filename. * @param null|string $deprecated Never used. Set to null. * @param mixed $bits File content * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. * @return array */ function hq_upload_bits($name, $deprecated, $bits, $time = null) { if (!empty($deprecated)) { _deprecated_argument(__FUNCTION__, '2.0'); } if (empty($name)) { return array('error' => __('Empty filename')); } $hq_filetype = hq_check_filetype($name); if (!$hq_filetype['ext'] && !current_user_can('unfiltered_upload')) { return array('error' => __('Invalid file type')); } $upload = hq_upload_dir($time); if ($upload['error'] !== false) { return $upload; } /** * Filter whether to treat the upload bits as an error. * * Passing a non-array to the filter will effectively short-circuit preparing * the upload bits, returning that value instead. * * @since 0.0.1 * * @param mixed $upload_bits_error An array of upload bits data, or a non-array error to return. */ $upload_bits_error = apply_filters('hq_upload_bits', array('name' => $name, 'bits' => $bits, 'time' => $time)); if (!is_array($upload_bits_error)) { $upload['error'] = $upload_bits_error; return $upload; } $filename = hq_unique_filename($upload['path'], $name); $new_file = $upload['path'] . "/{$filename}"; if (!hq_mkdir_p(dirname($new_file))) { if (0 === strpos($upload['basedir'], ABSPATH)) { $error_path = str_replace(ABSPATH, '', $upload['basedir']) . $upload['subdir']; } else { $error_path = basename($upload['basedir']) . $upload['subdir']; } $message = sprintf(__('Unable to create directory %s. Is its parent directory writable by the server?'), $error_path); return array('error' => $message); } $ifp = @fopen($new_file, 'hq'); if (!$ifp) { return array('error' => sprintf(__('Could not write file %s'), $new_file)); } @fwrite($ifp, $bits); fclose($ifp); clearstatcache(); // Set correct file permissions $stat = @stat(dirname($new_file)); $perms = $stat['mode'] & 07777; $perms = $perms & 0666; @chmod($new_file, $perms); clearstatcache(); // Compute the URL $url = $upload['url'] . "/{$filename}"; return array('file' => $new_file, 'url' => $url, 'error' => false); }
/** * Handle PHP uploads in HiveQueen, sanitizing file names, checking extensions for mime type, * and moving the file to the appropriate directory within the uploads directory. * * @since 0.0.1 * * @see hq_handle_upload_error * * @param array $file Reference to a single element of $_FILES. Call the function once for each uploaded file. * @param array|false $overrides An associative array of names => values to override default variables. Default false. * @param string $time Time formatted in 'yyyy/mm'. * @param string $action Expected value for $_POST['action']. * @return array On success, returns an associative array of file attributes. On failure, returns * $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ). */ function _hq_handle_upload(&$file, $overrides, $time, $action) { // The default error handler. if (!function_exists('hq_handle_upload_error')) { function hq_handle_upload_error(&$file, $message) { return array('error' => $message); } } /** * Filter the data for a file before it is uploaded to HiveQueen. * * The dynamic portion of the hook name, `$action`, refers to the post action. * * @since 0.0.1 * @since 0.0.1 * * @param array $file An array of data for a single file. */ $file = apply_filters("{$action}_prefilter", $file); // You may define your own function and pass the name in $overrides['upload_error_handler'] $upload_error_handler = 'hq_handle_upload_error'; if (isset($overrides['upload_error_handler'])) { $upload_error_handler = $overrides['upload_error_handler']; } // You may have had one or more 'hq_handle_upload_prefilter' functions error out the file. Handle that gracefully. if (isset($file['error']) && !is_numeric($file['error']) && $file['error']) { return $upload_error_handler($file, $file['error']); } // Install user overrides. Did we mention that this voids your warranty? // You may define your own function and pass the name in $overrides['unique_filename_callback'] $unique_filename_callback = null; if (isset($overrides['unique_filename_callback'])) { $unique_filename_callback = $overrides['unique_filename_callback']; } /* * This may not have orignially been intended to be overrideable, * but historically has been. */ if (isset($overrides['upload_error_strings'])) { $upload_error_strings = $overrides['upload_error_strings']; } else { // Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error']. $upload_error_strings = array(false, __('The uploaded file exceeds the upload_max_filesize directive in php.ini.'), __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.'), __('The uploaded file was only partially uploaded.'), __('No file was uploaded.'), '', __('Missing a temporary folder.'), __('Failed to write file to disk.'), __('File upload stopped by extension.')); } // All tests are on by default. Most can be turned off by $overrides[{test_name}] = false; $test_form = isset($overrides['test_form']) ? $overrides['test_form'] : true; $test_size = isset($overrides['test_size']) ? $overrides['test_size'] : true; // If you override this, you must provide $ext and $type!! $test_type = isset($overrides['test_type']) ? $overrides['test_type'] : true; $mimes = isset($overrides['mimes']) ? $overrides['mimes'] : false; // A correct form post will pass this test. if ($test_form && (!isset($_POST['action']) || $_POST['action'] != $action)) { return call_user_func($upload_error_handler, $file, __('Invalid form submission.')); } // A successful upload will pass this test. It makes no sense to override this one. if (isset($file['error']) && $file['error'] > 0) { return call_user_func($upload_error_handler, $file, $upload_error_strings[$file['error']]); } $test_file_size = 'hq_handle_upload' === $action ? $file['size'] : filesize($file['tmp_name']); // A non-empty file will pass this test. if ($test_size && !($test_file_size > 0)) { if (is_multisite()) { $error_msg = __('File is empty. Please upload something more substantial.'); } else { $error_msg = __('File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.'); } return call_user_func($upload_error_handler, $file, $error_msg); } // A properly uploaded file will pass this test. There should be no reason to override this one. $test_uploaded_file = 'hq_handle_upload' === $action ? @is_uploaded_file($file['tmp_name']) : @is_file($file['tmp_name']); if (!$test_uploaded_file) { return call_user_func($upload_error_handler, $file, __('Specified file failed upload test.')); } // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter. if ($test_type) { $hq_filetype = hq_check_filetype_and_ext($file['tmp_name'], $file['name'], $mimes); $ext = empty($hq_filetype['ext']) ? '' : $hq_filetype['ext']; $type = empty($hq_filetype['type']) ? '' : $hq_filetype['type']; $proper_filename = empty($hq_filetype['proper_filename']) ? '' : $hq_filetype['proper_filename']; // Check to see if hq_check_filetype_and_ext() determined the filename was incorrect if ($proper_filename) { $file['name'] = $proper_filename; } if ((!$type || !$ext) && !current_user_can('unfiltered_upload')) { return call_user_func($upload_error_handler, $file, __('Sorry, this file type is not permitted for security reasons.')); } if (!$type) { $type = $file['type']; } } else { $type = ''; } /* * A writable uploads dir will pass this test. Again, there's no point * overriding this one. */ if (!(($uploads = hq_upload_dir($time)) && false === $uploads['error'])) { return call_user_func($upload_error_handler, $file, $uploads['error']); } $filename = hq_unique_filename($uploads['path'], $file['name'], $unique_filename_callback); // Move the file to the uploads dir. $new_file = $uploads['path'] . "/{$filename}"; if ('hq_handle_upload' === $action) { $move_new_file = @move_uploaded_file($file['tmp_name'], $new_file); } else { $move_new_file = @rename($file['tmp_name'], $new_file); } if (false === $move_new_file) { if (0 === strpos($uploads['basedir'], ABSPATH)) { $error_path = str_replace(ABSPATH, '', $uploads['basedir']) . $uploads['subdir']; } else { $error_path = basename($uploads['basedir']) . $uploads['subdir']; } return $upload_error_handler($file, sprintf(__('The uploaded file could not be moved to %s.'), $error_path)); } // Set correct file permissions. $stat = stat(dirname($new_file)); $perms = $stat['mode'] & 0666; @chmod($new_file, $perms); // Compute the URL. $url = $uploads['url'] . "/{$filename}"; if (is_multisite()) { delete_transient('dirsize_cache'); } /** * Filter the data array for the uploaded file. * * @since 0.0.1 * * @param array $upload { * Array of upload data. * * @type string $file Filename of the newly-uploaded file. * @type string $url URL of the uploaded file. * @type string $type File type. * } * @param string $context The type of upload action. Values include 'upload' or 'sideload'. */ return apply_filters('hq_handle_upload', array('file' => $new_file, 'url' => $url, 'type' => $type), 'hq_handle_sideload' === $action ? 'sideload' : 'upload'); }