‪TYPO3CMS  ‪main
FileSpool.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
18 namespace ‪TYPO3\CMS\Core\Mail;
19 
20 use Psr\Log\LoggerInterface;
21 use Symfony\Component\Mailer\DelayedEnvelope;
22 use Symfony\Component\Mailer\Envelope;
23 use Symfony\Component\Mailer\Exception\TransportException;
24 use Symfony\Component\Mailer\SentMessage;
25 use Symfony\Component\Mailer\Transport\AbstractTransport;
26 use Symfony\Component\Mailer\Transport\TransportInterface;
27 use Symfony\Component\Mime\Email;
28 use Symfony\Component\Mime\Message;
29 use Symfony\Component\Mime\RawMessage;
30 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
32 
38 class ‪FileSpool extends AbstractTransport implements ‪DelayedTransportInterface
39 {
43  protected int ‪$retryLimit = 10;
44 
48  protected int ‪$messageLimit;
49 
53  protected int ‪$timeLimit;
54 
58  public function ‪__construct(
59  protected string $path,
60  ?EventDispatcherInterface $dispatcher = null,
61  protected readonly ?LoggerInterface $logger = null
62  ) {
63  parent::__construct($dispatcher, $logger);
64 
65  if (!file_exists($this->path)) {
66  ‪GeneralUtility::mkdir_deep($this->path);
67  }
68  }
69 
73  protected function ‪doSend(SentMessage $message): void
74  {
75  $fileName = $this->path . '/' . $this->‪getRandomString(9);
76  $i = 0;
77 
78  // We try an exclusive creation of the file. This is an atomic
79  // operation, it avoids a locking mechanism
80  do {
81  $fileName .= $this->‪getRandomString(1);
82  $filePointer = @fopen($fileName . '.message', 'x');
83  } while ($filePointer === false && ++$i < $this->retryLimit);
84 
85  if ($filePointer === false) {
86  throw new TransportException('Could not create file for spooling', 1602615347);
87  }
88 
89  try {
90  $ser = serialize($message);
91  if (fwrite($filePointer, $ser) === false) {
92  throw new TransportException('Could not write file for spooling', 1602615348);
93  }
94  } finally {
95  fclose($filePointer);
96  }
97  }
98 
104  public function ‪setRetryLimit(int $limit): void
105  {
106  $this->retryLimit = $limit;
107  }
108 
114  public function ‪recover(int $timeout = 900): void
115  {
116  foreach (new \DirectoryIterator($this->path) as $file) {
117  $file = (string)$file->getRealPath();
118 
119  if (str_ends_with($file, '.message.sending')) {
120  $lockedtime = filectime($file);
121  if ((time() - $lockedtime) > $timeout) {
122  rename($file, substr($file, 0, -8));
123  }
124  }
125  }
126  }
127 
128  public function ‪flushQueue(TransportInterface $transport): int
129  {
130  $directoryIterator = new \DirectoryIterator($this->path);
131 
132  $count = 0;
133  $time = time();
134  foreach ($directoryIterator as $file) {
135  $file = (string)$file->getRealPath();
136 
137  if (!str_ends_with($file, '.message')) {
138  continue;
139  }
140 
141  /* We try a rename, it's an atomic operation, and avoid locking the file */
142  if (rename($file, $file . '.sending')) {
143  $message = unserialize((string)file_get_contents($file . '.sending'), [
144  'allowedClasses' => [
145  RawMessage::class,
146  Message::class,
147  Email::class,
148  DelayedEnvelope::class,
149  Envelope::class,
150  ],
151  ]);
152 
153  $transport->send($message->getMessage(), $message->getEnvelope());
154  $count++;
155 
156  unlink($file . '.sending');
157  } else {
158  /* This message has just been caught by another process */
159  continue;
160  }
161 
162  if ($this->‪getMessageLimit() && $count >= $this->‪getMessageLimit()) {
163  break;
164  }
165 
166  if ($this->‪getTimeLimit() && (‪$GLOBALS['EXEC_TIME'] - $time) >= $this->‪getTimeLimit()) {
167  break;
168  }
169  }
170  return $count;
171  }
172 
176  protected function ‪getRandomString(int $count): string
177  {
178  // This string MUST stay FS safe, avoid special chars
179  $base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
180  $ret = '';
181  $strlen = strlen($base);
182  for ($i = 0; $i < $count; ++$i) {
183  $ret .= $base[random_int(0, $strlen - 1)];
184  }
185 
186  return $ret;
187  }
188 
192  public function ‪setMessageLimit(int $limit): void
193  {
194  $this->messageLimit = $limit;
195  }
196 
200  public function ‪getMessageLimit(): int
201  {
202  return ‪$this->messageLimit;
203  }
204 
208  public function ‪setTimeLimit(int $limit): void
209  {
210  $this->timeLimit = $limit;
211  }
212 
216  public function ‪getTimeLimit(): int
217  {
218  return ‪$this->timeLimit;
219  }
220 
221  public function ‪__toString(): string
222  {
223  return 'FileSpool:' . $this->path;
224  }
225 }
‪TYPO3\CMS\Core\Mail\FileSpool\doSend
‪doSend(SentMessage $message)
Definition: FileSpool.php:73
‪TYPO3\CMS\Core\Mail\FileSpool\getMessageLimit
‪getMessageLimit()
Definition: FileSpool.php:200
‪TYPO3\CMS\Core\Mail\FileSpool\recover
‪recover(int $timeout=900)
Definition: FileSpool.php:114
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep(string $directory)
Definition: GeneralUtility.php:1654
‪TYPO3\CMS\Core\Mail\FileSpool\$messageLimit
‪int $messageLimit
Definition: FileSpool.php:48
‪TYPO3\CMS\Core\Mail\FileSpool\__toString
‪__toString()
Definition: FileSpool.php:221
‪TYPO3\CMS\Core\Mail\FileSpool\getRandomString
‪getRandomString(int $count)
Definition: FileSpool.php:176
‪TYPO3\CMS\Core\Mail\FileSpool\__construct
‪__construct(protected string $path, ?EventDispatcherInterface $dispatcher=null, protected readonly ?LoggerInterface $logger=null)
Definition: FileSpool.php:58
‪TYPO3\CMS\Core\Mail\FileSpool\$retryLimit
‪int $retryLimit
Definition: FileSpool.php:43
‪TYPO3\CMS\Core\Mail\FileSpool
Definition: FileSpool.php:39
‪TYPO3\CMS\Core\Mail\FileSpool\setTimeLimit
‪setTimeLimit(int $limit)
Definition: FileSpool.php:208
‪TYPO3\CMS\Core\Mail\DelayedTransportInterface
Definition: DelayedTransportInterface.php:26
‪TYPO3\CMS\Core\Mail\FileSpool\getTimeLimit
‪getTimeLimit()
Definition: FileSpool.php:216
‪TYPO3\CMS\Core\Mail\FileSpool\setRetryLimit
‪setRetryLimit(int $limit)
Definition: FileSpool.php:104
‪TYPO3\CMS\Core\Mail\FileSpool\flushQueue
‪flushQueue(TransportInterface $transport)
Definition: FileSpool.php:128
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Mail\FileSpool\$timeLimit
‪int $timeLimit
Definition: FileSpool.php:53
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Mail
Definition: DelayedTransportInterface.php:18
‪TYPO3\CMS\Core\Mail\FileSpool\setMessageLimit
‪setMessageLimit(int $limit)
Definition: FileSpool.php:192