Commit 15f289a8 authored by Dries's avatar Dries
Browse files

- Added file handling.  Work by Kjartan.
parent c1898456
......@@ -935,6 +935,7 @@ function drupal_page_footer() {
include_once "includes/menu.inc";
include_once "includes/xmlrpc.inc";
include_once "includes/tablesort.inc";
include_once "includes/file.inc";
// set error handler:
set_error_handler("error_handler");
......
<?php
/** @file
* $Id$
*
* Common file handling functions
*
* @par Example:
* @code
* function module_validate($node) {
* if (!$node->file = file_save_upload('file')) {
* $error['file'] = t('File required.');
* }
* }
*
* function module_insert($node) {
* if ($node->file) {
* file_save($node->file);
* }
* }
* @endcode
*
*/
define('FILE_DOWNLOADS_PUBLIC', 1);
define('FILE_DOWNLOADS_PRIVATE', 2);
define('FILE_SEPARATOR', PHP_OS == 'WINNT' ? '\\' : '/');
/**
* Create the download path to a file.
*/
function file_create_url($path) {
switch (variable_get('file_downloads', FILE_DOWNLOADS_PRIVATE)) {
case FILE_DOWNLOADS_PUBLIC:
case FILE_DOWNLOADS_PRIVATE:
global $base_url;
if (strpos($path, variable_get('file_directory_path', 'files')) !== false) {
$path = trim(substr($path, strlen(variable_get('file_directory_path', 'files'))), '\\/');
}
return $base_url .'/'. variable_get('file_directory_path', 'files') .'/'. str_replace('\\', '/', $path);
case FILE_DOWNLOADS_PRIVATE:
return url('system/files', 'file='. $path);
}
}
/**
* Make sure the destination is a complete path, if it is not prepend the
* file system directory.
*/
function file_create_path($dest = 0) {
if (!$dest) {
return variable_get('file_directory_path', 'files');
}
$regex = (PHP_OS == 'WINNT' ? '.?:\\\\' : '/');
if (!file_check_location($dest, variable_get('file_directory_path', 'files')) && !preg_match("|^$regex|", $dest)) {
return variable_get('file_directory_path', 'files') . FILE_SEPARATOR . trim($dest, '\\/');
}
else {
return $dest;
}
}
/**
* Check that directory exists and is writable.
*
* @param $directory Path to extract and verify directory for.
* @return False when directory not found, or true when directory exists.
*/
function file_check_directory(&$directory) {
$directory = rtrim($directory, '/\\');
return is_dir($directory) && is_writable($directory);
}
/**
* 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 ($_FILES["edit"]["name"][$source] && is_uploaded_file($_FILES["edit"]["tmp_name"][$source])) {
$file->name = trim(basename($_FILES["edit"]["name"][$source]), '.');
$file->type = $_FILES["edit"]["type"][$source];
$file->path = $_FILES["edit"]["tmp_name"][$source];
$file->error = $_FILES["edit"]["error"][$source];
$file->size = $_FILES["edit"]["size"][$source];
return $file;
}
}
/**
* 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);
if ($directory && strpos($source, $directory) !== 0) {
return 0;
}
return $source;
}
/**
* Saves 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 it will append a number to the end of the
* filename, but before the file extension. It will increment the number until
* it finds a filename that is not already in use.
*
* @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 A boolean that when true will overwrite any existing files,
* but when false append a _X to the filename.
* @return True for success, false for failure.
*/
function file_copy(&$source, $dest = 0, $replace = 0) {
$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->path;
if (!$basename) {
$basename = $file->name;
}
}
$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 . FILE_SEPARATOR . $basename;
if (file_exists($dest) && !$replace) {
// Destination file already exists and we can't replace is so we try and
// and find a new filename.
list($name, $ext) = explode('.', $basename, 2);
$ext = $ext ? ".$ext" : '';
$counter = 0;
do {
$dest = $directory . FILE_SEPARATOR . $name .'_'. $counter++ . $ext;
$counter++;
} while (file_exists($dest));
}
if (!copy($source, $dest)) {
drupal_set_message(t('file copy failed.'), 'error');
return 0;
}
if (is_object($file)) {
$file->name = $basename;
$file->path = $dest;
$source = $file;
}
else {
$source = $dest;
}
return 1; // Everything went ok.
}
function file_move(&$source, $dest = 0, $replace = 0) {
$path = is_object($source) ? $source->path : $source;
if (file_copy($source, $dest, $replace)) {
if (unlink($path)) {
return 1;
}
drupal_set_message(t('removing original file failed.'), 'error');
}
return 0;
}
function file_delete($source) {
unlink(file_create_path($source));
}
/**
* 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 $dest 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 = 0) {
// Make sure $source exists in $_FILES.
if ($file = file_check_upload($source)) {
if ($dest === 0) {
$dest = variable_get('file_directory_temp', ini_get('upload_tmp_dir'));
$temporary = 1;
if (file_exists($_SESSION['file_uploads'][$source]->path)) {
// 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 UPLOAD_ERR_PARTIAL:
case UPLOAD_ERR_NO_FILE:
drupal_set_message(t("file upload failed: incomplete upload."), 'error');
return 0;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
drupal_set_message(t("file upload failed: file size too big."), 'error');
return 0;
}
unset($_SESSION['file_uploads'][$source]);
if (file_move($file, $dest, $replace)) {
if ($temporary) {
$_SESSION['file_uploads'][$source] = $file;
}
return $file;
}
return 0;
}
else {
// In case if previews return previous file object.
if (file_exists($_SESSION['file_uploads'][$source]->path)) {
return $_SESSION['file_uploads'][$source];
}
}
return 0;
}
/**
* 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 fgets($fd);
}
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) {
print theme('page', message_access());
}
elseif (is_array($headers)) {
file_transfer($file, $headers);
}
}
}
//drupal_not_found();
}
/**
* Finds all files that match a given mask in a given directory. Searches
* recursively.
*/
function file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0) {
$files = array();
if (is_dir($dir) && $handle = opendir($dir)) {
while ($file = readdir($handle)) {
if (!in_array($file, $nomask)) {
if (is_dir("$dir/$file")) {
$files = array_merge($files, file_scan_directory("$dir/$file", $mask, $nomask, $callback));
}
elseif (ereg($mask, $file)) {
$name = basename($file);
$files["$dir/$file"]->filename = "$dir/$file";
$files["$dir/$file"]->name = substr($name, 0, strrpos($name, '.'));
if ($callback) {
$callback("$dir/$file");
}
}
}
}
closedir($handle);
}
return $files;
}
?>
......@@ -57,6 +57,10 @@ function profile_settings() {
_profile_init();
}
if (!file_check_directory(file_create_path(variable_get('profile_avatar_path', 'avatars')))) {
$error['profile_avatar_path'] = theme('error', t('Directory does not exist, or is not writable.'));
}
$profile_public_fields = variable_get("profile_public_fields", array());
$profile_private_fields = variable_get("profile_private_fields", array());
$profile_required_fields = variable_get("profile_required_fields", array());
......@@ -74,7 +78,7 @@ function profile_settings() {
}
$output .= theme("table", $header, $row);
$output .= form_textfield(t("Avatar image path"), "profile_avatar_path", variable_get("profile_avatar_path", "misc/avatars/"), 30, 255, t("Path for avatar directory; it must be writable and visible from the web."));
$output .= form_textfield(t("Avatar image path"), "profile_avatar_path", variable_get("profile_avatar_path", "avatars"), 30, 255, t("Subdirectory in the directory '%dir' where avatars will be stored.", array('%dir' => variable_get('file_directory_path', 'files') . FILE_SEPARATOR)) . $error['profile_avatar_path']);
$output .= form_textfield(t("Avatar maximum dimensions"), "profile_avatar_dimensions", variable_get("profile_avatar_dimensions", "85x85"), 10, 10, t("Maximum dimensions for avatars."));
$output .= form_textfield(t("Avatar maximum file size"), "profile_avatar_file_size", variable_get("profile_avatar_file_size", "30"), 10, 10, t("Maximum file size for avatars, in kB."));
......@@ -133,11 +137,8 @@ function _profile_form($edit, $mode) {
}
if (in_array("avatar", $reg_fields)) {
if ($edit["profile_avatar"] && $edit["uid"]) {
$file = profile_avatar_path($edit["uid"], $edit["profile_avatar"]);
if ($file) {
$output .= "<img src=\"$file\" alt=\"\" title=\"\" /><br />";
}
if ($edit["profile_avatar"] && file_exists($edit["profile_avatar"])) {
$output .= form_item(t("Avatar"), '<img src="'. file_create_url($edit["profile_avatar"]) .'" alt="" title="" />');
}
$output .= form_file($profile_fields["avatar"][1], "profile_avatar", 64, $profile_fields["avatar"][2]);
}
......@@ -199,9 +200,8 @@ function _profile_user_view(&$user, $mode) {
case "":
// special
if ($t == "profile_avatar") {
$file = profile_avatar_path($user->uid, $user->profile_avatar);
if (file_exists($file)) {
$output .= form_item(t("Avatar"), "<img src=\"$file\" alt=\"\" title=\"\" />");
if (file_exists($user->$t)) {
$output .= form_item(t("Avatar"), '<img src="'. file_create_url($user->$t) .'" alt="" title="" />');
}
}
......@@ -218,51 +218,44 @@ function _profile_user_view(&$user, $mode) {
return $output;
}
function profile_file_download($file) {
if (strpos($file, variable_get("profile_avatar_path", "avatars")) === 0) {
return array("Content-type: $mime");
}
}
function _profile_validate_avatar(&$edit, $user) {
// check that uploaded file is an image, with a maximum file size and maximum height/width
unset($edit["profile_avatar"]);
if ($_FILES["edit"]["name"]["profile_avatar"] == "") {
if (!$file = file_check_upload('profile_avatar')) {
$edit["profile_avatar"] = $user->profile_avatar;
return "";
return;
}
$image_file = $_FILES["edit"]["tmp_name"]["profile_avatar"];
if (is_uploaded_file($image_file)) {
$extension = strtolower(strrchr($_FILES["edit"]["name"]["profile_avatar"], "."));
$size = getimagesize($image_file);
list($maxwidth, $maxheight) = explode("x", variable_get("profile_avatar_dimensions", "85x85"));
if ((!in_array($size[2], array(1, 2, 3))) || (!in_array($extension, array(".gif", ".jpg", ".png", ".jpeg")))) {
$error = t("The uploaded file was not an image.");
}
else if (filesize($image_file) > (variable_get("profile_avatar_file_size", "30") * 1000)) {
$error = t("The uploaded image is too large; the maximum file size is %a kB.", array("%a" => variable_get("profile_avatar_file_size", "30")));
}
else if ($size[0] > $maxwidth || $size[1] > $maxheight) {
$error = t("The uploaded image is too large; the maximum dimensions are %a pixels.", array("%a" => variable_get("profile_avatar_dimensions", "85x85")));
}
else if (!is_dir(variable_get("profile_avatar_path", "misc/avatars/"))) {
$error = t("Failed to upload the avatar image; the '%directory' directory doesn't exist.", array("%directory" => variable_get("profile_avatar_path", "misc/avatars/")));
}
else if (!is_writeable(variable_get("profile_avatar_path", "misc/avatars/"))) {
$error = t("Failed to upload the avatar image; the webserver has no write permission to the '%directory' directory.", array("%directory" => variable_get("profile_avatar_path", "misc/avatars/")));
}
else if (!copy($image_file, variable_get("profile_avatar_path", "misc/avatars/").md5($user->uid).$extension)) {
$error = t("Failed to upload the avatar image; could not copy file '%filename' to directory '%directory'.", array("%filename" => $_FILES["edit"]["name"]["profile_avatar"], "%directory" => variable_get("profile_avatar_path", "misc/avatars/")));
}
else {
$edit["profile_avatar"] = $extension;
}
$extension = strtolower(strrchr($file->name, "."));
$size = getimagesize($file->path);
list($maxwidth, $maxheight) = explode("x", variable_get("profile_avatar_dimensions", "85x85"));
if ((!in_array($size[2], array(1, 2, 3))) || (!in_array($extension, array(".gif", ".jpg", ".png", ".jpeg")))) {
$error = t("The uploaded file was not an image.");
}
else if ($file->size > (variable_get("profile_avatar_file_size", "30") * 1000)) {
$error = t("The uploaded image is too large; the maximum file size is %a kB.", array("%a" => variable_get("profile_avatar_file_size", "30")));
}
else if ($size[0] > $maxwidth || $size[1] > $maxheight) {
$error = t("The uploaded image is too large; the maximum dimensions are %a pixels.", array("%a" => variable_get("profile_avatar_dimensions", "85x85")));
}
else if ($file = file_save_upload('profile_avatar', variable_get("profile_avatar_path", "avatars") . FILE_SEPARATOR .'avatar-'. $user->uid . $extension, 1)) {
$edit["profile_avatar"] = $file->name;
}
else {
$error = t("Failed to upload the avatar image; the '%directory' directory doesn't exist.", array("%directory" => variable_get("profile_avatar_path", "avatars")));
}
return $error ? "$error<br />" : "";
}
function profile_avatar_path($uid, $extension) {
return $extension ? variable_get("profile_avatar_path", "misc/avatars/") . md5($uid) . $extension : "";
}
function _profile_active_fields($mode) {
return variable_get("profile_". $mode ."_fields", array());
}
......
......@@ -57,6 +57,10 @@ function profile_settings() {
_profile_init();
}
if (!file_check_directory(file_create_path(variable_get('profile_avatar_path', 'avatars')))) {
$error['profile_avatar_path'] = theme('error', t('Directory does not exist, or is not writable.'));
}
$profile_public_fields = variable_get("profile_public_fields", array());
$profile_private_fields = variable_get("profile_private_fields", array());
$profile_required_fields = variable_get("profile_required_fields", array());
......@@ -74,7 +78,7 @@ function profile_settings() {
}
$output .= theme("table", $header, $row);
$output .= form_textfield(t("Avatar image path"), "profile_avatar_path", variable_get("profile_avatar_path", "misc/avatars/"), 30, 255, t("Path for avatar directory; it must be writable and visible from the web."));
$output .= form_textfield(t("Avatar image path"), "profile_avatar_path", variable_get("profile_avatar_path", "avatars"), 30, 255, t("Subdirectory in the directory '%dir' where avatars will be stored.", array('%dir' => variable_get('file_directory_path', 'files') . FILE_SEPARATOR)) . $error['profile_avatar_path']);
$output .= form_textfield(t("Avatar maximum dimensions"), "profile_avatar_dimensions", variable_get("profile_avatar_dimensions", "85x85"), 10, 10, t("Maximum dimensions for avatars."));
$output .= form_textfield(t("Avatar maximum file size"), "profile_avatar_file_size", variable_get("profile_avatar_file_size", "30"), 10, 10, t("Maximum file size for avatars, in kB."));
......@@ -133,11 +137,8 @@ function _profile_form($edit, $mode) {
}
if (in_array("avatar", $reg_fields)) {
if ($edit["profile_avatar"] && $edit["uid"]) {
$file = profile_avatar_path($edit["uid"], $edit["profile_avatar"]);
if ($file) {
$output .= "<img src=\"$file\" alt=\"\" title=\"\" /><br />";
}
if ($edit["profile_avatar"] && file_exists($edit["profile_avatar"])) {
$output .= form_item(t("Avatar"), '<img src="'. file_create_url($edit["profile_avatar"]) .'" alt="" title="" />');
}
$output .= form_file($profile_fields["avatar"][1], "profile_avatar", 64, $profile_fields["avatar"][2]);
}
......@@ -199,9 +200,8 @@ function _profile_user_view(&$user, $mode) {
case "":
// special
if ($t == "profile_avatar") {
$file = profile_avatar_path($user->uid, $user->profile_avatar);
if (file_exists($file)) {
$output .= form_item(t("Avatar"), "<img src=\"$file\" alt=\"\" title=\"\" />");
if (file_exists($user->$t)) {
$output .= form_item(t("Avatar"), '<img src="'. file_create_url($user->$t) .'" alt="" title="" />');
}
}
......@@ -218,51 +218,44 @@ function _profile_user_view(&$user, $mode) {
return $output;
}
function profile_file_download($file) {
if (strpos($file, variable_get("profile_avatar_path", "avatars")) === 0) {
return array("Content-type: $mime");
}
}
function _profile_validate_avatar(&$edit, $user) {
// check that uploaded file is an image, with a maximum file size and maximum height/width
unset($edit["profile_avatar"]);
if ($_FILES["edit"]["name"]["profile_avatar"] == "") {
if (!$file = file_check_upload('profile_avatar')) {
$edit["profile_avatar"] = $user->profile_avatar;
return "";
return;
}
$image_file = $_FILES["edit"]["tmp_name"]["profile_avatar"];
if (is_uploaded_file($image_file)) {
$extension = strtolower(strrchr($_FILES["edit"]["name"]["profile_avatar"], "."));
$size = getimagesize($image_file);
list($maxwidth, $maxheight) = explode("x", variable_get("profile_avatar_dimensions", "85x85"));
if ((!in_array($size[2], array(1, 2, 3))) || (!in_array($extension, array(".gif", ".jpg", ".png", ".jpeg")))) {
$error = t("The uploaded file was not an image.");
}
else if (filesize($image_file) > (variable_get("profile_avatar_file_size", "30") * 1000)) {
$error = t("The uploaded image is too large; the maximum file size is %a kB.", array("%a" => variable_get("profile_avatar_file_size", "30")));
}
else if ($size[0] > $maxwidth || $size[1] > $maxheight) {
$error = t("The uploaded image is too large; the maximum dimensions are %a pixels.", array("%a" => variable_get("profile_avatar_dimensions", "85x85")));
}
else if (!is_dir(variable_get("profile_avatar_path", "misc/avatars/"))) {
$error = t("Failed to upload the avatar image; the '%directory' directory doesn't exist.", array("%directory" => variable_get("profile_avatar_path", "misc/avatars/")));
}
else if (!is_writeable(variable_get("profile_avatar_path", "misc/avatars/"))) {
$error = t("Failed to upload the avatar image; the webserver has no write permission to the '%directory' directory.", array("%directory" => variable_get("profile_avatar_path", "misc/avatars/")));
}
else if (!copy($image_file, variable_get("profile_avatar_path", "misc/avatars/").md5($user->uid).$extension)) {
$error = t("Failed to upload the avatar image; could not copy file '%filename' to directory '%directory'.", array("%filename" => $_FILES["edit"]["name"]["profile_avatar"], "%directory" => variable_get("profile_avatar_path", "misc/avatars/")));
}
else {
$edit["profile_avatar"] = $extension;
}
$extension = strtolower(strrchr($file->name, "."));
$size = getimagesize($file->path);
list($maxwidth, $maxheight) = explode("x", variable_get("profile_avatar_dimensions", "85x85"));
if ((!in_array($size[2], array(1, 2, 3))) || (!in_array($extension, array(".gif", ".jpg", ".png", ".jpeg")))) {
$error = t("The uploaded file was not an image.");
}
else if ($file->size > (variable_get("profile_avatar_file_size", "30") * 1000)) {
$error = t("The uploaded image is too large; the maximum file size is %a kB.", array("%a" => variable_get("profile_avatar_file_size", "30")));
}
else if ($size[0] > $maxwidth || $size[1] > $maxheight) {
$error = t("The uploaded image is too large; the maximum dimensions are %a pixels.", array("%a" => variable_get("profile_avatar_dimensions", "85x85")));
}
else if ($file = file_save_upload('profile_avatar', variable_get("profile_avatar_path", "avatars") . FILE_SEPARATOR .'avatar-'. $user->uid . $extension, 1)) {
$edit["profile_avatar"] = $file->name;
}
else {
$error = t("Failed to upload the avatar image; the '%directory' directory doesn't exist.", array("%directory" => variable_get("profile_avatar_path", "avatars")));
}
return $error ? "$error<br />" : "";
}
function profile_avatar_path($uid, $extension) {
return $extension ? variable_get("profile_avatar_path", "misc/avatars/") . md5($uid) . $extension : "";
}