Passed
Pull Request — 4 (#10048)
by Steve
08:16
created

Swift_Transport_MailTransport::setExtraParams()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
3
/**
4
 * This file was copied in from swiftmailer/swiftmailer v5.4.12 after it was removed from switftmailer v6
5
 * It has been slightly modified to meet phpcs standards and to update method signatures to match the swiftmailer v6
6
 */
7
8
/*
9
 * This file is part of SwiftMailer.
10
 * (c) 2004-2009 Chris Corbyn
11
 *
12
 * For the full copyright and license information, please view the LICENSE file (MIT)
13
 * https://github.com/swiftmailer/swiftmailer/blob/181b89f18a90f8925ef805f950d47a7190e9b950/LICENSE
14
 */
15
16
/**
17
 * Sends Messages using the mail() function.
18
 *
19
 * It is advised that users do not use this transport if at all possible
20
 * since a number of plugin features cannot be used in conjunction with this
21
 * transport due to the internal interface in PHP itself.
22
 *
23
 * The level of error reporting with this transport is incredibly weak, again
24
 * due to limitations of PHP's internal mail() function.  You'll get an
25
 * all-or-nothing result from sending.
26
 *
27
 * @author Chris Corbyn
28
 *
29
 * at deprecated since 5.4.5 (to be removed in 6.0)
30
 *
31
 * @internal
32
 */
33
// @codingStandardsIgnoreStart
34
// ignore missing namespace
35
class Swift_Transport_MailTransport implements Swift_Transport
36
// @codingStandardsIgnoreEnd
37
{
38
    /** Additional parameters to pass to mail() */
39
    private $_extraParams = '-f%s';
40
41
    /** The event dispatcher from the plugin API */
42
    private $_eventDispatcher;
43
44
    /** An invoker that calls the mail() function */
45
    private $_invoker;
46
47
    /**
48
     * Create a new MailTransport with the $log.
49
     *
50
     * @param Swift_Transport_MailInvoker  $invoker
51
     * @param Swift_Events_EventDispatcher $eventDispatcher
52
     */
53
    public function __construct(Swift_Transport_MailInvoker $invoker, Swift_Events_EventDispatcher $eventDispatcher)
54
    {
55
        // @trigger_error(sprintf('The %s class is deprecated since version 5.4.5 and will be removed in 6.0. Use the Sendmail or SMTP transport instead.', __CLASS__), E_USER_DEPRECATED);
56
57
        $this->_invoker = $invoker;
58
        $this->_eventDispatcher = $eventDispatcher;
59
    }
60
61
    /**
62
     * Not used.
63
     */
64
    public function isStarted()
65
    {
66
        return false;
67
    }
68
69
    /**
70
     * Not used.
71
     */
72
    public function start()
73
    {
74
    }
75
76
    /**
77
     * Not used.
78
     */
79
    public function stop()
80
    {
81
    }
82
83
    /**
84
     * Set the additional parameters used on the mail() function.
85
     *
86
     * This string is formatted for sprintf() where %s is the sender address.
87
     *
88
     * @param string $params
89
     *
90
     * @return $this
91
     */
92
    public function setExtraParams($params)
93
    {
94
        $this->_extraParams = $params;
95
96
        return $this;
97
    }
98
99
    /**
100
     * Get the additional parameters used on the mail() function.
101
     *
102
     * This string is formatted for sprintf() where %s is the sender address.
103
     *
104
     * @return string
105
     */
106
    public function getExtraParams()
107
    {
108
        return $this->_extraParams;
109
    }
110
111
    /**
112
     * Send the given Message.
113
     *
114
     * Recipient/sender data will be retrieved from the Message API.
115
     * The return value is the number of recipients who were accepted for delivery.
116
     *
117
     * @param Swift_Mime_Message $message
0 ignored issues
show
Bug introduced by
The type Swift_Mime_Message was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
118
     * @param string[]           $failedRecipients An array of failures by-reference
119
     *
120
     * @return int
121
     */
122
    public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
123
    {
124
        $failedRecipients = (array) $failedRecipients;
125
126
        if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) {
127
            $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
128
            if ($evt->bubbleCancelled()) {
129
                return 0;
130
            }
131
        }
132
133
        $count = (
134
            count((array) $message->getTo())
135
            + count((array) $message->getCc())
136
            + count((array) $message->getBcc())
137
            );
138
139
        $toHeader = $message->getHeaders()->get('To');
140
        $subjectHeader = $message->getHeaders()->get('Subject');
141
142
        if (0 === $count) {
143
            $this->_throwException(new Swift_TransportException('Cannot send message without a recipient'));
144
        }
145
        $to = $toHeader ? $toHeader->getFieldBody() : '';
146
        $subject = $subjectHeader ? $subjectHeader->getFieldBody() : '';
147
148
        $reversePath = $this->_getReversePath($message);
149
150
        // Remove headers that would otherwise be duplicated
151
        $message->getHeaders()->remove('To');
152
        $message->getHeaders()->remove('Subject');
153
154
        $messageStr = $message->toString();
155
156
        if ($toHeader) {
157
            $message->getHeaders()->set($toHeader);
158
        }
159
        $message->getHeaders()->set($subjectHeader);
0 ignored issues
show
Bug introduced by
It seems like $subjectHeader can also be of type null; however, parameter $header of Swift_Mime_SimpleHeaderSet::set() does only seem to accept Swift_Mime_Header, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

159
        $message->getHeaders()->set(/** @scrutinizer ignore-type */ $subjectHeader);
Loading history...
160
161
        // Separate headers from body
162
        if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n")) {
163
            $headers = substr($messageStr, 0, $endHeaders) . "\r\n"; //Keep last EOL
164
            $body = substr($messageStr, $endHeaders + 4);
165
        } else {
166
            $headers = $messageStr . "\r\n";
167
            $body = '';
168
        }
169
170
        unset($messageStr);
171
172
        if ("\r\n" != PHP_EOL) {
173
            // Non-windows (not using SMTP)
174
            $headers = str_replace("\r\n", PHP_EOL, $headers);
175
            $subject = str_replace("\r\n", PHP_EOL, $subject);
176
            $body = str_replace("\r\n", PHP_EOL, $body);
177
            $to = str_replace("\r\n", PHP_EOL, $to);
178
        } else {
179
            // Windows, using SMTP
180
            $headers = str_replace("\r\n.", "\r\n..", $headers);
181
            $subject = str_replace("\r\n.", "\r\n..", $subject);
182
            $body = str_replace("\r\n.", "\r\n..", $body);
183
            $to = str_replace("\r\n.", "\r\n..", $to);
184
        }
185
186
        if ($this->_invoker->mail($to, $subject, $body, $headers, $this->_formatExtraParams($this->_extraParams, $reversePath))) {
187
            if ($evt) {
0 ignored issues
show
introduced by
$evt is of type Swift_Events_SendEvent, thus it always evaluated to true.
Loading history...
188
                $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
189
                $evt->setFailedRecipients($failedRecipients);
190
                $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
191
            }
192
        } else {
193
            $failedRecipients = array_merge(
194
                $failedRecipients,
195
                array_keys((array) $message->getTo()),
196
                array_keys((array) $message->getCc()),
197
                array_keys((array) $message->getBcc())
198
            );
199
200
            if ($evt) {
0 ignored issues
show
introduced by
$evt is of type Swift_Events_SendEvent, thus it always evaluated to true.
Loading history...
201
                $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
202
                $evt->setFailedRecipients($failedRecipients);
203
                $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
204
            }
205
206
            $message->generateId();
207
208
            $count = 0;
209
        }
210
211
        return $count;
212
    }
