ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
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 define('MAILPATH', 'mail');
64 $this->mail_path = parent::getPath() . "/" . MAILPATH;
65 $this->checkReadWrite();
66 $this->user_id = $a_user_id;
67
68 $this->db = $DIC->database();
69 $this->tmpDirectory = $DIC->filesystem()->temp();
70 $this->storageDirectory = $DIC->filesystem()->storage();
71
73 }
74
81 public function initDirectory()
82 {
83 if (is_writable($this->getPath())) {
84 if (mkdir($this->getPath() . '/' . MAILPATH)) {
85 if (chmod($this->getPath() . '/' . MAILPATH, 0755)) {
86 $this->mail_path = $this->getPath() . '/' . MAILPATH;
87 return true;
88 }
89 }
90 }
91 return false;
92 }
93
97 public function getUploadLimit()
98 {
100 }
101
106 {
107 $max_size = $this->ilias->getSetting('mail_maxsize_attach', '');
108 if (!strlen($max_size)) {
109 return null;
110 }
111
112 return (float) $this->ilias->getSetting('mail_maxsize_attach', 0) * 1024;
113 }
114
120 public function getMailPath()
121 {
122 return $this->mail_path;
123 }
124
128 public function getAbsoluteAttachmentPoolPathPrefix() : string
129 {
130 return $this->mail_path . '/' . $this->user_id . '_';
131 }
132
139 public function getAttachmentPathAndFilenameByMd5Hash(string $md5FileHash, int $mailId) : array
140 {
141 $res = $this->db->queryF(
142 "SELECT path FROM mail_attachment WHERE mail_id = %s",
143 ['integer'],
144 [$mailId]
145 );
146
147 if (1 !== (int) $this->db->numRows($res)) {
148 throw new \OutOfBoundsException();
149 }
150
151 $row = $this->db->fetchAssoc($res);
152
153 $relativePath = $row['path'];
154 $path = $this->getMailPath() . '/' . $row['path'];
155
156 $files = ilUtil::getDir($path);
157 foreach ($files as $file) {
158 if ($file['type'] === 'file' && md5($file['entry']) === $md5FileHash) {
159 return [
160 'path' => $this->getMailPath() . '/' . $relativePath . '/' . $file['entry'],
161 'filename' => $file['entry']
162 ];
163 }
164 }
165
166 throw new \OutOfBoundsException();
167 }
168
173 private function getAttachmentPathByMailId(int $mailId) : string
174 {
175 $query = $this->db->query(
176 "SELECT path FROM mail_attachment WHERE mail_id = " . $this->db->quote($mailId, 'integer')
177 );
178
179 while ($row = $this->db->fetchObject($query)) {
180 return $row->path;
181 }
182
183 return '';
184 }
185
192 public function getAttachmentPath($a_filename, $a_mail_id)
193 {
194 $path = $this->getMailPath() . '/' . $this->getAttachmentPathByMailId($a_mail_id) . '/' . $a_filename;
195
196 if (file_exists($path) && is_readable($path)) {
197 return $path;
198 }
199
200 return '';
201 }
209 public function adoptAttachments($a_attachments, $a_mail_id)
210 {
211 if (is_array($a_attachments)) {
212 foreach ($a_attachments as $file) {
213 $path = $this->getAttachmentPath($file, $a_mail_id);
214 if (!copy($path, $this->getMailPath() . '/' . $this->user_id . '_' . $file)) {
215 return "ERROR: $this->getMailPath().'/'.$this->user_id.'_'.$file cannot be created";
216 }
217 }
218 } else {
219 return "ARRAY REQUIRED";
220 }
221 return '';
222 }
223
230 public function checkReadWrite()
231 {
232 if (is_writable($this->mail_path) && is_readable($this->mail_path)) {
233 return true;
234 } else {
235 $this->ilias->raiseError("Mail directory is not readable/writable by webserver: " . $this->mail_path, $this->ilias->error_obj->FATAL);
236 }
237 }
243 public function getUserFilesData()
244 {
245 return $this->getUnsentFiles();
246 }
247
254 private function getUnsentFiles()
255 {
256 $files = array();
257
258 $iter = new DirectoryIterator($this->mail_path);
259 foreach ($iter as $file) {
263 if ($file->isFile()) {
264 list($uid, $rest) = explode('_', $file->getFilename(), 2);
265 if ($uid == $this->user_id) {
266 $files[] = array(
267 'name' => $rest,
268 'size' => $file->getSize(),
269 'ctime' => $file->getCTime()
270 );
271 }
272 }
273 }
274
275 return $files;
276 }
277
284 public function storeAsAttachment($a_filename, $a_content)
285 {
286 if (strlen($a_content) >= $this->getUploadLimit()) {
287 return 1;
288 }
289
290 $name = ilUtil::_sanitizeFilemame($a_filename);
291 $this->rotateFiles($this->getMailPath() . '/' . $this->user_id . '_' . $name);
292
293 $abs_path = $this->getMailPath() . '/' . $this->user_id . '_' . $name;
294
295 if (!$fp = @fopen($abs_path, 'w+')) {
296 return false;
297 }
298 if (@fwrite($fp, $a_content) === false) {
299 @fclose($fp);
300 return false;
301 }
302 @fclose($fp);
303 return true;
304 }
305
309 public function storeUploadedFile($file)
310 {
311 $file['name'] = ilUtil::_sanitizeFilemame($file['name']);
312
313 $this->rotateFiles($this->getMailPath() . '/' . $this->user_id . '_' . $file['name']);
314
316 $file['tmp_name'],
317 $file['name'],
318 $this->getMailPath() . '/' . $this->user_id . '_' . $file['name']
319 );
320 }
321
328 public function copyAttachmentFile($a_abs_path, $a_new_name)
329 {
330 @copy($a_abs_path, $this->getMailPath() . "/" . $this->user_id . "_" . $a_new_name);
331
332 return true;
333 }
334
335
336
344 public function rotateFiles($a_path)
345 {
346 if (file_exists($a_path)) {
347 $this->rotateFiles($a_path . ".old");
348 return \ilFileUtils::rename($a_path, $a_path . '.old');
349 }
350 return true;
351 }
358 public function unlinkFiles($a_filenames)
359 {
360 if (is_array($a_filenames)) {
361 foreach ($a_filenames as $file) {
362 if (!$this->unlinkFile($file)) {
363 return $file;
364 }
365 }
366 }
367 return '';
368 }
375 public function unlinkFile($a_filename)
376 {
377 if (file_exists($this->mail_path . '/' . basename($this->user_id . '_' . $a_filename))) {
378 return unlink($this->mail_path . '/' . basename($this->user_id . '_' . $a_filename));
379 }
380 }
381
387 public function getAbsoluteAttachmentPoolPathByFilename(string $fileName) : string
388 {
389 return $this->getAbsoluteAttachmentPoolPathPrefix() . $fileName;
390 }
391
397 public function saveFiles($a_mail_id, array $a_attachments)
398 {
399 if (!is_numeric($a_mail_id) || $a_mail_id < 1) {
400 throw new InvalidArgumentException('The passed mail_id must be a valid integer!');
401 }
402
403 foreach ($a_attachments as $attachment) {
404 $this->saveFile($a_mail_id, $attachment);
405 }
406 }
407
413 public static function getStorage($a_mail_id, $a_usr_id) : \ilFSStorageMail
414 {
415 static $fsstorage_cache = array();
416
417 if (!is_object($fsstorage_cache[$a_mail_id][$a_usr_id])) {
418 include_once 'Services/Mail/classes/class.ilFSStorageMail.php';
419 $fsstorage_cache[$a_mail_id][$a_usr_id] = new ilFSStorageMail($a_mail_id, $a_usr_id);
420 }
421
422 return $fsstorage_cache[$a_mail_id][$a_usr_id];
423 }
424
432 public function saveFile($a_mail_id, $a_attachment)
433 {
434 $oStorage = self::getStorage($a_mail_id, $this->user_id);
435 $oStorage->create();
436 $storage_directory = $oStorage->getAbsolutePath();
437
438 if (@!is_dir($storage_directory)) {
439 return false;
440 }
441
442 return copy(
443 $this->mail_path . '/' . $this->user_id . '_' . $a_attachment,
444 $storage_directory . '/' . $a_attachment
445 );
446 }
453 public function checkFilesExist($a_files)
454 {
455 if ($a_files) {
456 foreach ($a_files as $file) {
457 if (!file_exists($this->mail_path . '/' . $this->user_id . '_' . $file)) {
458 return false;
459 }
460 }
461 return true;
462 }
463 return true;
464 }
472 public function assignAttachmentsToDirectory($a_mail_id, $a_sent_mail_id)
473 {
474 global $ilDB;
475
476 /* $query = "INSERT INTO mail_attachment ".
477 "SET mail_id = ".$ilDB->quote($a_mail_id).", ".
478 "path = ".$ilDB->quote($this->user_id."_".$a_sent_mail_id)." ";
479 $res = $this->ilias->db->query($query);
480 */
481
482 $oStorage = self::getStorage($a_sent_mail_id, $this->user_id);
483 $res = $ilDB->manipulateF(
484 '
485 INSERT INTO mail_attachment
486 ( mail_id, path) VALUES (%s, %s)',
487 array('integer', 'text'),
488 array($a_mail_id, $oStorage->getRelativePathExMailDirectory())
489 );
490 }
497 public function deassignAttachmentFromDirectory($a_mail_id)
498 {
499 global $ilDB;
500 // IF IT'S THE LAST MAIL CONTAINING THESE ATTACHMENTS => DELETE ATTACHMENTS
501 $res = $ilDB->query("SELECT path FROM mail_attachment
502 WHERE mail_id = " . $ilDB->quote($a_mail_id, 'integer'));
503
504 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
505 $path = $row->path;
506 }
507 if ($path) {
508 $res = $ilDB->query("SELECT COUNT(mail_id) count_mail_id FROM mail_attachment
509 WHERE path = " . $ilDB->quote($path, 'text')) ;
510
511 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
512 $cnt_mail_id = $row->count_mail_id;
513 }
514 if ($cnt_mail_id == 1) {
516 }
517 }
518
519 $res = $ilDB->manipulateF(
520 "DELETE FROM mail_attachment
521 WHERE mail_id = %s",
522 array('integer'),
523 array($a_mail_id)
524 );
525 return true;
526 }
527
528 public function __deleteAttachmentDirectory($a_rel_path)
529 {
530 ilUtil::delDir($this->mail_path . "/" . $a_rel_path);
531
532 return true;
533 }
534
538 protected function initAttachmentMaxUploadSize()
539 {
542 // Copy of ilFileInputGUI: begin
543 // get the value for the maximal uploadable filesize from the php.ini (if available)
544 $umf = ini_get("upload_max_filesize");
545 // get the value for the maximal post data from the php.ini (if available)
546 $pms = ini_get("post_max_size");
547
548 //convert from short-string representation to "real" bytes
549 $multiplier_a = array("K" => 1024, "M" => 1024 * 1024, "G" => 1024 * 1024 * 1024);
550
551 $umf_parts = preg_split("/(\d+)([K|G|M])/", $umf, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
552 $pms_parts = preg_split("/(\d+)([K|G|M])/", $pms, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
553
554 if (count($umf_parts) == 2) {
555 $umf = $umf_parts[0] * $multiplier_a[$umf_parts[1]];
556 }
557 if (count($pms_parts) == 2) {
558 $pms = $pms_parts[0] * $multiplier_a[$pms_parts[1]];
559 }
560
561 // use the smaller one as limit
562 $max_filesize = min($umf, $pms);
563
564 if (!$max_filesize) {
565 $max_filesize = max($umf, $pms);
566 }
567 // Copy of ilFileInputGUI: end
568
569 $this->mail_max_upload_file_size = $max_filesize;
570 }
571
580 public static function _lookupDiskUsageOfUser($user_id)
581 {
582 // XXX - This method is extremely slow. We should
583 // use a cache to speed it up, for example, we should
584 // store the disk space used in table mail_attachment.
585 global $DIC;
586
587 $mail_data_dir = ilUtil::getDataDir('filesystem') . DIRECTORY_SEPARATOR . "mail";
588
589 $q = "SELECT path " .
590 "FROM mail_attachment ma " .
591 "JOIN mail m ON ma.mail_id=m.mail_id " .
592 "WHERE m.user_id = " . $DIC->database()->quote($user_id);
593 $result_set = $DIC->database()->query($q);
594 $size = 0;
595 $count = 0;
596 while ($row = $result_set->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
597 $attachment_path = $mail_data_dir . DIRECTORY_SEPARATOR . $row['path'];
598 $attachment_size = ilUtil::dirsize($attachment_path);
599 if ($attachment_size != -1) {
600 $size += $attachment_size;
601 }
602 $count++;
603 }
604 return array('count' => $count, 'size' => $size);
605 }
606
610 public function onUserDelete()
611 {
615 global $ilDB;
616
617 // Delete uploaded mail files which are not attached to any message
618 try {
619 $iter = new RegexIterator(
620 new DirectoryIterator($this->getMailPath()),
621 '/^' . $this->user_id . '_/'
622 );
623 foreach ($iter as $file) {
628 if ($file->isFile()) {
629 @unlink($file->getPathname());
630 }
631 }
632 } catch (Exception $e) {
633 }
634
635 // Select all files attached to messages which are not shared (... = 1) with other messages anymore
636 $query = '
637 SELECT DISTINCT(ma1.path)
638 FROM mail_attachment ma1
639 INNER JOIN mail
640 ON mail.mail_id = ma1.mail_id
641 WHERE mail.user_id = %s
642 AND (SELECT COUNT(tmp.path) FROM mail_attachment tmp WHERE tmp.path = ma1.path) = 1
643 ';
644 $res = $ilDB->queryF(
645 $query,
646 array('integer'),
647 array($this->user_id)
648 );
649 while ($row = $ilDB->fetchAssoc($res)) {
650 try {
651 $path = $this->getMailPath() . DIRECTORY_SEPARATOR . $row['path'];
652 $iter = new RecursiveIteratorIterator(
653 new RecursiveDirectoryIterator($path),
654 RecursiveIteratorIterator::CHILD_FIRST
655 );
656 foreach ($iter as $file) {
661 if ($file->isDir()) {
662 @rmdir($file->getPathname());
663 } else {
664 @unlink($file->getPathname());
665 }
666 }
667 @rmdir($path);
668 } catch (Exception $e) {
669 }
670 }
671
672 // Delete each mail attachment rows assigned to a message of the deleted user.
673 $ilDB->manipulateF(
674 '
675 DELETE
676 FROM mail_attachment
677 WHERE EXISTS(
678 SELECT mail.mail_id
679 FROM mail
680 WHERE mail.user_id = %s AND mail.mail_id = mail_attachment.mail_id
681 )
682 ',
683 array('integer'),
684 array($this->user_id)
685 );
686 }
687
698 public function deliverAttachmentsAsZip(string $basename, int $mailId, $files = [], $isDraft = false)
699 {
700 $path = '';
701 if (!$isDraft) {
702 $path = $this->getAttachmentPathByMailId($mailId);
703 if (0 === strlen($path)) {
704 throw new \ilException('mail_download_zip_no_attachments');
705 }
706 }
707
708 $downloadFilename = \ilUtil::getASCIIFilename($basename);
709 if (0 === strlen($downloadFilename)) {
710 $downloadFilename = 'attachments';
711 }
712
713 $processingDirectory = \ilUtil::ilTempnam();
714 $relativeProcessingDirectory = basename($processingDirectory);
715
716 $absoluteZipDirectory = $processingDirectory . '/' . $downloadFilename;
717 $relativeZipDirectory = $relativeProcessingDirectory . '/' . $downloadFilename;
718
719 $this->tmpDirectory->createDir($relativeZipDirectory);
720
721 foreach ($files as $fileName) {
722 if ($isDraft) {
723 $source = str_replace(
724 $this->mail_path,
725 MAILPATH,
727 );
728 } else {
729 $source = MAILPATH . '/' . $path . '/' . $fileName;
730 }
731
732 $source = str_replace('//', '/', $source);
733 if (!$this->storageDirectory->has($source)) {
734 continue;
735 }
736
737 $target = $relativeZipDirectory . '/' . $fileName;
738
739 $stream = $this->storageDirectory->readStream($source);
740 $this->tmpDirectory->writeStream($target, $stream);
741 }
742
743 $pathToZipFile = $processingDirectory . '/' . $downloadFilename . '.zip';
744 \ilUtil::zip($absoluteZipDirectory, $pathToZipFile);
745
746 $this->tmpDirectory->deleteDir($relativeZipDirectory);
747
748 $delivery = new \ilFileDelivery($processingDirectory . '/' . $downloadFilename . '.zip');
749 $delivery->setDisposition(\ilFileDelivery::DISP_ATTACHMENT);
750 $delivery->setMimeType(\ilMimeTypeUtil::APPLICATION__ZIP);
751 $delivery->setConvertFileNameToAsci(true);
752 $delivery->setDownloadFileName(\ilFileUtils::getValidFilename($downloadFilename . '.zip'));
753 $delivery->setDeleteFile(true);
754
755 $delivery->deliver();
756 }
757}
$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
getUserFilesData()
get all attachments of a specific user @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.
$rest
Definition: goto.php:46
Interface Filesystem.
Definition: Filesystem.php:27
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
$a_content
Definition: workflow.php:93
$DIC
Definition: xapitoken.php:46