diff options
Diffstat (limited to 'application/models/Mfile.php')
-rw-r--r-- | application/models/Mfile.php | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/application/models/Mfile.php b/application/models/Mfile.php new file mode 100644 index 000000000..977240c89 --- /dev/null +++ b/application/models/Mfile.php @@ -0,0 +1,275 @@ +<?php +/* + * Copyright 2009-2013 Florian "Bluewind" Pritz <bluewind@server-speed.net> + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +class Mfile extends CI_Model { + + private $upload_path; + + function __construct() + { + parent::__construct(); + $this->load->model("muser"); + + $this->upload_path = $this->config->item('upload_path'); + $this->id_validation_config = array( + "upload_max_age" => $this->config->item("upload_max_age"), + "small_upload_size" => $this->config->item("small_upload_size"), + "sess_expiration" => $this->config->item("sess_expiration"), + ); + } + + // Returns an unused ID + function new_id($min = 3, $max = 6) + { + static $id_blacklist = NULL; + + if ($id_blacklist == NULL) { + // This prevents people from being unable to access their uploads + // because of URL rewriting + $id_blacklist = scandir(FCPATH); + $id_blacklist[] = "file"; + $id_blacklist[] = "user"; + } + + $max_tries = 100; + + for ($try = 0; $try < $max_tries; $try++) { + $id = random_alphanum($min, $max); + + if ($this->id_exists($id) || in_array($id, $id_blacklist)) { + continue; + } + + return $id; + } + + throw new \exceptions\PublicApiException("file/new_id-try-limit", "Failed to find unused ID after $max_tries tries"); + } + + function id_exists($id) + { + if (!$id) { + return false; + } + + $query = $this->db->select('id') + ->from('files') + ->where('id', $id) + ->limit(1) + ->get(); + + return $query->num_rows() == 1; + } + + function get_filedata($id) + { + return cache_function("filedatav2-$id", 300, function() use ($id) { + $query = $this->db + ->select('files.id, hash, file_storage.id storage_id, filename, mimetype, files.date, user, filesize') + ->from('files') + ->join('file_storage', 'file_storage.id = files.file_storage_id') + ->where('files.id', $id) + ->limit(1) + ->get(); + + if ($query->num_rows() > 0) { + $data = $query->row_array(); + $data["data_id"] = $data["hash"]."-".$data["storage_id"]; + return $data; + } else { + return false; + } + }); + } + + // return the folder in which the file with $data_id is stored + function folder($data_id) { + return $this->upload_path.'/'.substr($data_id, 0, 3); + } + + // Returns the full path to the file with $data_id + function file($data_id) { + return $this->folder($data_id).'/'.$data_id; + } + + // Add a file to the DB + function add_file($userid, $id, $filename, $storage_id) + { + $this->db->insert("files", array( + "id" => $id, + "filename" => $filename, + "date" => time(), + "user" => $userid, + "file_storage_id" => $storage_id, + )); + } + + function adopt($id) + { + $userid = $this->muser->get_userid(); + + $this->db->set(array('user' => $userid )) + ->where('id', $id) + ->where('user', 0) + ->update('files'); + return $this->db->affected_rows(); + } + + // remove old/invalid/broken IDs + public function valid_id($id) + { + $filedata = $this->get_filedata($id); + + if (!$filedata) { + return false; + } + + return $this->valid_filedata($filedata); + } + + public function valid_filedata($filedata) + { + return \service\files::valid_id($filedata, $this->id_validation_config, $this, time()); + } + + public function file_exists($file) + { + return file_exists($file); + } + + public function filemtime($file) + { + return filemtime($file); + } + + public function filesize($file) + { + return filesize($file); + } + + public function get_timeout($id) + { + $filedata = $this->get_filedata($id); + $file = $this->file($filedata["data_id"]); + + if ($this->config->item("upload_max_age") == 0) { + return -1; + } + + if (filesize($file) > $this->config->item("small_upload_size")) { + return $filedata["date"] + $this->config->item("upload_max_age"); + } else { + return -1; + } + } + + public function get_timeout_string($id) + { + $timeout = $this->get_timeout($id); + + if ($timeout >= 0) { + return date("r", $timeout); + } else { + return "unknown"; + } + } + + private function unused_file($data_id) + { + list ($hash, $storage_id) = explode("-", $data_id); + $query = $this->db->select('files.id') + ->from('files') + ->join('file_storage', 'file_storage.id = files.file_storage_id') + ->where('hash', $hash) + ->where('file_storage.id', $storage_id) + ->limit(1) + ->get(); + + return $query->num_rows() == 0; + } + + public function delete_by_user($userid) + { + $query = $this->db->select("id") + ->where("user", $userid) + ->get("files")->result_array(); + $ids = array_map(function ($a) {return $a['id'];}, $query); + + foreach ($ids as $id) { + $this->delete_id($id); + } + } + + public function delete_id($id) + { + $filedata = $this->get_filedata($id); + + // Delete the file and all multipastes using it + // Note that this does not delete all relations in multipaste_file_map + // which is actually done by an SQL contraint. + // TODO: make it work properly without the constraint + $map = $this->db->select('url_id') + ->distinct() + ->from('multipaste_file_map') + ->join("multipaste", "multipaste.multipaste_id = multipaste_file_map.multipaste_id") + ->where('file_url_id', $id) + ->get()->result_array(); + + $this->db->where('id', $id) + ->delete('files'); + delete_cache("filedata-$id"); + + foreach ($map as $entry) { + assert(!empty($entry['url_id'])); + $this->mmultipaste->delete_id($entry["url_id"]); + } + + if ($this->id_exists($id)) { + return false; + } + + if ($filedata !== false) { + assert(isset($filedata["data_id"])); + if ($this->unused_file($filedata['data_id'])) { + $this->delete_data_id($filedata['data_id']); + } + } + return true; + } + + public function delete_data_id($data_id) + { + list ($hash, $storage_id) = explode("-", $data_id); + + $this->db->where('id', $storage_id) + ->delete('file_storage'); + if (file_exists($this->file($data_id))) { + unlink($this->file($data_id)); + } + $dir = $this->folder($data_id); + if (file_exists($dir)) { + if (count(scandir($dir)) == 2) { + rmdir($dir); + } + } + delete_cache("${data_id}_thumb_150"); + } + + public function get_owner($id) + { + return $this->db->select('user') + ->from('files') + ->where('id', $id) + ->get()->row_array() + ['user']; + } + +} + +# vim: set noet: |