213
214
    /**
215
     * Register a plugin.
216
     *
217
     * @param Swift_Events_EventListener $plugin
218
     */
219
    public function registerPlugin(Swift_Events_EventListener $plugin)
220
    {
221
        $this->_eventDispatcher->bindEventListener($plugin);
222
    }
223
224
    /** Throw a TransportException, first sending it to any listeners */
225
    protected function _throwException(Swift_TransportException $e)
226
    {
227
        if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) {
228
            $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
229
            if (!$evt->bubbleCancelled()) {
230
                throw $e;
231
            }
232
        } else {
233
            throw $e;
234
        }
235
    }
236
237
    /** Determine the best-use reverse path for this message */
238
    private function _getReversePath(Swift_Message $message)
239
    {
240
        $return = $message->getReturnPath();
241
        // casting to array to fixed incorrect PHPDOC in Swift_Mime_SimpleMessage which specifies @string
242
        $sender = (array) $message->getSender();
243
        $from = $message->getFrom();
244
        $path = null;
245
        if (!empty($return)) {
246
            $path = $return;
247
        } elseif (!empty($sender)) {
248
            $keys = array_keys($sender);
249
            $path = array_shift($keys);
250
        } elseif (!empty($from)) {
251
            $keys = array_keys($from);
252
            $path = array_shift($keys);
253
        }
254
255
        return $path;
256
    }
257
258
    /**
259
     * Fix CVE-2016-10074 by disallowing potentially unsafe shell characters.
260
     *
261
     * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
262
     *
263
     * @param string $string The string to be validated
264
     *
265
     * @return bool
266
     */
267
    private function _isShellSafe($string)
268
    {
269
        // Future-proof
270
        if (escapeshellcmd($string) !== $string || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])) {
271
            return false;
272
        }
273
274
        $length = strlen($string);
275
        for ($i = 0; $i < $length; ++$i) {
276
            $c = $string[$i];
277
            // All other characters have a special meaning in at least one common shell, including = and +.
278
            // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
279
            // Note that this does permit non-Latin alphanumeric characters based on the current locale.
280
            if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
281
                return false;
282
            }
283
        }
284
285
        return true;
286
    }
287
288
    /**
289
     * Return php mail extra params to use for invoker->mail.
290
     *
291
     * @param $extraParams
292
     * @param $reversePath
293
     *
294
     * @return string|null
295
     */
296
    private function _formatExtraParams($extraParams, $reversePath)
297
    {
298
        if (false !== strpos($extraParams, '-f%s')) {
299
            if (empty($reversePath) || false === $this->_isShellSafe($reversePath)) {
300
                $extraParams = str_replace('-f%s', '', $extraParams);
301
            } else {
302
                $extraParams = sprintf($extraParams, $reversePath);
303
            }
304
        }
305
306
        return !empty($extraParams) ? $extraParams : null;
307
    }
308
309
    /**
310
     * {@inheritdoc}
311
     */
312
    public function ping()
313
    {
314
    }
315
}
316