"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; } ?>