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 
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  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 }
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:48
$size
Definition: RandomTest.php:84
unlinkFile($a_filename)
unlink one uploaded file expects a filename e.g &#39;foo&#39;
__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
const MAILPATH
Definition: constants.php:48
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
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
if($format !==null) $name
Definition: metadata.php:230
initDirectory()
init directory overwritten method public
static _sanitizeFilemame($a_filename)
foreach($_POST as $key=> $value) $res
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
static moveUploadedFile($a_file, $a_name, $a_target, $a_raise_errors=true, $a_mode="move_uploaded")
move uploaded file
global $DIC
Definition: goto.php:24
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...
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)
__construct(Container $dic, ilPlugin $plugin)
global $ilDB
deassignAttachmentFromDirectory($a_mail_id)
dassign attachments from mail directory
__deleteAttachmentDirectory($a_rel_path)
static delDir($a_dir, $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
$source
Definition: metadata.php:76
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