<?php
/**
* Filesystem module for the drupal CMS
*
* Version: $Revision$
* Last modified by: $Author$ on $Date$
* URL: $HeadURL$
*/
// list of fields fetched from the database
// this exists to keep from having to write it
// over and over, and to keep from ever pulling
// the "data" (potentially large) column from
// the database.
define("FETCH_FIELDS", "fid, owner_uid, name, path, mimtype, size, create_date, modify_date");
// file permissions
define("PERM_OWNER", "0");
define("PERM_SITE", "8");
define("PERM_REGISTERED", "16");
define("PERM_EVERYONE", "32");
/***********************************************************************/
/** HOOK FUNCTIONS **/
/***********************************************************************/
/**
* Implementation of hook_help()
*/
function fs_help($section="") {
switch ($section) {
case "admin/modules#description":
return t("Filesystem module and API");
}
return "";
}
/**
* Implementation of hook_menu()
*/
function fs_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
"path" => "fs",
"title" => t("Access File"),
"callback" => "fs_access_file",
"acess" => user_access("access content"),
"type" => MENU_CALLBACK
);
}
return $items;
}
/**
* Implementation of hook_perm()
*/
function fs_perm() {
return array("access content");
}
/***********************************************************************/
/** API FUNCTIONS **/
/***********************************************************************/
/**
* Returns the root folder where files are stored
* on disk.
*/
function fs_get_root_folder() {
return variable_get("fs_root_folder", sys_get_temp_dir());
}
/**
* Returns the path to the currently
* logged in user's directory.
*/
function fs_user_dir() {
global $user;
if ($user->uid) {
return "/home/".$user->uid;
} else {
return NULL;
}
}
/**
* Returns the full path to a file
*/
function fs_real_path(&$file) {
return fs_get_root_folder().$file->path."/".$file->name;
}
/**
* Returns a file by it's file id.
*/
function fs_get_file_by_id($fid) {
$result = db_query("select ".FETCH_FIELDS." from {filesystem} where fid=%d", $file->fid);
if ($row=db_fetch_object($result)) {
return _fs_assemble_file($row);
} else {
return NULL;
}
}
/**
* Returns a file by it's path and name
*/
function fs_get_file_by_name($path, $name) {
$result = db_query(
"select ".FETCH_FIELDS." from {filesystem} where path='%s' and name='%s'",
$path,
$name
);
if ($row=db_fetch_object($result)) {
return _fs_assemble_file($row);
} else {
return NULL;
}
}
/**
* Pulls the file from the database server
* to the web server.
*/
function fs_pull_file(&$file) {
// get the real path to the file
$real_path = fs_real_path($file);
// query for the file data
$result = db_query("select data from {filesystem} where fid=%d", $file->fid);
// pull the data and decode it
if ($row = db_fetch_object($result)) {
$data = db_decode_blob($row->data);
// make required directories
if (!_fs_mkdirs($file)) {
drupal_set_message(
"Unable to create directories for: ".$real_path,
"error"
);
return FALSE;
}
// create a new file (or overwrite existing)
// and write the data to it
$fh = fopen($real_path, "w+");
// check for errors
if ($fh===FALSE) {
drupal_set_message(
"Unable to write file to disk: ".$real_path,
"error"
);
return FALSE;
}
// write data and close it
fwrite($fh, $data);
fclose($fh);
// it worked
return TRUE;
}
// problems
drupal_set_message(
"Unable to find file in database",
"error"
);
return FALSE;
}
/**
* Removes a file from the filesystem, if the user
* has access.
*/
function fs_remove(&$file) {
// make sure we have permission
if (fs_can_write($file, $user->uid)) {
// get the real path
$real_path = fs_real_path($file);
// delete it from the file system
if (file_exists($real_path)) {
unlink($real_path);
}
// remove from db
db_query("delete from {filesystem} where fid=%d", $file->fid);
// good to go
return TRUE;
}
// access denied
drupal_set_message(
"Access denied to modify: ".$file->path."/".$file->name,
"error"
);
return FALSE;
}
/**
* Opens a file for reading and returns a file
* handle to it.
*/
function fs_fopen(&$file, $mode, $use_include_path = NULL, $zcontext = NULL) {
global $user;
// if they can't read it, deny them
if (fs_can_read($file, $user->uid)) {
drupal_set_message(
"Access denied to read: ".$file->path."/".$file->name,
"error"
);
return FALSE;
}
// get some information on the file
$real_path = fs_real_path($file);
$last_mod = strtotime($file->modify_date);
$grab_latest = FALSE;
// we need to grab the latest if the
// file isn't on disk yet
if (!file_exists($real_path)) {
$grab_latest = TRUE;
// the file has been modified from the
// database
} else if (filemtime($real_path)<$last_mod) {
$grab_latest = TRUE;
}
// grab the latest if needed
if ($grab_latest && !fs_pull_file($file)) {
drupal_set_message(
"Error pulling file: ".$file->path."/".$file->name,
"error"
);
}
// open the file now
return fopen($real_path, $mode, $use_include_path, $zcontext);
}
/**
* Saves a file, with optional data.
*/
function fs_save(&$file, $data = FALSE) {
global $user;
// only modify it if it can be modified
if (fs_can_write($file, $user->uid)) {
db_query(
"update {filesystem} set owner_uid=%d, name='%s', path='%s', ".
"mimetype='%s', size='%d', modify_date=now() where fid=%d",
$file->owner_uid,
$file->name,
$file->path,
$file->mimetype,
$file->size,
$file->fid
);
// make sure it worked
if (db_affected_rows()==0) {
drupal_set_message(
"File ".$file->path."/".$file->name." wasn't modified for an unknown reason.",
"error"
);
$file->name = $file->original_name;
$file->path = $file->original_path;
return FALSE;
// if it worked, then reset the
// original name and path to their new values
} else {
$file->original_name = $file->name;
$file->original_path = $file->path;
// if we've got data, lets update that as well.
if ($data) {
// open the file and read it's contents
$file_size = filesize(fs_real_path($file));
$fh = fs_fopen($file, "r");
$contents = fread($fh, $file_size);
fclose($fh);
// update size
$file->size = $file_size;
// update the database
db_query(
"update {filesystem} set data='%s', size=%d where fid=%d",
db_encode_blob($contents),
$file->size,
$file->fid
);
}
}
// all was good
return TRUE;
}
// no permission
drupal_set_message(
"Permission denied to ".$file->path."/".$file->name,
"error"
);
return FALSE;
}
/**
* Renames a file.
*/
function fs_move(&$file, $newname, $newpath = NULL) {
global $user;
// make sure they can write to it
if (!fs_can_write($file, $user->uid)) {
drupal_set_message(
"Permission denied to ".$file->path."/".$file->name,
"error"
);
}
// delete it from it's current location
if (file_exists(fs_real_path($file))) {
unlink(fs_real_path($file));
}
// set names
$file->name = $newname;
if (!empty($newpath)) {
$file->path = $newpath;
}
// save it
return fs_save($file);
}
/**
* Returns a list of available permissions, all standard
* permission types are returned as an array, along with
* any permission types returned via invoking the hook
* "fs_available_permissions" on all modules.
*/
function fs_available_permissions() {
$perms = array();
// add standard permissions
$perms[] = PERM_OWNER;
$perms[] = PERM_SITE;
$perms[] = PERM_REGISTERED;
$perms[] = PERM_EVERYONE;
// add custom permissions
$perms = module_invoke_all("fs_available_permissions");
foreach ($perms as $perm) {
$perms[] = $perm;
}
// return the permission list
return $perms;
}
/**
* Determines whether or not a user can write (change) a file.
*/
function fs_can_write($file, $uid) {
global $user;
return ($user->uid==1 || $file->owner_uid==$uid);
}
/**
* Determines whether or not a user can read a file.
* If the permission set on the file is not recognized
* by the filesystem module, then the module hook
* "fs_can_read" is invoked on all modules passing
* $file and $uid, if any module returns FALSE (even
* if more of them return TRUE) read access is denied.
*/
function fs_can_read($file, $uid = 0) {
// admin can read anything
if ($uid==1) {
return TRUE;
}
// check the file's permission
switch ($file->permission) {
case PERM_OWNER: return ($uid==$file->owner_uid);
case PERM_SITE: return ($uid==1);
case PERM_REGISTERED: return ($uid!=0);
case PERM_EVERYONE: return TRUE;
}
// if we've reached this point, it's
// a custom permission type
$perms = module_invoke_all("fs_can_read", $file, $uid);
// if any module returned FALSE, then
// permission is denied
foreach ($perms as $perm) {
if (!$perm) { return FALSE; }
}
// permission granted :)
return TRUE;
}
/**
* Returns an array of file objects.
*/
function fs_ls($path = "/", $recursive = TRUE) {
global $user;
// build where clause
$where = ($recursive) ? "lower(path) like lower('%s%%')" : "lower(path)=lower('%s')";
// execute the query
$result = db_query(
"select ".FETCH_FIELDS." from {filesystem} where ".$where,
$path
);
// the files to return
$files = array();
while ($row = db_fetch_object($result)) {
$file = _fs_assemble_file($row);
if ( !empty($user->uid) && fs_can_read($file, $user->uid) ) {
$files[] = $file;
}
}
// return the files
return $files;
}
/***********************************************************************/
/** PRIVATE FUNCTIONS **/
/***********************************************************************/
/**
* Sends the file to the browser
*/
function fs_access_file() {
global $user;
print "fs_access_file"; exit;
// the file we're trying to access
$file = "";
$i = 1;
while ($part=arg($i)) {
$file .= "/" . $part;
}
// get the parts of the file
$parts = explode("/", $file);
// get the file name
$filename = array_pop($parts);
$filepath = "/".implode("/", $parts);
// get the file
$file = fs_get_file_by_name($filepath, $filename);
// 404
if ($file==NULL) {
drupal_not_found();
exit;
}
// can we read the file?
if (fs_can_read($file, $user->uid)) {
// open the file
$fh = fs_fopen($file, "r");
$data = fread($fh, $file->size);
fclose($fh);
// set the header, and send the data
drupal_set_header("Content-Type: ".$file->mimetype);
drupal_set_header("Content-Length: ".$file->size);
// write the data and bail
print $data;
exit;
// can't acccess it
} else {
drupal_access_denied();
exit;
}
}
/**
* Creats the required dirs for the file
*/
function _fs_mkdirs(&$file) {
// get path to file
$parts = explode("/", $file->path);
// loop through ech part and
// create the required directories
$current_path = fs_get_root_folder();
foreach ($parts as $part) {
$current_path .= "/" . $part;
if (!file_exists($current_path)) {
mkdir($current_path);
}
}
}
/**
* Assembles a file object from a row in the database
*/
function _fs_assemble_file($row) {
$file = new StdClass();
$file = $row;
$file->original_name = $row->name;
$file->original_path = $row->path;
return $file;
}
?>