MDL-24283 improved stored_file encapsulation, this should allow us to hack with store...
[moodle.git] / lib / filestorage / stored_file.php
CommitLineData
16a95e8f 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18
19/**
8496fdac 20 * Definition of a class stored_file.
16a95e8f 21 *
64f93798
PS
22 * @package core
23 * @subpackage filestorage
8496fdac 24 * @copyright 2008 Petr Skoda {@link http://skodak.org}
64a19b38 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
16a95e8f 26 */
27
64f93798
PS
28defined('MOODLE_INTERNAL') || die();
29
30require_once("$CFG->libdir/filestorage/stored_file.php");
172dd12c 31
32/**
8496fdac
PS
33 * Class representing local files stored in a sha1 file pool.
34 *
35 * Since Moodle 2.0 file contents are stored in sha1 pool and
36 * all other file information is stored in new "files" database table.
37 *
38 * @copyright 2008 Petr Skoda {@link http://skodak.org}
39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 * @since Moodle 2.0
172dd12c 41 */
42class stored_file {
8496fdac 43 /** @var file_storage file storage pool instance */
172dd12c 44 private $fs;
8496fdac 45 /** @var object record from the files table */
172dd12c 46 private $file_record;
693ef3a8
PS
47 /** @var string location of content files */
48 private $filedir;
172dd12c 49
50 /**
8496fdac
PS
51 * Constructor, this constructor should be called ONLY from the file_storage class!
52 *
53 * @param file_storage $fs file storage instance
172dd12c 54 * @param object $file_record description of file
693ef3a8 55 * @param string $filepool location of file directory with sh1 named content files
172dd12c 56 */
693ef3a8 57 public function __construct(file_storage $fs, stdClass $file_record, $filedir) {
8496fdac
PS
58 $this->fs = $fs;
59 $this->file_record = clone($file_record); // prevent modifications
693ef3a8 60 $this->filedir = $filedir; // keep secret, do not expose!
172dd12c 61 }
62
63 /**
64 * Is this a directory?
8496fdac
PS
65 *
66 * Directories are only emulated, internally they are stored as empty
67 * files with a "." instead of name - this means empty directory contains
68 * exactly one empty file with name dot.
69 *
70 * @return bool true means directory, false means file
172dd12c 71 */
72 public function is_directory() {
8496fdac 73 return ($this->file_record->filename === '.');
172dd12c 74 }
75
76 /**
8496fdac
PS
77 * Delete file from files table.
78 *
79 * The content of files stored in sha1 pool is reclaimed
80 * later - the occupied disk space is reclaimed much later.
81 *
82 * @return bool always true or exception if error occurred
172dd12c 83 */
84 public function delete() {
85 global $DB;
1aa01caf 86 $DB->delete_records('files', array('id'=>$this->file_record->id));
87 // moves pool file to trash if content not needed any more
88 $this->fs->deleted_file_cleanup($this->file_record->contenthash);
8496fdac 89 return true; // BC only
172dd12c 90 }
91
92 /**
8496fdac
PS
93 * Protected - developers must not gain direct access to this function.
94 *
17d9269f 95 * NOTE: do not make this public, we must not modify or delete the pool files directly! ;-)
8496fdac
PS
96 *
97 * @return string full path to pool file with file content
172dd12c 98 **/
99 protected function get_content_file_location() {
17d9269f 100 $contenthash = $this->file_record->contenthash;
101 $l1 = $contenthash[0].$contenthash[1];
102 $l2 = $contenthash[2].$contenthash[3];
693ef3a8 103 return "$this->filedir/$l1/$l2/$contenthash";
172dd12c 104 }
105
5035a8b4 106 /**
107 * adds this file path to a curl request (POST only)
108 *
109 * @param curl $curlrequest the curl request object
110 * @param string $key what key to use in the POST request
8496fdac 111 * @return void
5035a8b4 112 */
113 public function add_to_curl_request(&$curlrequest, $key) {
114 $curlrequest->_tmp_file_post_params[$key] = '@' . $this->get_content_file_location();
115 }
116
172dd12c 117 /**
118 * Returns file handle - read only mode, no writing allowed into pool files!
8496fdac
PS
119 *
120 * When you want to modify a file, create a new file and delete the old one.
121 *
122 * @return resource file handle
172dd12c 123 */
124 public function get_content_file_handle() {
125 $path = $this->get_content_file_location();
126 if (!is_readable($path)) {
1aa01caf 127 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
128 throw new file_exception('storedfilecannotread');
129 }
172dd12c 130 }
131 return fopen($path, 'rb'); //binary reading only!!
132 }
133
134 /**
8496fdac
PS
135 * Dumps file content to page.
136 *
137 * @return void
172dd12c 138 */
139 public function readfile() {
140 $path = $this->get_content_file_location();
141 if (!is_readable($path)) {
1aa01caf 142 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
143 throw new file_exception('storedfilecannotread');
144 }
172dd12c 145 }
146 readfile($path);
147 }
148
149 /**
8496fdac
PS
150 * Returns file content as string.
151 *
172dd12c 152 * @return string content
153 */
154 public function get_content() {
155 $path = $this->get_content_file_location();
156 if (!is_readable($path)) {
1aa01caf 157 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
158 throw new file_exception('storedfilecannotread');
159 }
172dd12c 160 }
161 return file_get_contents($this->get_content_file_location());
162 }
163
6c0e2d08 164 /**
8496fdac
PS
165 * Copy content of file to given pathname.
166 *
167 * @param string $pathname real path to the new file
6c0e2d08 168 * @return bool success
169 */
170 public function copy_content_to($pathname) {
171 $path = $this->get_content_file_location();
172 if (!is_readable($path)) {
1aa01caf 173 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
174 throw new file_exception('storedfilecannotread');
175 }
6c0e2d08 176 }
177 return copy($path, $pathname);
178 }
179
17d9269f 180 /**
8496fdac
PS
181 * List contents of archive.
182 *
183 * @param file_packer $file_packer
c78a0558 184 * @return array of file infos
185 */
186 public function list_files(file_packer $packer) {
187 $archivefile = $this->get_content_file_location();
188 return $packer->list_files($archivefile);
189 }
190
191 /**
8496fdac
PS
192 * Extract file to given file path (real OS filesystem), existing files are overwritten.
193 *
194 * @param file_packer $file_packer
0b0bfa93 195 * @param string $pathname target directory
8496fdac 196 * @return array|bool list of processed files; false if error
17d9269f 197 */
0b0bfa93 198 public function extract_to_pathname(file_packer $packer, $pathname) {
199 $archivefile = $this->get_content_file_location();
200 return $packer->extract_to_pathname($archivefile, $pathname);
17d9269f 201 }
202
203 /**
8496fdac
PS
204 * Extract file to given file path (real OS filesystem), existing files are overwritten.
205 *
206 * @param file_packer $file_packer
17d9269f 207 * @param int $contextid
64f93798 208 * @param string $component
17d9269f 209 * @param string $filearea
210 * @param int $itemid
211 * @param string $pathbase
212 * @param int $userid
8496fdac 213 * @return array|bool list of processed files; false if error
17d9269f 214 */
64f93798 215 public function extract_to_storage(file_packer $packer, $contextid, $component, $filearea, $itemid, $pathbase, $userid = NULL) {
0b0bfa93 216 $archivefile = $this->get_content_file_location();
64f93798 217 return $packer->extract_to_storage($archivefile, $contextid, $component, $filearea, $itemid, $pathbase);
17d9269f 218 }
219
b1897a6d 220 /**
8496fdac
PS
221 * Add file/directory into archive.
222 *
223 * @param file_archive $filearch
c78a0558 224 * @param string $archivepath pathname in archive
b1897a6d 225 * @return bool success
226 */
0b0bfa93 227 public function archive_file(file_archive $filearch, $archivepath) {
b1897a6d 228 if ($this->is_directory()) {
0b0bfa93 229 return $filearch->add_directory($archivepath);
b1897a6d 230 } else {
231 $path = $this->get_content_file_location();
232 if (!is_readable($path)) {
233 return false;
234 }
0b0bfa93 235 return $filearch->add_file_from_pathname($archivepath, $path);
b1897a6d 236 }
237 }
238
797f19e8 239 /**
240 * Returns information about image,
241 * information is determined from the file content
242 * @return mixed array with width, height and mimetype; false if not an image
243 */
244 public function get_imageinfo() {
245 if (!$imageinfo = getimagesize($this->get_content_file_location())) {
246 return false;
247 }
248 $image = array('width'=>$imageinfo[0], 'height'=>$imageinfo[1], 'mimetype'=>image_type_to_mime_type($imageinfo[2]));
249 if (empty($image['width']) or empty($image['height']) or empty($image['mimetype'])) {
250 // gd can not parse it, sorry
251 return false;
252 }
253 return $image;
254 }
255
256 /**
257 * Verifies the file is a valid web image - gif, png and jpeg only.
8496fdac 258 *
797f19e8 259 * It should be ok to serve this image from server without any other security workarounds.
8496fdac 260 *
797f19e8 261 * @return bool true if file ok
262 */
263 public function is_valid_image() {
264 $mimetype = $this->get_mimetype();
265 if ($mimetype !== 'image/gif' and $mimetype !== 'image/jpeg' and $mimetype !== 'image/png') {
266 return false;
267 }
268 if (!$info = $this->get_imageinfo()) {
269 return false;
270 }
271 if ($info['mimetype'] !== $mimetype) {
272 return false;
273 }
274 // ok, GD likes this image
275 return true;
276 }
277
4b6b5ce7 278 /**
8496fdac
PS
279 * Returns parent directory, creates missing parents if needed.
280 *
281 * @return stored_file
4b6b5ce7 282 */
283 public function get_parent_directory() {
284 if ($this->file_record->filepath === '/' and $this->file_record->filename === '.') {
285 //root dir does not have parent
286 return null;
287 }
288
289 if ($this->file_record->filename !== '.') {
64f93798 290 return $this->fs->create_directory($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $this->file_record->filepath);
4b6b5ce7 291 }
292
293 $filepath = $this->file_record->filepath;
294 $filepath = trim($filepath, '/');
295 $dirs = explode('/', $filepath);
296 array_pop($dirs);
297 $filepath = implode('/', $dirs);
298 $filepath = ($filepath === '') ? '/' : "/$filepath/";
299
64f93798 300 return $this->fs->create_directory($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $filepath);
4b6b5ce7 301 }
302
16a95e8f 303 /**
8496fdac
PS
304 * Returns context id of the file-
305 *
16a95e8f 306 * @return int context id
307 */
172dd12c 308 public function get_contextid() {
309 return $this->file_record->contextid;
310 }
311
16a95e8f 312 /**
64f93798
PS
313 * Returns component name - this is the owner of the areas,
314 * nothing else is allowed to read or modify the files directly!!
315 *
316 * @return string
317 */
318 public function get_component() {
319 return $this->file_record->component;
320 }
321
322 /**
323 * Returns file area name, this divides files of one component into groups with different access control.
324 * All files in one area have the same access control.
8496fdac 325 *
16a95e8f 326 * @return string
327 */
172dd12c 328 public function get_filearea() {
329 return $this->file_record->filearea;
330 }
331
16a95e8f 332 /**
8496fdac
PS
333 * Returns returns item id of file.
334 *
16a95e8f 335 * @return int
336 */
172dd12c 337 public function get_itemid() {
338 return $this->file_record->itemid;
339 }
340
16a95e8f 341 /**
342 * Returns file path - starts and ends with /, \ are not allowed.
8496fdac 343 *
16a95e8f 344 * @return string
345 */
172dd12c 346 public function get_filepath() {
347 return $this->file_record->filepath;
348 }
349
16a95e8f 350 /**
351 * Returns file name or '.' in case of directories.
8496fdac 352 *
16a95e8f 353 * @return string
354 */
172dd12c 355 public function get_filename() {
356 return $this->file_record->filename;
357 }
358
16a95e8f 359 /**
360 * Returns id of user who created the file.
8496fdac 361 *
16a95e8f 362 * @return int
363 */
172dd12c 364 public function get_userid() {
365 return $this->file_record->userid;
366 }
367
16a95e8f 368 /**
369 * Returns the size of file in bytes.
8496fdac 370 *
16a95e8f 371 * @return int bytes
372 */
172dd12c 373 public function get_filesize() {
374 return $this->file_record->filesize;
375 }
376
16a95e8f 377 /**
8496fdac
PS
378 * Returns mime type of file.
379 *
16a95e8f 380 * @return string
381 */
172dd12c 382 public function get_mimetype() {
383 return $this->file_record->mimetype;
384 }
385
16a95e8f 386 /**
8496fdac
PS
387 * Returns unix timestamp of file creation date.
388 *
16a95e8f 389 * @return int
390 */
172dd12c 391 public function get_timecreated() {
392 return $this->file_record->timecreated;
393 }
394
16a95e8f 395 /**
8496fdac
PS
396 * Returns unix timestamp of last file modification.
397 *
16a95e8f 398 * @return int
399 */
172dd12c 400 public function get_timemodified() {
401 return $this->file_record->timemodified;
402 }
6c0e2d08 403
16a95e8f 404 /**
8496fdac
PS
405 * Returns file status flag.
406 *
16a95e8f 407 * @return int 0 means file OK, anything else is a problem and file can not be used
408 */
6c0e2d08 409 public function get_status() {
410 return $this->file_record->status;
411 }
ee03a651 412
16a95e8f 413 /**
8496fdac
PS
414 * Returns file id.
415 *
16a95e8f 416 * @return int
417 */
ee03a651 418 public function get_id() {
419 return $this->file_record->id;
420 }
4284e1cc 421
16a95e8f 422 /**
8496fdac
PS
423 * Returns sha1 hash of file content.
424 *
16a95e8f 425 * @return string
426 */
4284e1cc 427 public function get_contenthash() {
428 return $this->file_record->contenthash;
429 }
6ed19c74 430
16a95e8f 431 /**
64f93798 432 * Returns sha1 hash of all file path components sha1("contextid/component/filearea/itemid/dir/dir/filename.ext").
8496fdac 433 *
16a95e8f 434 * @return string
435 */
6ed19c74 436 public function get_pathnamehash() {
437 return $this->file_record->pathnamehash;
438 }
1dce6261
DC
439
440 /**
8496fdac
PS
441 * Returns the license type of the file, it is a short name referred from license table.
442 *
1dce6261
DC
443 * @return string
444 */
445 public function get_license() {
446 return $this->file_record->license;
447 }
448
449 /**
8496fdac
PS
450 * Returns the author name of the file.
451 *
1dce6261
DC
452 * @return string
453 */
454 public function get_author() {
455 return $this->file_record->license;
456 }
457
458 /**
8496fdac
PS
459 * Returns the source of the file, usually it is a url.
460 *
1dce6261
DC
461 * @return string
462 */
463 public function get_source() {
464 return $this->file_record->source;
465 }
8496fdac 466
f79321f1
DC
467 /**
468 * Returns the sort order of file
469 *
470 * @return int
471 */
472 public function get_sortorder() {
473 return $this->file_record->sortorder;
474 }
475}