XOOPS RMCommon Utilities  2.1.8.91RC
 All Classes Namespaces Files Functions Variables
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 
11 //@require 'Swift/Transport.php';
12 //@require 'Swift/Transport/IoBuffer.php';
13 //@require 'Swift/Transport/CommandSentException.php';
14 //@require 'Swift/TransportException.php';
15 //@require 'Swift/Mime/Message.php';
16 //@require 'Swift/Events/EventDispatcher.php';
17 //@require 'Swift/Events/EventListener.php';
18 
27  implements Swift_Transport
28 {
29 
31  protected $_buffer;
32 
34  protected $_started = false;
35 
37  protected $_domain = '[127.0.0.1]';
38 
40  protected $_eventDispatcher;
41 
43  abstract protected function _getBufferParams();
44 
51  public function __construct(Swift_Transport_IoBuffer $buf,
52  Swift_Events_EventDispatcher $dispatcher)
53  {
54  $this->_eventDispatcher = $dispatcher;
55  $this->_buffer = $buf;
56  $this->_lookupHostname();
57  }
58 
67  public function setLocalDomain($domain)
68  {
69  $this->_domain = $domain;
70  return $this;
71  }
72 
78  public function getLocalDomain()
79  {
80  return $this->_domain;
81  }
82 
86  public function start()
87  {
88  if (!$this->_started)
89  {
90  if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this))
91  {
92  $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted');
93  if ($evt->bubbleCancelled())
94  {
95  return;
96  }
97  }
98 
99  try
100  {
101  $this->_buffer->initialize($this->_getBufferParams());
102  }
103  catch (Swift_TransportException $e)
104  {
105  $this->_throwException($e);
106  }
107  $this->_readGreeting();
108  $this->_doHeloCommand();
109 
110  if ($evt)
111  {
112  $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted');
113  }
114 
115  $this->_started = true;
116  }
117  }
118 
124  public function isStarted()
125  {
126  return $this->_started;
127  }
128 
139  public function send(Swift_Mime_Message $message, &$failedRecipients = null)
140  {
141  $sent = 0;
142  $failedRecipients = (array) $failedRecipients;
143 
144  if (!$reversePath = $this->_getReversePath($message))
145  {
146  throw new Swift_TransportException(
147  'Cannot send message without a sender address'
148  );
149  }
150 
151  if ($evt = $this->_eventDispatcher->createSendEvent($this, $message))
152  {
153  $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
154  if ($evt->bubbleCancelled())
155  {
156  return 0;
157  }
158  }
159 
160  $to = (array) $message->getTo();
161  $cc = (array) $message->getCc();
162  $bcc = (array) $message->getBcc();
163 
164  $message->setBcc(array());
165 
166  try
167  {
168  $sent += $this->_sendTo($message, $reversePath, $to, $failedRecipients);
169  $sent += $this->_sendCc($message, $reversePath, $cc, $failedRecipients);
170  $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients);
171  }
172  catch (Exception $e)
173  {
174  $message->setBcc($bcc);
175  throw $e;
176  }
177 
178  $message->setBcc($bcc);
179 
180  if ($evt)
181  {
182  if ($sent == count($to) + count($cc) + count($bcc))
183  {
185  }
186  elseif ($sent > 0)
187  {
189  }
190  else
191  {
192  $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
193  }
194  $evt->setFailedRecipients($failedRecipients);
195  $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
196  }
197 
198  $message->generateId(); //Make sure a new Message ID is used
199 
200  return $sent;
201  }
202 
206  public function stop()
207  {
208  if ($this->_started)
209  {
210  if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this))
211  {
212  $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped');
213  if ($evt->bubbleCancelled())
214  {
215  return;
216  }
217  }
218 
219  try
220  {
221  $this->executeCommand("QUIT\r\n", array(221));
222  }
223  catch (Swift_TransportException $e) {}
224 
225  try
226  {
227  $this->_buffer->terminate();
228 
229  if ($evt)
230  {
231  $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped');
232  }
233  }
234  catch (Swift_TransportException $e)
235  {
236  $this->_throwException($e);
237  }
238  }
239  $this->_started = false;
240  }
241 
248  {
249  $this->_eventDispatcher->bindEventListener($plugin);
250  }
251 
255  public function reset()
256  {
257  $this->executeCommand("RSET\r\n", array(250));
258  }
259 
265  public function getBuffer()
266  {
267  return $this->_buffer;
268  }
269 
281  public function executeCommand($command, $codes = array(), &$failures = null)
282  {
283  $failures = (array) $failures;
284  $seq = $this->_buffer->write($command);
285  $response = $this->_getFullResponse($seq);
286  if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes))
287  {
288  $this->_eventDispatcher->dispatchEvent($evt, 'commandSent');
289  }
290  $this->_assertResponseCode($response, $codes);
291  return $response;
292  }
293 
294  // -- Protected methods
295 
297  protected function _readGreeting()
298  {
299  $this->_assertResponseCode($this->_getFullResponse(0), array(220));
300  }
301 
303  protected function _doHeloCommand()
304  {
305  $this->executeCommand(
306  sprintf("HELO %s\r\n", $this->_domain), array(250)
307  );
308  }
309 
311  protected function _doMailFromCommand($address)
312  {
313  $this->executeCommand(
314  sprintf("MAIL FROM: <%s>\r\n", $address), array(250)
315  );
316  }
317 
319  protected function _doRcptToCommand($address)
320  {
321  $this->executeCommand(
322  sprintf("RCPT TO: <%s>\r\n", $address), array(250, 251, 252)
323  );
324  }
325 
327  protected function _doDataCommand()
328  {
329  $this->executeCommand("DATA\r\n", array(354));
330  }
331 
333  protected function _streamMessage(Swift_Mime_Message $message)
334  {
335  $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n.."));
336  try
337  {
338  $message->toByteStream($this->_buffer);
339  $this->_buffer->flushBuffers();
340  }
341  catch (Swift_TransportException $e)
342  {
343  $this->_throwException($e);
344  }
345  $this->_buffer->setWriteTranslations(array());
346  $this->executeCommand("\r\n.\r\n", array(250));
347  }
348 
350  protected function _getReversePath(Swift_Mime_Message $message)
351  {
352  $return = $message->getReturnPath();
353  $sender = $message->getSender();
354  $from = $message->getFrom();
355  $path = null;
356  if (!empty($return))
357  {
358  $path = $return;
359  }
360  elseif (!empty($sender))
361  {
362  // Don't use array_keys
363  reset($sender); // Reset Pointer to first pos
364  $path = key($sender); // Get key
365  }
366  elseif (!empty($from))
367  {
368  reset($from); // Reset Pointer to first pos
369  $path = key($from); // Get key
370  }
371  return $path;
372  }
373 
376  {
377  if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e))
378  {
379  $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
380  if (!$evt->bubbleCancelled())
381  {
382  throw $e;
383  }
384  }
385  else
386  {
387  throw $e;
388  }
389  }
390 
392  protected function _assertResponseCode($response, $wanted)
393  {
394  list($code, $separator, $text) = sscanf($response, '%3d%[ -]%s');
395  $valid = (empty($wanted) || in_array($code, $wanted));
396 
397  if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response,
398  $valid))
399  {
400  $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived');
401  }
402 
403  if (!$valid)
404  {
405  $this->_throwException(
407  'Expected response code ' . implode('/', $wanted) . ' but got code ' .
408  '"' . $code . '", with message "' . $response . '"'
409  )
410  );
411  }
412  }
413 
415  protected function _getFullResponse($seq)
416  {
417  $response = '';
418  try
419  {
420  do
421  {
422  $line = $this->_buffer->readLine($seq);
423  $response .= $line;
424  }
425  while (null !== $line && false !== $line && ' ' != $line{3});
426  }
427  catch (Swift_TransportException $e)
428  {
429  $this->_throwException($e);
430  }
431  return $response;
432  }
433 
434  // -- Private methods
435 
437  private function _doMailTransaction($message, $reversePath,
438  array $recipients, array &$failedRecipients)
439  {
440  $sent = 0;
441  $this->_doMailFromCommand($reversePath);
442  foreach ($recipients as $forwardPath)
443  {
444  try
445  {
446  $this->_doRcptToCommand($forwardPath);
447  $sent++;
448  }
449  catch (Swift_TransportException $e)
450  {
451  $failedRecipients[] = $forwardPath;
452  }
453  }
454 
455  if ($sent != 0)
456  {
457  $this->_doDataCommand();
458  $this->_streamMessage($message);
459  }
460  else
461  {
462  $this->reset();
463  }
464 
465  return $sent;
466  }
467 
469  private function _sendTo(Swift_Mime_Message $message, $reversePath,
470  array $to, array &$failedRecipients)
471  {
472  if (empty($to))
473  {
474  return 0;
475  }
476  return $this->_doMailTransaction($message, $reversePath, array_keys($to),
477  $failedRecipients);
478  }
479 
481  private function _sendCc(Swift_Mime_Message $message, $reversePath,
482  array $cc, array &$failedRecipients)
483  {
484  if (empty($cc))
485  {
486  return 0;
487  }
488  return $this->_doMailTransaction($message, $reversePath, array_keys($cc),
489  $failedRecipients);
490  }
491 
493  private function _sendBcc(Swift_Mime_Message $message, $reversePath,
494  array $bcc, array &$failedRecipients)
495  {
496  $sent = 0;
497  foreach ($bcc as $forwardPath => $name)
498  {
499  $message->setBcc(array($forwardPath => $name));
500  $sent += $this->_doMailTransaction(
501  $message, $reversePath, array($forwardPath), $failedRecipients
502  );
503  }
504  return $sent;
505  }
506 
508  private function _lookupHostname()
509  {
510  if (!empty($_SERVER['SERVER_NAME'])
511  && $this->_isFqdn($_SERVER['SERVER_NAME']))
512  {
513  $this->_domain = $_SERVER['SERVER_NAME'];
514  }
515  elseif (!empty($_SERVER['SERVER_ADDR']))
516  {
517  $this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']);
518  }
519  }
520 
522  private function _isFqdn($hostname)
523  {
524  //We could do a really thorough check, but there's really no point
525  if (false !== $dotPos = strpos($hostname, '.'))
526  {
527  return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1);
528  }
529  else
530  {
531  return false;
532  }
533  }
534 
538  public function __destruct()
539  {
540  $this->stop();
541  }
542 
543 }