setLocalDomain()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 4
nop 1
dl 0
loc 14
ccs 10
cts 10
cp 1
crap 4
rs 9.2
c 0
b 0
f 0
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
/**
12
 * Sends Messages over SMTP.
13
 *
14
 * @author Chris Corbyn
15
 */
16
abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
17
{
18
    /**
19
     * Input-Output buffer for sending/receiving SMTP commands and responses
20
     *
21
     * @var Swift_Transport_IoBuffer|Swift_Transport_StreamBuffer
22
     */
23
    protected $_buffer;
24
25
    /**
26
     * Connection status
27
     *
28
     * @var bool
29
     */
30
    protected $_started = false;
31
32
    /**
33
     * The domain name to use in HELO command
34
     *
35
     * @var string
36
     */
37
    protected $_domain = '[127.0.0.1]';
38
39
    /**
40
     * The event dispatching layer
41
     *
42
     * @var Swift_Events_EventDispatcher
43
     */
44
    protected $_eventDispatcher;
45
46
    /**
47
     * Source Ip
48
     *
49
     * @var string
50
     */
51
    protected $_sourceIp;
52
53
    /**
54
     * Return an array of params for the Buffer
55
     */
56
    abstract protected function _getBufferParams();
57
58
    /**
59
     * Creates a new EsmtpTransport using the given I/O buffer.
60
     *
61
     * @param Swift_Transport_IoBuffer     $buf
62
     * @param Swift_Events_EventDispatcher $dispatcher
63
     * @param string                       $localDomain
64
     */
65 176
    public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1')
66
    {
67 176
        $this->_eventDispatcher = $dispatcher;
68 176
        $this->_buffer = $buf;
69 176
        $this->setLocalDomain($localDomain);
70 176
    }
71
72
    /**
73
     * Set the name of the local domain which Swift will identify itself as.
74
     *
75
     * This should be a fully-qualified domain name and should be truly the domain
76
     * you're using.
77
     *
78
     * If your server does not have a domain name, use the IP address. This will
79
     * automatically be wrapped in square brackets as described in RFC 5321,
80
     * section 4.1.3.
81
     *
82
     * @param string $domain
83
     *
84
     * @return $this
85
     */
86 176
    public function setLocalDomain($domain)
87
    {
88 176
        if ($domain[0] !== '[') {
89 176
            if (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
90 3
                $domain = '['.$domain.']';
91 176
            } elseif (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
92 3
                $domain = '[IPv6:'.$domain.']';
93 3
            }
94 176
        }
95
96 176
        $this->_domain = $domain;
97
98 176
        return $this;
99
    }
100
101
    /**
102
     * Get the name of the domain Swift will identify as.
103
     *
104
     * If an IP address was specified, this will be returned wrapped in square
105
     * brackets as described in RFC 5321, section 4.1.3.
106
     *
107
     * @return string
108
     */
109 4
    public function getLocalDomain()
110
    {
111 4
        return $this->_domain;
112
    }
113
114
    /**
115
     * Sets the source IP.
116
     *
117
     * @param string $source
118
     */
119
    public function setSourceIp($source)
120
    {
121
        $this->_sourceIp = $source;
122
    }
123
124
    /**
125
     * Returns the IP used to connect to the destination.
126
     *
127
     * @return string
128
     */
129
    public function getSourceIp()
130
    {
131
        return $this->_sourceIp;
132
    }
133
134
    /**
135
     * Start the SMTP connection.
136
     */
137 140
    public function start()
138
    {
139 140
        if (!$this->_started) {
140
141 140
            $evt = $this->_eventDispatcher->createTransportChangeEvent($this);
142 140
            if ($evt) {
143
144 22
                $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted');
145 22
                if ($evt->bubbleCancelled()) {
146 3
                    return;
147
                }
148 19
            }
149
150
            try {
151 137
                $this->_buffer->initialize($this->_getBufferParams());
152 137
            } catch (Swift_TransportException $e) {
153
                $this->_throwException($e);
154
            }
155 137
            $this->_readGreeting();
156 131
            $this->_doHeloCommand();
157
158 128
            if ($evt) {
159 19
                $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted');
160 19
            }
161
162 128
            $this->_started = true;
163 128
        }
164 128
    }
165
166
    /**
167
     * Test if an SMTP connection has been established.
168
     *
169
     * @return bool
170
     */
171 23
    public function isStarted()
172
    {
173 23
        return $this->_started;
174
    }
175
176
    /**
177
     * Send the given Message.
178
     *
179
     * Recipient/sender data will be retrieved from the Message API.
180
     * The return value is the number of recipients who were accepted for delivery.
181
     *
182
     * @param Swift_Mime_Message $message
183
     * @param string[]|null      $failedRecipients An array of failures by-reference
184
     *
185
     * @return int
186
     *
187
     * @throws Exception
188
     * @throws Swift_TransportException
189
     */
190 84
    public function send(Swift_Mime_Message $message, &$failedRecipients = null)
191
    {
192 84
        $sent = 0;
193 84
        $failedRecipients = (array)$failedRecipients;
194
195 84
        $evt = $this->_eventDispatcher->createSendEvent($this, $message);
196 84
        if ($evt) {
197
198 25
            $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
199 25
            if ($evt->bubbleCancelled()) {
200 3
                return 0;
201
            }
202 22
        }
203
204 81
        if (!$reversePath = $this->_getReversePath($message)) {
205
            $this->_throwException(
206
                new Swift_TransportException(
207
                    'Cannot send message without a sender address'
208
                )
209
            );
210
        }
211
212 81
        $to = (array)$message->getTo();
213 81
        $cc = (array)$message->getCc();
214 81
        $tos = array_merge($to, $cc);
215 81
        $bcc = (array)$message->getBcc();
216
217 81
        $message->setBcc(array());
218
219
        try {
220 81
            $sent += $this->_sendTo($message, $reversePath, $tos, $failedRecipients);
221 69
            $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients);
222 81
        } catch (Exception $e) {
223 12
            $message->setBcc($bcc);
224 12
            throw $e;
225
        }
226
227 69
        $message->setBcc($bcc);
228
229 69
        if ($evt) {
230
231 22
            if ($sent === count($to) + count($cc) + count($bcc)) {
232 13
                $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
233 22
            } elseif ($sent > 0) {
234 3
                $evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE);
235 3
            } else {
236 6
                $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
237
            }
238
239 22
            $evt->setFailedRecipients($failedRecipients);
240 22
            $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
241 22
        }
