Completed
Push — 5.x ( 68bc4d...817861 )
by Lars
13:43 queued 03:23
created

Swift_Transport_MailTransport   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 325
Duplicated Lines 9.85 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 66.96%

Importance

Changes 0
Metric Value
dl 32
loc 325
ccs 77
cts 115
cp 0.6696
rs 8.2857
c 0
b 0
f 0
wmc 39
lcom 1
cbo 5

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A isStarted() 0 4 1
A start() 0 3 1
A stop() 0 3 1
A setExtraParams() 0 6 1
A getExtraParams() 0 4 1
F send() 0 101 12
A registerPlugin() 0 4 1
A _throwException() 14 14 3
A mail() 0 11 2
A _getReversePath() 18 18 4
B _isShellSafe() 0 24 6
B _formatExtraParams() 0 16 5

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 using the mail() function.
13
 *
14
 * It is advised that users do not use this transport if at all possible
15
 * since a number of plugin features cannot be used in conjunction with this
16
 * transport due to the internal interface in PHP itself.
17
 *
18
 * The level of error reporting with this transport is incredibly weak, again
19
 * due to limitations of PHP's internal mail() function.  You'll get an
20
 * all-or-nothing result from sending.
21
 *
22
 * @author Chris Corbyn
23
 */
24
class Swift_Transport_MailTransport 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...
25
{
26
    /** Additional parameters to pass to mail() */
27
    private $_extraParams = '-f%s';
28
29
    /** The event dispatcher from the plugin API */
30
    private $_eventDispatcher;
31
32
    /**
33
     * Create a new MailTransport with the $log.
34
     *
35
     * @param Swift_Events_EventDispatcher $eventDispatcher
36
     */
37 20
    public function __construct(Swift_Events_EventDispatcher $eventDispatcher)
38
    {
39 20
        $this->_eventDispatcher = $eventDispatcher;
40 20
    }
41
42
    /**
43
     * Not used.
44
     */
45
    public function isStarted()
46
    {
47
        return false;
48
    }
49
50
    /**
51
     * Not used.
52
     */
53
    public function start()
54
    {
55
    }
56
57
    /**
58
     * Not used.
59
     */
60
    public function stop()
61
    {
62
    }
63
64
    /**
65
     * Set the additional parameters used on the mail() function.
66
     *
67
     * This string is formatted for sprintf() where %s is the sender address.
68
     *
69
     * @param string $params
70
     *
71
     * @return Swift_Transport_MailTransport
72
     */
73 2
    public function setExtraParams($params)
74
    {
75 2
        $this->_extraParams = $params;
76
77 2
        return $this;
78
    }
79
80
    /**
81
     * Get the additional parameters used on the mail() function.
82
     *
83
     * This string is formatted for sprintf() where %s is the sender address.
84
     *
85
     * @return string
86
     */
87
    public function getExtraParams()
88
    {
89
        return $this->_extraParams;
90
    }
91
92
    /**
93
     * Send the given Message.
94
     *
95
     * Recipient/sender data will be retrieved from the Message API.
96
     * The return value is the number of recipients who were accepted for delivery.
97
     *
98
     * @param Swift_Mime_Message $message
99
     * @param string[]           $failedRecipients An array of failures (by-reference)
100
     *
101
     * @return int
102
     *
103
     * @throws Swift_TransportException
104
     */
105 19
    public function send(Swift_Mime_Message $message, &$failedRecipients = null)
106
    {
107 19
        $failedRecipients = (array)$failedRecipients;
108
109 19
        $evt = $this->_eventDispatcher->createSendEvent($this, $message);
110 19
        if ($evt) {
111
112
            $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
113
            if ($evt->bubbleCancelled()) {
114
                return 0;
115
            }
116
        }
117
118
        $count = (
119 19
            count((array)$message->getTo())
120 19
            + count((array)$message->getCc())
121 19
            + count((array)$message->getBcc())
122 19
        );
123
124
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
125
        // TODO: check if we need this check, breaks "Mockery"-Tests
126
        if ($count === 0) {
127
            $this->_throwException(new Swift_TransportException('Cannot send message without a recipient'));
128
        }
129
        */
130
131 19
        $toHeader = $message->getHeaders()->get('To');
0 ignored issues
show
Bug introduced by
The method get does only exist in Swift_Mime_HeaderSet, but not in Swift_Mime_Header.

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...
132 19
        $subjectHeader = $message->getHeaders()->get('Subject');
133
134 19
        if (0 === $count) {
135 1
            $this->_throwException(new Swift_TransportException('Cannot send message without a recipient'));
136
        }
137
138 18
        $to = $toHeader ? $toHeader->getFieldBody() : '';
139 18
        $subject = $subjectHeader ? $subjectHeader->getFieldBody() : '';
140
141 18
        $reversePath = $this->_getReversePath($message);
142
143
        // Remove headers that would otherwise be duplicated
144 18
        $message->getHeaders()->remove('To');
0 ignored issues
show
Bug introduced by
The method remove does only exist in Swift_Mime_HeaderSet, but not in Swift_Mime_Header.

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...
145 18
        $message->getHeaders()->remove('Subject');
146
147 18
        $messageStr = $message->toString();
148
149 18
        if ($toHeader) {
150 18
          $message->getHeaders()->set($toHeader);
0 ignored issues
show
Bug introduced by
The method set does only exist in Swift_Mime_HeaderSet, but not in Swift_Mime_Header.

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...
151 18
        }
152 18
        $message->getHeaders()->set($subjectHeader);
153
154
        // Separate headers from body
155 18
        if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n")) {
156 3
            $headers = substr($messageStr, 0, $endHeaders) . "\r\n"; // Keep last EOL
157 3
            $body = substr($messageStr, $endHeaders + 4);
158 3
        } else {
159 15
            $headers = $messageStr . "\r\n";
160 15
            $body = '';
161
        }
162
163 18
        unset($messageStr);
164
165 18
        if ("\r\n" !== PHP_EOL) {
166
            // Non-windows (not using SMTP)
167 18
            $headers = str_replace("\r\n", PHP_EOL, $headers);
168 18
            $subject = str_replace("\r\n", PHP_EOL, $subject);
169 18
            $body = str_replace("\r\n", PHP_EOL, $body);
170 18
            $to = str_replace("\r\n", PHP_EOL, $to);
171
        } else {
172
            // Windows, using SMTP
173
            $headers = str_replace("\r\n.", "\r\n..", $headers);
174
            $subject = str_replace("\r\n.", "\r\n..", $subject);
175
            $body = str_replace("\r\n.", "\r\n..", $body);
176
            $to = str_replace("\r\n", "\r\n", $to);
177 18
        }
178 1
179
        if ($this->mail($to, $subject, $body, $headers, $this->_formatExtraParams($this->_extraParams, $reversePath))) {
180
            if ($evt) {
181
                $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
182
                $evt->setFailedRecipients($failedRecipients);
183 1
                $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
184 17
            }
185 17
        } else {
186 17
            $failedRecipients = array_merge(
187 17
                $failedRecipients,
188 17
                array_keys((array)$message->getTo()),
189 17
                array_keys((array)$message->getCc()),
190
                array_keys((array)$message->getBcc())
191 17
            );
192
193
            if ($evt) {
194
                $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
195
                $evt->setFailedRecipients($failedRecipients);
196
                $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
197 17
            }
198
199
            $count = 0;
200 18
        }
201
202 18
        $message->generateId();  // Make sure a new Message ID is used
203
204
        return $count;
205
    }
