‪TYPO3CMS  10.4
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 DirectoryIterator;
21 use Psr\Log\LoggerInterface;
22 use Symfony\Component\Mailer\DelayedEnvelope;
23 use Symfony\Component\Mailer\Envelope;
24 use Symfony\Component\Mailer\Exception\TransportException;
25 use Symfony\Component\Mailer\SentMessage;
26 use Symfony\Component\Mailer\Transport\AbstractTransport;
27 use Symfony\Component\Mailer\Transport\TransportInterface;
28 use Symfony\Component\Mime\Email;
29 use Symfony\Component\Mime\Message;
30 use Symfony\Component\Mime\RawMessage;
31 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
33 
39 class ‪FileSpool extends AbstractTransport implements ‪DelayedTransportInterface
40 {
45  protected ‪$path;
46 
52  protected ‪$logger;
53 
59  protected ‪$retryLimit = 10;
60 
65  protected ‪$messageLimit;
66 
71  protected ‪$timeLimit;
72 
80  public function ‪__construct(
81  string ‪$path,
82  EventDispatcherInterface $dispatcher = null,
83  LoggerInterface ‪$logger = null
84  ) {
85  parent::__construct($dispatcher, ‪$logger);
86 
87  $this->path = ‪$path;
88  $this->logger = ‪$logger;
89 
90  if (!file_exists($this->path)) {
91  ‪GeneralUtility::mkdir_deep($this->path);
92  }
93  }
94 
99  protected function ‪doSend(SentMessage $message): void
100  {
101  $fileName = $this->path . '/' . $this->‪getRandomString(9);
102  $i = 0;
103 
104  // We try an exclusive creation of the file. This is an atomic
105  // operation, it avoids a locking mechanism
106  do {
107  $fileName .= $this->‪getRandomString(1);
108  $filePointer = @fopen($fileName . '.message', 'x');
109  } while ($filePointer === false && ++$i < $this->retryLimit);
110 
111  if ($filePointer === false) {
112  throw new TransportException('Could not create file for spooling', 1602615347);
113  }
114 
115  try {
116  $ser = serialize($message);
117  if (fwrite($filePointer, $ser) === false) {
118  throw new TransportException('Could not write file for spooling', 1602615348);
119  }
120  } finally {
121  fclose($filePointer);
122  }
123  }
124 
132  public function ‪setRetryLimit(int $limit): void
133  {
134  $this->retryLimit = $limit;
135  }
136 
142  public function ‪recover(int $timeout = 900): void
143  {
144  foreach (new DirectoryIterator($this->path) as $file) {
145  $file = (string)$file->getRealPath();
146 
147  if (substr($file, -16) == '.message.sending') {
148  $lockedtime = filectime($file);
149  if ((time() - $lockedtime) > $timeout) {
150  rename($file, substr($file, 0, -8));
151  }
152  }
153  }
154  }
155 
159  public function ‪flushQueue(TransportInterface $transport): int
160  {
161  $directoryIterator = new DirectoryIterator($this->path);
162 
163  $count = 0;
164  $time = time();
165  foreach ($directoryIterator as $file) {
166  $file = (string)$file->getRealPath();
167 
168  if (substr($file, -8) != '.message') {
169  continue;
170  }
171 
172  /* We try a rename, it's an atomic operation, and avoid locking the file */
173  if (rename($file, $file . '.sending')) {
174  $message = unserialize((string)file_get_contents($file . '.sending'), [
175  'allowedClasses' => [
176  RawMessage::class,
177  Message::class,
178  Email::class,
179  DelayedEnvelope::class,
180  Envelope::class,
181  ],
182  ]);
183 
184  $transport->send($message->getMessage(), $message->getEnvelope());
185  $count++;
186 
187  unlink($file . '.sending');
188  } else {
189  /* This message has just been caught by another process */
190  continue;
191  }
192 
193  if ($this->‪getMessageLimit() && $count >= $this->‪getMessageLimit()) {
194  break;
195  }
196 
197  if ($this->‪getTimeLimit() && (‪$GLOBALS['EXEC_TIME'] - $time) >= $this->‪getTimeLimit()) {
198  break;
199  }
200  }
201  return $count;
202  }
203 
211  protected function ‪getRandomString(int $count): string
212  {
213  // This string MUST stay FS safe, avoid special chars
214  $base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
215  $ret = '';
216  $strlen = strlen($base);
217  for ($i = 0; $i < $count; ++$i) {
218  $ret .= $base[((int)random_int(0, $strlen - 1))];
219  }
220 
221  return $ret;
222  }
223 
229  public function ‪setMessageLimit(int $limit): void
230  {
231  $this->messageLimit = (int)$limit;
232  }
233 
239  public function ‪getMessageLimit(): int
240  {
241  return ‪$this->messageLimit;
242  }
243 
249  public function ‪setTimeLimit(int $limit): void
250  {
251  $this->timeLimit = (int)$limit;
252  }
253 
259  public function ‪getTimeLimit(): int
260  {
261  return ‪$this->timeLimit;
262  }
263 
264  public function ‪__toString(): string
265  {
266  return 'FileSpool:' . ‪$this->path;
267  }
268 }
‪TYPO3\CMS\Core\Mail\FileSpool\doSend
‪doSend(SentMessage $message)
Definition: FileSpool.php:94
‪TYPO3\CMS\Core\Mail\FileSpool\getTimeLimit
‪int getTimeLimit()
Definition: FileSpool.php:254
‪TYPO3\CMS\Core\Mail\FileSpool\$path
‪string $path
Definition: FileSpool.php:44
‪TYPO3\CMS\Core\Mail\FileSpool\getRandomString
‪string getRandomString(int $count)
Definition: FileSpool.php:206
‪TYPO3\CMS\Core\Mail\FileSpool\recover
‪recover(int $timeout=900)
Definition: FileSpool.php:137
‪TYPO3\CMS\Core\Mail\FileSpool\$messageLimit
‪int $messageLimit
Definition: FileSpool.php:61
‪TYPO3\CMS\Core\Mail\FileSpool\__toString
‪__toString()
Definition: FileSpool.php:259
‪TYPO3\CMS\Core\Mail\FileSpool\$retryLimit
‪int $retryLimit
Definition: FileSpool.php:56
‪TYPO3\CMS\Core\Mail\FileSpool\$logger
‪LoggerInterface $logger
Definition: FileSpool.php:50
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep($directory)
Definition: GeneralUtility.php:2022
‪TYPO3\CMS\Core\Mail\FileSpool\__construct
‪__construct(string $path, EventDispatcherInterface $dispatcher=null, LoggerInterface $logger=null)
Definition: FileSpool.php:75
‪TYPO3\CMS\Core\Mail\FileSpool
Definition: FileSpool.php:40
‪TYPO3\CMS\Core\Mail\FileSpool\setTimeLimit
‪setTimeLimit(int $limit)
Definition: FileSpool.php:244
‪TYPO3\CMS\Core\Mail\DelayedTransportInterface
Definition: DelayedTransportInterface.php:26
‪TYPO3\CMS\Core\Mail\FileSpool\setRetryLimit
‪setRetryLimit(int $limit)
Definition: FileSpool.php:127
‪TYPO3\CMS\Core\Mail\FileSpool\flushQueue
‪flushQueue(TransportInterface $transport)
Definition: FileSpool.php:154
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Mail\FileSpool\$timeLimit
‪int $timeLimit
Definition: FileSpool.php:66
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Mail
Definition: DelayedTransportInterface.php:18
‪TYPO3\CMS\Core\Mail\FileSpool\setMessageLimit
‪setMessageLimit(int $limit)
Definition: FileSpool.php:224
‪TYPO3\CMS\Core\Mail\FileSpool\getMessageLimit
‪int getMessageLimit()
Definition: FileSpool.php:234