* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Stores Messages on the filesystem. * @package Swift * @author Fabien Potencier * @author Xavier De Cock */ class Swift_FileSpool extends Swift_ConfigurableSpool { /** The spool directory */ private $_path; /** * File WriteRetry Limit * @var int */ private $_retryLimit=10; /** * Create a new FileSpool. * @param string $path * @throws Swift_IoException */ public function __construct($path) { $this->_path = $path; if (!file_exists($this->_path)) { if (!mkdir($this->_path, 0777, true)) { throw new Swift_IoException('Unable to create Path ['.$this->_path.']'); } } } /** * Tests if this Spool mechanism has started. * * @return boolean */ public function isStarted() { return true; } /** * Starts this Spool mechanism. */ public function start() { } /** * Stops this Spool mechanism. */ public function stop() { } /** * Allow to manage the enqueuing retry limit. * Default, is ten and allows over 64^20 different fileNames * * @param integer $limit */ public function setRetryLimit($limit) { $this->_retryLimit=$limit; } /** * Queues a message. * @param Swift_Mime_Message $message The message to store * @return boolean * @throws Swift_IoException */ public function queueMessage(Swift_Mime_Message $message) { $ser = serialize($message); $fileName=$this->_path.'/'.$this->getRandomString(10); for ($i = 0; $i < $this->_retryLimit; ++$i) { /* We try an exclusive creation of the file * This is an atomic operation, it avoid locking mechanism */ $fp = @fopen($fileName.'.message', 'x'); if (false !== $fp) { if (false === fwrite($fp, $ser)) { return false; } return fclose($fp); } else { /* The file allready exists, we try a longer fileName */ $fileName.=$this->getRandomString(1); } } throw new Swift_IoException('Unable to create a file for enqueuing Message'); } /** * Execute a recovery if for anyreason a process is sending for too long * * @param int $timeout in second Defaults is for very slow smtp responses */ public function recover($timeout=900) { foreach (new DirectoryIterator($this->_path) as $file) { $file = $file->getRealPath(); if (substr($file, -16)=='.message.sending') { $lockedtime=filectime($file); if ((time()-$lockedtime)>$timeout) { rename($file, substr($file, 0, -8)); } } } } /** * Sends messages using the given transport instance. * * @param Swift_Transport $transport A transport instance * @param string[] &$failedRecipients An array of failures by-reference * * @return int The number of sent emails */ public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) { if (!$transport->isStarted()) { $transport->start(); } $failedRecipients = (array) $failedRecipients; $count = 0; $time = time(); foreach (new DirectoryIterator($this->_path) as $file) { $file = $file->getRealPath(); if (substr($file, -8) != '.message') { continue; } /* We try a rename, it's an atomic operation, and avoid locking the file */ if (rename($file, $file.'.sending')) { $message = unserialize(file_get_contents($file.'.sending')); $count += $transport->send($message, $failedRecipients); unlink($file.'.sending'); } else { /* This message has just been catched by another process */ continue; } if ($this->getMessageLimit() && $count >= $this->getMessageLimit()) { break; } if ($this->getTimeLimit() && (time() - $time) >= $this->getTimeLimit()) { break; } } return $count; } /** * Returns a random string needed to generate a fileName for the queue. * @param int $count */ protected function getRandomString($count) { // This string MUST stay FS safe, avoid special chars $base="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-."; $ret=''; $strlen=strlen($base); for ($i=0; $i<$count; ++$i) { $ret.=$base[((int)rand(0,$strlen-1))]; } return $ret; } }