ILIAS  release_7 Revision v7.30-3-g800a261c036
class.ilFileDataMail.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4
14
15require_once("./Services/FileSystem/classes/class.ilFileData.php");
16require_once("./Services/Utilities/classes/class.ilFileUtils.php");
17
22{
28 public $user_id;
29
35 public $mail_path;
36
41
43 protected $tmpDirectory;
44
47
49 protected $db;
50
58 public function __construct($a_user_id = 0)
59 {
60 global $DIC;
61
62 if (!defined('MAILPATH')) {
63 define('MAILPATH', 'mail');
64 }
66 $this->mail_path = parent::getPath() . "/" . MAILPATH;
67 $this->checkReadWrite();
68 $this->user_id = $a_user_id;
69
70 $this->db = $DIC->database();
71 $this->tmpDirectory = $DIC->filesystem()->temp();
72 $this->storageDirectory = $DIC->filesystem()->storage();
73
75 }
76
83 public function initDirectory()
84 {
85 if (is_writable($this->getPath())) {
86 if (mkdir($this->getPath() . '/' . MAILPATH)) {
87 if (chmod($this->getPath() . '/' . MAILPATH, 0755)) {
88 $this->mail_path = $this->getPath() . '/' . MAILPATH;
89 return true;
90 }
91 }
92 }
93 return false;
94 }
95
99 public function getUploadLimit()
100 {
102 }
103
108 {
109 $max_size = $this->ilias->getSetting('mail_maxsize_attach', '');
110 if (!strlen($max_size)) {
111 return null;
112 }
113
114 return (float) $this->ilias->getSetting('mail_maxsize_attach', 0) * 1024;
115 }
116
122 public function getMailPath()
123 {
124 return $this->mail_path;
125 }
126
130 public function getAbsoluteAttachmentPoolPathPrefix() : string
131 {
132 return $this->mail_path . '/' . $this->user_id . '_';
133 }
134
141 public function getAttachmentPathAndFilenameByMd5Hash(string $md5FileHash, int $mailId) : array
142 {
143 $res = $this->db->queryF(
144 "SELECT path FROM mail_attachment WHERE mail_id = %s",
145 ['integer'],
146 [$mailId]
147 );
148
149 if (1 !== (int) $this->db->numRows($res)) {
150 throw new \OutOfBoundsException();
151 }
152
153 $row = $this->db->fetchAssoc($res);
154
155 $relativePath = $row['path'];
156 $path = $this->getMailPath() . '/' . $row['path'];
157
158 $files = ilUtil::getDir($path);
159 foreach ($files as $file) {
160 if ($file['type'] === 'file' && md5($file['entry']) === $md5FileHash) {
161 return [
162 'path' => $this->getMailPath() . '/' . $relativePath . '/' . $file['entry'],
163 'filename' => $file['entry']
164 ];
165 }
166 }
167
168 throw new \OutOfBoundsException();
169 }
170
175 private function getAttachmentPathByMailId(int $mailId) : string
176 {
177 $query = $this->db->query(
178 "SELECT path FROM mail_attachment WHERE mail_id = " . $this->db->quote($mailId, 'integer')
179 );
180
181 while ($row = $this->db->fetchObject($query)) {
182 return $row->path;
183 }
184
185 return '';
186 }
187
194 public function getAttachmentPath($a_filename, $a_mail_id)
195 {
196 $path = $this->getMailPath() . '/' . $this->getAttachmentPathByMailId($a_mail_id) . '/' . $a_filename;
197
198 if (file_exists($path) && is_readable($path)) {
199 return $path;
200 }
201
202 return '';
203 }
211 public function adoptAttachments($a_attachments, $a_mail_id)
212 {
213 if (is_array($a_attachments)) {
214 foreach ($a_attachments as $file) {
215 $path = $this->getAttachmentPath($file, $a_mail_id);
216 if (!copy($path, $this->getMailPath() . '/' . $this->user_id . '_' . $file)) {
217 return "ERROR: $this->getMailPath().'/'.$this->user_id.'_'.$file cannot be created";
218 }
219 }
220 } else {
221 return "ARRAY REQUIRED";
222 }
223 return '';
224 }
225
232 public function checkReadWrite()
233 {
234 if (is_writable($this->mail_path) && is_readable($this->mail_path)) {
235 return true;
236 } else {
237 $this->ilias->raiseError("Mail directory is not readable/writable by webserver: " . $this->mail_path, $this->ilias->error_obj->FATAL);
238 }
239 }
243 public function getUserFilesData() : array
244 {
245 return $this->getUnsentFiles();
246 }
247
251 private function getUnsentFiles() : array
252 {
253 $files = [];
254
255 $iter = new RegexIterator(new DirectoryIterator($this->mail_path), "/^{$this->user_id}_(.+)$/");
256 foreach ($iter as $file) {
258 if (!$file->isFile()) {
259 continue;
260 }
261
262 list($uid, $rest) = explode('_', $file->getFilename(), 2);
263 if ($uid === (string) $this->user_id) {
264 $files[] = [
265 'name' => $rest,
266 'size' => $file->getSize(),
267 'ctime' => $file->getCTime(),
268 ];
269 }
270 }
271
272 return $files;
273 }
274
281 public function storeAsAttachment($a_filename, $a_content)
282 {
283 if (strlen($a_content) >= $this->getUploadLimit()) {
284 return 1;
285 }
286
287 $name = ilUtil::_sanitizeFilemame($a_filename);
288 $this->rotateFiles($this->getMailPath() . '/' . $this->user_id . '_' . $name);
289
290 $abs_path = $this->getMailPath() . '/' . $this->user_id . '_' . $name;
291
292 if (!$fp = @fopen($abs_path, 'w+')) {
293 return false;
294 }
295 if (@fwrite($fp, $a_content) === false) {
296 @fclose($fp);
297 return false;
298 }
299 @fclose($fp);
300 return true;
301 }
302
306 public function storeUploadedFile($file)
307 {
308 $file['name'] = ilUtil::_sanitizeFilemame($file['name']);
309
310 $this->rotateFiles($this->getMailPath() . '/' . $this->user_id . '_' . $file['name']);
311
313 $file['tmp_name'],
314 $file['name'],
315 $this->getMailPath() . '/' . $this->user_id . '_' . $file['name']
316 );
317 }
318
325 public function copyAttachmentFile($a_abs_path, $a_new_name)
326 {
327 @copy($a_abs_path, $this->getMailPath() . "/" . $this->user_id . "_" . $a_new_name);
328
329 return true;
330 }
331
332
333
341 public function rotateFiles($a_path)
342 {
343 if (file_exists($a_path)) {
344 $this->rotateFiles($a_path . ".old");
345 return \ilFileUtils::rename($a_path, $a_path . '.old');
346 }
347 return true;
348 }
355 public function unlinkFiles($a_filenames)
356 {
357 if (is_array($a_filenames)) {
358 foreach ($a_filenames as $file) {
359 if (!$this->unlinkFile($file)) {
360 return $file;
361 }
362 }
363 }
364 return '';
365 }
372 public function unlinkFile($a_filename)
373 {
374 if (file_exists($this->mail_path . '/' . basename($this->user_id . '_' . $a_filename))) {
375 return unlink($this->mail_path . '/' . basename($this->user_id . '_' . $a_filename));
376 }
377 }
378
384 public function getAbsoluteAttachmentPoolPathByFilename(string $fileName) : string
385 {
386 return $this->getAbsoluteAttachmentPoolPathPrefix() . $fileName;
387 }
388
394 public function saveFiles($a_mail_id, array $a_attachments)
395 {
396 if (!is_numeric($a_mail_id) || $a_mail_id < 1) {
397 throw new InvalidArgumentException('The passed mail_id must be a valid integer!');
398 }
399
400 foreach ($a_attachments as $attachment) {
401 $this->saveFile($a_mail_id, $attachment);
402 }
403 }
404
410 public static function getStorage($a_mail_id, $a_usr_id) : \ilFSStorageMail
411 {
412 static $fsstorage_cache = array();
413
414 if (!is_object($fsstorage_cache[$a_mail_id][$a_usr_id])) {
415 include_once 'Services/Mail/classes/class.ilFSStorageMail.php';
416 $fsstorage_cache[$a_mail_id][$a_usr_id] = new ilFSStorageMail($a_mail_id, $a_usr_id);
417 }
418
419 return $fsstorage_cache[$a_mail_id][$a_usr_id];
420 }
421
429 public function saveFile($a_mail_id, $a_attachment)
430 {
431 $oStorage = self::getStorage($a_mail_id, $this->user_id);
432 $oStorage->create();
433 $storage_directory = $oStorage->getAbsolutePath();
434
435 if (@!is_dir($storage_directory)) {
436 return false;
437 }
438
439 return copy(
440 $this->mail_path . '/' . $this->user_id . '_' . $a_attachment,
441 $storage_directory . '/' . $a_attachment
442 );
443 }
450 public function checkFilesExist($a_files)
451 {
452 if ($a_files) {
453 foreach ($a_files as $file) {
454 if (!file_exists($this->mail_path . '/' . $this->user_id . '_' . $file)) {
455 return false;
456 }
457 }
458 return true;
459 }
460 return true;
461 }
469 public function assignAttachmentsToDirectory($a_mail_id, $a_sent_mail_id)
470 {
471 global $ilDB;
472
473 /* $query = "INSERT INTO mail_attachment ".
474 "SET mail_id = ".$ilDB->quote($a_mail_id).", ".
475 "path = ".$ilDB->quote($this->user_id."_".$a_sent_mail_id)." ";
476 $res = $this->ilias->db->query($query);
477 */
478
479 $oStorage = self::getStorage($a_sent_mail_id, $this->user_id);
480 $res = $ilDB->manipulateF(
481 '
482 INSERT INTO mail_attachment
483 ( mail_id, path) VALUES (%s, %s)',
484 array('integer', 'text'),
485 array($a_mail_id, $oStorage->getRelativePathExMailDirectory())
486 );
487 }
494 public function deassignAttachmentFromDirectory($a_mail_id)
495 {
496 global $ilDB;
497 // IF IT'S THE LAST MAIL CONTAINING THESE ATTACHMENTS => DELETE ATTACHMENTS
498 $res = $ilDB->query("SELECT path FROM mail_attachment
499 WHERE mail_id = " . $ilDB->quote($a_mail_id, 'integer'));
500
501 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
502 $path = $row->path;
503 }
504 if ($path) {
505 $res = $ilDB->query("SELECT COUNT(mail_id) count_mail_id FROM mail_attachment
506 WHERE path = " . $ilDB->quote($path, 'text')) ;
507
508 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
509 $cnt_mail_id = $row->count_mail_id;
510 }
511 if ($cnt_mail_id == 1) {
513 }
514 }
515
516 $res = $ilDB->manipulateF(
517 "DELETE FROM mail_attachment
518 WHERE mail_id = %s",
519 array('integer'),
520 array($a_mail_id)
521 );
522 return true;
523 }
524
525 public function __deleteAttachmentDirectory($a_rel_path)
526 {
527 ilUtil::delDir($this->mail_path . "/" . $a_rel_path);
528
529 return true;
530 }
531
535 protected function initAttachmentMaxUploadSize()
536 {
539 // Copy of ilFileInputGUI: begin
540 // get the value for the maximal uploadable filesize from the php.ini (if available)
541 $umf = ini_get("upload_max_filesize");
542 // get the value for the maximal post data from the php.ini (if available)
543 $pms = ini_get("post_max_size");
544
545 //convert from short-string representation to "real" bytes
546 $multiplier_a = array("K" => 1024, "M" => 1024 * 1024, "G" => 1024 * 1024 * 1024);
547
548 $umf_parts = preg_split("/(\d+)([K|G|M])/", $umf, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
549 $pms_parts = preg_split("/(\d+)([K|G|M])/", $pms, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
550
551 if (count($umf_parts) == 2) {
552 $umf = $umf_parts[0] * $multiplier_a[$umf_parts[1]];
553 }
554 if (count($pms_parts) == 2) {
555 $pms = $pms_parts[0] * $multiplier_a[$pms_parts[1]];
556 }
557
558 // use the smaller one as limit
559 $max_filesize = min($umf, $pms);
560
561 if (!$max_filesize) {
562 $max_filesize = max($umf, $pms);
563 }
564 // Copy of ilFileInputGUI: end
565
566 $this->mail_max_upload_file_size = $max_filesize;
567 }
568
577 public static function _lookupDiskUsageOfUser($user_id)
578 {
579 // XXX - This method is extremely slow. We should
580 // use a cache to speed it up, for example, we should
581 // store the disk space used in table mail_attachment.
582 global $DIC;
583
584 $mail_data_dir = ilUtil::getDataDir('filesystem') . DIRECTORY_SEPARATOR . "mail";
585
586 $q = "SELECT path " .
587 "FROM mail_attachment ma " .
588 "JOIN mail m ON ma.mail_id=m.mail_id " .
589 "WHERE m.user_id = " . $DIC->database()->quote($user_id);
590 $result_set = $DIC->database()->query($q);
591 $size = 0;
592 $count = 0;
593 while ($row = $result_set->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
594 $attachment_path = $mail_data_dir . DIRECTORY_SEPARATOR . $row['path'];
595 $attachment_size = ilUtil::dirsize($attachment_path);
596 if ($attachment_size != -1) {
597 $size += $attachment_size;
598 }
599 $count++;
600 }
601 return array('count' => $count, 'size' => $size);
602 }
603
607 public function onUserDelete()
608 {
612 global $ilDB;
613
614 // Delete uploaded mail files which are not attached to any message
615 try {
616 $iter = new RegexIterator(
617 new DirectoryIterator($this->getMailPath()),
618 '/^' . $this->user_id . '_/'
619 );
620 foreach ($iter as $file) {
625 if ($file->isFile()) {
626 @unlink($file->getPathname());
627 }
628 }
629 } catch (Exception $e) {
630 }
631
632 // Select all files attached to messages which are not shared (... = 1) with other messages anymore
633 $query = '
634 SELECT DISTINCT(ma1.path)
635 FROM mail_attachment ma1
636 INNER JOIN mail
637 ON mail.mail_id = ma1.mail_id
638 WHERE mail.user_id = %s
639 AND (SELECT COUNT(tmp.path) FROM mail_attachment tmp WHERE tmp.path = ma1.path) = 1
640 ';
641 $res = $ilDB->queryF(
642 $query,
643 array('integer'),
644 array($this->user_id)
645 );
646 while ($row = $ilDB->fetchAssoc($res)) {
647 try {
648 $path = $this->getMailPath() . DIRECTORY_SEPARATOR . $row['path'];
649 $iter = new RecursiveIteratorIterator(
650 new RecursiveDirectoryIterator($path),
651 RecursiveIteratorIterator::CHILD_FIRST
652 );
653 foreach ($iter as $file) {
658 if ($file->isDir()) {
659 @rmdir($file->getPathname());
660 } else {
661 @unlink($file->getPathname());
662 }
663 }
664 @rmdir($path);
665 } catch (Exception $e) {
666 }
667 }
668
669 // Delete each mail attachment rows assigned to a message of the deleted user.
670 $ilDB->manipulateF(
671 '
672 DELETE
673 FROM mail_attachment
674 WHERE EXISTS(
675 SELECT mail.mail_id
676 FROM mail
677 WHERE mail.user_id = %s AND mail.mail_id = mail_attachment.mail_id
678 )
679 ',
680 array('integer'),
681 array($this->user_id)
682 );
683 }
684
695 public function deliverAttachmentsAsZip(string $basename, int $mailId, $files = [], $isDraft = false)
696 {
697 $path = '';
698 if (!$isDraft) {
699 $path = $this->getAttachmentPathByMailId($mailId);
700 if (0 === strlen($path)) {
701 throw new \ilException('mail_download_zip_no_attachments');
702 }
703 }
704
705 $downloadFilename = \ilUtil::getASCIIFilename($basename);
706 if (0 === strlen($downloadFilename)) {
707 $downloadFilename = 'attachments';
708 }
709
710 $processingDirectory = \ilUtil::ilTempnam();
711 $relativeProcessingDirectory = basename($processingDirectory);
712
713 $absoluteZipDirectory = $processingDirectory . '/' . $downloadFilename;
714 $relativeZipDirectory = $relativeProcessingDirectory . '/' . $downloadFilename;
715
716 $this->tmpDirectory->createDir($relativeZipDirectory);
717
718 foreach ($files as $fileName) {
719 if ($isDraft) {
720 $source = str_replace(
721 $this->mail_path,
722 MAILPATH,
724 );
725 } else {
726 $source = MAILPATH . '/' . $path . '/' . $fileName;
727 }
728
729 $source = str_replace('//', '/', $source);
730 if (!$this->storageDirectory->has($source)) {
731 continue;
732 }
733
734 $target = $relativeZipDirectory . '/' . $fileName;
735
736 $stream = $this->storageDirectory->readStream($source);
737 $this->tmpDirectory->writeStream($target, $stream);
738 }
739
740 $pathToZipFile = $processingDirectory . '/' . $downloadFilename . '.zip';
741 \ilUtil::zip($absoluteZipDirectory, $pathToZipFile);
742
743 $this->tmpDirectory->deleteDir($relativeZipDirectory);
744
745 $delivery = new \ilFileDelivery($processingDirectory . '/' . $downloadFilename . '.zip');
746 $delivery->setDisposition(\ilFileDelivery::DISP_ATTACHMENT);
747 $delivery->setMimeType(\ilMimeTypeUtil::APPLICATION__ZIP);
748 $delivery->setConvertFileNameToAsci(true);
749 $delivery->setDownloadFileName(\ilFileUtils::getValidFilename($downloadFilename . '.zip'));
750 $delivery->setDeleteFile(true);
751
752 $delivery->deliver();
753 }
754}
$size
Definition: RandomTest.php:84
An exception for terminatinating execution or to throw for unit testing.
Class ilFileDataMail.
getAbsoluteAttachmentPoolPathByFilename(string $fileName)
Resolves a path for a passed filename in regards of a user's mail attachment pool,...
copyAttachmentFile($a_abs_path, $a_new_name)
Copy files in mail directory.
checkReadWrite()
check if directory is writable overwritten method from base class @access private
saveFile($a_mail_id, $a_attachment)
save attachment file in a specific mail directory .../mail/<calculated_path>/mail_<mail_id>_<user_id>...
deliverAttachmentsAsZip(string $basename, int $mailId, $files=[], $isDraft=false)
getAttachmentPath($a_filename, $a_mail_id)
get the path of a specific attachment
unlinkFiles($a_filenames)
unlink files: expects an array of filenames e.g.
assignAttachmentsToDirectory($a_mail_id, $a_sent_mail_id)
assign attachments to mail directory
static getStorage($a_mail_id, $a_usr_id)
static _lookupDiskUsageOfUser($user_id)
Returns the number of bytes used on the harddisk for mail attachments, by the user with the specified...
initDirectory()
init directory overwritten method @access public
adoptAttachments($a_attachments, $a_mail_id)
adopt attachments (in case of forwarding a mail)
unlinkFile($a_filename)
unlink one uploaded file expects a filename e.g 'foo'
getAttachmentPathAndFilenameByMd5Hash(string $md5FileHash, int $mailId)
__construct($a_user_id=0)
Constructor call base constructors checks if directory is writable and sets the optional user_id.
storeAsAttachment($a_filename, $a_content)
Store content as attachment.
rotateFiles($a_path)
rotate files with same name recursive method
getMailPath()
get mail path @access public
checkFilesExist($a_files)
check if files exist
getAttachmentPathByMailId(int $mailId)
deassignAttachmentFromDirectory($a_mail_id)
dassign attachments from mail directory
saveFiles($a_mail_id, array $a_attachments)
Saves all attachment files in a specific mail directory .../mail/<calculated_path>/mail_<mail_id>_<us...
__deleteAttachmentDirectory($a_rel_path)
This class handles all operations on files in directory /ilias_data/.
getPath()
get Path @access public
static getValidFilename($a_filename)
Get valid filename.
static getDataDir()
get data directory (outside webspace)
static moveUploadedFile($a_file, $a_name, $a_target, $a_raise_errors=true, $a_mode="move_uploaded")
move uploaded file
static delDir($a_dir, $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
static _sanitizeFilemame($a_filename)
static zip($a_dir, $a_file, $compress_content=false)
zips given directory/file into given zip.file
static getDir($a_dir, $a_rec=false, $a_sub_dir="")
get directory
static ilTempnam($a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
static getASCIIFilename($a_filename)
convert utf8 to ascii filename
static dirsize($directory)
get size of a directory or a file.
const MAILPATH
Definition: constants.php:48
global $DIC
Definition: goto.php:24
$rest
Definition: goto.php:48
Interface Filesystem The filesystem interface provides the public interface for the Filesystem servic...
Definition: Filesystem.php:22
if($format !==null) $name
Definition: metadata.php:230
$source
Definition: metadata.php:76
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
redirection script todo: (a better solution should control the processing via a xml file)
$query
foreach($_POST as $key=> $value) $res
global $ilDB