|
TYPO3 API
SVNRelease
|
00001 <?php 00002 00003 /* 00004 * This file is part of SwiftMailer. 00005 * (c) 2004-2009 Chris Corbyn 00006 * 00007 * For the full copyright and license information, please view the LICENSE 00008 * file that was distributed with this source code. 00009 */ 00010 00011 //@require 'Swift/Transport.php'; 00012 //@require 'Swift/Transport/IoBuffer.php'; 00013 //@require 'Swift/Transport/CommandSentException.php'; 00014 //@require 'Swift/TransportException.php'; 00015 //@require 'Swift/Mime/Message.php'; 00016 //@require 'Swift/Events/EventDispatcher.php'; 00017 //@require 'Swift/Events/EventListener.php'; 00018 00019 /** 00020 * Sends Messages over SMTP. 00021 * 00022 * @package Swift 00023 * @subpackage Transport 00024 * @author Chris Corbyn 00025 */ 00026 abstract class Swift_Transport_AbstractSmtpTransport 00027 implements Swift_Transport 00028 { 00029 00030 /** Input-Output buffer for sending/receiving SMTP commands and responses */ 00031 protected $_buffer; 00032 00033 /** Connection status */ 00034 protected $_started = false; 00035 00036 /** The domain name to use in HELO command */ 00037 protected $_domain = '[127.0.0.1]'; 00038 00039 /** The event dispatching layer */ 00040 protected $_eventDispatcher; 00041 00042 /** Return an array of params for the Buffer */ 00043 abstract protected function _getBufferParams(); 00044 00045 /** 00046 * Creates a new EsmtpTransport using the given I/O buffer. 00047 * 00048 * @param Swift_Transport_IoBuffer $buf 00049 * @param Swift_Events_EventDispatcher $dispatcher 00050 */ 00051 public function __construct(Swift_Transport_IoBuffer $buf, 00052 Swift_Events_EventDispatcher $dispatcher) 00053 { 00054 $this->_eventDispatcher = $dispatcher; 00055 $this->_buffer = $buf; 00056 $this->_lookupHostname(); 00057 } 00058 00059 /** 00060 * Set the name of the local domain which Swift will identify itself as. 00061 * This should be a fully-qualified domain name and should be truly the domain 00062 * you're using. If your server doesn't have a domain name, use the IP in square 00063 * brackets (i.e. [127.0.0.1]). 00064 * 00065 * @param string $domain 00066 */ 00067 public function setLocalDomain($domain) 00068 { 00069 $this->_domain = $domain; 00070 return $this; 00071 } 00072 00073 /** 00074 * Get the name of the domain Swift will identify as. 00075 * 00076 * @return string 00077 */ 00078 public function getLocalDomain() 00079 { 00080 return $this->_domain; 00081 } 00082 00083 /** 00084 * Start the SMTP connection. 00085 */ 00086 public function start() 00087 { 00088 if (!$this->_started) 00089 { 00090 if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) 00091 { 00092 $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted'); 00093 if ($evt->bubbleCancelled()) 00094 { 00095 return; 00096 } 00097 } 00098 00099 try 00100 { 00101 $this->_buffer->initialize($this->_getBufferParams()); 00102 } 00103 catch (Swift_TransportException $e) 00104 { 00105 $this->_throwException($e); 00106 } 00107 $this->_readGreeting(); 00108 $this->_doHeloCommand(); 00109 00110 if ($evt) 00111 { 00112 $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted'); 00113 } 00114 00115 $this->_started = true; 00116 } 00117 } 00118 00119 /** 00120 * Test if an SMTP connection has been established. 00121 * 00122 * @return boolean 00123 */ 00124 public function isStarted() 00125 { 00126 return $this->_started; 00127 } 00128 00129 /** 00130 * Send the given Message. 00131 * 00132 * Recipient/sender data will be retreived from the Message API. 00133 * The return value is the number of recipients who were accepted for delivery. 00134 * 00135 * @param Swift_Mime_Message $message 00136 * @param string[] &$failedRecipients to collect failures by-reference 00137 * @return int 00138 */ 00139 public function send(Swift_Mime_Message $message, &$failedRecipients = null) 00140 { 00141 $sent = 0; 00142 $failedRecipients = (array) $failedRecipients; 00143 00144 if (!$reversePath = $this->_getReversePath($message)) 00145 { 00146 throw new Swift_TransportException( 00147 'Cannot send message without a sender address' 00148 ); 00149 } 00150 00151 if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) 00152 { 00153 $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); 00154 if ($evt->bubbleCancelled()) 00155 { 00156 return 0; 00157 } 00158 } 00159 00160 $to = (array) $message->getTo(); 00161 $cc = (array) $message->getCc(); 00162 $bcc = (array) $message->getBcc(); 00163 00164 $message->setBcc(array()); 00165 00166 try 00167 { 00168 $sent += $this->_sendTo($message, $reversePath, $to, $failedRecipients); 00169 $sent += $this->_sendCc($message, $reversePath, $cc, $failedRecipients); 00170 $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients); 00171 } 00172 catch (Exception $e) 00173 { 00174 $message->setBcc($bcc); 00175 throw $e; 00176 } 00177 00178 $message->setBcc($bcc); 00179 00180 if ($evt) 00181 { 00182 if ($sent == count($to) + count($cc) + count($bcc)) 00183 { 00184 $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); 00185 } 00186 elseif ($sent > 0) 00187 { 00188 $evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE); 00189 } 00190 else 00191 { 00192 $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); 00193 } 00194 $evt->setFailedRecipients($failedRecipients); 00195 $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); 00196 } 00197 00198 $message->generateId(); //Make sure a new Message ID is used 00199 00200 return $sent; 00201 } 00202 00203 /** 00204 * Stop the SMTP connection. 00205 */ 00206 public function stop() 00207 { 00208 if ($this->_started) 00209 { 00210 if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) 00211 { 00212 $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped'); 00213 if ($evt->bubbleCancelled()) 00214 { 00215 return; 00216 } 00217 } 00218 00219 try 00220 { 00221 $this->executeCommand("QUIT\r\n", array(221)); 00222 } 00223 catch (Swift_TransportException $e) {} 00224 00225 try 00226 { 00227 $this->_buffer->terminate(); 00228 00229 if ($evt) 00230 { 00231 $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped'); 00232 } 00233 } 00234 catch (Swift_TransportException $e) 00235 { 00236 $this->_throwException($e); 00237 } 00238 } 00239 $this->_started = false; 00240 } 00241 00242 /** 00243 * Register a plugin. 00244 * 00245 * @param Swift_Events_EventListener $plugin 00246 */ 00247 public function registerPlugin(Swift_Events_EventListener $plugin) 00248 { 00249 $this->_eventDispatcher->bindEventListener($plugin); 00250 } 00251 00252 /** 00253 * Reset the current mail transaction. 00254 */ 00255 public function reset() 00256 { 00257 $this->executeCommand("RSET\r\n", array(250)); 00258 } 00259 00260 /** 00261 * Get the IoBuffer where read/writes are occurring. 00262 * 00263 * @return Swift_Transport_IoBuffer 00264 */ 00265 public function getBuffer() 00266 { 00267 return $this->_buffer; 00268 } 00269 00270 /** 00271 * Run a command against the buffer, expecting the given response codes. 00272 * 00273 * If no response codes are given, the response will not be validated. 00274 * If codes are given, an exception will be thrown on an invalid response. 00275 * 00276 * @param string $command 00277 * @param int[] $codes 00278 * @param string[] &$failures 00279 * @return string 00280 */ 00281 public function executeCommand($command, $codes = array(), &$failures = null) 00282 { 00283 $failures = (array) $failures; 00284 $seq = $this->_buffer->write($command); 00285 $response = $this->_getFullResponse($seq); 00286 if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)) 00287 { 00288 $this->_eventDispatcher->dispatchEvent($evt, 'commandSent'); 00289 } 00290 $this->_assertResponseCode($response, $codes); 00291 return $response; 00292 } 00293 00294 // -- Protected methods 00295 00296 /** Read the opening SMTP greeting */ 00297 protected function _readGreeting() 00298 { 00299 $this->_assertResponseCode($this->_getFullResponse(0), array(220)); 00300 } 00301 00302 /** Send the HELO welcome */ 00303 protected function _doHeloCommand() 00304 { 00305 $this->executeCommand( 00306 sprintf("HELO %s\r\n", $this->_domain), array(250) 00307 ); 00308 } 00309 00310 /** Send the MAIL FROM command */ 00311 protected function _doMailFromCommand($address) 00312 { 00313 $this->executeCommand( 00314 sprintf("MAIL FROM: <%s>\r\n", $address), array(250) 00315 ); 00316 } 00317 00318 /** Send the RCPT TO command */ 00319 protected function _doRcptToCommand($address) 00320 { 00321 $this->executeCommand( 00322 sprintf("RCPT TO: <%s>\r\n", $address), array(250, 251, 252) 00323 ); 00324 } 00325 00326 /** Send the DATA command */ 00327 protected function _doDataCommand() 00328 { 00329 $this->executeCommand("DATA\r\n", array(354)); 00330 } 00331 00332 /** Stream the contents of the message over the buffer */ 00333 protected function _streamMessage(Swift_Mime_Message $message) 00334 { 00335 $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n..")); 00336 try 00337 { 00338 $message->toByteStream($this->_buffer); 00339 $this->_buffer->flushBuffers(); 00340 } 00341 catch (Swift_TransportException $e) 00342 { 00343 $this->_throwException($e); 00344 } 00345 $this->_buffer->setWriteTranslations(array()); 00346 $this->executeCommand("\r\n.\r\n", array(250)); 00347 } 00348 00349 /** Determine the best-use reverse path for this message */ 00350 protected function _getReversePath(Swift_Mime_Message $message) 00351 { 00352 $return = $message->getReturnPath(); 00353 $sender = $message->getSender(); 00354 $from = $message->getFrom(); 00355 $path = null; 00356 if (!empty($return)) 00357 { 00358 $path = $return; 00359 } 00360 elseif (!empty($sender)) 00361 { 00362 // Don't use array_keys 00363 reset($sender); // Reset Pointer to first pos 00364 $path = key($sender); // Get key 00365 } 00366 elseif (!empty($from)) 00367 { 00368 reset($from); // Reset Pointer to first pos 00369 $path = key($from); // Get key 00370 } 00371 return $path; 00372 } 00373 00374 /** Throw a TransportException, first sending it to any listeners */ 00375 protected function _throwException(Swift_TransportException $e) 00376 { 00377 if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) 00378 { 00379 $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); 00380 if (!$evt->bubbleCancelled()) 00381 { 00382 throw $e; 00383 } 00384 } 00385 else 00386 { 00387 throw $e; 00388 } 00389 } 00390 00391 /** Throws an Exception if a response code is incorrect */ 00392 protected function _assertResponseCode($response, $wanted) 00393 { 00394 list($code, $separator, $text) = sscanf($response, '%3d%[ -]%s'); 00395 $valid = (empty($wanted) || in_array($code, $wanted)); 00396 00397 if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response, 00398 $valid)) 00399 { 00400 $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived'); 00401 } 00402 00403 if (!$valid) 00404 { 00405 $this->_throwException( 00406 new Swift_TransportException( 00407 'Expected response code ' . implode('/', $wanted) . ' but got code ' . 00408 '"' . $code . '", with message "' . $response . '"' 00409 ) 00410 ); 00411 } 00412 } 00413 00414 /** Get an entire multi-line response using its sequence number */ 00415 protected function _getFullResponse($seq) 00416 { 00417 $response = ''; 00418 try 00419 { 00420 do 00421 { 00422 $line = $this->_buffer->readLine($seq); 00423 $response .= $line; 00424 } 00425 while (null !== $line && false !== $line && ' ' != $line{3}); 00426 } 00427 catch (Swift_TransportException $e) 00428 { 00429 $this->_throwException($e); 00430 } 00431 return $response; 00432 } 00433 00434 // -- Private methods 00435 00436 /** Send an email to the given recipients from the given reverse path */ 00437 private function _doMailTransaction($message, $reversePath, 00438 array $recipients, array &$failedRecipients) 00439 { 00440 $sent = 0; 00441 $this->_doMailFromCommand($reversePath); 00442 foreach ($recipients as $forwardPath) 00443 { 00444 try 00445 { 00446 $this->_doRcptToCommand($forwardPath); 00447 $sent++; 00448 } 00449 catch (Swift_TransportException $e) 00450 { 00451 $failedRecipients[] = $forwardPath; 00452 } 00453 } 00454 00455 if ($sent != 0) 00456 { 00457 $this->_doDataCommand(); 00458 $this->_streamMessage($message); 00459 } 00460 else 00461 { 00462 $this->reset(); 00463 } 00464 00465 return $sent; 00466 } 00467 00468 /** Send a message to the given To: recipients */ 00469 private function _sendTo(Swift_Mime_Message $message, $reversePath, 00470 array $to, array &$failedRecipients) 00471 { 00472 if (empty($to)) 00473 { 00474 return 0; 00475 } 00476 return $this->_doMailTransaction($message, $reversePath, array_keys($to), 00477 $failedRecipients); 00478 } 00479 00480 /** Send a message to the given Cc: recipients */ 00481 private function _sendCc(Swift_Mime_Message $message, $reversePath, 00482 array $cc, array &$failedRecipients) 00483 { 00484 if (empty($cc)) 00485 { 00486 return 0; 00487 } 00488 return $this->_doMailTransaction($message, $reversePath, array_keys($cc), 00489 $failedRecipients); 00490 } 00491 00492 /** Send a message to all Bcc: recipients */ 00493 private function _sendBcc(Swift_Mime_Message $message, $reversePath, 00494 array $bcc, array &$failedRecipients) 00495 { 00496 $sent = 0; 00497 foreach ($bcc as $forwardPath => $name) 00498 { 00499 $message->setBcc(array($forwardPath => $name)); 00500 $sent += $this->_doMailTransaction( 00501 $message, $reversePath, array($forwardPath), $failedRecipients 00502 ); 00503 } 00504 return $sent; 00505 } 00506 00507 /** Try to determine the hostname of the server this is run on */ 00508 private function _lookupHostname() 00509 { 00510 if (!empty($_SERVER['SERVER_NAME']) 00511 && $this->_isFqdn($_SERVER['SERVER_NAME'])) 00512 { 00513 $this->_domain = $_SERVER['SERVER_NAME']; 00514 } 00515 elseif (!empty($_SERVER['SERVER_ADDR'])) 00516 { 00517 $this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']); 00518 } 00519 } 00520 00521 /** Determine is the $hostname is a fully-qualified name */ 00522 private function _isFqdn($hostname) 00523 { 00524 //We could do a really thorough check, but there's really no point 00525 if (false !== $dotPos = strpos($hostname, '.')) 00526 { 00527 return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1); 00528 } 00529 else 00530 { 00531 return false; 00532 } 00533 } 00534 00535 /** 00536 * Destructor. 00537 */ 00538 public function __destruct() 00539 { 00540 $this->stop(); 00541 } 00542 00543 }
1.8.0