1: <?php
2:
3: 4: 5: 6: 7: 8: 9:
10:
11: 12: 13: 14: 15: 16: 17:
18: abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport
19: {
20:
21: protected $_buffer;
22:
23:
24: protected $_started = false;
25:
26:
27: protected $_domain = '[127.0.0.1]';
28:
29:
30: protected $_eventDispatcher;
31:
32:
33: protected $_sourceIp;
34:
35:
36: abstract protected function _getBufferParams();
37:
38: 39: 40: 41: 42: 43:
44: public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher)
45: {
46: $this->_eventDispatcher = $dispatcher;
47: $this->_buffer = $buf;
48: $this->_lookupHostname();
49: }
50:
51: 52: 53: 54: 55: 56: 57: 58: 59:
60: public function setLocalDomain($domain)
61: {
62: $this->_domain = $domain;
63:
64: return $this;
65: }
66:
67: 68: 69: 70: 71:
72: public function getLocalDomain()
73: {
74: return $this->_domain;
75: }
76:
77: 78: 79: 80:
81: public function setSourceIp($source)
82: {
83: $this->_sourceIp=$source;
84: }
85:
86: 87: 88: 89:
90: public function getSourceIp()
91: {
92: return $this->_sourceIp;
93: }
94:
95: 96: 97:
98: public function start()
99: {
100: if (!$this->_started) {
101: if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) {
102: $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted');
103: if ($evt->bubbleCancelled()) {
104: return;
105: }
106: }
107:
108: try {
109: $this->_buffer->initialize($this->_getBufferParams());
110: } catch (Swift_TransportException $e) {
111: $this->_throwException($e);
112: }
113: $this->_readGreeting();
114: $this->_doHeloCommand();
115:
116: if ($evt) {
117: $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted');
118: }
119:
120: $this->_started = true;
121: }
122: }
123:
124: 125: 126: 127: 128:
129: public function isStarted()
130: {
131: return $this->_started;
132: }
133:
134: 135: 136: 137: 138: 139: 140: 141: 142: 143:
144: public function send(Swift_Mime_Message $message, &$failedRecipients = null)
145: {
146: $sent = 0;
147: $failedRecipients = (array) $failedRecipients;
148:
149: if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) {
150: $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
151: if ($evt->bubbleCancelled()) {
152: return 0;
153: }
154: }
155:
156: if (!$reversePath = $this->_getReversePath($message)) {
157: throw new Swift_TransportException(
158: 'Cannot send message without a sender address'
159: );
160: }
161:
162: $to = (array) $message->getTo();
163: $cc = (array) $message->getCc();
164: $bcc = (array) $message->getBcc();
165:
166: $message->setBcc(array());
167:
168: try {
169: $sent += $this->_sendTo($message, $reversePath, $to, $failedRecipients);
170: $sent += $this->_sendCc($message, $reversePath, $cc, $failedRecipients);
171: $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients);
172: } catch (Exception $e) {
173: $message->setBcc($bcc);
174: throw $e;
175: }
176:
177: $message->setBcc($bcc);
178:
179: if ($evt) {
180: if ($sent == count($to) + count($cc) + count($bcc)) {
181: $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
182: } elseif ($sent > 0) {
183: $evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE);
184: } else {
185: $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
186: }
187: $evt->setFailedRecipients($failedRecipients);
188: $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
189: }
190:
191: $message->generateId();
192:
193: return $sent;
194: }
195:
196: 197: 198:
199: public function stop()
200: {
201: if ($this->_started) {
202: if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) {
203: $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped');
204: if ($evt->bubbleCancelled()) {
205: return;
206: }
207: }
208:
209: try {
210: $this->executeCommand("QUIT\r\n", array(221));
211: } catch (Swift_TransportException $e) {}
212:
213: try {
214: $this->_buffer->terminate();
215:
216: if ($evt) {
217: $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped');
218: }
219: } catch (Swift_TransportException $e) {
220: $this->_throwException($e);
221: }
222: }
223: $this->_started = false;
224: }
225:
226: 227: 228: 229: 230:
231: public function registerPlugin(Swift_Events_EventListener $plugin)
232: {
233: $this->_eventDispatcher->bindEventListener($plugin);
234: }
235:
236: 237: 238:
239: public function reset()
240: {
241: $this->executeCommand("RSET\r\n", array(250));
242: }
243:
244: 245: 246: 247: 248:
249: public function getBuffer()
250: {
251: return $this->_buffer;
252: }
253:
254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264:
265: public function executeCommand($command, $codes = array(), &$failures = null)
266: {
267: $failures = (array) $failures;
268: $seq = $this->_buffer->write($command);
269: $response = $this->_getFullResponse($seq);
270: if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)) {
271: $this->_eventDispatcher->dispatchEvent($evt, 'commandSent');
272: }
273: $this->_assertResponseCode($response, $codes);
274:
275: return $response;
276: }
277:
278:
279:
280:
281: protected function _readGreeting()
282: {
283: $this->_assertResponseCode($this->_getFullResponse(0), array(220));
284: }
285:
286:
287: protected function _doHeloCommand()
288: {
289: $this->executeCommand(
290: sprintf("HELO %s\r\n", $this->_domain), array(250)
291: );
292: }
293:
294:
295: protected function _doMailFromCommand($address)
296: {
297: $this->executeCommand(
298: sprintf("MAIL FROM: <%s>\r\n", $address), array(250)
299: );
300: }
301:
302:
303: protected function _doRcptToCommand($address)
304: {
305: $this->executeCommand(
306: sprintf("RCPT TO: <%s>\r\n", $address), array(250, 251, 252)
307: );
308: }
309:
310:
311: protected function _doDataCommand()
312: {
313: $this->executeCommand("DATA\r\n", array(354));
314: }
315:
316:
317: protected function _streamMessage(Swift_Mime_Message $message)
318: {
319: $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n.."));
320: try {
321: $message->toByteStream($this->_buffer);
322: $this->_buffer->flushBuffers();
323: } catch (Swift_TransportException $e) {
324: $this->_throwException($e);
325: }
326: $this->_buffer->setWriteTranslations(array());
327: $this->executeCommand("\r\n.\r\n", array(250));
328: }
329:
330:
331: protected function _getReversePath(Swift_Mime_Message $message)
332: {
333: $return = $message->getReturnPath();
334: $sender = $message->getSender();
335: $from = $message->getFrom();
336: $path = null;
337: if (!empty($return)) {
338: $path = $return;
339: } elseif (!empty($sender)) {
340:
341: reset($sender);
342: $path = key($sender);
343: } elseif (!empty($from)) {
344: reset($from);
345: $path = key($from);
346: }
347:
348: return $path;
349: }
350:
351:
352: protected function _throwException(Swift_TransportException $e)
353: {
354: if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) {
355: $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
356: if (!$evt->bubbleCancelled()) {
357: throw $e;
358: }
359: } else {
360: throw $e;
361: }
362: }
363:
364:
365: protected function _assertResponseCode($response, $wanted)
366: {
367: list($code) = sscanf($response, '%3d');
368: $valid = (empty($wanted) || in_array($code, $wanted));
369:
370: if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response,
371: $valid))
372: {
373: $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived');
374: }
375:
376: if (!$valid) {
377: $this->_throwException(
378: new Swift_TransportException(
379: 'Expected response code ' . implode('/', $wanted) . ' but got code ' .
380: '"' . $code . '", with message "' . $response . '"'
381: )
382: );
383: }
384: }
385:
386:
387: protected function _getFullResponse($seq)
388: {
389: $response = '';
390: try {
391: do {
392: $line = $this->_buffer->readLine($seq);
393: $response .= $line;
394: } while (null !== $line && false !== $line && ' ' != $line{3});
395: } catch (Swift_TransportException $e) {
396: $this->_throwException($e);
397: }
398:
399: return $response;
400: }
401:
402:
403:
404:
405: private function _doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients)
406: {
407: $sent = 0;
408: $this->_doMailFromCommand($reversePath);
409: foreach ($recipients as $forwardPath) {
410: try {
411: $this->_doRcptToCommand($forwardPath);
412: $sent++;
413: } catch (Swift_TransportException $e) {
414: $failedRecipients[] = $forwardPath;
415: }
416: }
417:
418: if ($sent != 0) {
419: $this->_doDataCommand();
420: $this->_streamMessage($message);
421: } else {
422: $this->reset();
423: }
424:
425: return $sent;
426: }
427:
428:
429: private function _sendTo(Swift_Mime_Message $message, $reversePath, array $to, array &$failedRecipients)
430: {
431: if (empty($to)) {
432: return 0;
433: }
434:
435: return $this->_doMailTransaction($message, $reversePath, array_keys($to),
436: $failedRecipients);
437: }
438:
439:
440: private function _sendCc(Swift_Mime_Message $message, $reversePath, array $cc, array &$failedRecipients)
441: {
442: if (empty($cc)) {
443: return 0;
444: }
445:
446: return $this->_doMailTransaction($message, $reversePath, array_keys($cc),
447: $failedRecipients);
448: }
449:
450:
451: private function _sendBcc(Swift_Mime_Message $message, $reversePath, array $bcc, array &$failedRecipients)
452: {
453: $sent = 0;
454: foreach ($bcc as $forwardPath => $name) {
455: $message->setBcc(array($forwardPath => $name));
456: $sent += $this->_doMailTransaction(
457: $message, $reversePath, array($forwardPath), $failedRecipients
458: );
459: }
460:
461: return $sent;
462: }
463:
464:
465: private function _lookupHostname()
466: {
467: if (!empty($_SERVER['SERVER_NAME'])
468: && $this->_isFqdn($_SERVER['SERVER_NAME']))
469: {
470: $this->_domain = $_SERVER['SERVER_NAME'];
471: } elseif (!empty($_SERVER['SERVER_ADDR'])) {
472: $this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']);
473: }
474: }
475:
476:
477: private function _isFqdn($hostname)
478: {
479:
480: if (false !== $dotPos = strpos($hostname, '.')) {
481: return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1);
482: } else {
483: return false;
484: }
485: }
486:
487: 488: 489:
490: public function __destruct()
491: {
492: $this->stop();
493: }
494: }
495: