weekly release 2.3dev
[moodle.git] / lib / filestorage / stored_file.php
CommitLineData
16a95e8f 1<?php
16a95e8f 2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17
18/**
8496fdac 19 * Definition of a class stored_file.
16a95e8f 20 *
d2b7803e
DC
21 * @package core_files
22 * @copyright 2008 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
16a95e8f 24 */
25
64f93798
PS
26defined('MOODLE_INTERNAL') || die();
27
28require_once("$CFG->libdir/filestorage/stored_file.php");
172dd12c 29
30/**
8496fdac
PS
31 * Class representing local files stored in a sha1 file pool.
32 *
33 * Since Moodle 2.0 file contents are stored in sha1 pool and
34 * all other file information is stored in new "files" database table.
35 *
d2b7803e
DC
36 * @package core_files
37 * @category files
8496fdac
PS
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;
d2b7803e 45 /** @var stdClass 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
d2b7803e
DC
54 * @param stdClass $file_record description of file
55 * @param string $filedir 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)) {
d610cb89 128 throw new file_exception('storedfilecannotread', '', $path);
1aa01caf 129 }
172dd12c 130 }
131 return fopen($path, 'rb'); //binary reading only!!
132 }
133
134 /**
8496fdac 135 * Dumps file content to page.
172dd12c 136 */
137 public function readfile() {
138 $path = $this->get_content_file_location();
139 if (!is_readable($path)) {
1aa01caf 140 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
d610cb89 141 throw new file_exception('storedfilecannotread', '', $path);
1aa01caf 142 }
172dd12c 143 }
144 readfile($path);
145 }
146
147 /**
8496fdac
PS
148 * Returns file content as string.
149 *
172dd12c 150 * @return string content
151 */
152 public function get_content() {
153 $path = $this->get_content_file_location();
154 if (!is_readable($path)) {
1aa01caf 155 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
d610cb89 156 throw new file_exception('storedfilecannotread', '', $path);
1aa01caf 157 }
172dd12c 158 }
159 return file_get_contents($this->get_content_file_location());
160 }
161
6c0e2d08 162 /**
8496fdac
PS
163 * Copy content of file to given pathname.
164 *
165 * @param string $pathname real path to the new file
6c0e2d08 166 * @return bool success
167 */
168 public function copy_content_to($pathname) {
169 $path = $this->get_content_file_location();
170 if (!is_readable($path)) {
1aa01caf 171 if (!$this->fs->try_content_recovery($this) or !is_readable($path)) {
d610cb89 172 throw new file_exception('storedfilecannotread', '', $path);
1aa01caf 173 }
6c0e2d08 174 }
175 return copy($path, $pathname);
176 }
177
17d9269f 178 /**
8496fdac
PS
179 * List contents of archive.
180 *
d2b7803e 181 * @param file_packer $packer file packer instance
c78a0558 182 * @return array of file infos
183 */
184 public function list_files(file_packer $packer) {
185 $archivefile = $this->get_content_file_location();
186 return $packer->list_files($archivefile);
187 }
188
189 /**
8496fdac
PS
190 * Extract file to given file path (real OS filesystem), existing files are overwritten.
191 *
d2b7803e 192 * @param file_packer $packer file packer instance
0b0bfa93 193 * @param string $pathname target directory
8496fdac 194 * @return array|bool list of processed files; false if error
17d9269f 195 */
0b0bfa93 196 public function extract_to_pathname(file_packer $packer, $pathname) {
197 $archivefile = $this->get_content_file_location();
198 return $packer->extract_to_pathname($archivefile, $pathname);
17d9269f 199 }
200
201 /**
8496fdac
PS
202 * Extract file to given file path (real OS filesystem), existing files are overwritten.
203 *
d2b7803e
DC
204 * @param file_packer $packer file packer instance
205 * @param int $contextid context ID
206 * @param string $component component
207 * @param string $filearea file area
208 * @param int $itemid item ID
209 * @param string $pathbase path base
210 * @param int $userid user ID
8496fdac 211 * @return array|bool list of processed files; false if error
17d9269f 212 */
64f93798 213 public function extract_to_storage(file_packer $packer, $contextid, $component, $filearea, $itemid, $pathbase, $userid = NULL) {
0b0bfa93 214 $archivefile = $this->get_content_file_location();
64f93798 215 return $packer->extract_to_storage($archivefile, $contextid, $component, $filearea, $itemid, $pathbase);
17d9269f 216 }
217
b1897a6d 218 /**
8496fdac
PS
219 * Add file/directory into archive.
220 *
d2b7803e 221 * @param file_archive $filearch file archive instance
c78a0558 222 * @param string $archivepath pathname in archive
b1897a6d 223 * @return bool success
224 */
0b0bfa93 225 public function archive_file(file_archive $filearch, $archivepath) {
b1897a6d 226 if ($this->is_directory()) {
0b0bfa93 227 return $filearch->add_directory($archivepath);
b1897a6d 228 } else {
229 $path = $this->get_content_file_location();
230 if (!is_readable($path)) {
231 return false;
232 }
0b0bfa93 233 return $filearch->add_file_from_pathname($archivepath, $path);
b1897a6d 234 }
235 }
236
797f19e8 237 /**
238 * Returns information about image,
239 * information is determined from the file content
d2b7803e 240 *
797f19e8 241 * @return mixed array with width, height and mimetype; false if not an image
242 */
243 public function get_imageinfo() {
244 if (!$imageinfo = getimagesize($this->get_content_file_location())) {
245 return false;
246 }
247 $image = array('width'=>$imageinfo[0], 'height'=>$imageinfo[1], 'mimetype'=>image_type_to_mime_type($imageinfo[2]));
248 if (empty($image['width']) or empty($image['height']) or empty($image['mimetype'])) {
249 // gd can not parse it, sorry
250 return false;
251 }
252 return $image;
253 }
254
255 /**
256 * Verifies the file is a valid web image - gif, png and jpeg only.
8496fdac 257 *
797f19e8 258 * It should be ok to serve this image from server without any other security workarounds.
8496fdac 259 *
797f19e8 260 * @return bool true if file ok
261 */
262 public function is_valid_image() {
263 $mimetype = $this->get_mimetype();
264 if ($mimetype !== 'image/gif' and $mimetype !== 'image/jpeg' and $mimetype !== 'image/png') {
265 return false;
266 }
267 if (!$info = $this->get_imageinfo()) {
268 return false;
269 }
270 if ($info['mimetype'] !== $mimetype) {
271 return false;
272 }
273 // ok, GD likes this image
274 return true;
275 }
276
4b6b5ce7 277 /**
8496fdac
PS
278 * Returns parent directory, creates missing parents if needed.
279 *
280 * @return stored_file
4b6b5ce7 281 */
282 public function get_parent_directory() {
283 if ($this->file_record->filepath === '/' and $this->file_record->filename === '.') {
284 //root dir does not have parent
285 return null;
286 }
287
288 if ($this->file_record->filename !== '.') {
64f93798 289 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 290 }
291
292 $filepath = $this->file_record->filepath;
293 $filepath = trim($filepath, '/');
294 $dirs = explode('/', $filepath);
295 array_pop($dirs);
296 $filepath = implode('/', $dirs);
297 $filepath = ($filepath === '') ? '/' : "/$filepath/";
298
64f93798 299 return $this->fs->create_directory($this->file_record->contextid, $this->file_record->component, $this->file_record->filearea, $this->file_record->itemid, $filepath);
4b6b5ce7 300 }
301
16a95e8f 302 /**
8496fdac
PS
303 * Returns context id of the file-
304 *
16a95e8f 305 * @return int context id
306 */
172dd12c 307 public function get_contextid() {
308 return $this->file_record->contextid;
309 }
310
16a95e8f 311 /**
64f93798
PS
312 * Returns component name - this is the owner of the areas,
313 * nothing else is allowed to read or modify the files directly!!
314 *
315 * @return string
316 */
317 public function get_component() {
318 return $this->file_record->component;
319 }
320
321 /**
322 * Returns file area name, this divides files of one component into groups with different access control.
323 * All files in one area have the same access control.
8496fdac 324 *
16a95e8f 325 * @return string
326 */
172dd12c 327 public function get_filearea() {
328 return $this->file_record->filearea;
329 }
330
16a95e8f 331 /**
8496fdac
PS
332 * Returns returns item id of file.
333 *
16a95e8f 334 * @return int
335 */
172dd12c 336 public function get_itemid() {
337 return $this->file_record->itemid;
338 }
339
16a95e8f 340 /**
341 * Returns file path - starts and ends with /, \ are not allowed.
8496fdac 342 *
16a95e8f 343 * @return string
344 */
172dd12c 345 public function get_filepath() {
346 return $this->file_record->filepath;
347 }
348
16a95e8f 349 /**
350 * Returns file name or '.' in case of directories.
8496fdac 351 *
16a95e8f 352 * @return string
353 */
172dd12c 354 public function get_filename() {
355 return $this->file_record->filename;
356 }
357
16a95e8f 358 /**
359 * Returns id of user who created the file.
8496fdac 360 *
16a95e8f 361 * @return int
362 */
172dd12c 363 public function get_userid() {
364 return $this->file_record->userid;
365 }
366
16a95e8f 367 /**
368 * Returns the size of file in bytes.
8496fdac 369 *
16a95e8f 370 * @return int bytes
371 */
172dd12c 372 public function get_filesize() {
373 return $this->file_record->filesize;
374 }
375
16a95e8f 376 /**
8496fdac
PS
377 * Returns mime type of file.
378 *
16a95e8f 379 * @return string
380 */
172dd12c 381 public function get_mimetype() {
382 return $this->file_record->mimetype;
383 }
384
16a95e8f 385 /**
8496fdac
PS
386 * Returns unix timestamp of file creation date.
387 *
16a95e8f 388 * @return int
389 */
172dd12c 390 public function get_timecreated() {
391 return $this->file_record->timecreated;
392 }
393
16a95e8f 394 /**
8496fdac
PS
395 * Returns unix timestamp of last file modification.
396 *
16a95e8f 397 * @return int
398 */
172dd12c 399 public function get_timemodified() {
400 return $this->file_record->timemodified;
401 }
6c0e2d08 402
16a95e8f 403 /**
8496fdac
PS
404 * Returns file status flag.
405 *
16a95e8f 406 * @return int 0 means file OK, anything else is a problem and file can not be used
407 */
6c0e2d08 408 public function get_status() {
409 return $this->file_record->status;
410 }
ee03a651 411
16a95e8f 412 /**
8496fdac
PS
413 * Returns file id.
414 *
16a95e8f 415 * @return int
416 */
ee03a651 417 public function get_id() {
418 return $this->file_record->id;
419 }
4284e1cc 420
16a95e8f 421 /**
8496fdac
PS
422 * Returns sha1 hash of file content.
423 *
16a95e8f 424 * @return string
425 */
4284e1cc 426 public function get_contenthash() {
427 return $this->file_record->contenthash;
428 }
6ed19c74 429
16a95e8f 430 /**
64f93798 431 * Returns sha1 hash of all file path components sha1("contextid/component/filearea/itemid/dir/dir/filename.ext").
8496fdac 432 *
16a95e8f 433 * @return string
434 */
6ed19c74 435 public function get_pathnamehash() {
436 return $this->file_record->pathnamehash;
437 }
1dce6261
DC
438
439 /**
8496fdac
PS
440 * Returns the license type of the file, it is a short name referred from license table.
441 *
1dce6261
DC
442 * @return string
443 */
444 public function get_license() {
445 return $this->file_record->license;
446 }
447
448 /**
8496fdac
PS
449 * Returns the author name of the file.
450 *
1dce6261
DC
451 * @return string
452 */
453 public function get_author() {
31cd5fe8 454 return $this->file_record->author;
1dce6261
DC
455 }
456
457 /**
8496fdac
PS
458 * Returns the source of the file, usually it is a url.
459 *
1dce6261
DC
460 * @return string
461 */
462 public function get_source() {
463 return $this->file_record->source;
464 }
8496fdac 465
f79321f1
DC
466 /**
467 * Returns the sort order of file
468 *
469 * @return int
470 */
471 public function get_sortorder() {
472 return $this->file_record->sortorder;
473 }
474}