ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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 
15 require_once("./Services/FileSystem/classes/class.ilFileData.php");
16 require_once("./Services/Utilities/classes/class.ilFileUtils.php");
17 
22 {
28  public $user_id;
29 
35  public $mail_path;
36 
41 
43  protected $tmpDirectory;
44 
46  protected $storageDirectory;
47 
49  protected $db;
50 
58  public function __construct($a_user_id = 0)
59  {
60  global $DIC;
61 
62  define('MAILPATH', 'mail');
63  parent::__construct();
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 
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 
315  ilUtil::moveUploadedFile(
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(
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 }
getAttachmentPathAndFilenameByMd5Hash(string $md5FileHash, int $mailId)
checkReadWrite()
check if directory is writable overwritten method from base class private
getAttachmentPath($a_filename, $a_mail_id)
get the path of a specific attachment
$rest
Definition: goto.php:46
$size
Definition: RandomTest.php:84
unlinkFile($a_filename)
unlink one uploaded file expects a filename e.g &#39;foo&#39;
$files
Definition: metarefresh.php:49
global $DIC
Definition: saml.php:7
__construct($a_user_id=0)
Constructor call base constructors checks if directory is writable and sets the optional user_id...
getMailPath()
get mail path public
Class ilFileDataMail.
deliverAttachmentsAsZip(string $basename, int $mailId, $files=[], $isDraft=false)
getPath()
get Path public
static getDir($a_dir, $a_rec=false, $a_sub_dir="")
get directory
saveFile($a_mail_id, $a_attachment)
save attachment file in a specific mail directory .../mail/<calculated_path>/mail_<mail_id>_<user_id>...
unlinkFiles($a_filenames)
unlink files: expects an array of filenames e.g.
static getASCIIFilename($a_filename)
convert utf8 to ascii filename
$stream
PHP stream implementation.
getAttachmentPathByMailId(int $mailId)
adoptAttachments($a_attachments, $a_mail_id)
adopt attachments (in case of forwarding a mail)
storeAsAttachment($a_filename, $a_content)
Store content as attachment.
checkFilesExist($a_files)
check if files exist
initDirectory()
init directory overwritten method public
static _sanitizeFilemame($a_filename)
getUserFilesData()
get all attachments of a specific user public
foreach($_POST as $key=> $value) $res
$a_content
Definition: workflow.php:93
This class handles all operations on files in directory /ilias_data/.
getAbsoluteAttachmentPoolPathByFilename(string $fileName)
Resolves a path for a passed filename in regards of a user&#39;s mail attachment pool, meaning attachments not being sent.
assignAttachmentsToDirectory($a_mail_id, $a_sent_mail_id)
assign attachments to mail directory
redirection script todo: (a better solution should control the processing via a xml file) ...
$query
static zip($a_dir, $a_file, $compress_content=false)
zips given directory/file into given zip.file
static _lookupDiskUsageOfUser($user_id)
Returns the number of bytes used on the harddisk for mail attachments, by the user with the specified...
$row
static ilTempnam($a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
static dirsize($directory)
get size of a directory or a file.
saveFiles($a_mail_id, array $a_attachments)
Saves all attachment files in a specific mail directory .../mail/<calculated_path>/mail_<mail_id>_<us...
copyAttachmentFile($a_abs_path, $a_new_name)
Copy files in mail directory.
static getDataDir()
get data directory (outside webspace)
global $ilDB
deassignAttachmentFromDirectory($a_mail_id)
dassign attachments from mail directory
$source
Definition: linkback.php:22
__deleteAttachmentDirectory($a_rel_path)
$target
Definition: test.php:19
static delDir($a_dir, $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
static getStorage($a_mail_id, $a_usr_id)
static getValidFilename($a_filename)
Get valid filename.
rotateFiles($a_path)
rotate files with same name recursive method