All pastes #192475 Raw Edit

Mine

public text v1 · immutable
·published 2006-10-05 23:35 UTC
rendered paste body
<?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;
}

?>