Completed
Push — 5.x ( 5ed7f6...781e5e )
by Lars
05:12
created

Swift_Transport_EsmtpTransport::setVerifySsl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 1 Features 1
Metric Value
cc 1
eloc 5
c 1
b 1
f 1
nc 1
nop 1
dl 0
loc 9
ccs 0
cts 5
cp 0
crap 2
rs 9.6666
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 with ESMTP support.
13
 *
14
 * @author Chris Corbyn
15
 */
16
class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTransport implements Swift_Transport_SmtpAgent
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
     * ESMTP extension handlers.
20
     *
21
     * @var Swift_Transport_EsmtpHandler[]
22
     */
23
    private $_handlers = array();
24
25
    /**
26
     * ESMTP capabilities.
27
     *
28
     * @var string[]
29
     */
30
    private $_capabilities = array();
31
32
    /**
33
     * Connection buffer parameters.
34
     *
35
     * @var array
36
     */
37
    private $_params = array(
38
        'protocol'               => 'tcp',
39
        'host'                   => 'localhost',
40
        'port'                   => 25,
41
        'timeout'                => 30,
42
        'blocking'               => 1,
43
        'tls'                    => false,
44
        'type'                   => Swift_Transport_IoBuffer::TYPE_SOCKET,
45
        'stream_context_options' => array(),
46
        'verifySsl'              => array(),
47
    );
48
49
    /**
50
     * Creates a new EsmtpTransport using the given I/O buffer.
51
     *
52
     * @param Swift_Transport_IoBuffer       $buf
53
     * @param Swift_Transport_EsmtpHandler[] $extensionHandlers
54
     * @param Swift_Events_EventDispatcher   $dispatcher
55
     */
56 121
    public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher)
57
    {
58 121
        parent::__construct($buf, $dispatcher);
59 121
        $this->setExtensionHandlers($extensionHandlers);
60 121
    }
61
62
    /**
63
     * Set the host to connect to.
64
     *
65
     * @param string $host
66
     *
67
     * @return Swift_Transport_EsmtpTransport
68
     */
69 8
    public function setHost($host)
70
    {
71 8
        $this->_params['host'] = $host;
72
73 8
        return $this;
74
    }
75
76
    /**
77
     * Get the host to connect to.
78
     *
79
     * @return string
80
     */
81 2
    public function getHost()
82
    {
83 2
        return $this->_params['host'];
84
    }
85
86
    /**
87
     * Set the port to connect to.
88
     *
89
     * @param int $port
90
     *
91
     * @return Swift_Transport_EsmtpTransport
92
     */
93 8
    public function setPort($port)
94
    {
95 8
        $this->_params['port'] = (int)$port;
96
97 8
        return $this;
98
    }
99
100
    /**
101
     * Get the port to connect to.
102
     *
103
     * @return int
104
     */
105 2
    public function getPort()
106
    {
107 2
        return $this->_params['port'];
108
    }
109
110
    /**
111
     * Set the connection timeout.
112
     *
113
     * @param int $timeout seconds
114
     *
115
     * @return Swift_Transport_EsmtpTransport
116
     */
117 4
    public function setTimeout($timeout)
118
    {
119 4
        $this->_params['timeout'] = (int)$timeout;
120 4
        $this->_buffer->setParam('timeout', (int)$timeout);
121
122 4
        return $this;
123
    }
124
125
    /**
126
     * Get the connection timeout.
127
     *
128
     * @return int
129
     */
130 2
    public function getTimeout()
131
    {
132 2
        return $this->_params['timeout'];
133
    }
134
135
    /**
136
     * Set the encryption type (tls or ssl).
137
     *
138
     * @param string $encryption
139
     *
140
     * @return Swift_Transport_EsmtpTransport
141
     */
142 8
    public function setEncryption($encryption)
143
    {
144 8
        $encryption = Swift::strtolowerWithStaticCache($encryption);
145
146 8
        if ('tls' === $encryption) {
147 4
            $this->_params['protocol'] = 'tcp';
148 4
            $this->_params['tls'] = true;
149
        } else {
150 4
            $this->_params['protocol'] = $encryption;
151 4
            $this->_params['tls'] = false;
152
        }
153
154 8
        return $this;
155
    }
156
157
    /**
158
     * Get the encryption type.
159
     *
160
     * @return string
161
     */
162 2
    public function getEncryption()
163
    {
164 2
        return $this->_params['tls'] ? 'tls' : $this->_params['protocol'];
165
    }
166
167
    /**
168
     * Disable SSL-Verify to prevent error like "SSL3_GET_SERVER_CERTIFICATE:certificate verify failed"
169
     *
170
     * @param bool $bool
171
     *
172
     * @return Swift_Transport_EsmtpTransport
173
     */
174
    public function setVerifySsl($bool = false)
175
    {
176
        $options['ssl']['verify_peer'] = $bool;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
177
        $options['ssl']['verify_peer_name'] = $bool;
178
179
        $this->_params['verifySsl'] = $options;
180
181
        return $this;
182
    }