242
243 69
        $message->generateId(); // Make sure a new Message ID is used
244
245 69
        return $sent;
246
    }
247
248
    /**
249
     * Stop the SMTP connection.
250
     */
251 42
    public function stop()
252
    {
253 42
        if ($this->_started) {
254
255 16
            $evt = $this->_eventDispatcher->createTransportChangeEvent($this);
256 16
            if ($evt) {
257
258 13
                $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped');
259 13
                if ($evt->bubbleCancelled()) {
260 3
                    return;
261
                }
262 10
            }
263
264
            try {
265 13
                $this->executeCommand("QUIT\r\n", array(221));
266 13
            } catch (Swift_TransportException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
267
            }
268
269
            try {
270 13
                $this->_buffer->terminate();
271
272 13
                if ($evt) {
273 10
                    $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped');
274 10
                }
275 13
            } catch (Swift_TransportException $e) {
276
                $this->_throwException($e);
277
            }
278 13
        }
279 39
        $this->_started = false;
280 39
    }
281
282
    /**
283
     * Register a plugin.
284
     *
285
     * @param Swift_Events_EventListener $plugin
286
     */
287 3
    public function registerPlugin(Swift_Events_EventListener $plugin)
288
    {
289 3
        $this->_eventDispatcher->bindEventListener($plugin);
290 3
    }
291
292
    /**
293
     * Reset the current mail transaction.
294
     */
295 12
    public function reset()
296
    {
297 12
        $this->executeCommand("RSET\r\n", array(250));
298 12
    }
299
300
    /**
301
     * Get the IoBuffer where read/writes are occurring.
302
     *
303
     * @return Swift_Transport_IoBuffer
304
     */
305 34
    public function getBuffer()
306
    {
307 34
        return $this->_buffer;
308
    }
309
310
    /**
311
     * Run a command against the buffer, expecting the given response codes.
312
     *
313
     * If no response codes are given, the response will not be validated.
314
     * If codes are given, an exception will be thrown on an invalid response.
315
     *
316
     * @param string   $command
317
     * @param int[]    $codes
318
     * @param string[] $failures An array of failures by-reference
319
     *
320
     * @return string
321
     */
322 137
    public function executeCommand($command, $codes = array(), &$failures = null)