206
207
    /**
208
     * Register a plugin.
209
     *
210
     * @param Swift_Events_EventListener $plugin
211
     */
212
    public function registerPlugin(Swift_Events_EventListener $plugin)
213
    {
214
        $this->_eventDispatcher->bindEventListener($plugin);
215
    }
216
217
    /**
218
     * Throw a TransportException, first sending it to any listeners
219
     *
220
     * @param Swift_TransportException $e
221
     *
222 1
     * @throws Swift_TransportException
223
     */
224 1 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...
225 1
    {
226
        $evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e);
227
        if ($evt) {
228
229
            $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
230
            if (!$evt->bubbleCancelled()) {
231
                throw $e;
232
            }
233 1
234
        } else {
235
            throw $e;
236
        }
237
    }
238
239
    /**
240
     * Send mail via the mail() function.
241
     *
242
     * This method takes the same arguments as PHP mail().
243
     *
244
     * @param string $to
245
     * @param string $subject
246
     * @param string $body
247
     * @param string $headers
248
     * @param string $extraParams
249
     *
250 3
     * @return bool
251
     */
252
    public function mail($to, $subject, $body, $headers = null, $extraParams = null)
253 3
    {
254 3
        /** @noinspection DeprecatedIniOptionsInspection */
255
        if (!ini_get('safe_mode')) {
256
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
257
            return @mail($to, $subject, $body, $headers, $extraParams);
258
        }
259
260
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
261
        return @mail($to, $subject, $body, $headers);
262
    }
263
264
    /**
265
     * Determine the best-use reverse path for this message
266
     *
267 18
     * @param Swift_Mime_Message $message
268
     *
269 18
     * @return mixed|null|string
270 18
     */
271 18 View Code Duplication
    private 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...
272 18
    {
273 18
        $return = $message->getReturnPath();
274 3
        $sender = $message->getSender();
275 18
        $from = $message->getFrom();
276
        $path = null;
277
        if (!empty($return)) {
278 15
            $path = $return;
279
        } elseif (!empty($sender)) {
280
            $keys = array_keys($sender);
281
            $path = array_shift($keys);
282
        } elseif (!empty($from)) {
283 18
            $keys = array_keys($from);
284
            $path = array_shift($keys);
285
        }
286
287
        return $path;
288
    }
289
290
    /**
291
     * Fix CVE-2016-10074 by disallowing potentially unsafe shell characters.
292
     *
293
     * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
294 18
     *
295
     * @param string $string The string to be validated
296 18
     *
297 17
     * @return bool
298 17
     */
299 17
    private function _isShellSafe($string)
300 17
    {
301
        // Future-proof
302 18
        if (
303
            escapeshellcmd($string) !== $string
304
            ||
305
            !in_array(escapeshellarg($string), array("'$string'", "\"$string\""), true)
306
        ) {
307
            return false;
308
        }
309
310
        $length = strlen($string);
311
        for ($i = 0; $i < $length; ++$i) {
312
            $c = $string[$i];
313
            // All other characters have a special meaning in at least one common shell, including = and +.
314
            // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
315
            // Note that this does permit non-Latin alphanumeric characters based on the current locale.
316
            if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
317
                return false;
318
            }
319
        }
320
321
        return true;
322
    }
323
324
    /**
325
     * Return php mail extra params to use for invoker->mail.
326
     *
327
     * @param string $extraParams
328
     * @param string $reversePath
329
     *
330
     * @return null|string
331
     */
332
    private function _formatExtraParams($extraParams, $reversePath)
333
    {
334
        if (strpos($extraParams, '-f%s') !== false) {
335
            if (
336
                empty($reversePath)
337
                ||
338
                false === $this->_isShellSafe($reversePath)
339
            ) {
340
                $extraParams = str_replace('-f%s', '', $extraParams);
341
            } else {
342
                $extraParams = sprintf($extraParams, $reversePath);
343
            }
344
        }
345
346
        return !empty($extraParams) ? $extraParams : null;
347
    }
348
}
349