183
184
    /**
185
     * Sets the stream context options.
186
     *
187
     * @param array $options
188
     *
189
     * @return Swift_Transport_EsmtpTransport
190
     */
191
    public function setStreamOptions($options)
192
    {
193
        $this->_params['stream_context_options'] = $options;
194
195
        return $this;
196
    }
197
198
    /**
199
     * Returns the stream context options.
200
     *
201
     * @return array
202
     */
203
    public function getStreamOptions()
204
    {
205
        return $this->_params['stream_context_options'];
206
    }
207
208
    /**
209
     * Sets the source IP.
210
     *
211
     * @param string $source
212
     *
213
     * @return Swift_Transport_EsmtpTransport
214
     */
215
    public function setSourceIp($source)
216
    {
217
        $this->_params['sourceIp'] = $source;
218
219
        return $this;
220
    }
221
222
    /**
223
     * Returns the IP used to connect to the destination.
224
     *
225
     * @return string
226
     */
227
    public function getSourceIp()
228
    {
229
        return isset($this->_params['sourceIp']) ? $this->_params['sourceIp'] : null;
230
    }
231
232
    /**
233
     * Set ESMTP extension handlers.
234
     *
235
     * @param Swift_Transport_EsmtpHandler[] $handlers
236
     *
237
     * @return Swift_Transport_EsmtpTransport
238
     */
239 121
    public function setExtensionHandlers(array $handlers)
240
    {
241 121
        $assoc = array();
242 121
        foreach ($handlers as $handler) {
243 15
            $assoc[$handler->getHandledKeyword()] = $handler;
244
        }
245
246 121
        @uasort($assoc, array($this, '_sortHandlers'));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
247 121
        $this->_handlers = $assoc;
248 121
        $this->_setHandlerParams();
249
250 121
        return $this;
251
    }
252
253
    /**
254
     * Get ESMTP extension handlers.
255
     *
256
     * @return Swift_Transport_EsmtpHandler[]
257
     */
258 1
    public function getExtensionHandlers()
259
    {
260 1
        return array_values($this->_handlers);
261
    }
262
263
    /**
264
     * Run a command against the buffer, expecting the given response codes.
265
     *
266
     * If no response codes are given, the response will not be validated.
267
     * If codes are given, an exception will be thrown on an invalid response.
268
     *
269
     * @param string   $command
270
     * @param int[]    $codes
271
     * @param string[] $failures An array of failures by-reference
272
     *
273
     * @return string
274
     */
275 96
    public function executeCommand($command, $codes = array(), &$failures = null)
276
    {
277 96
        $failures = (array)$failures;
278 96
        $stopSignal = false;
279 96
        $response = null;
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
280
281 96
        foreach ($this->_getActiveHandlers() as $handler) {
282 4
            $response = $handler->onCommand(
283
                $this, $command, $codes, $failures, $stopSignal
0 ignored issues
show
Bug introduced by
It seems like $failures defined by (array) $failures on line 277 can also be of type array; however, Swift_Transport_EsmtpHandler::onCommand() does only seem to accept array<integer,string>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
284
            );
285
286 4
            if ($stopSignal) {
287 4
                return $response;
288
            }
289
        }
290
291 96
        return parent::executeCommand($command, $codes, $failures);
0 ignored issues
show
Bug introduced by
It seems like $failures defined by (array) $failures on line 277 can also be of type array; however, Swift_Transport_Abstract...sport::executeCommand() does only seem to accept array<integer,string>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
292
    }
293
294
    /**
295
     * Mixin handling method for ESMTP handlers
296
     *
297
     * @param $method
298
     * @param $args
299
     *
300
     * @return $this|mixed
301
     */
302 7
    public function __call($method, $args)
303
    {
304 7
        foreach ($this->_handlers as $handler) {
305
306 7
            $inArray = in_array(
307 7
                Swift::strtolowerWithStaticCache($method),
308 7
                array_map(array('Swift', 'strtolowerWithStaticCache'), (array)$handler->exposeMixinMethods()),
309 7
                true
310
            );
311
312 7
            if ($inArray) {
313 7
                $return = call_user_func_array(array($handler, $method), $args);
314
315
                // allow fluid method calls
316
                if (
317 7
                    null === $return
318
                    &&
319 7
                    0 === strpos($method, 'set')
320
                ) {
321 6
                    return $this;
322
                } else {
323 1
                    return $return;
324
                }
325
            }
326
        }
327
        trigger_error('Call to undefined method ' . $method, E_USER_ERROR);
328
    }
329
330
    /** Get the params to initialize the buffer */
331 96
    protected function _getBufferParams()
332
    {
333 96
        return $this->_params;
334
    }
335
336
    /**
337
     * Overridden to perform EHLO instead
338
     *
339
     * @throws Swift_TransportException
340
     */
341 92
    protected function _doHeloCommand()
