"$directory"))); } else { if ($form_item) { form_set_error($form_item, t('The directory %directory does not exist.', array('%directory' => "$directory"))); } return false; } } // Check to see if the directory is writable. if (!is_writable($directory)) { if (($mode & FILE_MODIFY_PERMISSIONS) && @chmod($directory, 0760)) { drupal_set_message(t('Modified permissions on directory %directory.', array('%directory' => "$directory"))); } else { form_set_error($form_item, t('The directory %directory is not writable.', array('%directory' => "$directory"))); return false; } } return true; } /** * Checks path to see if it is a directory, or a dir/file. * * @param $path */ function file_check_path(&$path) { // Check if path is a directory. if (file_check_directory($path)) { return ''; } // Check if path is a possible dir/file. $filename = basename($path); $path = dirname($path); if (file_check_directory($path)) { return $filename; } return false; } /** * Check if $source is a valid file upload. * * @param $source */ function file_check_upload($source) { if (is_object($source)) { if (is_file($source->filepath)) { return $source; } } elseif ($_FILES["edit"]["name"][$source] && is_uploaded_file($_FILES["edit"]["tmp_name"][$source])) { $file->filename = trim(basename($_FILES["edit"]["name"][$source]), '.'); $file->filemime = $_FILES["edit"]["type"][$source]; $file->filepath = $_FILES["edit"]["tmp_name"][$source]; $file->error = $_FILES["edit"]["error"][$source]; $file->filesize = $_FILES["edit"]["size"][$source]; $file->source = $source; return $file; } else { // In case of previews return previous file object. if (file_exists($_SESSION['file_uploads'][$source]->filepath)) { return $_SESSION['file_uploads'][$source]; } } } /** * Check if a file is really located inside $directory. Should be used to make * sure a file specified is really located within the directory to prevent * exploits. * * @code * // Returns false: * file_check_location('/www/example.com/files/../../../etc/passwd', '/www/example.com/files'); * @endcode * * @param $source A string set to the file to check. * @param $directory A string where the file should be located. * @return 0 for invalid path or the real path of the source. */ function file_check_location($source, $directory = 0) { $source = realpath($source); $directory = realpath($directory); if ($directory && strpos($source, $directory) !== 0) { return 0; } return $source; } /** * Copies a file to a new location. This is a powerful function that in many ways * performs like an advanced version of copy(). * - Checks if $source and $dest are valid and readable/writable. * - Performs a file copy if $source is not equal to $dest. * - If file already exists in $dest either the call will error out, replace the * file or rename the file based on the $replace parameter. * * @param $source A string specifying the file location of the original file. * This parameter will contain the resulting destination filename in case of * success. * @param $dest A string containing the directory $source should be copied to. * @param $replace Replace behavior when the destination file already exists. * - FILE_EXISTS_REPLACE - Replace the existing file * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique * - FILE_EXISTS_ERROR - Do nothing and return false. * @return True for success, false for failure. */ function file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) { $dest = file_create_path($dest); $directory = $dest; $basename = file_check_path($directory); // Make sure we at least have a valid directory. if ($basename === false) { drupal_set_message(t('File copy failed: no directory configured, or it could not be accessed.'), 'error'); return 0; } // Process a file upload object. if (is_object($source)) { $file = $source; $source = $file->filepath; if (!$basename) { $basename = $file->filename; } } $source = realpath($source); if (!file_exists($source)) { drupal_set_message(t('File copy failed: source file does not exist.'), 'error'); return 0; } // If destination file is not specified then use filename of source file. $basename = $basename ? $basename : basename($source); $dest = $directory .'/'. $basename; // Make sure source and destination filenames are not the same, makes no sense // to copy it if they are. In fact copying the file will most likely result in // a 0 byte file. Which is bad. Real bad. if ($source != realpath($dest)) { if (file_exists($dest)) { switch ($replace) { case FILE_EXISTS_RENAME: // Destination file already exists and we can't replace is so we try and // and find a new filename. if ($pos = strrpos($basename, '.')) { $name = substr($basename, 0, $pos); $ext = substr($basename, $pos); } else { $name = $basename; } $counter = 0; do { $dest = $directory .'/'. $name .'_'. $counter++ . $ext; } while (file_exists($dest)); break; case FILE_EXISTS_ERROR: drupal_set_message(t('File copy failed. File already exists.'), 'error'); return 0; case FILE_EXISTS_REPLACE: // Leave $dest where it is for replace. } } if (!copy($source, $dest)) { drupal_set_message(t('File copy failed.'), 'error'); return 0; } } if (is_object($file)) { $file->filename = $basename; $file->filepath = $dest; $source = $file; } else { $source = $dest; } return 1; // Everything went ok. } /** * Moves a file to a new location. * - Checks if $source and $dest are valid and readable/writable. * - Performs a file move if $source is not equal to $dest. * - If file already exists in $dest either the call will error out, replace the * file or rename the file based on the $replace parameter. * * @param $source A string specifying the file location of the original file. * This parameter will contain the resulting destination filename in case of * success. * @param $dest A string containing the directory $source should be copied to. * @param $replace Replace behavior when the destination file already exists. * - FILE_EXISTS_REPLACE - Replace the existing file * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique * - FILE_EXISTS_ERROR - Do nothing and return false. * @return True for success, false for failure. */ function file_move(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) { $path_original = is_object($source) ? $source->filepath : $source; if (file_copy($source, $dest, $replace)) { $path_current = is_object($source) ? $source->filepath : $source; if ($path_original == $path_current || file_delete($path_original)) { return 1; } drupal_set_message(t('Removing original file failed.'), 'error'); } return 0; } function file_create_filename($basename, $directory) { $dest = $directory .'/'. $basename; if (file_exists($dest)) { // Destination file already exists, generate an alternative. if ($pos = strrpos($basename, '.')) { $name = substr($basename, 0, $pos); $ext = substr($basename, $pos); } else { $name = $basename; } $counter = 0; do { $dest = $directory .'/'. $name .'_'. $counter++ . $ext; } while (file_exists($dest)); } return $dest; } function file_delete($path) { if (is_file($path)) { return unlink($path); } } /** * Saves a file upload to a new location. The source file is validated as a * proper upload and handled as such. * * @param $source A string specifying the name of the upload field to save. * This parameter will contain the resulting destination filename in case of * success. * @param $dest A string containing the directory $source should be copied to, * will use the temporary directory in case no other value is set. * @param $replace A boolean, set to true if the destination should be replaced * when in use, but when false append a _X to the filename. * @return An object containing file info or 0 in case of error. */ function file_save_upload($source, $dest = 0, $replace = FILE_EXISTS_RENAME) { // Make sure $source exists in $_FILES. if ($file = file_check_upload($source)) { if (!$dest) { $dest = variable_get('file_directory_temp', FILE_DIRECTORY_TEMP); $temporary = 1; if (is_file($file->filepath)) { // If this file was uploaded by this user before replace the temporary copy. $replace = 1; } } if (!valid_input_data($file)) { watchdog('error', t('Possible exploit abuse: invalid data.')); drupal_set_message(t('File upload failed: invalid data.'), 'error'); return 0; } // Check for file upload errors. switch ($file->error) { case 0: // UPLOAD_ERR_OK break; case 1: // UPLOAD_ERR_INI_SIZE case 2: // UPLOAD_ERR_FORM_SIZE drupal_set_message(t('File upload failed: file size too big.'), 'error'); return 0; case 3: // UPLOAD_ERR_PARTIAL case 4: // UPLOAD_ERR_NO_FILE drupal_set_message(t('File upload failed: incomplete upload.'), 'error'); return 0; default: // Unknown error drupal_set_message(t('File upload failed: unknown error.'), 'error'); return 0; } unset($_SESSION['file_uploads'][is_object($source) ? $source->source : $source]); if (file_move($file, $dest, $replace)) { if ($temporary) { $_SESSION['file_uploads'][is_object($source) ? $source->source : $source] = $file; } return $file; } return 0; } return 0; } /** * Save a string to the specified destination * * @param $data A string containing the contents of the file * @param $dest A string containing the destination location * * @return A string containing the resulting filename or 0 on error */ function file_save_data($data, $dest, $replace = 0) { if (!valid_input_data($data)) { watchdog('error', t('Possible exploit abuse: invalid data.')); drupal_set_message(t('File upload failed: invalid data.'), 'error'); return 0; } $temp = variable_get('file_directory_temp', FILE_DIRECTORY_TEMP); $file = tempnam($temp, 'file'); if (!$fp = fopen($file, 'wb')) { drupal_set_message(t('Unable to create file.'), 'error'); return 0; } fwrite($fp, $data); fclose($fp); if (!file_move($file, $dest, $replace)) { return 0; } return $file; } /** * Transfer file using http to client. Pipes a file through Drupal to the * client. * * @param $source File to transfer. * @param $headers An array of http headers to send along with file. */ function file_transfer($source, $headers) { ob_end_clean(); foreach ($headers as $header) { header($header); } $source = file_create_path($source); // Transfer file in 1024 byte chunks to save memory usage. $fd = fopen($source, 'rb'); while (!feof($fd)) { print fread($fd, 1024); } fclose($fd); exit(); } /** * Call modules to find out if a file is accessible for a given user. */ function file_download() { $file = $_GET['file']; if (file_exists(file_create_path($file))) { $list = module_list(); foreach ($list as $module) { $headers = module_invoke($module, 'file_download', $file); if ($headers === -1) { drupal_access_denied(); } elseif (is_array($headers)) { file_transfer($file, $headers); } } } drupal_not_found(); } /** * Finds all files that match a given mask in a given directory. * * @param $dir Directory to scan * @param $mask Regular expression to filter out files. Only filenames that * match the mask will be returned. * @param $nomask Array of filenames which should never be returned regardless * if they match the $mask * @param $callback Function to call for qualifying file. * Set to 0 or false if you do not want callbacks. * @param $recurse When true directory scan will recurse the entire tree starting at $dir * @return Array of qualifying files */ function file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE) { $files = array(); if (is_dir($dir) && $handle = opendir($dir)) { while ($file = readdir($handle)) { if (!in_array($file, $nomask)) { if (is_dir("$dir/$file") && $recurse) { $files = array_merge($files, file_scan_directory("$dir/$file", $mask, $nomask, $callback)); } elseif (ereg($mask, $file)) { $name = basename($file); $files["$dir/$file"] = new stdClass(); $files["$dir/$file"]->filename = "$dir/$file"; $files["$dir/$file"]->name = substr($name, 0, strrpos($name, '.')); if ($callback) { $callback("$dir/$file"); } } } } closedir($handle); } return $files; } ?>