323
    {
324 137
        $failures = (array)$failures;
325 137
        $seq = $this->_buffer->write($command);
326 137
        $response = $this->_getFullResponse($seq);
327
328 137
        $evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes);
329 137
        if ($evt) {
330 7
            $this->_eventDispatcher->dispatchEvent($evt, 'commandSent');
331 7
        }
332
333 137
        $this->_assertResponseCode($response, $codes);
334
335 131
        return $response;
336
    }
337
338
    /** Read the opening SMTP greeting */
339 137
    protected function _readGreeting()
340
    {
341 137
        $this->_assertResponseCode($this->_getFullResponse(0), array(220));
342 131
    }
343
344
    /** Send the HELO welcome */
345 45
    protected function _doHeloCommand()
346
    {
347 45
        $this->executeCommand(
348 45
            sprintf("HELO %s\r\n", $this->_domain), array(250)
349 45
        );
350 42
    }
351
352
    /**
353
     * Send the MAIL FROM command
354
     *
355
     * @param string $address
356
     */
357 25
    protected function _doMailFromCommand($address)
358
    {
359 25
        $this->executeCommand(
360 25
            sprintf("MAIL FROM:<%s>\r\n", $address), array(250)
361 25
        );
362 24
    }
363
364
    /**
365
     * Send the RCPT TO command
366
     *
367
     * @param string $address
368
     */
369 24
    protected function _doRcptToCommand($address)
370
    {
371 24
        $this->executeCommand(
372 24
            sprintf("RCPT TO:<%s>\r\n", $address), array(250, 251, 252)
373 24
        );
374 21
    }
375
376
    /** Send the DATA command */
377 69
    protected function _doDataCommand()
378
    {
379 69
        $this->executeCommand("DATA\r\n", array(354));
380 63
    }
381
382
    /**
383
     * Stream the contents of the message over the buffer
384
     *
385
     * @param Swift_Mime_Message $message
386
     *
387
     * @throws Swift_TransportException
388
     */
389 63
    protected function _streamMessage(Swift_Mime_Message $message)
390
    {
391 63
        $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n.."));
392
393
        try {
394 63
            $message->toByteStream($this->_buffer);
395 63
            $this->_buffer->flushBuffers();
396 63
        } catch (Swift_TransportException $e) {
397
            $this->_throwException($e);
398
        }
399
400 63
        $this->_buffer->setWriteTranslations(array());
401 63
        $this->executeCommand("\r\n.\r\n", array(250));
402 60
    }
403
404
    /**
405
     * Determine the best-use reverse path for this message
406
     *
407
     * @param Swift_Mime_Message $message
408
     *
409
     * @return mixed|null|string
410
     */
411 85 View Code Duplication
    protected function _getReversePath(Swift_Mime_Message $message)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
412
    {
413 85
        $return = $message->getReturnPath();
414 85
        $sender = $message->getSender();
415 85
        $from = $message->getFrom();
416 85
        $path = null;
417
418 85
        if (!empty($return)) {
419 3
            $path = $return;
420 85
        } elseif (!empty($sender)) {
421
            // don't use array_keys
422 3
            reset($sender); // reset Pointer to first pos
423 3
            $path = key($sender); // get key
424 82
        } elseif (!empty($from)) {
425 75
            reset($from); // reset Pointer to first pos
426 75
            $path = key($from); // get key
427 75
        }
428
429 85
        return $path;
430
    }
431
432
    /**
433
     * Throw a TransportException, first sending it to any listeners.
434
     *
435
     * @param Swift_TransportException $e
436
     *
437
     * @throws Swift_TransportException
438
     */
439 55 View Code Duplication
    protected function _throwException(Swift_TransportException $e)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
440
    {
441 55
        $evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e);
442 55
        if ($evt) {
443
444 6
            $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
445 6
            if (!$evt->bubbleCancelled()) {
446 3
                throw $e;
447
            }
448
449 3
        } else {
450 49
            throw $e;
451
        }
452 3
    }
453
454
    /**
455
     * Throws an Exception if a response code is incorrect
456
     *
457
     * @param string    $response
458
     * @param integer[] $wanted
459
     *
460
     * @throws Swift_TransportException
461
     */
462 143
    protected function _assertResponseCode($response, $wanted)