342
    {
343
        try {
344 92
            $response = $this->executeCommand(
345 92
                sprintf("EHLO %s\r\n", $this->_domain), array(250)
346
            );
347 6
        } catch (Swift_TransportException $e) {
348 6
            parent::_doHeloCommand();
349
350 4
            return null;
351
        }
352
353 86
        if ($this->_params['tls']) {
354
            try {
355
                $this->executeCommand("STARTTLS\r\n", array(220));
356
357
                if (!$this->_buffer->startTLS()) {
0 ignored issues
show
Bug introduced by
The method startTLS does only exist in Swift_Transport_StreamBuffer, but not in Swift_Transport_IoBuffer.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
358
                    throw new Swift_TransportException('Unable to connect with TLS encryption');
359
                }
360
361
                try {
362
                    $response = $this->executeCommand(
363
                        sprintf("EHLO %s\r\n", $this->_domain), array(250)
364
                    );
365
                } catch (Swift_TransportException $e) {
366
                    parent::_doHeloCommand();
367
368
                    return null;
369
                }
370
            } catch (Swift_TransportException $e) {
371
                $this->_throwException($e);
372
            }
373
        }
374
375 86
        $this->_capabilities = $this->_getCapabilities($response);
376 86
        $this->_setHandlerParams();
377 86
        foreach ($this->_getActiveHandlers() as $handler) {
378 6
            $handler->afterEhlo($this);
379
        }
380 86
    }
381
382
    /**
383
     * Overridden to add Extension support
384
     *
385
     * @param $address
386
     */
387 56 View Code Duplication
    protected function _doMailFromCommand($address)
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...
388
    {
389 56
        $handlers = $this->_getActiveHandlers();
390
391 56
        $params = array();
392 56
        foreach ($handlers as $handler) {
393 2
            $params = array_merge($params, (array)$handler->getMailParams());
394
        }
395
396 56
        $paramStr = !empty($params) ? ' ' . implode(' ', $params) : '';
397 56
        $this->executeCommand(
398 56
            sprintf("MAIL FROM:<%s>%s\r\n", $address, $paramStr), array(250)
399
        );
400 54
    }
401
402
    /**
403
     * Overridden to add Extension support
404
     *
405
     * @param $address
406
     */
407 54 View Code Duplication
    protected function _doRcptToCommand($address)
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...
408
    {
409 54
        $handlers = $this->_getActiveHandlers();
410
411 54
        $params = array();
412 54
        foreach ($handlers as $handler) {
413 2
            $params = array_merge($params, (array)$handler->getRcptParams());
414
        }
415
416 54
        $paramStr = !empty($params) ? ' ' . implode(' ', $params) : '';
417 54
        $this->executeCommand(
418 54
            sprintf("RCPT TO:<%s>%s\r\n", $address, $paramStr), array(250, 251, 252)
419
        );
420 48
    }
421
422
    /**
423
     * Determine ESMTP capabilities by function group
424
     *
425
     * @param string $ehloResponse
426
     *
427
     * @return array
428
     */
429 86
    private function _getCapabilities($ehloResponse)
430
    {
431 86
        $capabilities = array();
432 86
        $ehloResponse = trim($ehloResponse);
433 86
        $lines = explode("\r\n", $ehloResponse);
434 86
        array_shift($lines);
435 86
        foreach ($lines as $line) {
436 10
            if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) {
437 10
                $keyword = strtoupper($matches[1]);
438 10
                $paramStr = strtoupper(ltrim($matches[2], ' ='));
439 10
                $params = !empty($paramStr) ? explode(' ', $paramStr) : array();
440 10
                $capabilities[$keyword] = $params;
441
            }
442
        }
443
444 86
        return $capabilities;
445
    }
446
447
    /**
448
     * Set parameters which are used by each extension handler
449
     */
450 121
    private function _setHandlerParams()
451
    {
452 121
        foreach ($this->_handlers as $keyword => $handler) {
453 15
            if (isset($this->_capabilities[$keyword])) {
454 15
                $handler->setKeywordParams($this->_capabilities[$keyword]);
0 ignored issues
show
Documentation introduced by
$this->_capabilities[$keyword] is of type string, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
455
            }
456
        }
457 121
    }
458
459
    /**
460
     * Get ESMTP handlers which are currently ok to use
461
     *
462
     * @return Swift_Transport_EsmtpHandler[]
463
     */
464 96
    private function _getActiveHandlers()
465
    {
466 96
        $handlers = array();
467 96
        foreach ($this->_handlers as $keyword => $handler) {
468 10
            if (isset($this->_capabilities[$keyword])) {
469 10
                $handlers[] = $handler;
470
            }
471
        }
472
473 96
        return $handlers;
474
    }
475
476
    /**
477
     * Custom sort for extension handler ordering
478
     *
479
     * @param Swift_Transport_EsmtpHandler $a
480
     * @param Swift_Transport_EsmtpHandler $b
481
     *
482
     * @return integer
483
     */
484 10
    private function _sortHandlers($a, $b)
485
    {
486 10
        return $a->getPriorityOver($b->getHandledKeyword());
487
    }
488
}
489