TYPO3  7.6
AbstractSmtpTransport.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of SwiftMailer.
5  * (c) 2004-2009 Chris Corbyn
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10 
17 {
19  protected $_buffer;
20 
22  protected $_started = false;
23 
25  protected $_domain = '[127.0.0.1]';
26 
28  protected $_eventDispatcher;
29 
31  protected $_sourceIp;
32 
34  abstract protected function _getBufferParams();
35 
43  {
44  $this->_eventDispatcher = $dispatcher;
45  $this->_buffer = $buf;
46  $this->_lookupHostname();
47  }
48 
62  public function setLocalDomain($domain)
63  {
64  $this->_domain = $domain;
65 
66  return $this;
67  }
68 
74  public function getLocalDomain()
75  {
76  return $this->_domain;
77  }
78 
84  public function setSourceIp($source)
85  {
86  $this->_sourceIp = $source;
87  }
88 
94  public function getSourceIp()
95  {
96  return $this->_sourceIp;
97  }
98 
102  public function start()
103  {
104  if (!$this->_started) {
105  if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) {
106  $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted');
107  if ($evt->bubbleCancelled()) {
108  return;
109  }
110  }
111 
112  try {
113  $this->_buffer->initialize($this->_getBufferParams());
114  } catch (Swift_TransportException $e) {
115  $this->_throwException($e);
116  }
117  $this->_readGreeting();
118  $this->_doHeloCommand();
119 
120  if ($evt) {
121  $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted');
122  }
123 
124  $this->_started = true;
125  }
126  }
127 
133  public function isStarted()
134  {
135  return $this->_started;
136  }
137 
149  public function send(Swift_Mime_Message $message, &$failedRecipients = null)
150  {
151  $sent = 0;
152  $failedRecipients = (array) $failedRecipients;
153 
154  if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) {
155  $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
156  if ($evt->bubbleCancelled()) {
157  return 0;
158  }
159  }
160 
161  if (!$reversePath = $this->_getReversePath($message)) {
163  'Cannot send message without a sender address'
164  )
165  );
166  }
167 
168  $to = (array) $message->getTo();
169  $cc = (array) $message->getCc();
170  $tos = array_merge($to, $cc);
171  $bcc = (array) $message->getBcc();
172 
173  $message->setBcc(array());
174 
175  try {
176  $sent += $this->_sendTo($message, $reversePath, $tos, $failedRecipients);
177  $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients);
178  } catch (Exception $e) {
179  $message->setBcc($bcc);
180  throw $e;
181  }
182 
183  $message->setBcc($bcc);
184 
185  if ($evt) {
186  if ($sent == count($to) + count($cc) + count($bcc)) {
188  } elseif ($sent > 0) {
190  } else {
191  $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
192  }
193  $evt->setFailedRecipients($failedRecipients);
194  $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
195  }
196 
197  $message->generateId(); //Make sure a new Message ID is used
198 
199  return $sent;
200  }
201 
205  public function stop()
206  {
207  if ($this->_started) {
208  if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) {
209  $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped');
210  if ($evt->bubbleCancelled()) {
211  return;
212  }
213  }
214 
215  try {
216  $this->executeCommand("QUIT\r\n", array(221));
217  } catch (Swift_TransportException $e) {
218  }
219 
220  try {
221  $this->_buffer->terminate();
222 
223  if ($evt) {
224  $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped');
225  }
226  } catch (Swift_TransportException $e) {
227  $this->_throwException($e);
228  }
229  }
230  $this->_started = false;
231  }
232 
239  {
240  $this->_eventDispatcher->bindEventListener($plugin);
241  }
242 
246  public function reset()
247  {
248  $this->executeCommand("RSET\r\n", array(250));
249  }
250 
256  public function getBuffer()
257  {
258  return $this->_buffer;
259  }
260 
273  public function executeCommand($command, $codes = array(), &$failures = null)
274  {
275  $failures = (array) $failures;
276  $seq = $this->_buffer->write($command);
277  $response = $this->_getFullResponse($seq);
278  if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)) {
279  $this->_eventDispatcher->dispatchEvent($evt, 'commandSent');
280  }
281  $this->_assertResponseCode($response, $codes);
282 
283  return $response;
284  }
285 
287  protected function _readGreeting()
288  {
289  $this->_assertResponseCode($this->_getFullResponse(0), array(220));
290  }
291 
293  protected function _doHeloCommand()
294  {
295  $this->executeCommand(
296  sprintf("HELO %s\r\n", $this->_domain), array(250)
297  );
298  }
299 
301  protected function _doMailFromCommand($address)
302  {
303  $this->executeCommand(
304  sprintf("MAIL FROM:<%s>\r\n", $address), array(250)
305  );
306  }
307 
309  protected function _doRcptToCommand($address)
310  {
311  $this->executeCommand(
312  sprintf("RCPT TO:<%s>\r\n", $address), array(250, 251, 252)
313  );
314  }
315 
317  protected function _doDataCommand()
318  {
319  $this->executeCommand("DATA\r\n", array(354));
320  }
321 
323  protected function _streamMessage(Swift_Mime_Message $message)
324  {
325  $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n.."));
326  try {
327  $message->toByteStream($this->_buffer);
328  $this->_buffer->flushBuffers();
329  } catch (Swift_TransportException $e) {
330  $this->_throwException($e);
331  }
332  $this->_buffer->setWriteTranslations(array());
333  $this->executeCommand("\r\n.\r\n", array(250));
334  }
335 
337  protected function _getReversePath(Swift_Mime_Message $message)
338  {
339  $return = $message->getReturnPath();
340  $sender = $message->getSender();
341  $from = $message->getFrom();
342  $path = null;
343  if (!empty($return)) {
344  $path = $return;
345  } elseif (!empty($sender)) {
346  // Don't use array_keys
347  reset($sender); // Reset Pointer to first pos
348  $path = key($sender); // Get key
349  } elseif (!empty($from)) {
350  reset($from); // Reset Pointer to first pos
351  $path = key($from); // Get key
352  }
353 
354  return $path;
355  }
356 
359  {
360  if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) {
361  $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
362  if (!$evt->bubbleCancelled()) {
363  throw $e;
364  }
365  } else {
366  throw $e;
367  }
368  }
369 
371  protected function _assertResponseCode($response, $wanted)
372  {
373  list($code) = sscanf($response, '%3d');
374  $valid = (empty($wanted) || in_array($code, $wanted));
375 
376  if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response,
377  $valid)) {
378  $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived');
379  }
380 
381  if (!$valid) {
382  $this->_throwException(
384  'Expected response code '.implode('/', $wanted).' but got code '.
385  '"'.$code.'", with message "'.$response.'"',
386  $code)
387  );
388  }
389  }
390 
392  protected function _getFullResponse($seq)
393  {
394  $response = '';
395  try {
396  do {
397  $line = $this->_buffer->readLine($seq);
398  $response .= $line;
399  } while (null !== $line && false !== $line && ' ' != $line{3});
400  } catch (Swift_TransportException $e) {
401  $this->_throwException($e);
402  } catch (Swift_IoException $e) {
403  $this->_throwException(
405  $e->getMessage())
406  );
407  }
408 
409  return $response;
410  }
411 
413  private function _doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients)
414  {
415  $sent = 0;
416  $this->_doMailFromCommand($reversePath);
417  foreach ($recipients as $forwardPath) {
418  try {
419  $this->_doRcptToCommand($forwardPath);
420  $sent++;
421  } catch (Swift_TransportException $e) {
422  $failedRecipients[] = $forwardPath;
423  }
424  }
425 
426  if ($sent != 0) {
427  $this->_doDataCommand();
428  $this->_streamMessage($message);
429  } else {
430  $this->reset();
431  }
432 
433  return $sent;
434  }
435 
437  private function _sendTo(Swift_Mime_Message $message, $reversePath, array $to, array &$failedRecipients)
438  {
439  if (empty($to)) {
440  return 0;
441  }
442 
443  return $this->_doMailTransaction($message, $reversePath, array_keys($to),
444  $failedRecipients);
445  }
446 
448  private function _sendBcc(Swift_Mime_Message $message, $reversePath, array $bcc, array &$failedRecipients)
449  {
450  $sent = 0;
451  foreach ($bcc as $forwardPath => $name) {
452  $message->setBcc(array($forwardPath => $name));
453  $sent += $this->_doMailTransaction(
454  $message, $reversePath, array($forwardPath), $failedRecipients
455  );
456  }
457 
458  return $sent;
459  }
460 
462  private function _lookupHostname()
463  {
464  if (!empty($_SERVER['SERVER_NAME'])
465  && $this->_isFqdn($_SERVER['SERVER_NAME'])) {
466  $this->_domain = $_SERVER['SERVER_NAME'];
467  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
468  $this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']);
469  }
470  }
471 
473  private function _isFqdn($hostname)
474  {
475  // We could do a really thorough check, but there's really no point
476  if (false !== $dotPos = strpos($hostname, '.')) {
477  return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1);
478  } else {
479  return false;
480  }
481  }
482 
486  public function __destruct()
487  {
488  $this->stop();
489  }
490 }