463
    {
464
        /** @noinspection PrintfScanfArgumentsInspection */
465 143
        list($code) = sscanf($response, '%3d');
466
467
        /** @noinspection TypeUnsafeArraySearchInspection */
468 143
        $valid = (empty($wanted) || in_array($code, $wanted));
469
470 143
        $evt = $this->_eventDispatcher->createResponseEvent($this, $response, $valid);
471 143
        if ($evt) {
472 7
            $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived');
473 7
        }
474
475 143
        if (!$valid) {
476 55
            $this->_throwException(
477 55
                new Swift_TransportException(
478 55
                    'Expected response code ' . implode('/', $wanted) . ' but got code ' .
479 55
                    '"' . $code . '", with message "' . $response . '"',
480
                    $code
481 55
                )
482 55
            );
483 3
        }
484 134
    }
485
486
    /**
487
     * Get an entire multi-line response using its sequence number
488
     *
489
     * @param int $seq
490
     *
491
     * @return string
492
     * @throws Swift_TransportException
493
     */
494 143
    protected function _getFullResponse($seq)
495
    {
496 143
        $response = '';
497
        try {
498
            do {
499 143
                $line = $this->_buffer->readLine($seq);
500 143
                $response .= $line;
501 143
            } while (null !== $line && false !== $line && ' ' !== $line[3]);
502 143
        } catch (Swift_TransportException $e) {
503
            $this->_throwException($e);
504
        } catch (Swift_IoException $e) {
505
            $this->_throwException(
506
                new Swift_TransportException(
507
                    $e->getMessage()
508
                )
509
            );
510
        }
511
512 143
        return $response;
513
    }
514
515
    /**
516
     * Send an email to the given recipients from the given reverse path
517
     *
518
     * @param Swift_Mime_Message $message
519
     * @param string             $reversePath
520
     * @param array              $recipients
521
     * @param array              $failedRecipients
522
     *
523
     * @return int
524
     *
525
     * @throws Swift_TransportException
526
     */
527 81
    private function _doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients)
528
    {
529 81
        $sent = 0;
530 81
        $this->_doMailFromCommand($reversePath);
531
532 78
        foreach ($recipients as $forwardPath) {
533
            try {
534 78
                $this->_doRcptToCommand($forwardPath);
535 69
                ++$sent;
536 78
            } catch (Swift_TransportException $e) {
537 18
                $failedRecipients[] = $forwardPath;
538
539
                // $this->_throwException($e);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
540
                // throw $e;
541
                // <-- TODO: check this
542
                // <-- https://github.com/michaelhogg/swiftmailer/commit/b824cba068d10c46291018947e463cb201a3e572
543
            }
544 78
        }
545
546 78
        if ($sent !== 0) {
547 69
            $this->_doDataCommand();
548 63
            $this->_streamMessage($message);
549 60
        } else {
550 12
            $this->reset();
551
        }
552
553 69
        return $sent;
554
    }
555
556
    /**
557
     * Send a message to the given To: recipients
558
     *
559
     * @param Swift_Mime_Message $message
560
     * @param                    $reversePath
561
     * @param array              $to
562
     * @param array              $failedRecipients
563
     *
564
     * @return int
565
     *
566
     * @throws Swift_TransportException
567
     */
568 81
    private function _sendTo(Swift_Mime_Message $message, $reversePath, array $to, array &$failedRecipients)
569
    {
570 81
        if (empty($to)) {
571
            return 0;
572
        }
573
574 81
        return $this->_doMailTransaction($message, $reversePath, array_keys($to), $failedRecipients);
575
    }
576
577
    /**
578
     * Send a message to all Bcc: recipients
579
     *
580
     * @param Swift_Mime_Message $message
581
     * @param                    $reversePath
582
     * @param array              $bcc
583
     * @param array              $failedRecipients
584
     *
585
     * @return int
586
     *
587
     * @throws Swift_TransportException
588
     */
589 69
    private function _sendBcc(Swift_Mime_Message $message, $reversePath, array $bcc, array &$failedRecipients)
590
    {
591 69
        $sent = 0;
592 69
        foreach ($bcc as $forwardPath => $name) {
593 9
            $message->setBcc(array($forwardPath => $name));
594 9
            $sent += $this->_doMailTransaction(
595 9
                $message,
596 9
                $reversePath,
597 9
                array($forwardPath),
598
                $failedRecipients
599 9
            );
600 69
        }
601
602 69
        return $sent;
603
    }
604
605
606
607
    /**
608
     * Destructor.
609
     */
610 30
    public function __destruct()
611
    {
612
        try {
613 30
            $this->stop();
614 30
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
615
        }
616 30
    }
617
}
618