Passed
Branch v1.5.0 (a688ba)
by Wanderson
03:47
created
www/app/autoload.php 1 patch
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -3,7 +3,7 @@  discard block
 block discarded – undo
3 3
 /**
4 4
  * Autoload Base
5 5
  */
6
-spl_autoload_register(function ($className) {
6
+spl_autoload_register(function($className) {
7 7
 	$className = str_replace('App\\', 'app\\', $className);
8 8
 	$file = BASE_PATH . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
9 9
 	if (file_exists($file)) {
@@ -14,7 +14,7 @@  discard block
 block discarded – undo
14 14
 /**
15 15
  * Autoload Vendor
16 16
  */
17
-spl_autoload_register(function ($className) {
17
+spl_autoload_register(function($className) {
18 18
 	$file = BASE_PATH . '/vendor/' . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
19 19
 	if (file_exists($file)) {
20 20
 		return require $file;
Please login to merge, or discard this patch.
www/vendor/Win/Repositories/Database/Sql/Builders/Update.php 1 patch
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -19,7 +19,7 @@
 block discarded – undo
19 19
 	/** @return string */
20 20
 	protected function set()
21 21
 	{
22
-		return implode(', ', array_map(function ($column) {
22
+		return implode(', ', array_map(function($column) {
23 23
 			return $column . ' = ?';
24 24
 		}, array_keys($this->query->values)));
25 25
 	}
Please login to merge, or discard this patch.
www/vendor/PHPMailer/PHPMailer/SMTP.php 1 patch
Indentation   +1272 added lines, -1272 removed lines patch added patch discarded remove patch
@@ -29,621 +29,621 @@  discard block
 block discarded – undo
29 29
  */
30 30
 class SMTP
31 31
 {
32
-    /**
33
-     * The PHPMailer SMTP version number.
34
-     *
35
-     * @var string
36
-     */
37
-    const VERSION = '6.0.1';
38
-
39
-    /**
40
-     * SMTP line break constant.
41
-     *
42
-     * @var string
43
-     */
44
-    const LE = "\r\n";
45
-
46
-    /**
47
-     * The SMTP port to use if one is not specified.
48
-     *
49
-     * @var int
50
-     */
51
-    const DEFAULT_PORT = 25;
52
-
53
-    /**
54
-     * The maximum line length allowed by RFC 2822 section 2.1.1.
55
-     *
56
-     * @var int
57
-     */
58
-    const MAX_LINE_LENGTH = 998;
59
-
60
-    /**
61
-     * Debug level for no output.
62
-     */
63
-    const DEBUG_OFF = 0;
64
-
65
-    /**
66
-     * Debug level to show client -> server messages.
67
-     */
68
-    const DEBUG_CLIENT = 1;
69
-
70
-    /**
71
-     * Debug level to show client -> server and server -> client messages.
72
-     */
73
-    const DEBUG_SERVER = 2;
74
-
75
-    /**
76
-     * Debug level to show connection status, client -> server and server -> client messages.
77
-     */
78
-    const DEBUG_CONNECTION = 3;
79
-
80
-    /**
81
-     * Debug level to show all messages.
82
-     */
83
-    const DEBUG_LOWLEVEL = 4;
84
-
85
-    /**
86
-     * Debug output level.
87
-     * Options:
88
-     * * self::DEBUG_OFF (`0`) No debug output, default
89
-     * * self::DEBUG_CLIENT (`1`) Client commands
90
-     * * self::DEBUG_SERVER (`2`) Client commands and server responses
91
-     * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
92
-     * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages.
93
-     *
94
-     * @var int
95
-     */
96
-    public $do_debug = self::DEBUG_OFF;
97
-
98
-    /**
99
-     * How to handle debug output.
100
-     * Options:
101
-     * * `echo` Output plain-text as-is, appropriate for CLI
102
-     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
103
-     * * `error_log` Output to error log as configured in php.ini
104
-     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
105
-     *
106
-     * ```php
107
-     * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
108
-     * ```
109
-     *
110
-     * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug`
111
-     * level output is used:
112
-     *
113
-     * ```php
114
-     * $mail->Debugoutput = new myPsr3Logger;
115
-     * ```
116
-     *
117
-     * @var string|callable|\Psr\Log\LoggerInterface
118
-     */
119
-    public $Debugoutput = 'echo';
120
-
121
-    /**
122
-     * Whether to use VERP.
123
-     *
124
-     * @see http://en.wikipedia.org/wiki/Variable_envelope_return_path
125
-     * @see http://www.postfix.org/VERP_README.html Info on VERP
126
-     *
127
-     * @var bool
128
-     */
129
-    public $do_verp = false;
130
-
131
-    /**
132
-     * The timeout value for connection, in seconds.
133
-     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
134
-     * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
135
-     *
136
-     * @see http://tools.ietf.org/html/rfc2821#section-4.5.3.2
137
-     *
138
-     * @var int
139
-     */
140
-    public $Timeout = 300;
141
-
142
-    /**
143
-     * How long to wait for commands to complete, in seconds.
144
-     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
145
-     *
146
-     * @var int
147
-     */
148
-    public $Timelimit = 300;
149
-
150
-    /**
151
-     * Patterns to extract an SMTP transaction id from reply to a DATA command.
152
-     * The first capture group in each regex will be used as the ID.
153
-     * MS ESMTP returns the message ID, which may not be correct for internal tracking.
154
-     *
155
-     * @var string[]
156
-     */
157
-    protected $smtp_transaction_id_patterns = [
158
-        'exim' => '/[0-9]{3} OK id=(.*)/',
159
-        'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/',
160
-        'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/',
161
-        'Microsoft_ESMTP' => '/[0-9]{3} 2.[0-9].0 (.*)@(?:.*) Queued mail for delivery/',
162
-        'Amazon_SES' => '/[0-9]{3} Ok (.*)/',
163
-    ];
164
-
165
-    /**
166
-     * The last transaction ID issued in response to a DATA command,
167
-     * if one was detected.
168
-     *
169
-     * @var string|bool|null
170
-     */
171
-    protected $last_smtp_transaction_id;
172
-
173
-    /**
174
-     * The socket for the server connection.
175
-     *
176
-     * @var ?resource
177
-     */
178
-    protected $smtp_conn;
179
-
180
-    /**
181
-     * Error information, if any, for the last SMTP command.
182
-     *
183
-     * @var array
184
-     */
185
-    protected $error = [
186
-        'error' => '',
187
-        'detail' => '',
188
-        'smtp_code' => '',
189
-        'smtp_code_ex' => '',
190
-    ];
191
-
192
-    /**
193
-     * The reply the server sent to us for HELO.
194
-     * If null, no HELO string has yet been received.
195
-     *
196
-     * @var string|null
197
-     */
198
-    protected $helo_rply = null;
199
-
200
-    /**
201
-     * The set of SMTP extensions sent in reply to EHLO command.
202
-     * Indexes of the array are extension names.
203
-     * Value at index 'HELO' or 'EHLO' (according to command that was sent)
204
-     * represents the server name. In case of HELO it is the only element of the array.
205
-     * Other values can be boolean TRUE or an array containing extension options.
206
-     * If null, no HELO/EHLO string has yet been received.
207
-     *
208
-     * @var array|null
209
-     */
210
-    protected $server_caps = null;
211
-
212
-    /**
213
-     * The most recent reply received from the server.
214
-     *
215
-     * @var string
216
-     */
217
-    protected $last_reply = '';
218
-
219
-    /**
220
-     * Output debugging info via a user-selected method.
221
-     *
222
-     * @param string $str   Debug string to output
223
-     * @param int    $level The debug level of this message; see DEBUG_* constants
224
-     *
225
-     * @see SMTP::$Debugoutput
226
-     * @see SMTP::$do_debug
227
-     */
228
-    protected function edebug($str, $level = 0)
229
-    {
230
-        if ($level > $this->do_debug) {
231
-            return;
232
-        }
233
-        //Is this a PSR-3 logger?
234
-        if (is_a($this->Debugoutput, 'Psr\Log\LoggerInterface')) {
235
-            $this->Debugoutput->debug($str);
236
-
237
-            return;
238
-        }
239
-        //Avoid clash with built-in function names
240
-        if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) {
241
-            call_user_func($this->Debugoutput, $str, $level);
242
-
243
-            return;
244
-        }
245
-        switch ($this->Debugoutput) {
246
-            case 'error_log':
247
-                //Don't output, just log
248
-                error_log($str);
249
-                break;
250
-            case 'html':
251
-                //Cleans up output a bit for a better looking, HTML-safe output
252
-                echo gmdate('Y-m-d H:i:s'), ' ', htmlentities(
253
-                    preg_replace('/[\r\n]+/', '', $str),
254
-                    ENT_QUOTES,
255
-                    'UTF-8'
256
-                ), "<br>\n";
257
-                break;
258
-            case 'echo':
259
-            default:
260
-                //Normalize line breaks
261
-                $str = preg_replace('/\r\n|\r/ms', "\n", $str);
262
-                echo gmdate('Y-m-d H:i:s'),
263
-                "\t",
264
-                    //Trim trailing space
265
-                trim(
266
-                //Indent for readability, except for trailing break
267
-                    str_replace(
268
-                        "\n",
269
-                        "\n                   \t                  ",
270
-                        trim($str)
271
-                    )
272
-                ),
273
-                "\n";
274
-        }
275
-    }
276
-
277
-    /**
278
-     * Connect to an SMTP server.
279
-     *
280
-     * @param string $host    SMTP server IP or host name
281
-     * @param int    $port    The port number to connect to
282
-     * @param int    $timeout How long to wait for the connection to open
283
-     * @param array  $options An array of options for stream_context_create()
284
-     *
285
-     * @return bool
286
-     */
287
-    public function connect($host, $port = null, $timeout = 30, $options = [])
288
-    {
289
-        static $streamok;
290
-        //This is enabled by default since 5.0.0 but some providers disable it
291
-        //Check this once and cache the result
292
-        if (null === $streamok) {
293
-            $streamok = function_exists('stream_socket_client');
294
-        }
295
-        // Clear errors to avoid confusion
296
-        $this->setError('');
297
-        // Make sure we are __not__ connected
298
-        if ($this->connected()) {
299
-            // Already connected, generate error
300
-            $this->setError('Already connected to a server');
301
-
302
-            return false;
303
-        }
304
-        if (empty($port)) {
305
-            $port = self::DEFAULT_PORT;
306
-        }
307
-        // Connect to the SMTP server
308
-        $this->edebug(
309
-            "Connection: opening to $host:$port, timeout=$timeout, options=" .
310
-            (count($options) > 0 ? var_export($options, true) : 'array()'),
311
-            self::DEBUG_CONNECTION
312
-        );
313
-        $errno = 0;
314
-        $errstr = '';
315
-        if ($streamok) {
316
-            $socket_context = stream_context_create($options);
317
-            set_error_handler([$this, 'errorHandler']);
318
-            $this->smtp_conn = stream_socket_client(
319
-                $host . ':' . $port,
320
-                $errno,
321
-                $errstr,
322
-                $timeout,
323
-                STREAM_CLIENT_CONNECT,
324
-                $socket_context
325
-            );
326
-            restore_error_handler();
327
-        } else {
328
-            //Fall back to fsockopen which should work in more places, but is missing some features
329
-            $this->edebug(
330
-                'Connection: stream_socket_client not available, falling back to fsockopen',
331
-                self::DEBUG_CONNECTION
332
-            );
333
-            set_error_handler([$this, 'errorHandler']);
334
-            $this->smtp_conn = fsockopen(
335
-                $host,
336
-                $port,
337
-                $errno,
338
-                $errstr,
339
-                $timeout
340
-            );
341
-            restore_error_handler();
342
-        }
343
-        // Verify we connected properly
344
-        if (!is_resource($this->smtp_conn)) {
345
-            $this->setError(
346
-                'Failed to connect to server',
347
-                '',
348
-                (string) $errno,
349
-                (string) $errstr
350
-            );
351
-            $this->edebug(
352
-                'SMTP ERROR: ' . $this->error['error']
353
-                . ": $errstr ($errno)",
354
-                self::DEBUG_CLIENT
355
-            );
356
-
357
-            return false;
358
-        }
359
-        $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
360
-        // SMTP server can take longer to respond, give longer timeout for first read
361
-        // Windows does not have support for this timeout function
362
-        if (substr(PHP_OS, 0, 3) != 'WIN') {
363
-            $max = ini_get('max_execution_time');
364
-            // Don't bother if unlimited
365
-            if (0 != $max and $timeout > $max) {
366
-                @set_time_limit($timeout);
367
-            }
368
-            stream_set_timeout($this->smtp_conn, $timeout, 0);
369
-        }
370
-        // Get any announcement
371
-        $announce = $this->get_lines();
372
-        $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
373
-
374
-        return true;
375
-    }
376
-
377
-    /**
378
-     * Initiate a TLS (encrypted) session.
379
-     *
380
-     * @return bool
381
-     */
382
-    public function startTLS()
383
-    {
384
-        if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
385
-            return false;
386
-        }
387
-
388
-        //Allow the best TLS version(s) we can
389
-        $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
390
-
391
-        //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
392
-        //so add them back in manually if we can
393
-        if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
394
-            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
395
-            $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
396
-        }
397
-
398
-        // Begin encrypted connection
399
-        set_error_handler([$this, 'errorHandler']);
400
-        $crypto_ok = stream_socket_enable_crypto(
401
-            $this->smtp_conn,
402
-            true,
403
-            $crypto_method
404
-        );
405
-        restore_error_handler();
406
-
407
-        return (bool) $crypto_ok;
408
-    }
409
-
410
-    /**
411
-     * Perform SMTP Login.
412
-     * Must be run after hello().
413
-     *
414
-     * @see    hello()
415
-     *
416
-     * @param string $username The user name
417
-     * @param string $password The password
418
-     * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2)
419
-     * @param OAuth  $OAuth    An optional OAuth instance for XOAUTH2 Login
420
-     *
421
-     * @return bool True if successfully authenticated
422
-     */
423
-    public function authenticate(
424
-        $username,
425
-        $password,
426
-        $authtype = null,
427
-        $OAuth = null
428
-    ) {
429
-        if (!$this->server_caps) {
430
-            $this->setError('Login is not allowed before HELO/EHLO');
431
-
432
-            return false;
433
-        }
434
-
435
-        if (array_key_exists('EHLO', $this->server_caps)) {
436
-            // SMTP extensions are available; try to find a proper Login method
437
-            if (!array_key_exists('AUTH', $this->server_caps)) {
438
-                $this->setError('Login is not allowed at this stage');
439
-                // 'at this stage' means that auth may be allowed after the stage changes
440
-                // e.g. after STARTTLS
441
-
442
-                return false;
443
-            }
444
-
445
-            $this->edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
446
-            $this->edebug(
447
-                'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
448
-                self::DEBUG_LOWLEVEL
449
-            );
450
-
451
-            //If we have requested a specific auth type, check the server supports it before trying others
452
-            if (!in_array($authtype, $this->server_caps['AUTH'])) {
453
-                $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL);
454
-                $authtype = null;
455
-            }
456
-
457
-            if (empty($authtype)) {
458
-                //If no auth mechanism is specified, attempt to use these, in this order
459
-                //Try CRAM-MD5 first as it's more secure than the others
460
-                foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) {
461
-                    if (in_array($method, $this->server_caps['AUTH'])) {
462
-                        $authtype = $method;
463
-                        break;
464
-                    }
465
-                }
466
-                if (empty($authtype)) {
467
-                    $this->setError('No supported Login methods found');
468
-
469
-                    return false;
470
-                }
471
-                self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL);
472
-            }
473
-
474
-            if (!in_array($authtype, $this->server_caps['AUTH'])) {
475
-                $this->setError("The requested Login method \"$authtype\" is not supported by the server");
476
-
477
-                return false;
478
-            }
479
-        } elseif (empty($authtype)) {
480
-            $authtype = 'LOGIN';
481
-        }
482
-        switch ($authtype) {
483
-            case 'PLAIN':
484
-                // Start Login
485
-                if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
486
-                    return false;
487
-                }
488
-                // Send encoded username and password
489
-                if (!$this->sendCommand(
490
-                    'User & Password',
491
-                    base64_encode("\0" . $username . "\0" . $password),
492
-                    235
493
-                )
494
-                ) {
495
-                    return false;
496
-                }
497
-                break;
498
-            case 'LOGIN':
499
-                // Start Login
500
-                if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
501
-                    return false;
502
-                }
503
-                if (!$this->sendCommand('Username', base64_encode($username), 334)) {
504
-                    return false;
505
-                }
506
-                if (!$this->sendCommand('Password', base64_encode($password), 235)) {
507
-                    return false;
508
-                }
509
-                break;
510
-            case 'CRAM-MD5':
511
-                // Start Login
512
-                if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
513
-                    return false;
514
-                }
515
-                // Get the challenge
516
-                $challenge = base64_decode(substr($this->last_reply, 4));
517
-
518
-                // Build the response
519
-                $response = $username . ' ' . $this->hmac($challenge, $password);
520
-
521
-                // send encoded credentials
522
-                return $this->sendCommand('Username', base64_encode($response), 235);
523
-            case 'XOAUTH2':
524
-                //The OAuth instance must be set up prior to requesting auth.
525
-                if (null === $OAuth) {
526
-                    return false;
527
-                }
528
-                $oauth = $OAuth->getOauth64();
529
-
530
-                // Start Login
531
-                if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
532
-                    return false;
533
-                }
534
-                break;
535
-            default:
536
-                $this->setError("Login method \"$authtype\" is not supported");
537
-
538
-                return false;
539
-        }
540
-
541
-        return true;
542
-    }
543
-
544
-    /**
545
-     * Calculate an MD5 HMAC hash.
546
-     * Works like hash_hmac('md5', $data, $key)
547
-     * in case that function is not available.
548
-     *
549
-     * @param string $data The data to hash
550
-     * @param string $key  The key to hash with
551
-     *
552
-     * @return string
553
-     */
554
-    protected function hmac($data, $key)
555
-    {
556
-        if (function_exists('hash_hmac')) {
557
-            return hash_hmac('md5', $data, $key);
558
-        }
559
-
560
-        // The following borrowed from
561
-        // http://php.net/manual/en/function.mhash.php#27225
562
-
563
-        // RFC 2104 HMAC implementation for php.
564
-        // Creates an md5 HMAC.
565
-        // Eliminates the need to install mhash to compute a HMAC
566
-        // by Lance Rushing
567
-
568
-        $bytelen = 64; // byte length for md5
569
-        if (strlen($key) > $bytelen) {
570
-            $key = pack('H*', md5($key));
571
-        }
572
-        $key = str_pad($key, $bytelen, chr(0x00));
573
-        $ipad = str_pad('', $bytelen, chr(0x36));
574
-        $opad = str_pad('', $bytelen, chr(0x5c));
575
-        $k_ipad = $key ^ $ipad;
576
-        $k_opad = $key ^ $opad;
577
-
578
-        return md5($k_opad . pack('H*', md5($k_ipad . $data)));
579
-    }
580
-
581
-    /**
582
-     * Check connection state.
583
-     *
584
-     * @return bool True if connected
585
-     */
586
-    public function connected()
587
-    {
588
-        if (is_resource($this->smtp_conn)) {
589
-            $sock_status = stream_get_meta_data($this->smtp_conn);
590
-            if ($sock_status['eof']) {
591
-                // The socket is valid but we are not connected
592
-                $this->edebug(
593
-                    'SMTP NOTICE: EOF caught while checking if connected',
594
-                    self::DEBUG_CLIENT
595
-                );
596
-                $this->close();
597
-
598
-                return false;
599
-            }
600
-
601
-            return true; // everything looks good
602
-        }
603
-
604
-        return false;
605
-    }
606
-
607
-    /**
608
-     * Close the socket and clean up the state of the class.
609
-     * Don't use this function without first trying to use QUIT.
610
-     *
611
-     * @see quit()
612
-     */
613
-    public function close()
614
-    {
615
-        $this->setError('');
616
-        $this->server_caps = null;
617
-        $this->helo_rply = null;
618
-        if (is_resource($this->smtp_conn)) {
619
-            // close the connection and cleanup
620
-            fclose($this->smtp_conn);
621
-            $this->smtp_conn = null; //Makes for cleaner serialization
622
-            $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
623
-        }
624
-    }
625
-
626
-    /**
627
-     * Send an SMTP DATA command.
628
-     * Issues a data command and sends the msg_data to the server,
629
-     * finializing the mail transaction. $msg_data is the message
630
-     * that is to be send with the headers. Each header needs to be
631
-     * on a single line followed by a <CRLF> with the message headers
632
-     * and the message body being separated by an additional <CRLF>.
633
-     * Implements RFC 821: DATA <CRLF>.
634
-     *
635
-     * @param string $msg_data Message data to send
636
-     *
637
-     * @return bool
638
-     */
639
-    public function data($msg_data)
640
-    {
641
-        //This will use the standard timelimit
642
-        if (!$this->sendCommand('DATA', 'DATA', 354)) {
643
-            return false;
644
-        }
645
-
646
-        /* The server is ready to accept data!
32
+	/**
33
+	 * The PHPMailer SMTP version number.
34
+	 *
35
+	 * @var string
36
+	 */
37
+	const VERSION = '6.0.1';
38
+
39
+	/**
40
+	 * SMTP line break constant.
41
+	 *
42
+	 * @var string
43
+	 */
44
+	const LE = "\r\n";
45
+
46
+	/**
47
+	 * The SMTP port to use if one is not specified.
48
+	 *
49
+	 * @var int
50
+	 */
51
+	const DEFAULT_PORT = 25;
52
+
53
+	/**
54
+	 * The maximum line length allowed by RFC 2822 section 2.1.1.
55
+	 *
56
+	 * @var int
57
+	 */
58
+	const MAX_LINE_LENGTH = 998;
59
+
60
+	/**
61
+	 * Debug level for no output.
62
+	 */
63
+	const DEBUG_OFF = 0;
64
+
65
+	/**
66
+	 * Debug level to show client -> server messages.
67
+	 */
68
+	const DEBUG_CLIENT = 1;
69
+
70
+	/**
71
+	 * Debug level to show client -> server and server -> client messages.
72
+	 */
73
+	const DEBUG_SERVER = 2;
74
+
75
+	/**
76
+	 * Debug level to show connection status, client -> server and server -> client messages.
77
+	 */
78
+	const DEBUG_CONNECTION = 3;
79
+
80
+	/**
81
+	 * Debug level to show all messages.
82
+	 */
83
+	const DEBUG_LOWLEVEL = 4;
84
+
85
+	/**
86
+	 * Debug output level.
87
+	 * Options:
88
+	 * * self::DEBUG_OFF (`0`) No debug output, default
89
+	 * * self::DEBUG_CLIENT (`1`) Client commands
90
+	 * * self::DEBUG_SERVER (`2`) Client commands and server responses
91
+	 * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
92
+	 * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages.
93
+	 *
94
+	 * @var int
95
+	 */
96
+	public $do_debug = self::DEBUG_OFF;
97
+
98
+	/**
99
+	 * How to handle debug output.
100
+	 * Options:
101
+	 * * `echo` Output plain-text as-is, appropriate for CLI
102
+	 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
103
+	 * * `error_log` Output to error log as configured in php.ini
104
+	 * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
105
+	 *
106
+	 * ```php
107
+	 * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
108
+	 * ```
109
+	 *
110
+	 * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug`
111
+	 * level output is used:
112
+	 *
113
+	 * ```php
114
+	 * $mail->Debugoutput = new myPsr3Logger;
115
+	 * ```
116
+	 *
117
+	 * @var string|callable|\Psr\Log\LoggerInterface
118
+	 */
119
+	public $Debugoutput = 'echo';
120
+
121
+	/**
122
+	 * Whether to use VERP.
123
+	 *
124
+	 * @see http://en.wikipedia.org/wiki/Variable_envelope_return_path
125
+	 * @see http://www.postfix.org/VERP_README.html Info on VERP
126
+	 *
127
+	 * @var bool
128
+	 */
129
+	public $do_verp = false;
130
+
131
+	/**
132
+	 * The timeout value for connection, in seconds.
133
+	 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
134
+	 * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
135
+	 *
136
+	 * @see http://tools.ietf.org/html/rfc2821#section-4.5.3.2
137
+	 *
138
+	 * @var int
139
+	 */
140
+	public $Timeout = 300;
141
+
142
+	/**
143
+	 * How long to wait for commands to complete, in seconds.
144
+	 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
145
+	 *
146
+	 * @var int
147
+	 */
148
+	public $Timelimit = 300;
149
+
150
+	/**
151
+	 * Patterns to extract an SMTP transaction id from reply to a DATA command.
152
+	 * The first capture group in each regex will be used as the ID.
153
+	 * MS ESMTP returns the message ID, which may not be correct for internal tracking.
154
+	 *
155
+	 * @var string[]
156
+	 */
157
+	protected $smtp_transaction_id_patterns = [
158
+		'exim' => '/[0-9]{3} OK id=(.*)/',
159
+		'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/',
160
+		'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/',
161
+		'Microsoft_ESMTP' => '/[0-9]{3} 2.[0-9].0 (.*)@(?:.*) Queued mail for delivery/',
162
+		'Amazon_SES' => '/[0-9]{3} Ok (.*)/',
163
+	];
164
+
165
+	/**
166
+	 * The last transaction ID issued in response to a DATA command,
167
+	 * if one was detected.
168
+	 *
169
+	 * @var string|bool|null
170
+	 */
171
+	protected $last_smtp_transaction_id;
172
+
173
+	/**
174
+	 * The socket for the server connection.
175
+	 *
176
+	 * @var ?resource
177
+	 */
178
+	protected $smtp_conn;
179
+
180
+	/**
181
+	 * Error information, if any, for the last SMTP command.
182
+	 *
183
+	 * @var array
184
+	 */
185
+	protected $error = [
186
+		'error' => '',
187
+		'detail' => '',
188
+		'smtp_code' => '',
189
+		'smtp_code_ex' => '',
190
+	];
191
+
192
+	/**
193
+	 * The reply the server sent to us for HELO.
194
+	 * If null, no HELO string has yet been received.
195
+	 *
196
+	 * @var string|null
197
+	 */
198
+	protected $helo_rply = null;
199
+
200
+	/**
201
+	 * The set of SMTP extensions sent in reply to EHLO command.
202
+	 * Indexes of the array are extension names.
203
+	 * Value at index 'HELO' or 'EHLO' (according to command that was sent)
204
+	 * represents the server name. In case of HELO it is the only element of the array.
205
+	 * Other values can be boolean TRUE or an array containing extension options.
206
+	 * If null, no HELO/EHLO string has yet been received.
207
+	 *
208
+	 * @var array|null
209
+	 */
210
+	protected $server_caps = null;
211
+
212
+	/**
213
+	 * The most recent reply received from the server.
214
+	 *
215
+	 * @var string
216
+	 */
217
+	protected $last_reply = '';
218
+
219
+	/**
220
+	 * Output debugging info via a user-selected method.
221
+	 *
222
+	 * @param string $str   Debug string to output
223
+	 * @param int    $level The debug level of this message; see DEBUG_* constants
224
+	 *
225
+	 * @see SMTP::$Debugoutput
226
+	 * @see SMTP::$do_debug
227
+	 */
228
+	protected function edebug($str, $level = 0)
229
+	{
230
+		if ($level > $this->do_debug) {
231
+			return;
232
+		}
233
+		//Is this a PSR-3 logger?
234
+		if (is_a($this->Debugoutput, 'Psr\Log\LoggerInterface')) {
235
+			$this->Debugoutput->debug($str);
236
+
237
+			return;
238
+		}
239
+		//Avoid clash with built-in function names
240
+		if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) {
241
+			call_user_func($this->Debugoutput, $str, $level);
242
+
243
+			return;
244
+		}
245
+		switch ($this->Debugoutput) {
246
+			case 'error_log':
247
+				//Don't output, just log
248
+				error_log($str);
249
+				break;
250
+			case 'html':
251
+				//Cleans up output a bit for a better looking, HTML-safe output
252
+				echo gmdate('Y-m-d H:i:s'), ' ', htmlentities(
253
+					preg_replace('/[\r\n]+/', '', $str),
254
+					ENT_QUOTES,
255
+					'UTF-8'
256
+				), "<br>\n";
257
+				break;
258
+			case 'echo':
259
+			default:
260
+				//Normalize line breaks
261
+				$str = preg_replace('/\r\n|\r/ms', "\n", $str);
262
+				echo gmdate('Y-m-d H:i:s'),
263
+				"\t",
264
+					//Trim trailing space
265
+				trim(
266
+				//Indent for readability, except for trailing break
267
+					str_replace(
268
+						"\n",
269
+						"\n                   \t                  ",
270
+						trim($str)
271
+					)
272
+				),
273
+				"\n";
274
+		}
275
+	}
276
+
277
+	/**
278
+	 * Connect to an SMTP server.
279
+	 *
280
+	 * @param string $host    SMTP server IP or host name
281
+	 * @param int    $port    The port number to connect to
282
+	 * @param int    $timeout How long to wait for the connection to open
283
+	 * @param array  $options An array of options for stream_context_create()
284
+	 *
285
+	 * @return bool
286
+	 */
287
+	public function connect($host, $port = null, $timeout = 30, $options = [])
288
+	{
289
+		static $streamok;
290
+		//This is enabled by default since 5.0.0 but some providers disable it
291
+		//Check this once and cache the result
292
+		if (null === $streamok) {
293
+			$streamok = function_exists('stream_socket_client');
294
+		}
295
+		// Clear errors to avoid confusion
296
+		$this->setError('');
297
+		// Make sure we are __not__ connected
298
+		if ($this->connected()) {
299
+			// Already connected, generate error
300
+			$this->setError('Already connected to a server');
301
+
302
+			return false;
303
+		}
304
+		if (empty($port)) {
305
+			$port = self::DEFAULT_PORT;
306
+		}
307
+		// Connect to the SMTP server
308
+		$this->edebug(
309
+			"Connection: opening to $host:$port, timeout=$timeout, options=" .
310
+			(count($options) > 0 ? var_export($options, true) : 'array()'),
311
+			self::DEBUG_CONNECTION
312
+		);
313
+		$errno = 0;
314
+		$errstr = '';
315
+		if ($streamok) {
316
+			$socket_context = stream_context_create($options);
317
+			set_error_handler([$this, 'errorHandler']);
318
+			$this->smtp_conn = stream_socket_client(
319
+				$host . ':' . $port,
320
+				$errno,
321
+				$errstr,
322
+				$timeout,
323
+				STREAM_CLIENT_CONNECT,
324
+				$socket_context
325
+			);
326
+			restore_error_handler();
327
+		} else {
328
+			//Fall back to fsockopen which should work in more places, but is missing some features
329
+			$this->edebug(
330
+				'Connection: stream_socket_client not available, falling back to fsockopen',
331
+				self::DEBUG_CONNECTION
332
+			);
333
+			set_error_handler([$this, 'errorHandler']);
334
+			$this->smtp_conn = fsockopen(
335
+				$host,
336
+				$port,
337
+				$errno,
338
+				$errstr,
339
+				$timeout
340
+			);
341
+			restore_error_handler();
342
+		}
343
+		// Verify we connected properly
344
+		if (!is_resource($this->smtp_conn)) {
345
+			$this->setError(
346
+				'Failed to connect to server',
347
+				'',
348
+				(string) $errno,
349
+				(string) $errstr
350
+			);
351
+			$this->edebug(
352
+				'SMTP ERROR: ' . $this->error['error']
353
+				. ": $errstr ($errno)",
354
+				self::DEBUG_CLIENT
355
+			);
356
+
357
+			return false;
358
+		}
359
+		$this->edebug('Connection: opened', self::DEBUG_CONNECTION);
360
+		// SMTP server can take longer to respond, give longer timeout for first read
361
+		// Windows does not have support for this timeout function
362
+		if (substr(PHP_OS, 0, 3) != 'WIN') {
363
+			$max = ini_get('max_execution_time');
364
+			// Don't bother if unlimited
365
+			if (0 != $max and $timeout > $max) {
366
+				@set_time_limit($timeout);
367
+			}
368
+			stream_set_timeout($this->smtp_conn, $timeout, 0);
369
+		}
370
+		// Get any announcement
371
+		$announce = $this->get_lines();
372
+		$this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
373
+
374
+		return true;
375
+	}
376
+
377
+	/**
378
+	 * Initiate a TLS (encrypted) session.
379
+	 *
380
+	 * @return bool
381
+	 */
382
+	public function startTLS()
383
+	{
384
+		if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
385
+			return false;
386
+		}
387
+
388
+		//Allow the best TLS version(s) we can
389
+		$crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
390
+
391
+		//PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
392
+		//so add them back in manually if we can
393
+		if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
394
+			$crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
395
+			$crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
396
+		}
397
+
398
+		// Begin encrypted connection
399
+		set_error_handler([$this, 'errorHandler']);
400
+		$crypto_ok = stream_socket_enable_crypto(
401
+			$this->smtp_conn,
402
+			true,
403
+			$crypto_method
404
+		);
405
+		restore_error_handler();
406
+
407
+		return (bool) $crypto_ok;
408
+	}
409
+
410
+	/**
411
+	 * Perform SMTP Login.
412
+	 * Must be run after hello().
413
+	 *
414
+	 * @see    hello()
415
+	 *
416
+	 * @param string $username The user name
417
+	 * @param string $password The password
418
+	 * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2)
419
+	 * @param OAuth  $OAuth    An optional OAuth instance for XOAUTH2 Login
420
+	 *
421
+	 * @return bool True if successfully authenticated
422
+	 */
423
+	public function authenticate(
424
+		$username,
425
+		$password,
426
+		$authtype = null,
427
+		$OAuth = null
428
+	) {
429
+		if (!$this->server_caps) {
430
+			$this->setError('Login is not allowed before HELO/EHLO');
431
+
432
+			return false;
433
+		}
434
+
435
+		if (array_key_exists('EHLO', $this->server_caps)) {
436
+			// SMTP extensions are available; try to find a proper Login method
437
+			if (!array_key_exists('AUTH', $this->server_caps)) {
438
+				$this->setError('Login is not allowed at this stage');
439
+				// 'at this stage' means that auth may be allowed after the stage changes
440
+				// e.g. after STARTTLS
441
+
442
+				return false;
443
+			}
444
+
445
+			$this->edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
446
+			$this->edebug(
447
+				'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
448
+				self::DEBUG_LOWLEVEL
449
+			);
450
+
451
+			//If we have requested a specific auth type, check the server supports it before trying others
452
+			if (!in_array($authtype, $this->server_caps['AUTH'])) {
453
+				$this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL);
454
+				$authtype = null;
455
+			}
456
+
457
+			if (empty($authtype)) {
458
+				//If no auth mechanism is specified, attempt to use these, in this order
459
+				//Try CRAM-MD5 first as it's more secure than the others
460
+				foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) {
461
+					if (in_array($method, $this->server_caps['AUTH'])) {
462
+						$authtype = $method;
463
+						break;
464
+					}
465
+				}
466
+				if (empty($authtype)) {
467
+					$this->setError('No supported Login methods found');
468
+
469
+					return false;
470
+				}
471
+				self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL);
472
+			}
473
+
474
+			if (!in_array($authtype, $this->server_caps['AUTH'])) {
475
+				$this->setError("The requested Login method \"$authtype\" is not supported by the server");
476
+
477
+				return false;
478
+			}
479
+		} elseif (empty($authtype)) {
480
+			$authtype = 'LOGIN';
481
+		}
482
+		switch ($authtype) {
483
+			case 'PLAIN':
484
+				// Start Login
485
+				if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
486
+					return false;
487
+				}
488
+				// Send encoded username and password
489
+				if (!$this->sendCommand(
490
+					'User & Password',
491
+					base64_encode("\0" . $username . "\0" . $password),
492
+					235
493
+				)
494
+				) {
495
+					return false;
496
+				}
497
+				break;
498
+			case 'LOGIN':
499
+				// Start Login
500
+				if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
501
+					return false;
502
+				}
503
+				if (!$this->sendCommand('Username', base64_encode($username), 334)) {
504
+					return false;
505
+				}
506
+				if (!$this->sendCommand('Password', base64_encode($password), 235)) {
507
+					return false;
508
+				}
509
+				break;
510
+			case 'CRAM-MD5':
511
+				// Start Login
512
+				if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
513
+					return false;
514
+				}
515
+				// Get the challenge
516
+				$challenge = base64_decode(substr($this->last_reply, 4));
517
+
518
+				// Build the response
519
+				$response = $username . ' ' . $this->hmac($challenge, $password);
520
+
521
+				// send encoded credentials
522
+				return $this->sendCommand('Username', base64_encode($response), 235);
523
+			case 'XOAUTH2':
524
+				//The OAuth instance must be set up prior to requesting auth.
525
+				if (null === $OAuth) {
526
+					return false;
527
+				}
528
+				$oauth = $OAuth->getOauth64();
529
+
530
+				// Start Login
531
+				if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
532
+					return false;
533
+				}
534
+				break;
535
+			default:
536
+				$this->setError("Login method \"$authtype\" is not supported");
537
+
538
+				return false;
539
+		}
540
+
541
+		return true;
542
+	}
543
+
544
+	/**
545
+	 * Calculate an MD5 HMAC hash.
546
+	 * Works like hash_hmac('md5', $data, $key)
547
+	 * in case that function is not available.
548
+	 *
549
+	 * @param string $data The data to hash
550
+	 * @param string $key  The key to hash with
551
+	 *
552
+	 * @return string
553
+	 */
554
+	protected function hmac($data, $key)
555
+	{
556
+		if (function_exists('hash_hmac')) {
557
+			return hash_hmac('md5', $data, $key);
558
+		}
559
+
560
+		// The following borrowed from
561
+		// http://php.net/manual/en/function.mhash.php#27225
562
+
563
+		// RFC 2104 HMAC implementation for php.
564
+		// Creates an md5 HMAC.
565
+		// Eliminates the need to install mhash to compute a HMAC
566
+		// by Lance Rushing
567
+
568
+		$bytelen = 64; // byte length for md5
569
+		if (strlen($key) > $bytelen) {
570
+			$key = pack('H*', md5($key));
571
+		}
572
+		$key = str_pad($key, $bytelen, chr(0x00));
573
+		$ipad = str_pad('', $bytelen, chr(0x36));
574
+		$opad = str_pad('', $bytelen, chr(0x5c));
575
+		$k_ipad = $key ^ $ipad;
576
+		$k_opad = $key ^ $opad;
577
+
578
+		return md5($k_opad . pack('H*', md5($k_ipad . $data)));
579
+	}
580
+
581
+	/**
582
+	 * Check connection state.
583
+	 *
584
+	 * @return bool True if connected
585
+	 */
586
+	public function connected()
587
+	{
588
+		if (is_resource($this->smtp_conn)) {
589
+			$sock_status = stream_get_meta_data($this->smtp_conn);
590
+			if ($sock_status['eof']) {
591
+				// The socket is valid but we are not connected
592
+				$this->edebug(
593
+					'SMTP NOTICE: EOF caught while checking if connected',
594
+					self::DEBUG_CLIENT
595
+				);
596
+				$this->close();
597
+
598
+				return false;
599
+			}
600
+
601
+			return true; // everything looks good
602
+		}
603
+
604
+		return false;
605
+	}
606
+
607
+	/**
608
+	 * Close the socket and clean up the state of the class.
609
+	 * Don't use this function without first trying to use QUIT.
610
+	 *
611
+	 * @see quit()
612
+	 */
613
+	public function close()
614
+	{
615
+		$this->setError('');
616
+		$this->server_caps = null;
617
+		$this->helo_rply = null;
618
+		if (is_resource($this->smtp_conn)) {
619
+			// close the connection and cleanup
620
+			fclose($this->smtp_conn);
621
+			$this->smtp_conn = null; //Makes for cleaner serialization
622
+			$this->edebug('Connection: closed', self::DEBUG_CONNECTION);
623
+		}
624
+	}
625
+
626
+	/**
627
+	 * Send an SMTP DATA command.
628
+	 * Issues a data command and sends the msg_data to the server,
629
+	 * finializing the mail transaction. $msg_data is the message
630
+	 * that is to be send with the headers. Each header needs to be
631
+	 * on a single line followed by a <CRLF> with the message headers
632
+	 * and the message body being separated by an additional <CRLF>.
633
+	 * Implements RFC 821: DATA <CRLF>.
634
+	 *
635
+	 * @param string $msg_data Message data to send
636
+	 *
637
+	 * @return bool
638
+	 */
639
+	public function data($msg_data)
640
+	{
641
+		//This will use the standard timelimit
642
+		if (!$this->sendCommand('DATA', 'DATA', 354)) {
643
+			return false;
644
+		}
645
+
646
+		/* The server is ready to accept data!
647 647
          * According to rfc821 we should not send more than 1000 characters on a single line (including the LE)
648 648
          * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
649 649
          * smaller lines to fit within the limit.
@@ -651,666 +651,666 @@  discard block
 block discarded – undo
651 651
          * NOTE: this does not count towards line-length limit.
652 652
          */
653 653
 
654
-        // Normalize line breaks before exploding
655
-        $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
654
+		// Normalize line breaks before exploding
655
+		$lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
656 656
 
657
-        /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
657
+		/* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
658 658
          * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
659 659
          * process all lines before a blank line as headers.
660 660
          */
661 661
 
662
-        $field = substr($lines[0], 0, strpos($lines[0], ':'));
663
-        $in_headers = false;
664
-        if (!empty($field) and strpos($field, ' ') === false) {
665
-            $in_headers = true;
666
-        }
667
-
668
-        foreach ($lines as $line) {
669
-            $lines_out = [];
670
-            if ($in_headers and $line == '') {
671
-                $in_headers = false;
672
-            }
673
-            //Break this line up into several smaller lines if it's too long
674
-            //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
675
-            while (isset($line[self::MAX_LINE_LENGTH])) {
676
-                //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
677
-                //so as to avoid breaking in the middle of a word
678
-                $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
679
-                //Deliberately matches both false and 0
680
-                if (!$pos) {
681
-                    //No nice break found, add a hard break
682
-                    $pos = self::MAX_LINE_LENGTH - 1;
683
-                    $lines_out[] = substr($line, 0, $pos);
684
-                    $line = substr($line, $pos);
685
-                } else {
686
-                    //Break at the found point
687
-                    $lines_out[] = substr($line, 0, $pos);
688
-                    //Move along by the amount we dealt with
689
-                    $line = substr($line, $pos + 1);
690
-                }
691
-                //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
692
-                if ($in_headers) {
693
-                    $line = "\t" . $line;
694
-                }
695
-            }
696
-            $lines_out[] = $line;
697
-
698
-            //Send the lines to the server
699
-            foreach ($lines_out as $line_out) {
700
-                //RFC2821 section 4.5.2
701
-                if (!empty($line_out) and $line_out[0] == '.') {
702
-                    $line_out = '.' . $line_out;
703
-                }
704
-                $this->client_send($line_out . static::LE);
705
-            }
706
-        }
707
-
708
-        //Message data has been sent, complete the command
709
-        //Increase timelimit for end of DATA command
710
-        $savetimelimit = $this->Timelimit;
711
-        $this->Timelimit = $this->Timelimit * 2;
712
-        $result = $this->sendCommand('DATA END', '.', 250);
713
-        $this->recordLastTransactionID();
714
-        //Restore timelimit
715
-        $this->Timelimit = $savetimelimit;
716
-
717
-        return $result;
718
-    }
719
-
720
-    /**
721
-     * Send an SMTP HELO or EHLO command.
722
-     * Used to identify the sending server to the receiving server.
723
-     * This makes sure that client and server are in a known state.
724
-     * Implements RFC 821: HELO <SP> <domain> <CRLF>
725
-     * and RFC 2821 EHLO.
726
-     *
727
-     * @param string $host The host name or IP to connect to
728
-     *
729
-     * @return bool
730
-     */
731
-    public function hello($host = '')
732
-    {
733
-        //Try extended hello first (RFC 2821)
734
-        return (bool) ($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
735
-    }
736
-
737
-    /**
738
-     * Send an SMTP HELO or EHLO command.
739
-     * Low-level implementation used by hello().
740
-     *
741
-     * @param string $hello The HELO string
742
-     * @param string $host  The hostname to say we are
743
-     *
744
-     * @return bool
745
-     *
746
-     * @see    hello()
747
-     */
748
-    protected function sendHello($hello, $host)
749
-    {
750
-        $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
751
-        $this->helo_rply = $this->last_reply;
752
-        if ($noerror) {
753
-            $this->parseHelloFields($hello);
754
-        } else {
755
-            $this->server_caps = null;
756
-        }
757
-
758
-        return $noerror;
759
-    }
760
-
761
-    /**
762
-     * Parse a reply to HELO/EHLO command to discover server extensions.
763
-     * In case of HELO, the only parameter that can be discovered is a server name.
764
-     *
765
-     * @param string $type `HELO` or `EHLO`
766
-     */
767
-    protected function parseHelloFields($type)
768
-    {
769
-        $this->server_caps = [];
770
-        $lines = explode("\n", $this->helo_rply);
771
-
772
-        foreach ($lines as $n => $s) {
773
-            //First 4 chars contain response code followed by - or space
774
-            $s = trim(substr($s, 4));
775
-            if (empty($s)) {
776
-                continue;
777
-            }
778
-            $fields = explode(' ', $s);
779
-            if (!empty($fields)) {
780
-                if (!$n) {
781
-                    $name = $type;
782
-                    $fields = $fields[0];
783
-                } else {
784
-                    $name = array_shift($fields);
785
-                    switch ($name) {
786
-                        case 'SIZE':
787
-                            $fields = ($fields ? $fields[0] : 0);
788
-                            break;
789
-                        case 'AUTH':
790
-                            if (!is_array($fields)) {
791
-                                $fields = [];
792
-                            }
793
-                            break;
794
-                        default:
795
-                            $fields = true;
796
-                    }
797
-                }
798
-                $this->server_caps[$name] = $fields;
799
-            }
800
-        }
801
-    }
802
-
803
-    /**
804
-     * Send an SMTP MAIL command.
805
-     * Starts a mail transaction from the email address specified in
806
-     * $from. Returns true if successful or false otherwise. If True
807
-     * the mail transaction is started and then one or more recipient
808
-     * commands may be called followed by a data command.
809
-     * Implements RFC 821: MAIL <SP> FROM:<reverse-path> <CRLF>.
810
-     *
811
-     * @param string $from Source address of this message
812
-     *
813
-     * @return bool
814
-     */
815
-    public function mail($from)
816
-    {
817
-        $useVerp = ($this->do_verp ? ' XVERP' : '');
818
-
819
-        return $this->sendCommand(
820
-            'MAIL FROM',
821
-            'MAIL FROM:<' . $from . '>' . $useVerp,
822
-            250
823
-        );
824
-    }
825
-
826
-    /**
827
-     * Send an SMTP QUIT command.
828
-     * Closes the socket if there is no error or the $close_on_error argument is true.
829
-     * Implements from RFC 821: QUIT <CRLF>.
830
-     *
831
-     * @param bool $close_on_error Should the connection close if an error occurs?
832
-     *
833
-     * @return bool
834
-     */
835
-    public function quit($close_on_error = true)
836
-    {
837
-        $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
838
-        $err = $this->error; //Save any error
839
-        if ($noerror or $close_on_error) {
840
-            $this->close();
841
-            $this->error = $err; //Restore any error from the quit command
842
-        }
843
-
844
-        return $noerror;
845
-    }
846
-
847
-    /**
848
-     * Send an SMTP RCPT command.
849
-     * Sets the TO argument to $toaddr.
850
-     * Returns true if the recipient was accepted false if it was rejected.
851
-     * Implements from RFC 821: RCPT <SP> TO:<forward-path> <CRLF>.
852
-     *
853
-     * @param string $address The address the message is being sent to
854
-     *
855
-     * @return bool
856
-     */
857
-    public function recipient($address)
858
-    {
859
-        return $this->sendCommand(
860
-            'RCPT TO',
861
-            'RCPT TO:<' . $address . '>',
862
-            [250, 251]
863
-        );
864
-    }
865
-
866
-    /**
867
-     * Send an SMTP RSET command.
868
-     * Abort any transaction that is currently in progress.
869
-     * Implements RFC 821: RSET <CRLF>.
870
-     *
871
-     * @return bool True on success
872
-     */
873
-    public function reset()
874
-    {
875
-        return $this->sendCommand('RSET', 'RSET', 250);
876
-    }
877
-
878
-    /**
879
-     * Send a command to an SMTP server and check its return code.
880
-     *
881
-     * @param string    $command       The command name - not sent to the server
882
-     * @param string    $commandstring The actual command to send
883
-     * @param int|array $expect        One or more expected integer success codes
884
-     *
885
-     * @return bool True on success
886
-     */
887
-    protected function sendCommand($command, $commandstring, $expect)
888
-    {
889
-        if (!$this->connected()) {
890
-            $this->setError("Called $command without being connected");
891
-
892
-            return false;
893
-        }
894
-        //Reject line breaks in all commands
895
-        if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
896
-            $this->setError("Command '$command' contained line breaks");
897
-
898
-            return false;
899
-        }
900
-        $this->client_send($commandstring . static::LE);
901
-
902
-        $this->last_reply = $this->get_lines();
903
-        // Fetch SMTP code and possible error code explanation
904
-        $matches = [];
905
-        if (preg_match('/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/', $this->last_reply, $matches)) {
906
-            $code = $matches[1];
907
-            $code_ex = (count($matches) > 2 ? $matches[2] : null);
908
-            // Cut off error code from each response line
909
-            $detail = preg_replace(
910
-                "/{$code}[ -]" .
911
-                ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m',
912
-                '',
913
-                $this->last_reply
914
-            );
915
-        } else {
916
-            // Fall back to simple parsing if regex fails
917
-            $code = substr($this->last_reply, 0, 3);
918
-            $code_ex = null;
919
-            $detail = substr($this->last_reply, 4);
920
-        }
921
-
922
-        $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
923
-
924
-        if (!in_array($code, (array) $expect)) {
925
-            $this->setError(
926
-                "$command command failed",
927
-                $detail,
928
-                $code,
929
-                $code_ex
930
-            );
931
-            $this->edebug(
932
-                'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
933
-                self::DEBUG_CLIENT
934
-            );
935
-
936
-            return false;
937
-        }
938
-
939
-        $this->setError('');
940
-
941
-        return true;
942
-    }
943
-
944
-    /**
945
-     * Send an SMTP SAML command.
946
-     * Starts a mail transaction from the email address specified in $from.
947
-     * Returns true if successful or false otherwise. If True
948
-     * the mail transaction is started and then one or more recipient
949
-     * commands may be called followed by a data command. This command
950
-     * will send the message to the users terminal if they are logged
951
-     * in and send them an email.
952
-     * Implements RFC 821: SAML <SP> FROM:<reverse-path> <CRLF>.
953
-     *
954
-     * @param string $from The address the message is from
955
-     *
956
-     * @return bool
957
-     */
958
-    public function sendAndMail($from)
959
-    {
960
-        return $this->sendCommand('SAML', "SAML FROM:$from", 250);
961
-    }
962
-
963
-    /**
964
-     * Send an SMTP VRFY command.
965
-     *
966
-     * @param string $name The name to verify
967
-     *
968
-     * @return bool
969
-     */
970
-    public function verify($name)
971
-    {
972
-        return $this->sendCommand('VRFY', "VRFY $name", [250, 251]);
973
-    }
974
-
975
-    /**
976
-     * Send an SMTP NOOP command.
977
-     * Used to keep keep-alives alive, doesn't actually do anything.
978
-     *
979
-     * @return bool
980
-     */
981
-    public function noop()
982
-    {
983
-        return $this->sendCommand('NOOP', 'NOOP', 250);
984
-    }
985
-
986
-    /**
987
-     * Send an SMTP TURN command.
988
-     * This is an optional command for SMTP that this class does not support.
989
-     * This method is here to make the RFC821 Definition complete for this class
990
-     * and _may_ be implemented in future.
991
-     * Implements from RFC 821: TURN <CRLF>.
992
-     *
993
-     * @return bool
994
-     */
995
-    public function turn()
996
-    {
997
-        $this->setError('The SMTP TURN command is not implemented');
998
-        $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
999
-
1000
-        return false;
1001
-    }
1002
-
1003
-    /**
1004
-     * Send raw data to the server.
1005
-     *
1006
-     * @param string $data The data to send
1007
-     *
1008
-     * @return int|bool The number of bytes sent to the server or false on error
1009
-     */
1010
-    public function client_send($data)
1011
-    {
1012
-        $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
1013
-        set_error_handler([$this, 'errorHandler']);
1014
-        $result = fwrite($this->smtp_conn, $data);
1015
-        restore_error_handler();
1016
-
1017
-        return $result;
1018
-    }
1019
-
1020
-    /**
1021
-     * Get the latest error.
1022
-     *
1023
-     * @return array
1024
-     */
1025
-    public function getError()
1026
-    {
1027
-        return $this->error;
1028
-    }
1029
-
1030
-    /**
1031
-     * Get SMTP extensions available on the server.
1032
-     *
1033
-     * @return array|null
1034
-     */
1035
-    public function getServerExtList()
1036
-    {
1037
-        return $this->server_caps;
1038
-    }
1039
-
1040
-    /**
1041
-     * Get metadata about the SMTP server from its HELO/EHLO response.
1042
-     * The method works in three ways, dependent on argument value and current state:
1043
-     *   1. HELO/EHLO has not been sent - returns null and populates $this->error.
1044
-     *   2. HELO has been sent -
1045
-     *     $name == 'HELO': returns server name
1046
-     *     $name == 'EHLO': returns boolean false
1047
-     *     $name == any other string: returns null and populates $this->error
1048
-     *   3. EHLO has been sent -
1049
-     *     $name == 'HELO'|'EHLO': returns the server name
1050
-     *     $name == any other string: if extension $name exists, returns True
1051
-     *       or its options (e.g. AUTH mechanisms supported). Otherwise returns False.
1052
-     *
1053
-     * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
1054
-     *
1055
-     * @return mixed
1056
-     */
1057
-    public function getServerExt($name)
1058
-    {
1059
-        if (!$this->server_caps) {
1060
-            $this->setError('No HELO/EHLO was sent');
1061
-
1062
-            return;
1063
-        }
1064
-
1065
-        if (!array_key_exists($name, $this->server_caps)) {
1066
-            if ('HELO' == $name) {
1067
-                return $this->server_caps['EHLO'];
1068
-            }
1069
-            if ('EHLO' == $name || array_key_exists('EHLO', $this->server_caps)) {
1070
-                return false;
1071
-            }
1072
-            $this->setError('HELO handshake was used; No information about server extensions available');
1073
-
1074
-            return;
1075
-        }
1076
-
1077
-        return $this->server_caps[$name];
1078
-    }
1079
-
1080
-    /**
1081
-     * Get the last reply from the server.
1082
-     *
1083
-     * @return string
1084
-     */
1085
-    public function getLastReply()
1086
-    {
1087
-        return $this->last_reply;
1088
-    }
1089
-
1090
-    /**
1091
-     * Read the SMTP server's response.
1092
-     * Either before eof or socket timeout occurs on the operation.
1093
-     * With SMTP we can tell if we have more lines to read if the
1094
-     * 4th character is '-' symbol. If it is a space then we don't
1095
-     * need to read anything else.
1096
-     *
1097
-     * @return string
1098
-     */
1099
-    protected function get_lines()
1100
-    {
1101
-        // If the connection is bad, give up straight away
1102
-        if (!is_resource($this->smtp_conn)) {
1103
-            return '';
1104
-        }
1105
-        $data = '';
1106
-        $endtime = 0;
1107
-        stream_set_timeout($this->smtp_conn, $this->Timeout);
1108
-        if ($this->Timelimit > 0) {
1109
-            $endtime = time() + $this->Timelimit;
1110
-        }
1111
-        $selR = [$this->smtp_conn];
1112
-        $selW = null;
1113
-        while (is_resource($this->smtp_conn) and !feof($this->smtp_conn)) {
1114
-            //Must pass vars in here as params are by reference
1115
-            if (!stream_select($selR, $selW, $selW, $this->Timelimit)) {
1116
-                $this->edebug(
1117
-                    'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
1118
-                    self::DEBUG_LOWLEVEL
1119
-                );
1120
-                break;
1121
-            }
1122
-            //Deliberate noise suppression - errors are handled afterwards
1123
-            $str = @fgets($this->smtp_conn, 515);
1124
-            $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL);
1125
-            $data .= $str;
1126
-            // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
1127
-            // or 4th character is a space, we are done reading, break the loop,
1128
-            // string array access is a micro-optimisation over strlen
1129
-            if (!isset($str[3]) or (isset($str[3]) and $str[3] == ' ')) {
1130
-                break;
1131
-            }
1132
-            // Timed-out? Log and break
1133
-            $info = stream_get_meta_data($this->smtp_conn);
1134
-            if ($info['timed_out']) {
1135
-                $this->edebug(
1136
-                    'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
1137
-                    self::DEBUG_LOWLEVEL
1138
-                );
1139
-                break;
1140
-            }
1141
-            // Now check if reads took too long
1142
-            if ($endtime and time() > $endtime) {
1143
-                $this->edebug(
1144
-                    'SMTP -> get_lines(): timelimit reached (' .
1145
-                    $this->Timelimit . ' sec)',
1146
-                    self::DEBUG_LOWLEVEL
1147
-                );
1148
-                break;
1149
-            }
1150
-        }
1151
-
1152
-        return $data;
1153
-    }
1154
-
1155
-    /**
1156
-     * Enable or disable VERP address generation.
1157
-     *
1158
-     * @param bool $enabled
1159
-     */
1160
-    public function setVerp($enabled = false)
1161
-    {
1162
-        $this->do_verp = $enabled;
1163
-    }
1164
-
1165
-    /**
1166
-     * Get VERP address generation mode.
1167
-     *
1168
-     * @return bool
1169
-     */
1170
-    public function getVerp()
1171
-    {
1172
-        return $this->do_verp;
1173
-    }
1174
-
1175
-    /**
1176
-     * Set error messages and codes.
1177
-     *
1178
-     * @param string $message      The error message
1179
-     * @param string $detail       Further detail on the error
1180
-     * @param string $smtp_code    An associated SMTP error code
1181
-     * @param string $smtp_code_ex Extended SMTP code
1182
-     */
1183
-    protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
1184
-    {
1185
-        $this->error = [
1186
-            'error' => $message,
1187
-            'detail' => $detail,
1188
-            'smtp_code' => $smtp_code,
1189
-            'smtp_code_ex' => $smtp_code_ex,
1190
-        ];
1191
-    }
1192
-
1193
-    /**
1194
-     * Set debug output method.
1195
-     *
1196
-     * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it
1197
-     */
1198
-    public function setDebugOutput($method = 'echo')
1199
-    {
1200
-        $this->Debugoutput = $method;
1201
-    }
1202
-
1203
-    /**
1204
-     * Get debug output method.
1205
-     *
1206
-     * @return string
1207
-     */
1208
-    public function getDebugOutput()
1209
-    {
1210
-        return $this->Debugoutput;
1211
-    }
1212
-
1213
-    /**
1214
-     * Set debug output level.
1215
-     *
1216
-     * @param int $level
1217
-     */
1218
-    public function setDebugLevel($level = 0)
1219
-    {
1220
-        $this->do_debug = $level;
1221
-    }
1222
-
1223
-    /**
1224
-     * Get debug output level.
1225
-     *
1226
-     * @return int
1227
-     */
1228
-    public function getDebugLevel()
1229
-    {
1230
-        return $this->do_debug;
1231
-    }
1232
-
1233
-    /**
1234
-     * Set SMTP timeout.
1235
-     *
1236
-     * @param int $timeout The timeout duration in seconds
1237
-     */
1238
-    public function setTimeout($timeout = 0)
1239
-    {
1240
-        $this->Timeout = $timeout;
1241
-    }
1242
-
1243
-    /**
1244
-     * Get SMTP timeout.
1245
-     *
1246
-     * @return int
1247
-     */
1248
-    public function getTimeout()
1249
-    {
1250
-        return $this->Timeout;
1251
-    }
1252
-
1253
-    /**
1254
-     * Reports an error number and string.
1255
-     *
1256
-     * @param int    $errno   The error number returned by PHP
1257
-     * @param string $errmsg  The error message returned by PHP
1258
-     * @param string $errfile The file the error occurred in
1259
-     * @param int    $errline The line number the error occurred on
1260
-     */
1261
-    protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
1262
-    {
1263
-        $notice = 'Connection failed.';
1264
-        $this->setError(
1265
-            $notice,
1266
-            $errmsg,
1267
-            (string) $errno
1268
-        );
1269
-        $this->edebug(
1270
-            "$notice Error #$errno: $errmsg [$errfile line $errline]",
1271
-            self::DEBUG_CONNECTION
1272
-        );
1273
-    }
1274
-
1275
-    /**
1276
-     * Extract and return the ID of the last SMTP transaction based on
1277
-     * a list of patterns provided in SMTP::$smtp_transaction_id_patterns.
1278
-     * Relies on the host providing the ID in response to a DATA command.
1279
-     * If no reply has been received yet, it will return null.
1280
-     * If no pattern was matched, it will return false.
1281
-     *
1282
-     * @return bool|null|string
1283
-     */
1284
-    protected function recordLastTransactionID()
1285
-    {
1286
-        $reply = $this->getLastReply();
1287
-
1288
-        if (empty($reply)) {
1289
-            $this->last_smtp_transaction_id = null;
1290
-        } else {
1291
-            $this->last_smtp_transaction_id = false;
1292
-            foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
1293
-                if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
1294
-                    $this->last_smtp_transaction_id = $matches[1];
1295
-                    break;
1296
-                }
1297
-            }
1298
-        }
1299
-
1300
-        return $this->last_smtp_transaction_id;
1301
-    }
1302
-
1303
-    /**
1304
-     * Get the queue/transaction ID of the last SMTP transaction
1305
-     * If no reply has been received yet, it will return null.
1306
-     * If no pattern was matched, it will return false.
1307
-     *
1308
-     * @return bool|null|string
1309
-     *
1310
-     * @see recordLastTransactionID()
1311
-     */
1312
-    public function getLastTransactionID()
1313
-    {
1314
-        return $this->last_smtp_transaction_id;
1315
-    }
662
+		$field = substr($lines[0], 0, strpos($lines[0], ':'));
663
+		$in_headers = false;
664
+		if (!empty($field) and strpos($field, ' ') === false) {
665
+			$in_headers = true;
666
+		}
667
+
668
+		foreach ($lines as $line) {
669
+			$lines_out = [];
670
+			if ($in_headers and $line == '') {
671
+				$in_headers = false;
672
+			}
673
+			//Break this line up into several smaller lines if it's too long
674
+			//Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
675
+			while (isset($line[self::MAX_LINE_LENGTH])) {
676
+				//Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
677
+				//so as to avoid breaking in the middle of a word
678
+				$pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
679
+				//Deliberately matches both false and 0
680
+				if (!$pos) {
681
+					//No nice break found, add a hard break
682
+					$pos = self::MAX_LINE_LENGTH - 1;
683
+					$lines_out[] = substr($line, 0, $pos);
684
+					$line = substr($line, $pos);
685
+				} else {
686
+					//Break at the found point
687
+					$lines_out[] = substr($line, 0, $pos);
688
+					//Move along by the amount we dealt with
689
+					$line = substr($line, $pos + 1);
690
+				}
691
+				//If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
692
+				if ($in_headers) {
693
+					$line = "\t" . $line;
694
+				}
695
+			}
696
+			$lines_out[] = $line;
697
+
698
+			//Send the lines to the server
699
+			foreach ($lines_out as $line_out) {
700
+				//RFC2821 section 4.5.2
701
+				if (!empty($line_out) and $line_out[0] == '.') {
702
+					$line_out = '.' . $line_out;
703
+				}
704
+				$this->client_send($line_out . static::LE);
705
+			}
706
+		}
707
+
708
+		//Message data has been sent, complete the command
709
+		//Increase timelimit for end of DATA command
710
+		$savetimelimit = $this->Timelimit;
711
+		$this->Timelimit = $this->Timelimit * 2;
712
+		$result = $this->sendCommand('DATA END', '.', 250);
713
+		$this->recordLastTransactionID();
714
+		//Restore timelimit
715
+		$this->Timelimit = $savetimelimit;
716
+
717
+		return $result;
718
+	}
719
+
720
+	/**
721
+	 * Send an SMTP HELO or EHLO command.
722
+	 * Used to identify the sending server to the receiving server.
723
+	 * This makes sure that client and server are in a known state.
724
+	 * Implements RFC 821: HELO <SP> <domain> <CRLF>
725
+	 * and RFC 2821 EHLO.
726
+	 *
727
+	 * @param string $host The host name or IP to connect to
728
+	 *
729
+	 * @return bool
730
+	 */
731
+	public function hello($host = '')
732
+	{
733
+		//Try extended hello first (RFC 2821)
734
+		return (bool) ($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
735
+	}
736
+
737
+	/**
738
+	 * Send an SMTP HELO or EHLO command.
739
+	 * Low-level implementation used by hello().
740
+	 *
741
+	 * @param string $hello The HELO string
742
+	 * @param string $host  The hostname to say we are
743
+	 *
744
+	 * @return bool
745
+	 *
746
+	 * @see    hello()
747
+	 */
748
+	protected function sendHello($hello, $host)
749
+	{
750
+		$noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
751
+		$this->helo_rply = $this->last_reply;
752
+		if ($noerror) {
753
+			$this->parseHelloFields($hello);
754
+		} else {
755
+			$this->server_caps = null;
756
+		}
757
+
758
+		return $noerror;
759
+	}
760
+
761
+	/**
762
+	 * Parse a reply to HELO/EHLO command to discover server extensions.
763
+	 * In case of HELO, the only parameter that can be discovered is a server name.
764
+	 *
765
+	 * @param string $type `HELO` or `EHLO`
766
+	 */
767
+	protected function parseHelloFields($type)
768
+	{
769
+		$this->server_caps = [];
770
+		$lines = explode("\n", $this->helo_rply);
771
+
772
+		foreach ($lines as $n => $s) {
773
+			//First 4 chars contain response code followed by - or space
774
+			$s = trim(substr($s, 4));
775
+			if (empty($s)) {
776
+				continue;
777
+			}
778
+			$fields = explode(' ', $s);
779
+			if (!empty($fields)) {
780
+				if (!$n) {
781
+					$name = $type;
782
+					$fields = $fields[0];
783
+				} else {
784
+					$name = array_shift($fields);
785
+					switch ($name) {
786
+						case 'SIZE':
787
+							$fields = ($fields ? $fields[0] : 0);
788
+							break;
789
+						case 'AUTH':
790
+							if (!is_array($fields)) {
791
+								$fields = [];
792
+							}
793
+							break;
794
+						default:
795
+							$fields = true;
796
+					}
797
+				}
798
+				$this->server_caps[$name] = $fields;
799
+			}
800
+		}
801
+	}
802
+
803
+	/**
804
+	 * Send an SMTP MAIL command.
805
+	 * Starts a mail transaction from the email address specified in
806
+	 * $from. Returns true if successful or false otherwise. If True
807
+	 * the mail transaction is started and then one or more recipient
808
+	 * commands may be called followed by a data command.
809
+	 * Implements RFC 821: MAIL <SP> FROM:<reverse-path> <CRLF>.
810
+	 *
811
+	 * @param string $from Source address of this message
812
+	 *
813
+	 * @return bool
814
+	 */
815
+	public function mail($from)
816
+	{
817
+		$useVerp = ($this->do_verp ? ' XVERP' : '');
818
+
819
+		return $this->sendCommand(
820
+			'MAIL FROM',
821
+			'MAIL FROM:<' . $from . '>' . $useVerp,
822
+			250
823
+		);
824
+	}
825
+
826
+	/**
827
+	 * Send an SMTP QUIT command.
828
+	 * Closes the socket if there is no error or the $close_on_error argument is true.
829
+	 * Implements from RFC 821: QUIT <CRLF>.
830
+	 *
831
+	 * @param bool $close_on_error Should the connection close if an error occurs?
832
+	 *
833
+	 * @return bool
834
+	 */
835
+	public function quit($close_on_error = true)
836
+	{
837
+		$noerror = $this->sendCommand('QUIT', 'QUIT', 221);
838
+		$err = $this->error; //Save any error
839
+		if ($noerror or $close_on_error) {
840
+			$this->close();
841
+			$this->error = $err; //Restore any error from the quit command
842
+		}
843
+
844
+		return $noerror;
845
+	}
846
+
847
+	/**
848
+	 * Send an SMTP RCPT command.
849
+	 * Sets the TO argument to $toaddr.
850
+	 * Returns true if the recipient was accepted false if it was rejected.
851
+	 * Implements from RFC 821: RCPT <SP> TO:<forward-path> <CRLF>.
852
+	 *
853
+	 * @param string $address The address the message is being sent to
854
+	 *
855
+	 * @return bool
856
+	 */
857
+	public function recipient($address)
858
+	{
859
+		return $this->sendCommand(
860
+			'RCPT TO',
861
+			'RCPT TO:<' . $address . '>',
862
+			[250, 251]
863
+		);
864
+	}
865
+
866
+	/**
867
+	 * Send an SMTP RSET command.
868
+	 * Abort any transaction that is currently in progress.
869
+	 * Implements RFC 821: RSET <CRLF>.
870
+	 *
871
+	 * @return bool True on success
872
+	 */
873
+	public function reset()
874
+	{
875
+		return $this->sendCommand('RSET', 'RSET', 250);
876
+	}
877
+
878
+	/**
879
+	 * Send a command to an SMTP server and check its return code.
880
+	 *
881
+	 * @param string    $command       The command name - not sent to the server
882
+	 * @param string    $commandstring The actual command to send
883
+	 * @param int|array $expect        One or more expected integer success codes
884
+	 *
885
+	 * @return bool True on success
886
+	 */
887
+	protected function sendCommand($command, $commandstring, $expect)
888
+	{
889
+		if (!$this->connected()) {
890
+			$this->setError("Called $command without being connected");
891
+
892
+			return false;
893
+		}
894
+		//Reject line breaks in all commands
895
+		if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
896
+			$this->setError("Command '$command' contained line breaks");
897
+
898
+			return false;
899
+		}
900
+		$this->client_send($commandstring . static::LE);
901
+
902
+		$this->last_reply = $this->get_lines();
903
+		// Fetch SMTP code and possible error code explanation
904
+		$matches = [];
905
+		if (preg_match('/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/', $this->last_reply, $matches)) {
906
+			$code = $matches[1];
907
+			$code_ex = (count($matches) > 2 ? $matches[2] : null);
908
+			// Cut off error code from each response line
909
+			$detail = preg_replace(
910
+				"/{$code}[ -]" .
911
+				($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m',
912
+				'',
913
+				$this->last_reply
914
+			);
915
+		} else {
916
+			// Fall back to simple parsing if regex fails
917
+			$code = substr($this->last_reply, 0, 3);
918
+			$code_ex = null;
919
+			$detail = substr($this->last_reply, 4);
920
+		}
921
+
922
+		$this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
923
+
924
+		if (!in_array($code, (array) $expect)) {
925
+			$this->setError(
926
+				"$command command failed",
927
+				$detail,
928
+				$code,
929
+				$code_ex
930
+			);
931
+			$this->edebug(
932
+				'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
933
+				self::DEBUG_CLIENT
934
+			);
935
+
936
+			return false;
937
+		}
938
+
939
+		$this->setError('');
940
+
941
+		return true;
942
+	}
943
+
944
+	/**
945
+	 * Send an SMTP SAML command.
946
+	 * Starts a mail transaction from the email address specified in $from.
947
+	 * Returns true if successful or false otherwise. If True
948
+	 * the mail transaction is started and then one or more recipient
949
+	 * commands may be called followed by a data command. This command
950
+	 * will send the message to the users terminal if they are logged
951
+	 * in and send them an email.
952
+	 * Implements RFC 821: SAML <SP> FROM:<reverse-path> <CRLF>.
953
+	 *
954
+	 * @param string $from The address the message is from
955
+	 *
956
+	 * @return bool
957
+	 */
958
+	public function sendAndMail($from)
959
+	{
960
+		return $this->sendCommand('SAML', "SAML FROM:$from", 250);
961
+	}
962
+
963
+	/**
964
+	 * Send an SMTP VRFY command.
965
+	 *
966
+	 * @param string $name The name to verify
967
+	 *
968
+	 * @return bool
969
+	 */
970
+	public function verify($name)
971
+	{
972
+		return $this->sendCommand('VRFY', "VRFY $name", [250, 251]);
973
+	}
974
+
975
+	/**
976
+	 * Send an SMTP NOOP command.
977
+	 * Used to keep keep-alives alive, doesn't actually do anything.
978
+	 *
979
+	 * @return bool
980
+	 */
981
+	public function noop()
982
+	{
983
+		return $this->sendCommand('NOOP', 'NOOP', 250);
984
+	}
985
+
986
+	/**
987
+	 * Send an SMTP TURN command.
988
+	 * This is an optional command for SMTP that this class does not support.
989
+	 * This method is here to make the RFC821 Definition complete for this class
990
+	 * and _may_ be implemented in future.
991
+	 * Implements from RFC 821: TURN <CRLF>.
992
+	 *
993
+	 * @return bool
994
+	 */
995
+	public function turn()
996
+	{
997
+		$this->setError('The SMTP TURN command is not implemented');
998
+		$this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
999
+
1000
+		return false;
1001
+	}
1002
+
1003
+	/**
1004
+	 * Send raw data to the server.
1005
+	 *
1006
+	 * @param string $data The data to send
1007
+	 *
1008
+	 * @return int|bool The number of bytes sent to the server or false on error
1009
+	 */
1010
+	public function client_send($data)
1011
+	{
1012
+		$this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
1013
+		set_error_handler([$this, 'errorHandler']);
1014
+		$result = fwrite($this->smtp_conn, $data);
1015
+		restore_error_handler();
1016
+
1017
+		return $result;
1018
+	}
1019
+
1020
+	/**
1021
+	 * Get the latest error.
1022
+	 *
1023
+	 * @return array
1024
+	 */
1025
+	public function getError()
1026
+	{
1027
+		return $this->error;
1028
+	}
1029
+
1030
+	/**
1031
+	 * Get SMTP extensions available on the server.
1032
+	 *
1033
+	 * @return array|null
1034
+	 */
1035
+	public function getServerExtList()
1036
+	{
1037
+		return $this->server_caps;
1038
+	}
1039
+
1040
+	/**
1041
+	 * Get metadata about the SMTP server from its HELO/EHLO response.
1042
+	 * The method works in three ways, dependent on argument value and current state:
1043
+	 *   1. HELO/EHLO has not been sent - returns null and populates $this->error.
1044
+	 *   2. HELO has been sent -
1045
+	 *     $name == 'HELO': returns server name
1046
+	 *     $name == 'EHLO': returns boolean false
1047
+	 *     $name == any other string: returns null and populates $this->error
1048
+	 *   3. EHLO has been sent -
1049
+	 *     $name == 'HELO'|'EHLO': returns the server name
1050
+	 *     $name == any other string: if extension $name exists, returns True
1051
+	 *       or its options (e.g. AUTH mechanisms supported). Otherwise returns False.
1052
+	 *
1053
+	 * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
1054
+	 *
1055
+	 * @return mixed
1056
+	 */
1057
+	public function getServerExt($name)
1058
+	{
1059
+		if (!$this->server_caps) {
1060
+			$this->setError('No HELO/EHLO was sent');
1061
+
1062
+			return;
1063
+		}
1064
+
1065
+		if (!array_key_exists($name, $this->server_caps)) {
1066
+			if ('HELO' == $name) {
1067
+				return $this->server_caps['EHLO'];
1068
+			}
1069
+			if ('EHLO' == $name || array_key_exists('EHLO', $this->server_caps)) {
1070
+				return false;
1071
+			}
1072
+			$this->setError('HELO handshake was used; No information about server extensions available');
1073
+
1074
+			return;
1075
+		}
1076
+
1077
+		return $this->server_caps[$name];
1078
+	}
1079
+
1080
+	/**
1081
+	 * Get the last reply from the server.
1082
+	 *
1083
+	 * @return string
1084
+	 */
1085
+	public function getLastReply()
1086
+	{
1087
+		return $this->last_reply;
1088
+	}
1089
+
1090
+	/**
1091
+	 * Read the SMTP server's response.
1092
+	 * Either before eof or socket timeout occurs on the operation.
1093
+	 * With SMTP we can tell if we have more lines to read if the
1094
+	 * 4th character is '-' symbol. If it is a space then we don't
1095
+	 * need to read anything else.
1096
+	 *
1097
+	 * @return string
1098
+	 */
1099
+	protected function get_lines()
1100
+	{
1101
+		// If the connection is bad, give up straight away
1102
+		if (!is_resource($this->smtp_conn)) {
1103
+			return '';
1104
+		}
1105
+		$data = '';
1106
+		$endtime = 0;
1107
+		stream_set_timeout($this->smtp_conn, $this->Timeout);
1108
+		if ($this->Timelimit > 0) {
1109
+			$endtime = time() + $this->Timelimit;
1110
+		}
1111
+		$selR = [$this->smtp_conn];
1112
+		$selW = null;
1113
+		while (is_resource($this->smtp_conn) and !feof($this->smtp_conn)) {
1114
+			//Must pass vars in here as params are by reference
1115
+			if (!stream_select($selR, $selW, $selW, $this->Timelimit)) {
1116
+				$this->edebug(
1117
+					'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
1118
+					self::DEBUG_LOWLEVEL
1119
+				);
1120
+				break;
1121
+			}
1122
+			//Deliberate noise suppression - errors are handled afterwards
1123
+			$str = @fgets($this->smtp_conn, 515);
1124
+			$this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL);
1125
+			$data .= $str;
1126
+			// If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
1127
+			// or 4th character is a space, we are done reading, break the loop,
1128
+			// string array access is a micro-optimisation over strlen
1129
+			if (!isset($str[3]) or (isset($str[3]) and $str[3] == ' ')) {
1130
+				break;
1131
+			}
1132
+			// Timed-out? Log and break
1133
+			$info = stream_get_meta_data($this->smtp_conn);
1134
+			if ($info['timed_out']) {
1135
+				$this->edebug(
1136
+					'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
1137
+					self::DEBUG_LOWLEVEL
1138
+				);
1139
+				break;
1140
+			}
1141
+			// Now check if reads took too long
1142
+			if ($endtime and time() > $endtime) {
1143
+				$this->edebug(
1144
+					'SMTP -> get_lines(): timelimit reached (' .
1145
+					$this->Timelimit . ' sec)',
1146
+					self::DEBUG_LOWLEVEL
1147
+				);
1148
+				break;
1149
+			}
1150
+		}
1151
+
1152
+		return $data;
1153
+	}
1154
+
1155
+	/**
1156
+	 * Enable or disable VERP address generation.
1157
+	 *
1158
+	 * @param bool $enabled
1159
+	 */
1160
+	public function setVerp($enabled = false)
1161
+	{
1162
+		$this->do_verp = $enabled;
1163
+	}
1164
+
1165
+	/**
1166
+	 * Get VERP address generation mode.
1167
+	 *
1168
+	 * @return bool
1169
+	 */
1170
+	public function getVerp()
1171
+	{
1172
+		return $this->do_verp;
1173
+	}
1174
+
1175
+	/**
1176
+	 * Set error messages and codes.
1177
+	 *
1178
+	 * @param string $message      The error message
1179
+	 * @param string $detail       Further detail on the error
1180
+	 * @param string $smtp_code    An associated SMTP error code
1181
+	 * @param string $smtp_code_ex Extended SMTP code
1182
+	 */
1183
+	protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
1184
+	{
1185
+		$this->error = [
1186
+			'error' => $message,
1187
+			'detail' => $detail,
1188
+			'smtp_code' => $smtp_code,
1189
+			'smtp_code_ex' => $smtp_code_ex,
1190
+		];
1191
+	}
1192
+
1193
+	/**
1194
+	 * Set debug output method.
1195
+	 *
1196
+	 * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it
1197
+	 */
1198
+	public function setDebugOutput($method = 'echo')
1199
+	{
1200
+		$this->Debugoutput = $method;
1201
+	}
1202
+
1203
+	/**
1204
+	 * Get debug output method.
1205
+	 *
1206
+	 * @return string
1207
+	 */
1208
+	public function getDebugOutput()
1209
+	{
1210
+		return $this->Debugoutput;
1211
+	}
1212
+
1213
+	/**
1214
+	 * Set debug output level.
1215
+	 *
1216
+	 * @param int $level
1217
+	 */
1218
+	public function setDebugLevel($level = 0)
1219
+	{
1220
+		$this->do_debug = $level;
1221
+	}
1222
+
1223
+	/**
1224
+	 * Get debug output level.
1225
+	 *
1226
+	 * @return int
1227
+	 */
1228
+	public function getDebugLevel()
1229
+	{
1230
+		return $this->do_debug;
1231
+	}
1232
+
1233
+	/**
1234
+	 * Set SMTP timeout.
1235
+	 *
1236
+	 * @param int $timeout The timeout duration in seconds
1237
+	 */
1238
+	public function setTimeout($timeout = 0)
1239
+	{
1240
+		$this->Timeout = $timeout;
1241
+	}
1242
+
1243
+	/**
1244
+	 * Get SMTP timeout.
1245
+	 *
1246
+	 * @return int
1247
+	 */
1248
+	public function getTimeout()
1249
+	{
1250
+		return $this->Timeout;
1251
+	}
1252
+
1253
+	/**
1254
+	 * Reports an error number and string.
1255
+	 *
1256
+	 * @param int    $errno   The error number returned by PHP
1257
+	 * @param string $errmsg  The error message returned by PHP
1258
+	 * @param string $errfile The file the error occurred in
1259
+	 * @param int    $errline The line number the error occurred on
1260
+	 */
1261
+	protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
1262
+	{
1263
+		$notice = 'Connection failed.';
1264
+		$this->setError(
1265
+			$notice,
1266
+			$errmsg,
1267
+			(string) $errno
1268
+		);
1269
+		$this->edebug(
1270
+			"$notice Error #$errno: $errmsg [$errfile line $errline]",
1271
+			self::DEBUG_CONNECTION
1272
+		);
1273
+	}
1274
+
1275
+	/**
1276
+	 * Extract and return the ID of the last SMTP transaction based on
1277
+	 * a list of patterns provided in SMTP::$smtp_transaction_id_patterns.
1278
+	 * Relies on the host providing the ID in response to a DATA command.
1279
+	 * If no reply has been received yet, it will return null.
1280
+	 * If no pattern was matched, it will return false.
1281
+	 *
1282
+	 * @return bool|null|string
1283
+	 */
1284
+	protected function recordLastTransactionID()
1285
+	{
1286
+		$reply = $this->getLastReply();
1287
+
1288
+		if (empty($reply)) {
1289
+			$this->last_smtp_transaction_id = null;
1290
+		} else {
1291
+			$this->last_smtp_transaction_id = false;
1292
+			foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
1293
+				if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
1294
+					$this->last_smtp_transaction_id = $matches[1];
1295
+					break;
1296
+				}
1297
+			}
1298
+		}
1299
+
1300
+		return $this->last_smtp_transaction_id;
1301
+	}
1302
+
1303
+	/**
1304
+	 * Get the queue/transaction ID of the last SMTP transaction
1305
+	 * If no reply has been received yet, it will return null.
1306
+	 * If no pattern was matched, it will return false.
1307
+	 *
1308
+	 * @return bool|null|string
1309
+	 *
1310
+	 * @see recordLastTransactionID()
1311
+	 */
1312
+	public function getLastTransactionID()
1313
+	{
1314
+		return $this->last_smtp_transaction_id;
1315
+	}
1316 1316
 }
Please login to merge, or discard this patch.
www/vendor/PHPMailer/PHPMailer/POP3.php 1 patch
Indentation   +376 added lines, -376 removed lines patch added patch discarded remove patch
@@ -40,380 +40,380 @@
 block discarded – undo
40 40
  */
41 41
 class POP3
42 42
 {
43
-    /**
44
-     * The POP3 PHPMailer Version number.
45
-     *
46
-     * @var string
47
-     */
48
-    const VERSION = '6.0.1';
49
-
50
-    /**
51
-     * Default POP3 port number.
52
-     *
53
-     * @var int
54
-     */
55
-    const DEFAULT_PORT = 110;
56
-
57
-    /**
58
-     * Default timeout in seconds.
59
-     *
60
-     * @var int
61
-     */
62
-    const DEFAULT_TIMEOUT = 30;
63
-
64
-    /**
65
-     * Debug display level.
66
-     * Options: 0 = no, 1+ = yes.
67
-     *
68
-     * @var int
69
-     */
70
-    public $do_debug = 0;
71
-
72
-    /**
73
-     * POP3 mail server hostname.
74
-     *
75
-     * @var string
76
-     */
77
-    public $host;
78
-
79
-    /**
80
-     * POP3 port number.
81
-     *
82
-     * @var int
83
-     */
84
-    public $port;
85
-
86
-    /**
87
-     * POP3 Timeout Value in seconds.
88
-     *
89
-     * @var int
90
-     */
91
-    public $tval;
92
-
93
-    /**
94
-     * POP3 username.
95
-     *
96
-     * @var string
97
-     */
98
-    public $username;
99
-
100
-    /**
101
-     * POP3 password.
102
-     *
103
-     * @var string
104
-     */
105
-    public $password;
106
-
107
-    /**
108
-     * Resource handle for the POP3 connection socket.
109
-     *
110
-     * @var resource
111
-     */
112
-    protected $pop_conn;
113
-
114
-    /**
115
-     * Are we connected?
116
-     *
117
-     * @var bool
118
-     */
119
-    protected $connected = false;
120
-
121
-    /**
122
-     * Error container.
123
-     *
124
-     * @var array
125
-     */
126
-    protected $errors = [];
127
-
128
-    /**
129
-     * Line break constant.
130
-     */
131
-    const LE = "\r\n";
132
-
133
-    /**
134
-     * Simple static wrapper for all-in-one POP before SMTP.
135
-     *
136
-     * @param string   $host        The hostname to connect to
137
-     * @param int|bool $port        The port number to connect to
138
-     * @param int|bool $timeout     The timeout value
139
-     * @param string   $username
140
-     * @param string   $password
141
-     * @param int      $debug_level
142
-     *
143
-     * @return bool
144
-     */
145
-    public static function popBeforeSmtp(
146
-        $host,
147
-        $port = false,
148
-        $timeout = false,
149
-        $username = '',
150
-        $password = '',
151
-        $debug_level = 0
152
-    ) {
153
-        $pop = new self();
154
-
155
-        return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level);
156
-    }
157
-
158
-    /**
159
-     * Authenticate with a POP3 server.
160
-     * A connect, login, disconnect sequence
161
-     * appropriate for POP-before SMTP authorisation.
162
-     *
163
-     * @param string   $host        The hostname to connect to
164
-     * @param int|bool $port        The port number to connect to
165
-     * @param int|bool $timeout     The timeout value
166
-     * @param string   $username
167
-     * @param string   $password
168
-     * @param int      $debug_level
169
-     *
170
-     * @return bool
171
-     */
172
-    public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
173
-    {
174
-        $this->host = $host;
175
-        // If no port value provided, use default
176
-        if (false === $port) {
177
-            $this->port = static::DEFAULT_PORT;
178
-        } else {
179
-            $this->port = (int) $port;
180
-        }
181
-        // If no timeout value provided, use default
182
-        if (false === $timeout) {
183
-            $this->tval = static::DEFAULT_TIMEOUT;
184
-        } else {
185
-            $this->tval = (int) $timeout;
186
-        }
187
-        $this->do_debug = $debug_level;
188
-        $this->username = $username;
189
-        $this->password = $password;
190
-        //  Reset the error log
191
-        $this->errors = [];
192
-        //  connect
193
-        $result = $this->connect($this->host, $this->port, $this->tval);
194
-        if ($result) {
195
-            $login_result = $this->login($this->username, $this->password);
196
-            if ($login_result) {
197
-                $this->disconnect();
198
-
199
-                return true;
200
-            }
201
-        }
202
-        // We need to disconnect regardless of whether the login succeeded
203
-        $this->disconnect();
204
-
205
-        return false;
206
-    }
207
-
208
-    /**
209
-     * Connect to a POP3 server.
210
-     *
211
-     * @param string   $host
212
-     * @param int|bool $port
213
-     * @param int      $tval
214
-     *
215
-     * @return bool
216
-     */
217
-    public function connect($host, $port = false, $tval = 30)
218
-    {
219
-        //  Are we already connected?
220
-        if ($this->connected) {
221
-            return true;
222
-        }
223
-
224
-        //On Windows this will raise a PHP Warning error if the hostname doesn't exist.
225
-        //Rather than suppress it with @fsockopen, capture it cleanly instead
226
-        set_error_handler([$this, 'catchWarning']);
227
-
228
-        if (false === $port) {
229
-            $port = static::DEFAULT_PORT;
230
-        }
231
-
232
-        //  connect to the POP3 server
233
-        $this->pop_conn = fsockopen(
234
-            $host, //  POP3 Host
235
-            $port, //  Port #
236
-            $errno, //  Error Number
237
-            $errstr, //  Error Message
238
-            $tval
239
-        ); //  Timeout (seconds)
240
-        //  Restore the error handler
241
-        restore_error_handler();
242
-
243
-        //  Did we connect?
244
-        if (false === $this->pop_conn) {
245
-            //  It would appear not...
246
-            $this->setError(
247
-                "Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr"
248
-            );
249
-
250
-            return false;
251
-        }
252
-
253
-        //  Increase the stream time-out
254
-        stream_set_timeout($this->pop_conn, $tval, 0);
255
-
256
-        //  Get the POP3 server response
257
-        $pop3_response = $this->getResponse();
258
-        //  Check for the +OK
259
-        if ($this->checkResponse($pop3_response)) {
260
-            //  The connection is established and the POP3 server is talking
261
-            $this->connected = true;
262
-
263
-            return true;
264
-        }
265
-
266
-        return false;
267
-    }
268
-
269
-    /**
270
-     * Log in to the POP3 server.
271
-     * Does not support APOP (RFC 2828, 4949).
272
-     *
273
-     * @param string $username
274
-     * @param string $password
275
-     *
276
-     * @return bool
277
-     */
278
-    public function login($username = '', $password = '')
279
-    {
280
-        if (!$this->connected) {
281
-            $this->setError('Not connected to POP3 server');
282
-        }
283
-        if (empty($username)) {
284
-            $username = $this->username;
285
-        }
286
-        if (empty($password)) {
287
-            $password = $this->password;
288
-        }
289
-
290
-        // Send the Username
291
-        $this->sendString("USER $username" . static::LE);
292
-        $pop3_response = $this->getResponse();
293
-        if ($this->checkResponse($pop3_response)) {
294
-            // Send the Password
295
-            $this->sendString("PASS $password" . static::LE);
296
-            $pop3_response = $this->getResponse();
297
-            if ($this->checkResponse($pop3_response)) {
298
-                return true;
299
-            }
300
-        }
301
-
302
-        return false;
303
-    }
304
-
305
-    /**
306
-     * Disconnect from the POP3 server.
307
-     */
308
-    public function disconnect()
309
-    {
310
-        $this->sendString('QUIT');
311
-        //The QUIT command may cause the daemon to exit, which will kill our connection
312
-        //So ignore errors here
313
-        try {
314
-            @fclose($this->pop_conn);
315
-        } catch (Exception $e) {
316
-            //Do nothing
317
-        }
318
-    }
319
-
320
-    /**
321
-     * Get a response from the POP3 server.
322
-     *
323
-     * @param int $size The maximum number of bytes to retrieve
324
-     *
325
-     * @return string
326
-     */
327
-    protected function getResponse($size = 128)
328
-    {
329
-        $response = fgets($this->pop_conn, $size);
330
-        if ($this->do_debug >= 1) {
331
-            echo 'Server -> Client: ', $response;
332
-        }
333
-
334
-        return $response;
335
-    }
336
-
337
-    /**
338
-     * Send raw data to the POP3 server.
339
-     *
340
-     * @param string $string
341
-     *
342
-     * @return int
343
-     */
344
-    protected function sendString($string)
345
-    {
346
-        if ($this->pop_conn) {
347
-            if ($this->do_debug >= 2) { //Show client messages when debug >= 2
348
-                echo 'Client -> Server: ', $string;
349
-            }
350
-
351
-            return fwrite($this->pop_conn, $string, strlen($string));
352
-        }
353
-
354
-        return 0;
355
-    }
356
-
357
-    /**
358
-     * Checks the POP3 server response.
359
-     * Looks for for +OK or -ERR.
360
-     *
361
-     * @param string $string
362
-     *
363
-     * @return bool
364
-     */
365
-    protected function checkResponse($string)
366
-    {
367
-        if (substr($string, 0, 3) !== '+OK') {
368
-            $this->setError("Server reported an error: $string");
369
-
370
-            return false;
371
-        }
372
-
373
-        return true;
374
-    }
375
-
376
-    /**
377
-     * Add an error to the internal error store.
378
-     * Also display debug output if it's enabled.
379
-     *
380
-     * @param string $error
381
-     */
382
-    protected function setError($error)
383
-    {
384
-        $this->errors[] = $error;
385
-        if ($this->do_debug >= 1) {
386
-            echo '<pre>';
387
-            foreach ($this->errors as $error) {
388
-                print_r($error);
389
-            }
390
-            echo '</pre>';
391
-        }
392
-    }
393
-
394
-    /**
395
-     * Get an array of error messages, if any.
396
-     *
397
-     * @return array
398
-     */
399
-    public function getErrors()
400
-    {
401
-        return $this->errors;
402
-    }
403
-
404
-    /**
405
-     * POP3 connection error handler.
406
-     *
407
-     * @param int    $errno
408
-     * @param string $errstr
409
-     * @param string $errfile
410
-     * @param int    $errline
411
-     */
412
-    protected function catchWarning($errno, $errstr, $errfile, $errline)
413
-    {
414
-        $this->setError(
415
-            'Connecting to the POP3 server raised a PHP warning:' .
416
-            "errno: $errno errstr: $errstr; errfile: $errfile; errline: $errline"
417
-        );
418
-    }
43
+	/**
44
+	 * The POP3 PHPMailer Version number.
45
+	 *
46
+	 * @var string
47
+	 */
48
+	const VERSION = '6.0.1';
49
+
50
+	/**
51
+	 * Default POP3 port number.
52
+	 *
53
+	 * @var int
54
+	 */
55
+	const DEFAULT_PORT = 110;
56
+
57
+	/**
58
+	 * Default timeout in seconds.
59
+	 *
60
+	 * @var int
61
+	 */
62
+	const DEFAULT_TIMEOUT = 30;
63
+
64
+	/**
65
+	 * Debug display level.
66
+	 * Options: 0 = no, 1+ = yes.
67
+	 *
68
+	 * @var int
69
+	 */
70
+	public $do_debug = 0;
71
+
72
+	/**
73
+	 * POP3 mail server hostname.
74
+	 *
75
+	 * @var string
76
+	 */
77
+	public $host;
78
+
79
+	/**
80
+	 * POP3 port number.
81
+	 *
82
+	 * @var int
83
+	 */
84
+	public $port;
85
+
86
+	/**
87
+	 * POP3 Timeout Value in seconds.
88
+	 *
89
+	 * @var int
90
+	 */
91
+	public $tval;
92
+
93
+	/**
94
+	 * POP3 username.
95
+	 *
96
+	 * @var string
97
+	 */
98
+	public $username;
99
+
100
+	/**
101
+	 * POP3 password.
102
+	 *
103
+	 * @var string
104
+	 */
105
+	public $password;
106
+
107
+	/**
108
+	 * Resource handle for the POP3 connection socket.
109
+	 *
110
+	 * @var resource
111
+	 */
112
+	protected $pop_conn;
113
+
114
+	/**
115
+	 * Are we connected?
116
+	 *
117
+	 * @var bool
118
+	 */
119
+	protected $connected = false;
120
+
121
+	/**
122
+	 * Error container.
123
+	 *
124
+	 * @var array
125
+	 */
126
+	protected $errors = [];
127
+
128
+	/**
129
+	 * Line break constant.
130
+	 */
131
+	const LE = "\r\n";
132
+
133
+	/**
134
+	 * Simple static wrapper for all-in-one POP before SMTP.
135
+	 *
136
+	 * @param string   $host        The hostname to connect to
137
+	 * @param int|bool $port        The port number to connect to
138
+	 * @param int|bool $timeout     The timeout value
139
+	 * @param string   $username
140
+	 * @param string   $password
141
+	 * @param int      $debug_level
142
+	 *
143
+	 * @return bool
144
+	 */
145
+	public static function popBeforeSmtp(
146
+		$host,
147
+		$port = false,
148
+		$timeout = false,
149
+		$username = '',
150
+		$password = '',
151
+		$debug_level = 0
152
+	) {
153
+		$pop = new self();
154
+
155
+		return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level);
156
+	}
157
+
158
+	/**
159
+	 * Authenticate with a POP3 server.
160
+	 * A connect, login, disconnect sequence
161
+	 * appropriate for POP-before SMTP authorisation.
162
+	 *
163
+	 * @param string   $host        The hostname to connect to
164
+	 * @param int|bool $port        The port number to connect to
165
+	 * @param int|bool $timeout     The timeout value
166
+	 * @param string   $username
167
+	 * @param string   $password
168
+	 * @param int      $debug_level
169
+	 *
170
+	 * @return bool
171
+	 */
172
+	public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
173
+	{
174
+		$this->host = $host;
175
+		// If no port value provided, use default
176
+		if (false === $port) {
177
+			$this->port = static::DEFAULT_PORT;
178
+		} else {
179
+			$this->port = (int) $port;
180
+		}
181
+		// If no timeout value provided, use default
182
+		if (false === $timeout) {
183
+			$this->tval = static::DEFAULT_TIMEOUT;
184
+		} else {
185
+			$this->tval = (int) $timeout;
186
+		}
187
+		$this->do_debug = $debug_level;
188
+		$this->username = $username;
189
+		$this->password = $password;
190
+		//  Reset the error log
191
+		$this->errors = [];
192
+		//  connect
193
+		$result = $this->connect($this->host, $this->port, $this->tval);
194
+		if ($result) {
195
+			$login_result = $this->login($this->username, $this->password);
196
+			if ($login_result) {
197
+				$this->disconnect();
198
+
199
+				return true;
200
+			}
201
+		}
202
+		// We need to disconnect regardless of whether the login succeeded
203
+		$this->disconnect();
204
+
205
+		return false;
206
+	}
207
+
208
+	/**
209
+	 * Connect to a POP3 server.
210
+	 *
211
+	 * @param string   $host
212
+	 * @param int|bool $port
213
+	 * @param int      $tval
214
+	 *
215
+	 * @return bool
216
+	 */
217
+	public function connect($host, $port = false, $tval = 30)
218
+	{
219
+		//  Are we already connected?
220
+		if ($this->connected) {
221
+			return true;
222
+		}
223
+
224
+		//On Windows this will raise a PHP Warning error if the hostname doesn't exist.
225
+		//Rather than suppress it with @fsockopen, capture it cleanly instead
226
+		set_error_handler([$this, 'catchWarning']);
227
+
228
+		if (false === $port) {
229
+			$port = static::DEFAULT_PORT;
230
+		}
231
+
232
+		//  connect to the POP3 server
233
+		$this->pop_conn = fsockopen(
234
+			$host, //  POP3 Host
235
+			$port, //  Port #
236
+			$errno, //  Error Number
237
+			$errstr, //  Error Message
238
+			$tval
239
+		); //  Timeout (seconds)
240
+		//  Restore the error handler
241
+		restore_error_handler();
242
+
243
+		//  Did we connect?
244
+		if (false === $this->pop_conn) {
245
+			//  It would appear not...
246
+			$this->setError(
247
+				"Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr"
248
+			);
249
+
250
+			return false;
251
+		}
252
+
253
+		//  Increase the stream time-out
254
+		stream_set_timeout($this->pop_conn, $tval, 0);
255
+
256
+		//  Get the POP3 server response
257
+		$pop3_response = $this->getResponse();
258
+		//  Check for the +OK
259
+		if ($this->checkResponse($pop3_response)) {
260
+			//  The connection is established and the POP3 server is talking
261
+			$this->connected = true;
262
+
263
+			return true;
264
+		}
265
+
266
+		return false;
267
+	}
268
+
269
+	/**
270
+	 * Log in to the POP3 server.
271
+	 * Does not support APOP (RFC 2828, 4949).
272
+	 *
273
+	 * @param string $username
274
+	 * @param string $password
275
+	 *
276
+	 * @return bool
277
+	 */
278
+	public function login($username = '', $password = '')
279
+	{
280
+		if (!$this->connected) {
281
+			$this->setError('Not connected to POP3 server');
282
+		}
283
+		if (empty($username)) {
284
+			$username = $this->username;
285
+		}
286
+		if (empty($password)) {
287
+			$password = $this->password;
288
+		}
289
+
290
+		// Send the Username
291
+		$this->sendString("USER $username" . static::LE);
292
+		$pop3_response = $this->getResponse();
293
+		if ($this->checkResponse($pop3_response)) {
294
+			// Send the Password
295
+			$this->sendString("PASS $password" . static::LE);
296
+			$pop3_response = $this->getResponse();
297
+			if ($this->checkResponse($pop3_response)) {
298
+				return true;
299
+			}
300
+		}
301
+
302
+		return false;
303
+	}
304
+
305
+	/**
306
+	 * Disconnect from the POP3 server.
307
+	 */
308
+	public function disconnect()
309
+	{
310
+		$this->sendString('QUIT');
311
+		//The QUIT command may cause the daemon to exit, which will kill our connection
312
+		//So ignore errors here
313
+		try {
314
+			@fclose($this->pop_conn);
315
+		} catch (Exception $e) {
316
+			//Do nothing
317
+		}
318
+	}
319
+
320
+	/**
321
+	 * Get a response from the POP3 server.
322
+	 *
323
+	 * @param int $size The maximum number of bytes to retrieve
324
+	 *
325
+	 * @return string
326
+	 */
327
+	protected function getResponse($size = 128)
328
+	{
329
+		$response = fgets($this->pop_conn, $size);
330
+		if ($this->do_debug >= 1) {
331
+			echo 'Server -> Client: ', $response;
332
+		}
333
+
334
+		return $response;
335
+	}
336
+
337
+	/**
338
+	 * Send raw data to the POP3 server.
339
+	 *
340
+	 * @param string $string
341
+	 *
342
+	 * @return int
343
+	 */
344
+	protected function sendString($string)
345
+	{
346
+		if ($this->pop_conn) {
347
+			if ($this->do_debug >= 2) { //Show client messages when debug >= 2
348
+				echo 'Client -> Server: ', $string;
349
+			}
350
+
351
+			return fwrite($this->pop_conn, $string, strlen($string));
352
+		}
353
+
354
+		return 0;
355
+	}
356
+
357
+	/**
358
+	 * Checks the POP3 server response.
359
+	 * Looks for for +OK or -ERR.
360
+	 *
361
+	 * @param string $string
362
+	 *
363
+	 * @return bool
364
+	 */
365
+	protected function checkResponse($string)
366
+	{
367
+		if (substr($string, 0, 3) !== '+OK') {
368
+			$this->setError("Server reported an error: $string");
369
+
370
+			return false;
371
+		}
372
+
373
+		return true;
374
+	}
375
+
376
+	/**
377
+	 * Add an error to the internal error store.
378
+	 * Also display debug output if it's enabled.
379
+	 *
380
+	 * @param string $error
381
+	 */
382
+	protected function setError($error)
383
+	{
384
+		$this->errors[] = $error;
385
+		if ($this->do_debug >= 1) {
386
+			echo '<pre>';
387
+			foreach ($this->errors as $error) {
388
+				print_r($error);
389
+			}
390
+			echo '</pre>';
391
+		}
392
+	}
393
+
394
+	/**
395
+	 * Get an array of error messages, if any.
396
+	 *
397
+	 * @return array
398
+	 */
399
+	public function getErrors()
400
+	{
401
+		return $this->errors;
402
+	}
403
+
404
+	/**
405
+	 * POP3 connection error handler.
406
+	 *
407
+	 * @param int    $errno
408
+	 * @param string $errstr
409
+	 * @param string $errfile
410
+	 * @param int    $errline
411
+	 */
412
+	protected function catchWarning($errno, $errstr, $errfile, $errline)
413
+	{
414
+		$this->setError(
415
+			'Connecting to the POP3 server raised a PHP warning:' .
416
+			"errno: $errno errstr: $errstr; errfile: $errfile; errline: $errline"
417
+		);
418
+	}
419 419
 }
Please login to merge, or discard this patch.
www/vendor/PHPMailer/PHPMailer/PHPMailer.php 2 patches
Indentation   +4332 added lines, -4332 removed lines patch added patch discarded remove patch
@@ -30,1172 +30,1172 @@  discard block
 block discarded – undo
30 30
  */
31 31
 class PHPMailer
32 32
 {
33
-    /**
34
-     * Email priority.
35
-     * Options: null (default), 1 = High, 3 = Normal, 5 = low.
36
-     * When null, the header is not set at all.
37
-     *
38
-     * @var int
39
-     */
40
-    public $Priority = null;
41
-
42
-    /**
43
-     * The character set of the message.
44
-     *
45
-     * @var string
46
-     */
47
-    public $CharSet = 'iso-8859-1';
48
-
49
-    /**
50
-     * The MIME Content-type of the message.
51
-     *
52
-     * @var string
53
-     */
54
-    public $ContentType = 'text/plain';
55
-
56
-    /**
57
-     * The message encoding.
58
-     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
59
-     *
60
-     * @var string
61
-     */
62
-    public $Encoding = '8bit';
63
-
64
-    /**
65
-     * Holds the most recent mailer error message.
66
-     *
67
-     * @var string
68
-     */
69
-    public $ErrorInfo = '';
70
-
71
-    /**
72
-     * The From email address for the message.
73
-     *
74
-     * @var string
75
-     */
76
-    public $From = 'root@localhost';
77
-
78
-    /**
79
-     * The From name of the message.
80
-     *
81
-     * @var string
82
-     */
83
-    public $FromName = 'Root User';
84
-
85
-    /**
86
-     * The envelope sender of the message.
87
-     * This will usually be turned into a Return-Path header by the receiver,
88
-     * and is the address that bounces will be sent to.
89
-     * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' value over SMTP.
90
-     *
91
-     * @var string
92
-     */
93
-    public $Sender = '';
94
-
95
-    /**
96
-     * The Subject of the message.
97
-     *
98
-     * @var string
99
-     */
100
-    public $Subject = '';
101
-
102
-    /**
103
-     * An HTML or plain text message body.
104
-     * If HTML then call isHTML(true).
105
-     *
106
-     * @var string
107
-     */
108
-    public $Body = '';
109
-
110
-    /**
111
-     * The plain-text message body.
112
-     * This body can be read by mail clients that do not have HTML email
113
-     * capability such as mutt & Eudora.
114
-     * Clients that can read HTML will view the normal Body.
115
-     *
116
-     * @var string
117
-     */
118
-    public $AltBody = '';
119
-
120
-    /**
121
-     * An iCal message part body.
122
-     * Only supported in simple alt or alt_inline message types
123
-     * To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator.
124
-     *
125
-     * @see http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
126
-     * @see http://kigkonsult.se/iCalcreator/
127
-     *
128
-     * @var string
129
-     */
130
-    public $Ical = '';
131
-
132
-    /**
133
-     * The complete compiled MIME message body.
134
-     *
135
-     * @var string
136
-     */
137
-    protected $MIMEBody = '';
138
-
139
-    /**
140
-     * The complete compiled MIME message headers.
141
-     *
142
-     * @var string
143
-     */
144
-    protected $MIMEHeader = '';
145
-
146
-    /**
147
-     * Extra headers that createHeader() doesn't fold in.
148
-     *
149
-     * @var string
150
-     */
151
-    protected $mailHeader = '';
152
-
153
-    /**
154
-     * Word-wrap the message body to this number of chars.
155
-     * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
156
-     *
157
-     * @see static::STD_LINE_LENGTH
158
-     *
159
-     * @var int
160
-     */
161
-    public $WordWrap = 0;
162
-
163
-    /**
164
-     * Which method to use to send mail.
165
-     * Options: "mail", "sendmail", or "smtp".
166
-     *
167
-     * @var string
168
-     */
169
-    public $Mailer = 'mail';
170
-
171
-    /**
172
-     * The path to the sendmail program.
173
-     *
174
-     * @var string
175
-     */
176
-    public $Sendmail = '/usr/sbin/sendmail';
177
-
178
-    /**
179
-     * Whether mail() uses a fully sendmail-compatible MTA.
180
-     * One which supports sendmail's "-oi -f" options.
181
-     *
182
-     * @var bool
183
-     */
184
-    public $UseSendmailOptions = true;
185
-
186
-    /**
187
-     * The email address that a reading confirmation should be sent to, also known as read receipt.
188
-     *
189
-     * @var string
190
-     */
191
-    public $ConfirmReadingTo = '';
192
-
193
-    /**
194
-     * The hostname to use in the Message-ID header and as default HELO string.
195
-     * If empty, PHPMailer attempts to find one with, in order,
196
-     * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
197
-     * 'localhost.localdomain'.
198
-     *
199
-     * @var string
200
-     */
201
-    public $Hostname = '';
202
-
203
-    /**
204
-     * An ID to be used in the Message-ID header.
205
-     * If empty, a unique id will be generated.
206
-     * You can set your own, but it must be in the format "<id@domain>",
207
-     * as defined in RFC5322 section 3.6.4 or it will be ignored.
208
-     *
209
-     * @see https://tools.ietf.org/html/rfc5322#section-3.6.4
210
-     *
211
-     * @var string
212
-     */
213
-    public $MessageID = '';
214
-
215
-    /**
216
-     * The message Date to be used in the Date header.
217
-     * If empty, the current date will be added.
218
-     *
219
-     * @var string
220
-     */
221
-    public $MessageDate = '';
222
-
223
-    /**
224
-     * SMTP hosts.
225
-     * Either a single hostname or multiple semicolon-delimited hostnames.
226
-     * You can also specify a different port
227
-     * for each host by using this format: [hostname:port]
228
-     * (e.g. "smtp1.example.com:25;smtp2.example.com").
229
-     * You can also specify encryption type, for example:
230
-     * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
231
-     * Hosts will be tried in order.
232
-     *
233
-     * @var string
234
-     */
235
-    public $Host = 'localhost';
236
-
237
-    /**
238
-     * The default SMTP server port.
239
-     *
240
-     * @var int
241
-     */
242
-    public $Port = 25;
243
-
244
-    /**
245
-     * The SMTP HELO of the message.
246
-     * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
247
-     * one with the same method described above for $Hostname.
248
-     *
249
-     * @see PHPMailer::$Hostname
250
-     *
251
-     * @var string
252
-     */
253
-    public $Helo = '';
254
-
255
-    /**
256
-     * What kind of encryption to use on the SMTP connection.
257
-     * Options: '', 'ssl' or 'tls'.
258
-     *
259
-     * @var string
260
-     */
261
-    public $SMTPSecure = '';
262
-
263
-    /**
264
-     * Whether to enable TLS encryption automatically if a server supports it,
265
-     * even if `SMTPSecure` is not set to 'tls'.
266
-     * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
267
-     *
268
-     * @var bool
269
-     */
270
-    public $SMTPAutoTLS = true;
271
-
272
-    /**
273
-     * Whether to use SMTP Login.
274
-     * Uses the Username and Password properties.
275
-     *
276
-     * @see PHPMailer::$Username
277
-     * @see PHPMailer::$Password
278
-     *
279
-     * @var bool
280
-     */
281
-    public $SMTPAuth = false;
282
-
283
-    /**
284
-     * Options array passed to stream_context_create when connecting via SMTP.
285
-     *
286
-     * @var array
287
-     */
288
-    public $SMTPOptions = [];
289
-
290
-    /**
291
-     * SMTP username.
292
-     *
293
-     * @var string
294
-     */
295
-    public $Username = '';
296
-
297
-    /**
298
-     * SMTP password.
299
-     *
300
-     * @var string
301
-     */
302
-    public $Password = '';
303
-
304
-    /**
305
-     * SMTP auth type.
306
-     * Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2, attempted in that order if not specified.
307
-     *
308
-     * @var string
309
-     */
310
-    public $AuthType = '';
311
-
312
-    /**
313
-     * An instance of the PHPMailer OAuth class.
314
-     *
315
-     * @var OAuth
316
-     */
317
-    protected $oauth = null;
318
-
319
-    /**
320
-     * The SMTP server timeout in seconds.
321
-     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
322
-     *
323
-     * @var int
324
-     */
325
-    public $Timeout = 300;
326
-
327
-    /**
328
-     * SMTP class debug output mode.
329
-     * Debug output level.
330
-     * Options:
331
-     * * `0` No output
332
-     * * `1` Commands
333
-     * * `2` Data and commands
334
-     * * `3` As 2 plus connection status
335
-     * * `4` Low-level data output.
336
-     *
337
-     * @see SMTP::$do_debug
338
-     *
339
-     * @var int
340
-     */
341
-    public $SMTPDebug = 0;
342
-
343
-    /**
344
-     * How to handle debug output.
345
-     * Options:
346
-     * * `echo` Output plain-text as-is, appropriate for CLI
347
-     * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
348
-     * * `error_log` Output to error log as configured in php.ini
349
-     * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise.
350
-     * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
351
-     *
352
-     * ```php
353
-     * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
354
-     * ```
355
-     *
356
-     * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug`
357
-     * level output is used:
358
-     *
359
-     * ```php
360
-     * $mail->Debugoutput = new myPsr3Logger;
361
-     * ```
362
-     *
363
-     * @see SMTP::$Debugoutput
364
-     *
365
-     * @var string|callable|\Psr\Log\LoggerInterface
366
-     */
367
-    public $Debugoutput = 'echo';
368
-
369
-    /**
370
-     * Whether to keep SMTP connection open after each message.
371
-     * If this is set to true then to close the connection
372
-     * requires an explicit call to smtpClose().
373
-     *
374
-     * @var bool
375
-     */
376
-    public $SMTPKeepAlive = false;
377
-
378
-    /**
379
-     * Whether to split multiple to addresses into multiple messages
380
-     * or send them all in one message.
381
-     * Only supported in `mail` and `sendmail` transports, not in SMTP.
382
-     *
383
-     * @var bool
384
-     */
385
-    public $SingleTo = false;
386
-
387
-    /**
388
-     * Storage for addresses when SingleTo is enabled.
389
-     *
390
-     * @var array
391
-     */
392
-    protected $SingleToArray = [];
393
-
394
-    /**
395
-     * Whether to generate VERP addresses on send.
396
-     * Only applicable when sending via SMTP.
397
-     *
398
-     * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
399
-     * @see http://www.postfix.org/VERP_README.html Postfix VERP info
400
-     *
401
-     * @var bool
402
-     */
403
-    public $do_verp = false;
404
-
405
-    /**
406
-     * Whether to allow sending messages with an empty body.
407
-     *
408
-     * @var bool
409
-     */
410
-    public $AllowEmpty = false;
411
-
412
-    /**
413
-     * DKIM selector.
414
-     *
415
-     * @var string
416
-     */
417
-    public $DKIM_selector = '';
418
-
419
-    /**
420
-     * DKIM Identity.
421
-     * Usually the email address used as the source of the email.
422
-     *
423
-     * @var string
424
-     */
425
-    public $DKIM_identity = '';
426
-
427
-    /**
428
-     * DKIM passphrase.
429
-     * Used if your key is encrypted.
430
-     *
431
-     * @var string
432
-     */
433
-    public $DKIM_passphrase = '';
434
-
435
-    /**
436
-     * DKIM signing domain name.
437
-     *
438
-     * @example 'example.com'
439
-     *
440
-     * @var string
441
-     */
442
-    public $DKIM_domain = '';
443
-
444
-    /**
445
-     * DKIM private key file path.
446
-     *
447
-     * @var string
448
-     */
449
-    public $DKIM_private = '';
450
-
451
-    /**
452
-     * DKIM private key string.
453
-     *
454
-     * If set, takes precedence over `$DKIM_private`.
455
-     *
456
-     * @var string
457
-     */
458
-    public $DKIM_private_string = '';
459
-
460
-    /**
461
-     * Callback Action function name.
462
-     *
463
-     * The function that handles the result of the send email action.
464
-     * It is called out by send() for each email sent.
465
-     *
466
-     * Value can be any php callable: http://www.php.net/is_callable
467
-     *
468
-     * Parameters:
469
-     *   bool $result        result of the send action
470
-     *   array   $to            email addresses of the recipients
471
-     *   array   $cc            cc email addresses
472
-     *   array   $bcc           bcc email addresses
473
-     *   string  $subject       the subject
474
-     *   string  $body          the email body
475
-     *   string  $from          email address of sender
476
-     *   string  $extra         extra information of possible use
477
-     *                          "smtp_transaction_id' => last smtp transaction id
478
-     *
479
-     * @var string
480
-     */
481
-    public $action_function = '';
482
-
483
-    /**
484
-     * What to put in the X-Mailer header.
485
-     * Options: An empty string for PHPMailer default, whitespace for none, or a string to use.
486
-     *
487
-     * @var string
488
-     */
489
-    public $XMailer = '';
490
-
491
-    /**
492
-     * Which validator to use by default when validating email addresses.
493
-     * May be a callable to inject your own validator, but there are several built-in validators.
494
-     * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option.
495
-     *
496
-     * @see PHPMailer::validateAddress()
497
-     *
498
-     * @var string|callable
499
-     */
500
-    public static $validator = 'php';
501
-
502
-    /**
503
-     * An instance of the SMTP sender class.
504
-     *
505
-     * @var SMTP
506
-     */
507
-    protected $smtp = null;
508
-
509
-    /**
510
-     * The array of 'to' names and addresses.
511
-     *
512
-     * @var array
513
-     */
514
-    protected $to = [];
515
-
516
-    /**
517
-     * The array of 'cc' names and addresses.
518
-     *
519
-     * @var array
520
-     */
521
-    protected $cc = [];
522
-
523
-    /**
524
-     * The array of 'bcc' names and addresses.
525
-     *
526
-     * @var array
527
-     */
528
-    protected $bcc = [];
529
-
530
-    /**
531
-     * The array of reply-to names and addresses.
532
-     *
533
-     * @var array
534
-     */
535
-    protected $ReplyTo = [];
536
-
537
-    /**
538
-     * An array of all kinds of addresses.
539
-     * Includes all of $to, $cc, $bcc.
540
-     *
541
-     * @see PHPMailer::$to
542
-     * @see PHPMailer::$cc
543
-     * @see PHPMailer::$bcc
544
-     *
545
-     * @var array
546
-     */
547
-    protected $all_recipients = [];
548
-
549
-    /**
550
-     * An array of names and addresses queued for validation.
551
-     * In send(), valid and non duplicate entries are moved to $all_recipients
552
-     * and one of $to, $cc, or $bcc.
553
-     * This array is used only for addresses with IDN.
554
-     *
555
-     * @see PHPMailer::$to
556
-     * @see PHPMailer::$cc
557
-     * @see PHPMailer::$bcc
558
-     * @see PHPMailer::$all_recipients
559
-     *
560
-     * @var array
561
-     */
562
-    protected $RecipientsQueue = [];
563
-
564
-    /**
565
-     * An array of reply-to names and addresses queued for validation.
566
-     * In send(), valid and non duplicate entries are moved to $ReplyTo.
567
-     * This array is used only for addresses with IDN.
568
-     *
569
-     * @see PHPMailer::$ReplyTo
570
-     *
571
-     * @var array
572
-     */
573
-    protected $ReplyToQueue = [];
574
-
575
-    /**
576
-     * The array of attachments.
577
-     *
578
-     * @var array
579
-     */
580
-    protected $attachment = [];
581
-
582
-    /**
583
-     * The array of custom headers.
584
-     *
585
-     * @var array
586
-     */
587
-    protected $CustomHeader = [];
588
-
589
-    /**
590
-     * The most recent Message-ID (including angular brackets).
591
-     *
592
-     * @var string
593
-     */
594
-    protected $lastMessageID = '';
595
-
596
-    /**
597
-     * The message's MIME type.
598
-     *
599
-     * @var string
600
-     */
601
-    protected $message_type = '';
602
-
603
-    /**
604
-     * The array of MIME boundary strings.
605
-     *
606
-     * @var array
607
-     */
608
-    protected $boundary = [];
609
-
610
-    /**
611
-     * The array of available languages.
612
-     *
613
-     * @var array
614
-     */
615
-    protected $language = [];
616
-
617
-    /**
618
-     * The number of errors encountered.
619
-     *
620
-     * @var int
621
-     */
622
-    protected $error_count = 0;
623
-
624
-    /**
625
-     * The S/MIME certificate file path.
626
-     *
627
-     * @var string
628
-     */
629
-    protected $sign_cert_file = '';
630
-
631
-    /**
632
-     * The S/MIME key file path.
633
-     *
634
-     * @var string
635
-     */
636
-    protected $sign_key_file = '';
637
-
638
-    /**
639
-     * The optional S/MIME extra certificates ("CA Chain") file path.
640
-     *
641
-     * @var string
642
-     */
643
-    protected $sign_extracerts_file = '';
644
-
645
-    /**
646
-     * The S/MIME password for the key.
647
-     * Used only if the key is encrypted.
648
-     *
649
-     * @var string
650
-     */
651
-    protected $sign_key_pass = '';
652
-
653
-    /**
654
-     * Whether to throw exceptions for errors.
655
-     *
656
-     * @var bool
657
-     */
658
-    protected $exceptions = false;
659
-
660
-    /**
661
-     * Unique ID used for message ID and boundaries.
662
-     *
663
-     * @var string
664
-     */
665
-    protected $uniqueid = '';
666
-
667
-    /**
668
-     * The PHPMailer Version number.
669
-     *
670
-     * @var string
671
-     */
672
-    const VERSION = '6.0.1';
673
-
674
-    /**
675
-     * Error severity: message only, continue processing.
676
-     *
677
-     * @var int
678
-     */
679
-    const STOP_MESSAGE = 0;
680
-
681
-    /**
682
-     * Error severity: message, likely ok to continue processing.
683
-     *
684
-     * @var int
685
-     */
686
-    const STOP_CONTINUE = 1;
687
-
688
-    /**
689
-     * Error severity: message, plus full stop, critical error reached.
690
-     *
691
-     * @var int
692
-     */
693
-    const STOP_CRITICAL = 2;
694
-
695
-    /**
696
-     * SMTP RFC standard line ending.
697
-     *
698
-     * @var string
699
-     */
700
-    protected static $LE = "\r\n";
701
-
702
-    /**
703
-     * The maximum line length allowed by RFC 2822 section 2.1.1.
704
-     *
705
-     * @var int
706
-     */
707
-    const MAX_LINE_LENGTH = 998;
708
-
709
-    /**
710
-     * The lower maximum line length allowed by RFC 2822 section 2.1.1.
711
-     *
712
-     * @var int
713
-     */
714
-    const STD_LINE_LENGTH = 78;
715
-
716
-    /**
717
-     * Constructor.
718
-     *
719
-     * @param bool $exceptions Should we throw external exceptions?
720
-     */
721
-    public function __construct($exceptions = null)
722
-    {
723
-        if (null !== $exceptions) {
724
-            $this->exceptions = (bool) $exceptions;
725
-        }
726
-        //Pick an appropriate debug output format automatically
727
-        $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html');
728
-    }
729
-
730
-    /**
731
-     * Destructor.
732
-     */
733
-    public function __destruct()
734
-    {
735
-        //Close any open SMTP connection nicely
736
-        $this->smtpClose();
737
-    }
738
-
739
-    /**
740
-     * Call mail() in a safe_mode-aware fashion.
741
-     * Also, unless sendmail_path points to sendmail (or something that
742
-     * claims to be sendmail), don't pass params (not a perfect fix,
743
-     * but it will do).
744
-     *
745
-     * @param string      $to      To
746
-     * @param string      $subject Subject
747
-     * @param string      $body    Message Body
748
-     * @param string      $header  Additional Header(s)
749
-     * @param string|null $params  Params
750
-     *
751
-     * @return bool
752
-     */
753
-    private function mailPassthru($to, $subject, $body, $header, $params)
754
-    {
755
-        //Check overloading of mail function to avoid double-encoding
756
-        if (ini_get('mbstring.func_overload') & 1) {
757
-            $subject = $this->secureHeader($subject);
758
-        } else {
759
-            $subject = $this->encodeHeader($this->secureHeader($subject));
760
-        }
761
-        //Calling mail() with null params breaks
762
-        if (!$this->UseSendmailOptions or null === $params) {
763
-            $result = @mail($to, $subject, $body, $header);
764
-        } else {
765
-            $result = @mail($to, $subject, $body, $header, $params);
766
-        }
767
-
768
-        return $result;
769
-    }
770
-
771
-    /**
772
-     * Output debugging info via user-defined method.
773
-     * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
774
-     *
775
-     * @see PHPMailer::$Debugoutput
776
-     * @see PHPMailer::$SMTPDebug
777
-     *
778
-     * @param string $str
779
-     */
780
-    protected function edebug($str)
781
-    {
782
-        if ($this->SMTPDebug <= 0) {
783
-            return;
784
-        }
785
-        //Is this a PSR-3 logger?
786
-        if (is_a($this->Debugoutput, 'Psr\Log\LoggerInterface')) {
787
-            $this->Debugoutput->debug($str);
788
-
789
-            return;
790
-        }
791
-        //Avoid clash with built-in function names
792
-        if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) {
793
-            call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
794
-
795
-            return;
796
-        }
797
-        switch ($this->Debugoutput) {
798
-            case 'error_log':
799
-                //Don't output, just log
800
-                error_log($str);
801
-                break;
802
-            case 'html':
803
-                //Cleans up output a bit for a better looking, HTML-safe output
804
-                echo htmlentities(
805
-                    preg_replace('/[\r\n]+/', '', $str),
806
-                    ENT_QUOTES,
807
-                    'UTF-8'
808
-                ), "<br>\n";
809
-                break;
810
-            case 'echo':
811
-            default:
812
-                //Normalize line breaks
813
-                $str = preg_replace('/\r\n|\r/ms', "\n", $str);
814
-                echo gmdate('Y-m-d H:i:s'),
815
-                "\t",
816
-                    //Trim trailing space
817
-                trim(
818
-                //Indent for readability, except for trailing break
819
-                    str_replace(
820
-                        "\n",
821
-                        "\n                   \t                  ",
822
-                        trim($str)
823
-                    )
824
-                ),
825
-                "\n";
826
-        }
827
-    }
828
-
829
-    /**
830
-     * Sets message type to HTML or plain.
831
-     *
832
-     * @param bool $isHtml True for HTML mode
833
-     */
834
-    public function isHTML($isHtml = true)
835
-    {
836
-        if ($isHtml) {
837
-            $this->ContentType = 'text/html';
838
-        } else {
839
-            $this->ContentType = 'text/plain';
840
-        }
841
-    }
842
-
843
-    /**
844
-     * Send messages using SMTP.
845
-     */
846
-    public function isSMTP()
847
-    {
848
-        $this->Mailer = 'smtp';
849
-    }
850
-
851
-    /**
852
-     * Send messages using PHP's mail() function.
853
-     */
854
-    public function isMail()
855
-    {
856
-        $this->Mailer = 'mail';
857
-    }
858
-
859
-    /**
860
-     * Send messages using $Sendmail.
861
-     */
862
-    public function isSendmail()
863
-    {
864
-        $ini_sendmail_path = ini_get('sendmail_path');
865
-
866
-        if (!stristr($ini_sendmail_path, 'sendmail')) {
867
-            $this->Sendmail = '/usr/sbin/sendmail';
868
-        } else {
869
-            $this->Sendmail = $ini_sendmail_path;
870
-        }
871
-        $this->Mailer = 'sendmail';
872
-    }
873
-
874
-    /**
875
-     * Send messages using qmail.
876
-     */
877
-    public function isQmail()
878
-    {
879
-        $ini_sendmail_path = ini_get('sendmail_path');
880
-
881
-        if (!stristr($ini_sendmail_path, 'qmail')) {
882
-            $this->Sendmail = '/var/qmail/bin/qmail-inject';
883
-        } else {
884
-            $this->Sendmail = $ini_sendmail_path;
885
-        }
886
-        $this->Mailer = 'qmail';
887
-    }
888
-
889
-    /**
890
-     * Add a "To" address.
891
-     *
892
-     * @param string $address The email address to send to
893
-     * @param string $name
894
-     *
895
-     * @return bool true on success, false if address already used or invalid in some way
896
-     */
897
-    public function addAddress($address, $name = '')
898
-    {
899
-        return $this->addOrEnqueueAnAddress('to', $address, $name);
900
-    }
901
-
902
-    /**
903
-     * Add a "CC" address.
904
-     *
905
-     * @param string $address The email address to send to
906
-     * @param string $name
907
-     *
908
-     * @return bool true on success, false if address already used or invalid in some way
909
-     */
910
-    public function addCC($address, $name = '')
911
-    {
912
-        return $this->addOrEnqueueAnAddress('cc', $address, $name);
913
-    }
914
-
915
-    /**
916
-     * Add a "BCC" address.
917
-     *
918
-     * @param string $address The email address to send to
919
-     * @param string $name
920
-     *
921
-     * @return bool true on success, false if address already used or invalid in some way
922
-     */
923
-    public function addBCC($address, $name = '')
924
-    {
925
-        return $this->addOrEnqueueAnAddress('bcc', $address, $name);
926
-    }
927
-
928
-    /**
929
-     * Add a "Reply-To" address.
930
-     *
931
-     * @param string $address The email address to reply to
932
-     * @param string $name
933
-     *
934
-     * @return bool true on success, false if address already used or invalid in some way
935
-     */
936
-    public function addReplyTo($address, $name = '')
937
-    {
938
-        return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
939
-    }
940
-
941
-    /**
942
-     * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
943
-     * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
944
-     * be modified after calling this function), addition of such addresses is delayed until send().
945
-     * Addresses that have been added already return false, but do not throw exceptions.
946
-     *
947
-     * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
948
-     * @param string $address The email address to send, resp. to reply to
949
-     * @param string $name
950
-     *
951
-     * @throws Exception
952
-     *
953
-     * @return bool true on success, false if address already used or invalid in some way
954
-     */
955
-    protected function addOrEnqueueAnAddress($kind, $address, $name)
956
-    {
957
-        $address = trim($address);
958
-        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
959
-        $pos = strrpos($address, '@');
960
-        if (false === $pos) {
961
-            // At-sign is missing.
962
-            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
963
-            $this->setError($error_message);
964
-            $this->edebug($error_message);
965
-            if ($this->exceptions) {
966
-                throw new Exception($error_message);
967
-            }
968
-
969
-            return false;
970
-        }
971
-        $params = [$kind, $address, $name];
972
-        // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
973
-        if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
974
-            if ('Reply-To' != $kind) {
975
-                if (!array_key_exists($address, $this->RecipientsQueue)) {
976
-                    $this->RecipientsQueue[$address] = $params;
977
-
978
-                    return true;
979
-                }
980
-            } else {
981
-                if (!array_key_exists($address, $this->ReplyToQueue)) {
982
-                    $this->ReplyToQueue[$address] = $params;
983
-
984
-                    return true;
985
-                }
986
-            }
987
-
988
-            return false;
989
-        }
990
-
991
-        // Immediately add standard addresses without IDN.
992
-        return call_user_func_array([$this, 'addAnAddress'], $params);
993
-    }
994
-
995
-    /**
996
-     * Add an address to one of the recipient arrays or to the ReplyTo array.
997
-     * Addresses that have been added already return false, but do not throw exceptions.
998
-     *
999
-     * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
1000
-     * @param string $address The email address to send, resp. to reply to
1001
-     * @param string $name
1002
-     *
1003
-     * @throws Exception
1004
-     *
1005
-     * @return bool true on success, false if address already used or invalid in some way
1006
-     */
1007
-    protected function addAnAddress($kind, $address, $name = '')
1008
-    {
1009
-        if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) {
1010
-            $error_message = $this->lang('Invalid recipient kind: ') . $kind;
1011
-            $this->setError($error_message);
1012
-            $this->edebug($error_message);
1013
-            if ($this->exceptions) {
1014
-                throw new Exception($error_message);
1015
-            }
1016
-
1017
-            return false;
1018
-        }
1019
-        if (!static::validateAddress($address)) {
1020
-            $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
1021
-            $this->setError($error_message);
1022
-            $this->edebug($error_message);
1023
-            if ($this->exceptions) {
1024
-                throw new Exception($error_message);
1025
-            }
1026
-
1027
-            return false;
1028
-        }
1029
-        if ('Reply-To' != $kind) {
1030
-            if (!array_key_exists(strtolower($address), $this->all_recipients)) {
1031
-                array_push($this->$kind, [$address, $name]);
1032
-                $this->all_recipients[strtolower($address)] = true;
1033
-
1034
-                return true;
1035
-            }
1036
-        } else {
1037
-            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
1038
-                $this->ReplyTo[strtolower($address)] = [$address, $name];
1039
-
1040
-                return true;
1041
-            }
1042
-        }
1043
-
1044
-        return false;
1045
-    }
1046
-
1047
-    /**
1048
-     * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
1049
-     * of the form "display name <address>" into an array of name/address pairs.
1050
-     * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
1051
-     * Note that quotes in the name part are removed.
1052
-     *
1053
-     * @see    http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
1054
-     *
1055
-     * @param string $addrstr The address list string
1056
-     * @param bool   $useimap Whether to use the IMAP extension to parse the list
1057
-     *
1058
-     * @return array
1059
-     */
1060
-    public static function parseAddresses($addrstr, $useimap = true)
1061
-    {
1062
-        $addresses = [];
1063
-        if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
1064
-            //Use this built-in parser if it's available
1065
-            $list = imap_rfc822_parse_adrlist($addrstr, '');
1066
-            foreach ($list as $address) {
1067
-                if ('.SYNTAX-ERROR.' != $address->host) {
1068
-                    if (static::validateAddress($address->mailbox . '@' . $address->host)) {
1069
-                        $addresses[] = [
1070
-                            'name' => (property_exists($address, 'personal') ? $address->personal : ''),
1071
-                            'address' => $address->mailbox . '@' . $address->host,
1072
-                        ];
1073
-                    }
1074
-                }
1075
-            }
1076
-        } else {
1077
-            //Use this simpler parser
1078
-            $list = explode(',', $addrstr);
1079
-            foreach ($list as $address) {
1080
-                $address = trim($address);
1081
-                //Is there a separate name part?
1082
-                if (strpos($address, '<') === false) {
1083
-                    //No separate name, just use the whole thing
1084
-                    if (static::validateAddress($address)) {
1085
-                        $addresses[] = [
1086
-                            'name' => '',
1087
-                            'address' => $address,
1088
-                        ];
1089
-                    }
1090
-                } else {
1091
-                    list($name, $email) = explode('<', $address);
1092
-                    $email = trim(str_replace('>', '', $email));
1093
-                    if (static::validateAddress($email)) {
1094
-                        $addresses[] = [
1095
-                            'name' => trim(str_replace(['"', "'"], '', $name)),
1096
-                            'address' => $email,
1097
-                        ];
1098
-                    }
1099
-                }
1100
-            }
1101
-        }
1102
-
1103
-        return $addresses;
1104
-    }
1105
-
1106
-    /**
1107
-     * Set the From and FromName properties.
1108
-     *
1109
-     * @param string $address
1110
-     * @param string $name
1111
-     * @param bool   $auto    Whether to also set the Sender address, defaults to true
1112
-     *
1113
-     * @throws Exception
1114
-     *
1115
-     * @return bool
1116
-     */
1117
-    public function setFrom($address, $name = '', $auto = true)
1118
-    {
1119
-        $address = trim($address);
1120
-        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
1121
-        // Don't validate now addresses with IDN. Will be done in send().
1122
-        $pos = strrpos($address, '@');
1123
-        if (false === $pos or
1124
-            (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
1125
-            !static::validateAddress($address)) {
1126
-            $error_message = $this->lang('invalid_address') . " (setFrom) $address";
1127
-            $this->setError($error_message);
1128
-            $this->edebug($error_message);
1129
-            if ($this->exceptions) {
1130
-                throw new Exception($error_message);
1131
-            }
1132
-
1133
-            return false;
1134
-        }
1135
-        $this->From = $address;
1136
-        $this->FromName = $name;
1137
-        if ($auto) {
1138
-            if (empty($this->Sender)) {
1139
-                $this->Sender = $address;
1140
-            }
1141
-        }
1142
-
1143
-        return true;
1144
-    }
1145
-
1146
-    /**
1147
-     * Return the Message-ID header of the last email.
1148
-     * Technically this is the value from the last time the headers were created,
1149
-     * but it's also the message ID of the last sent message except in
1150
-     * pathological cases.
1151
-     *
1152
-     * @return string
1153
-     */
1154
-    public function getLastMessageID()
1155
-    {
1156
-        return $this->lastMessageID;
1157
-    }
1158
-
1159
-    /**
1160
-     * Check that a string looks like an email address.
1161
-     * Validation patterns supported:
1162
-     * * `auto` Pick best pattern automatically;
1163
-     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0;
1164
-     * * `pcre` Use old PCRE implementation;
1165
-     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
1166
-     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
1167
-     * * `noregex` Don't use a regex: super fast, really dumb.
1168
-     * Alternatively you may pass in a callable to inject your own validator, for example:
1169
-     *
1170
-     * ```php
1171
-     * PHPMailer::validateAddress('[email protected]', function($address) {
1172
-     *     return (strpos($address, '@') !== false);
1173
-     * });
1174
-     * ```
1175
-     *
1176
-     * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
1177
-     *
1178
-     * @param string          $address       The email address to check
1179
-     * @param string|callable $patternselect Which pattern to use
1180
-     *
1181
-     * @return bool
1182
-     */
1183
-    public static function validateAddress($address, $patternselect = null)
1184
-    {
1185
-        if (null === $patternselect) {
1186
-            $patternselect = static::$validator;
1187
-        }
1188
-        if (is_callable($patternselect)) {
1189
-            return call_user_func($patternselect, $address);
1190
-        }
1191
-        //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1192
-        if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1193
-            return false;
1194
-        }
1195
-        switch ($patternselect) {
1196
-            case 'pcre': //Kept for BC
1197
-            case 'pcre8':
1198
-                /*
33
+	/**
34
+	 * Email priority.
35
+	 * Options: null (default), 1 = High, 3 = Normal, 5 = low.
36
+	 * When null, the header is not set at all.
37
+	 *
38
+	 * @var int
39
+	 */
40
+	public $Priority = null;
41
+
42
+	/**
43
+	 * The character set of the message.
44
+	 *
45
+	 * @var string
46
+	 */
47
+	public $CharSet = 'iso-8859-1';
48
+
49
+	/**
50
+	 * The MIME Content-type of the message.
51
+	 *
52
+	 * @var string
53
+	 */
54
+	public $ContentType = 'text/plain';
55
+
56
+	/**
57
+	 * The message encoding.
58
+	 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
59
+	 *
60
+	 * @var string
61
+	 */
62
+	public $Encoding = '8bit';
63
+
64
+	/**
65
+	 * Holds the most recent mailer error message.
66
+	 *
67
+	 * @var string
68
+	 */
69
+	public $ErrorInfo = '';
70
+
71
+	/**
72
+	 * The From email address for the message.
73
+	 *
74
+	 * @var string
75
+	 */
76
+	public $From = 'root@localhost';
77
+
78
+	/**
79
+	 * The From name of the message.
80
+	 *
81
+	 * @var string
82
+	 */
83
+	public $FromName = 'Root User';
84
+
85
+	/**
86
+	 * The envelope sender of the message.
87
+	 * This will usually be turned into a Return-Path header by the receiver,
88
+	 * and is the address that bounces will be sent to.
89
+	 * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' value over SMTP.
90
+	 *
91
+	 * @var string
92
+	 */
93
+	public $Sender = '';
94
+
95
+	/**
96
+	 * The Subject of the message.
97
+	 *
98
+	 * @var string
99
+	 */
100
+	public $Subject = '';
101
+
102
+	/**
103
+	 * An HTML or plain text message body.
104
+	 * If HTML then call isHTML(true).
105
+	 *
106
+	 * @var string
107
+	 */
108
+	public $Body = '';
109
+
110
+	/**
111
+	 * The plain-text message body.
112
+	 * This body can be read by mail clients that do not have HTML email
113
+	 * capability such as mutt & Eudora.
114
+	 * Clients that can read HTML will view the normal Body.
115
+	 *
116
+	 * @var string
117
+	 */
118
+	public $AltBody = '';
119
+
120
+	/**
121
+	 * An iCal message part body.
122
+	 * Only supported in simple alt or alt_inline message types
123
+	 * To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator.
124
+	 *
125
+	 * @see http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
126
+	 * @see http://kigkonsult.se/iCalcreator/
127
+	 *
128
+	 * @var string
129
+	 */
130
+	public $Ical = '';
131
+
132
+	/**
133
+	 * The complete compiled MIME message body.
134
+	 *
135
+	 * @var string
136
+	 */
137
+	protected $MIMEBody = '';
138
+
139
+	/**
140
+	 * The complete compiled MIME message headers.
141
+	 *
142
+	 * @var string
143
+	 */
144
+	protected $MIMEHeader = '';
145
+
146
+	/**
147
+	 * Extra headers that createHeader() doesn't fold in.
148
+	 *
149
+	 * @var string
150
+	 */
151
+	protected $mailHeader = '';
152
+
153
+	/**
154
+	 * Word-wrap the message body to this number of chars.
155
+	 * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
156
+	 *
157
+	 * @see static::STD_LINE_LENGTH
158
+	 *
159
+	 * @var int
160
+	 */
161
+	public $WordWrap = 0;
162
+
163
+	/**
164
+	 * Which method to use to send mail.
165
+	 * Options: "mail", "sendmail", or "smtp".
166
+	 *
167
+	 * @var string
168
+	 */
169
+	public $Mailer = 'mail';
170
+
171
+	/**
172
+	 * The path to the sendmail program.
173
+	 *
174
+	 * @var string
175
+	 */
176
+	public $Sendmail = '/usr/sbin/sendmail';
177
+
178
+	/**
179
+	 * Whether mail() uses a fully sendmail-compatible MTA.
180
+	 * One which supports sendmail's "-oi -f" options.
181
+	 *
182
+	 * @var bool
183
+	 */
184
+	public $UseSendmailOptions = true;
185
+
186
+	/**
187
+	 * The email address that a reading confirmation should be sent to, also known as read receipt.
188
+	 *
189
+	 * @var string
190
+	 */
191
+	public $ConfirmReadingTo = '';
192
+
193
+	/**
194
+	 * The hostname to use in the Message-ID header and as default HELO string.
195
+	 * If empty, PHPMailer attempts to find one with, in order,
196
+	 * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
197
+	 * 'localhost.localdomain'.
198
+	 *
199
+	 * @var string
200
+	 */
201
+	public $Hostname = '';
202
+
203
+	/**
204
+	 * An ID to be used in the Message-ID header.
205
+	 * If empty, a unique id will be generated.
206
+	 * You can set your own, but it must be in the format "<id@domain>",
207
+	 * as defined in RFC5322 section 3.6.4 or it will be ignored.
208
+	 *
209
+	 * @see https://tools.ietf.org/html/rfc5322#section-3.6.4
210
+	 *
211
+	 * @var string
212
+	 */
213
+	public $MessageID = '';
214
+
215
+	/**
216
+	 * The message Date to be used in the Date header.
217
+	 * If empty, the current date will be added.
218
+	 *
219
+	 * @var string
220
+	 */
221
+	public $MessageDate = '';
222
+
223
+	/**
224
+	 * SMTP hosts.
225
+	 * Either a single hostname or multiple semicolon-delimited hostnames.
226
+	 * You can also specify a different port
227
+	 * for each host by using this format: [hostname:port]
228
+	 * (e.g. "smtp1.example.com:25;smtp2.example.com").
229
+	 * You can also specify encryption type, for example:
230
+	 * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
231
+	 * Hosts will be tried in order.
232
+	 *
233
+	 * @var string
234
+	 */
235
+	public $Host = 'localhost';
236
+
237
+	/**
238
+	 * The default SMTP server port.
239
+	 *
240
+	 * @var int
241
+	 */
242
+	public $Port = 25;
243
+
244
+	/**
245
+	 * The SMTP HELO of the message.
246
+	 * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
247
+	 * one with the same method described above for $Hostname.
248
+	 *
249
+	 * @see PHPMailer::$Hostname
250
+	 *
251
+	 * @var string
252
+	 */
253
+	public $Helo = '';
254
+
255
+	/**
256
+	 * What kind of encryption to use on the SMTP connection.
257
+	 * Options: '', 'ssl' or 'tls'.
258
+	 *
259
+	 * @var string
260
+	 */
261
+	public $SMTPSecure = '';
262
+
263
+	/**
264
+	 * Whether to enable TLS encryption automatically if a server supports it,
265
+	 * even if `SMTPSecure` is not set to 'tls'.
266
+	 * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
267
+	 *
268
+	 * @var bool
269
+	 */
270
+	public $SMTPAutoTLS = true;
271
+
272
+	/**
273
+	 * Whether to use SMTP Login.
274
+	 * Uses the Username and Password properties.
275
+	 *
276
+	 * @see PHPMailer::$Username
277
+	 * @see PHPMailer::$Password
278
+	 *
279
+	 * @var bool
280
+	 */
281
+	public $SMTPAuth = false;
282
+
283
+	/**
284
+	 * Options array passed to stream_context_create when connecting via SMTP.
285
+	 *
286
+	 * @var array
287
+	 */
288
+	public $SMTPOptions = [];
289
+
290
+	/**
291
+	 * SMTP username.
292
+	 *
293
+	 * @var string
294
+	 */
295
+	public $Username = '';
296
+
297
+	/**
298
+	 * SMTP password.
299
+	 *
300
+	 * @var string
301
+	 */
302
+	public $Password = '';
303
+
304
+	/**
305
+	 * SMTP auth type.
306
+	 * Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2, attempted in that order if not specified.
307
+	 *
308
+	 * @var string
309
+	 */
310
+	public $AuthType = '';
311
+
312
+	/**
313
+	 * An instance of the PHPMailer OAuth class.
314
+	 *
315
+	 * @var OAuth
316
+	 */
317
+	protected $oauth = null;
318
+
319
+	/**
320
+	 * The SMTP server timeout in seconds.
321
+	 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
322
+	 *
323
+	 * @var int
324
+	 */
325
+	public $Timeout = 300;
326
+
327
+	/**
328
+	 * SMTP class debug output mode.
329
+	 * Debug output level.
330
+	 * Options:
331
+	 * * `0` No output
332
+	 * * `1` Commands
333
+	 * * `2` Data and commands
334
+	 * * `3` As 2 plus connection status
335
+	 * * `4` Low-level data output.
336
+	 *
337
+	 * @see SMTP::$do_debug
338
+	 *
339
+	 * @var int
340
+	 */
341
+	public $SMTPDebug = 0;
342
+
343
+	/**
344
+	 * How to handle debug output.
345
+	 * Options:
346
+	 * * `echo` Output plain-text as-is, appropriate for CLI
347
+	 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
348
+	 * * `error_log` Output to error log as configured in php.ini
349
+	 * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise.
350
+	 * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
351
+	 *
352
+	 * ```php
353
+	 * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
354
+	 * ```
355
+	 *
356
+	 * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug`
357
+	 * level output is used:
358
+	 *
359
+	 * ```php
360
+	 * $mail->Debugoutput = new myPsr3Logger;
361
+	 * ```
362
+	 *
363
+	 * @see SMTP::$Debugoutput
364
+	 *
365
+	 * @var string|callable|\Psr\Log\LoggerInterface
366
+	 */
367
+	public $Debugoutput = 'echo';
368
+
369
+	/**
370
+	 * Whether to keep SMTP connection open after each message.
371
+	 * If this is set to true then to close the connection
372
+	 * requires an explicit call to smtpClose().
373
+	 *
374
+	 * @var bool
375
+	 */
376
+	public $SMTPKeepAlive = false;
377
+
378
+	/**
379
+	 * Whether to split multiple to addresses into multiple messages
380
+	 * or send them all in one message.
381
+	 * Only supported in `mail` and `sendmail` transports, not in SMTP.
382
+	 *
383
+	 * @var bool
384
+	 */
385
+	public $SingleTo = false;
386
+
387
+	/**
388
+	 * Storage for addresses when SingleTo is enabled.
389
+	 *
390
+	 * @var array
391
+	 */
392
+	protected $SingleToArray = [];
393
+
394
+	/**
395
+	 * Whether to generate VERP addresses on send.
396
+	 * Only applicable when sending via SMTP.
397
+	 *
398
+	 * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
399
+	 * @see http://www.postfix.org/VERP_README.html Postfix VERP info
400
+	 *
401
+	 * @var bool
402
+	 */
403
+	public $do_verp = false;
404
+
405
+	/**
406
+	 * Whether to allow sending messages with an empty body.
407
+	 *
408
+	 * @var bool
409
+	 */
410
+	public $AllowEmpty = false;
411
+
412
+	/**
413
+	 * DKIM selector.
414
+	 *
415
+	 * @var string
416
+	 */
417
+	public $DKIM_selector = '';
418
+
419
+	/**
420
+	 * DKIM Identity.
421
+	 * Usually the email address used as the source of the email.
422
+	 *
423
+	 * @var string
424
+	 */
425
+	public $DKIM_identity = '';
426
+
427
+	/**
428
+	 * DKIM passphrase.
429
+	 * Used if your key is encrypted.
430
+	 *
431
+	 * @var string
432
+	 */
433
+	public $DKIM_passphrase = '';
434
+
435
+	/**
436
+	 * DKIM signing domain name.
437
+	 *
438
+	 * @example 'example.com'
439
+	 *
440
+	 * @var string
441
+	 */
442
+	public $DKIM_domain = '';
443
+
444
+	/**
445
+	 * DKIM private key file path.
446
+	 *
447
+	 * @var string
448
+	 */
449
+	public $DKIM_private = '';
450
+
451
+	/**
452
+	 * DKIM private key string.
453
+	 *
454
+	 * If set, takes precedence over `$DKIM_private`.
455
+	 *
456
+	 * @var string
457
+	 */
458
+	public $DKIM_private_string = '';
459
+
460
+	/**
461
+	 * Callback Action function name.
462
+	 *
463
+	 * The function that handles the result of the send email action.
464
+	 * It is called out by send() for each email sent.
465
+	 *
466
+	 * Value can be any php callable: http://www.php.net/is_callable
467
+	 *
468
+	 * Parameters:
469
+	 *   bool $result        result of the send action
470
+	 *   array   $to            email addresses of the recipients
471
+	 *   array   $cc            cc email addresses
472
+	 *   array   $bcc           bcc email addresses
473
+	 *   string  $subject       the subject
474
+	 *   string  $body          the email body
475
+	 *   string  $from          email address of sender
476
+	 *   string  $extra         extra information of possible use
477
+	 *                          "smtp_transaction_id' => last smtp transaction id
478
+	 *
479
+	 * @var string
480
+	 */
481
+	public $action_function = '';
482
+
483
+	/**
484
+	 * What to put in the X-Mailer header.
485
+	 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use.
486
+	 *
487
+	 * @var string
488
+	 */
489
+	public $XMailer = '';
490
+
491
+	/**
492
+	 * Which validator to use by default when validating email addresses.
493
+	 * May be a callable to inject your own validator, but there are several built-in validators.
494
+	 * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option.
495
+	 *
496
+	 * @see PHPMailer::validateAddress()
497
+	 *
498
+	 * @var string|callable
499
+	 */
500
+	public static $validator = 'php';
501
+
502
+	/**
503
+	 * An instance of the SMTP sender class.
504
+	 *
505
+	 * @var SMTP
506
+	 */
507
+	protected $smtp = null;
508
+
509
+	/**
510
+	 * The array of 'to' names and addresses.
511
+	 *
512
+	 * @var array
513
+	 */
514
+	protected $to = [];
515
+
516
+	/**
517
+	 * The array of 'cc' names and addresses.
518
+	 *
519
+	 * @var array
520
+	 */
521
+	protected $cc = [];
522
+
523
+	/**
524
+	 * The array of 'bcc' names and addresses.
525
+	 *
526
+	 * @var array
527
+	 */
528
+	protected $bcc = [];
529
+
530
+	/**
531
+	 * The array of reply-to names and addresses.
532
+	 *
533
+	 * @var array
534
+	 */
535
+	protected $ReplyTo = [];
536
+
537
+	/**
538
+	 * An array of all kinds of addresses.
539
+	 * Includes all of $to, $cc, $bcc.
540
+	 *
541
+	 * @see PHPMailer::$to
542
+	 * @see PHPMailer::$cc
543
+	 * @see PHPMailer::$bcc
544
+	 *
545
+	 * @var array
546
+	 */
547
+	protected $all_recipients = [];
548
+
549
+	/**
550
+	 * An array of names and addresses queued for validation.
551
+	 * In send(), valid and non duplicate entries are moved to $all_recipients
552
+	 * and one of $to, $cc, or $bcc.
553
+	 * This array is used only for addresses with IDN.
554
+	 *
555
+	 * @see PHPMailer::$to
556
+	 * @see PHPMailer::$cc
557
+	 * @see PHPMailer::$bcc
558
+	 * @see PHPMailer::$all_recipients
559
+	 *
560
+	 * @var array
561
+	 */
562
+	protected $RecipientsQueue = [];
563
+
564
+	/**
565
+	 * An array of reply-to names and addresses queued for validation.
566
+	 * In send(), valid and non duplicate entries are moved to $ReplyTo.
567
+	 * This array is used only for addresses with IDN.
568
+	 *
569
+	 * @see PHPMailer::$ReplyTo
570
+	 *
571
+	 * @var array
572
+	 */
573
+	protected $ReplyToQueue = [];
574
+
575
+	/**
576
+	 * The array of attachments.
577
+	 *
578
+	 * @var array
579
+	 */
580
+	protected $attachment = [];
581
+
582
+	/**
583
+	 * The array of custom headers.
584
+	 *
585
+	 * @var array
586
+	 */
587
+	protected $CustomHeader = [];
588
+
589
+	/**
590
+	 * The most recent Message-ID (including angular brackets).
591
+	 *
592
+	 * @var string
593
+	 */
594
+	protected $lastMessageID = '';
595
+
596
+	/**
597
+	 * The message's MIME type.
598
+	 *
599
+	 * @var string
600
+	 */
601
+	protected $message_type = '';
602
+
603
+	/**
604
+	 * The array of MIME boundary strings.
605
+	 *
606
+	 * @var array
607
+	 */
608
+	protected $boundary = [];
609
+
610
+	/**
611
+	 * The array of available languages.
612
+	 *
613
+	 * @var array
614
+	 */
615
+	protected $language = [];
616
+
617
+	/**
618
+	 * The number of errors encountered.
619
+	 *
620
+	 * @var int
621
+	 */
622
+	protected $error_count = 0;
623
+
624
+	/**
625
+	 * The S/MIME certificate file path.
626
+	 *
627
+	 * @var string
628
+	 */
629
+	protected $sign_cert_file = '';
630
+
631
+	/**
632
+	 * The S/MIME key file path.
633
+	 *
634
+	 * @var string
635
+	 */
636
+	protected $sign_key_file = '';
637
+
638
+	/**
639
+	 * The optional S/MIME extra certificates ("CA Chain") file path.
640
+	 *
641
+	 * @var string
642
+	 */
643
+	protected $sign_extracerts_file = '';
644
+
645
+	/**
646
+	 * The S/MIME password for the key.
647
+	 * Used only if the key is encrypted.
648
+	 *
649
+	 * @var string
650
+	 */
651
+	protected $sign_key_pass = '';
652
+
653
+	/**
654
+	 * Whether to throw exceptions for errors.
655
+	 *
656
+	 * @var bool
657
+	 */
658
+	protected $exceptions = false;
659
+
660
+	/**
661
+	 * Unique ID used for message ID and boundaries.
662
+	 *
663
+	 * @var string
664
+	 */
665
+	protected $uniqueid = '';
666
+
667
+	/**
668
+	 * The PHPMailer Version number.
669
+	 *
670
+	 * @var string
671
+	 */
672
+	const VERSION = '6.0.1';
673
+
674
+	/**
675
+	 * Error severity: message only, continue processing.
676
+	 *
677
+	 * @var int
678
+	 */
679
+	const STOP_MESSAGE = 0;
680
+
681
+	/**
682
+	 * Error severity: message, likely ok to continue processing.
683
+	 *
684
+	 * @var int
685
+	 */
686
+	const STOP_CONTINUE = 1;
687
+
688
+	/**
689
+	 * Error severity: message, plus full stop, critical error reached.
690
+	 *
691
+	 * @var int
692
+	 */
693
+	const STOP_CRITICAL = 2;
694
+
695
+	/**
696
+	 * SMTP RFC standard line ending.
697
+	 *
698
+	 * @var string
699
+	 */
700
+	protected static $LE = "\r\n";
701
+
702
+	/**
703
+	 * The maximum line length allowed by RFC 2822 section 2.1.1.
704
+	 *
705
+	 * @var int
706
+	 */
707
+	const MAX_LINE_LENGTH = 998;
708
+
709
+	/**
710
+	 * The lower maximum line length allowed by RFC 2822 section 2.1.1.
711
+	 *
712
+	 * @var int
713
+	 */
714
+	const STD_LINE_LENGTH = 78;
715
+
716
+	/**
717
+	 * Constructor.
718
+	 *
719
+	 * @param bool $exceptions Should we throw external exceptions?
720
+	 */
721
+	public function __construct($exceptions = null)
722
+	{
723
+		if (null !== $exceptions) {
724
+			$this->exceptions = (bool) $exceptions;
725
+		}
726
+		//Pick an appropriate debug output format automatically
727
+		$this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html');
728
+	}
729
+
730
+	/**
731
+	 * Destructor.
732
+	 */
733
+	public function __destruct()
734
+	{
735
+		//Close any open SMTP connection nicely
736
+		$this->smtpClose();
737
+	}
738
+
739
+	/**
740
+	 * Call mail() in a safe_mode-aware fashion.
741
+	 * Also, unless sendmail_path points to sendmail (or something that
742
+	 * claims to be sendmail), don't pass params (not a perfect fix,
743
+	 * but it will do).
744
+	 *
745
+	 * @param string      $to      To
746
+	 * @param string      $subject Subject
747
+	 * @param string      $body    Message Body
748
+	 * @param string      $header  Additional Header(s)
749
+	 * @param string|null $params  Params
750
+	 *
751
+	 * @return bool
752
+	 */
753
+	private function mailPassthru($to, $subject, $body, $header, $params)
754
+	{
755
+		//Check overloading of mail function to avoid double-encoding
756
+		if (ini_get('mbstring.func_overload') & 1) {
757
+			$subject = $this->secureHeader($subject);
758
+		} else {
759
+			$subject = $this->encodeHeader($this->secureHeader($subject));
760
+		}
761
+		//Calling mail() with null params breaks
762
+		if (!$this->UseSendmailOptions or null === $params) {
763
+			$result = @mail($to, $subject, $body, $header);
764
+		} else {
765
+			$result = @mail($to, $subject, $body, $header, $params);
766
+		}
767
+
768
+		return $result;
769
+	}
770
+
771
+	/**
772
+	 * Output debugging info via user-defined method.
773
+	 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
774
+	 *
775
+	 * @see PHPMailer::$Debugoutput
776
+	 * @see PHPMailer::$SMTPDebug
777
+	 *
778
+	 * @param string $str
779
+	 */
780
+	protected function edebug($str)
781
+	{
782
+		if ($this->SMTPDebug <= 0) {
783
+			return;
784
+		}
785
+		//Is this a PSR-3 logger?
786
+		if (is_a($this->Debugoutput, 'Psr\Log\LoggerInterface')) {
787
+			$this->Debugoutput->debug($str);
788
+
789
+			return;
790
+		}
791
+		//Avoid clash with built-in function names
792
+		if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) {
793
+			call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
794
+
795
+			return;
796
+		}
797
+		switch ($this->Debugoutput) {
798
+			case 'error_log':
799
+				//Don't output, just log
800
+				error_log($str);
801
+				break;
802
+			case 'html':
803
+				//Cleans up output a bit for a better looking, HTML-safe output
804
+				echo htmlentities(
805
+					preg_replace('/[\r\n]+/', '', $str),
806
+					ENT_QUOTES,
807
+					'UTF-8'
808
+				), "<br>\n";
809
+				break;
810
+			case 'echo':
811
+			default:
812
+				//Normalize line breaks
813
+				$str = preg_replace('/\r\n|\r/ms', "\n", $str);
814
+				echo gmdate('Y-m-d H:i:s'),
815
+				"\t",
816
+					//Trim trailing space
817
+				trim(
818
+				//Indent for readability, except for trailing break
819
+					str_replace(
820
+						"\n",
821
+						"\n                   \t                  ",
822
+						trim($str)
823
+					)
824
+				),
825
+				"\n";
826
+		}
827
+	}
828
+
829
+	/**
830
+	 * Sets message type to HTML or plain.
831
+	 *
832
+	 * @param bool $isHtml True for HTML mode
833
+	 */
834
+	public function isHTML($isHtml = true)
835
+	{
836
+		if ($isHtml) {
837
+			$this->ContentType = 'text/html';
838
+		} else {
839
+			$this->ContentType = 'text/plain';
840
+		}
841
+	}
842
+
843
+	/**
844
+	 * Send messages using SMTP.
845
+	 */
846
+	public function isSMTP()
847
+	{
848
+		$this->Mailer = 'smtp';
849
+	}
850
+
851
+	/**
852
+	 * Send messages using PHP's mail() function.
853
+	 */
854
+	public function isMail()
855
+	{
856
+		$this->Mailer = 'mail';
857
+	}
858
+
859
+	/**
860
+	 * Send messages using $Sendmail.
861
+	 */
862
+	public function isSendmail()
863
+	{
864
+		$ini_sendmail_path = ini_get('sendmail_path');
865
+
866
+		if (!stristr($ini_sendmail_path, 'sendmail')) {
867
+			$this->Sendmail = '/usr/sbin/sendmail';
868
+		} else {
869
+			$this->Sendmail = $ini_sendmail_path;
870
+		}
871
+		$this->Mailer = 'sendmail';
872
+	}
873
+
874
+	/**
875
+	 * Send messages using qmail.
876
+	 */
877
+	public function isQmail()
878
+	{
879
+		$ini_sendmail_path = ini_get('sendmail_path');
880
+
881
+		if (!stristr($ini_sendmail_path, 'qmail')) {
882
+			$this->Sendmail = '/var/qmail/bin/qmail-inject';
883
+		} else {
884
+			$this->Sendmail = $ini_sendmail_path;
885
+		}
886
+		$this->Mailer = 'qmail';
887
+	}
888
+
889
+	/**
890
+	 * Add a "To" address.
891
+	 *
892
+	 * @param string $address The email address to send to
893
+	 * @param string $name
894
+	 *
895
+	 * @return bool true on success, false if address already used or invalid in some way
896
+	 */
897
+	public function addAddress($address, $name = '')
898
+	{
899
+		return $this->addOrEnqueueAnAddress('to', $address, $name);
900
+	}
901
+
902
+	/**
903
+	 * Add a "CC" address.
904
+	 *
905
+	 * @param string $address The email address to send to
906
+	 * @param string $name
907
+	 *
908
+	 * @return bool true on success, false if address already used or invalid in some way
909
+	 */
910
+	public function addCC($address, $name = '')
911
+	{
912
+		return $this->addOrEnqueueAnAddress('cc', $address, $name);
913
+	}
914
+
915
+	/**
916
+	 * Add a "BCC" address.
917
+	 *
918
+	 * @param string $address The email address to send to
919
+	 * @param string $name
920
+	 *
921
+	 * @return bool true on success, false if address already used or invalid in some way
922
+	 */
923
+	public function addBCC($address, $name = '')
924
+	{
925
+		return $this->addOrEnqueueAnAddress('bcc', $address, $name);
926
+	}
927
+
928
+	/**
929
+	 * Add a "Reply-To" address.
930
+	 *
931
+	 * @param string $address The email address to reply to
932
+	 * @param string $name
933
+	 *
934
+	 * @return bool true on success, false if address already used or invalid in some way
935
+	 */
936
+	public function addReplyTo($address, $name = '')
937
+	{
938
+		return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
939
+	}
940
+
941
+	/**
942
+	 * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
943
+	 * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
944
+	 * be modified after calling this function), addition of such addresses is delayed until send().
945
+	 * Addresses that have been added already return false, but do not throw exceptions.
946
+	 *
947
+	 * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
948
+	 * @param string $address The email address to send, resp. to reply to
949
+	 * @param string $name
950
+	 *
951
+	 * @throws Exception
952
+	 *
953
+	 * @return bool true on success, false if address already used or invalid in some way
954
+	 */
955
+	protected function addOrEnqueueAnAddress($kind, $address, $name)
956
+	{
957
+		$address = trim($address);
958
+		$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
959
+		$pos = strrpos($address, '@');
960
+		if (false === $pos) {
961
+			// At-sign is missing.
962
+			$error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
963
+			$this->setError($error_message);
964
+			$this->edebug($error_message);
965
+			if ($this->exceptions) {
966
+				throw new Exception($error_message);
967
+			}
968
+
969
+			return false;
970
+		}
971
+		$params = [$kind, $address, $name];
972
+		// Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
973
+		if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
974
+			if ('Reply-To' != $kind) {
975
+				if (!array_key_exists($address, $this->RecipientsQueue)) {
976
+					$this->RecipientsQueue[$address] = $params;
977
+
978
+					return true;
979
+				}
980
+			} else {
981
+				if (!array_key_exists($address, $this->ReplyToQueue)) {
982
+					$this->ReplyToQueue[$address] = $params;
983
+
984
+					return true;
985
+				}
986
+			}
987
+
988
+			return false;
989
+		}
990
+
991
+		// Immediately add standard addresses without IDN.
992
+		return call_user_func_array([$this, 'addAnAddress'], $params);
993
+	}
994
+
995
+	/**
996
+	 * Add an address to one of the recipient arrays or to the ReplyTo array.
997
+	 * Addresses that have been added already return false, but do not throw exceptions.
998
+	 *
999
+	 * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
1000
+	 * @param string $address The email address to send, resp. to reply to
1001
+	 * @param string $name
1002
+	 *
1003
+	 * @throws Exception
1004
+	 *
1005
+	 * @return bool true on success, false if address already used or invalid in some way
1006
+	 */
1007
+	protected function addAnAddress($kind, $address, $name = '')
1008
+	{
1009
+		if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) {
1010
+			$error_message = $this->lang('Invalid recipient kind: ') . $kind;
1011
+			$this->setError($error_message);
1012
+			$this->edebug($error_message);
1013
+			if ($this->exceptions) {
1014
+				throw new Exception($error_message);
1015
+			}
1016
+
1017
+			return false;
1018
+		}
1019
+		if (!static::validateAddress($address)) {
1020
+			$error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
1021
+			$this->setError($error_message);
1022
+			$this->edebug($error_message);
1023
+			if ($this->exceptions) {
1024
+				throw new Exception($error_message);
1025
+			}
1026
+
1027
+			return false;
1028
+		}
1029
+		if ('Reply-To' != $kind) {
1030
+			if (!array_key_exists(strtolower($address), $this->all_recipients)) {
1031
+				array_push($this->$kind, [$address, $name]);
1032
+				$this->all_recipients[strtolower($address)] = true;
1033
+
1034
+				return true;
1035
+			}
1036
+		} else {
1037
+			if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
1038
+				$this->ReplyTo[strtolower($address)] = [$address, $name];
1039
+
1040
+				return true;
1041
+			}
1042
+		}
1043
+
1044
+		return false;
1045
+	}
1046
+
1047
+	/**
1048
+	 * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
1049
+	 * of the form "display name <address>" into an array of name/address pairs.
1050
+	 * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
1051
+	 * Note that quotes in the name part are removed.
1052
+	 *
1053
+	 * @see    http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
1054
+	 *
1055
+	 * @param string $addrstr The address list string
1056
+	 * @param bool   $useimap Whether to use the IMAP extension to parse the list
1057
+	 *
1058
+	 * @return array
1059
+	 */
1060
+	public static function parseAddresses($addrstr, $useimap = true)
1061
+	{
1062
+		$addresses = [];
1063
+		if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
1064
+			//Use this built-in parser if it's available
1065
+			$list = imap_rfc822_parse_adrlist($addrstr, '');
1066
+			foreach ($list as $address) {
1067
+				if ('.SYNTAX-ERROR.' != $address->host) {
1068
+					if (static::validateAddress($address->mailbox . '@' . $address->host)) {
1069
+						$addresses[] = [
1070
+							'name' => (property_exists($address, 'personal') ? $address->personal : ''),
1071
+							'address' => $address->mailbox . '@' . $address->host,
1072
+						];
1073
+					}
1074
+				}
1075
+			}
1076
+		} else {
1077
+			//Use this simpler parser
1078
+			$list = explode(',', $addrstr);
1079
+			foreach ($list as $address) {
1080
+				$address = trim($address);
1081
+				//Is there a separate name part?
1082
+				if (strpos($address, '<') === false) {
1083
+					//No separate name, just use the whole thing
1084
+					if (static::validateAddress($address)) {
1085
+						$addresses[] = [
1086
+							'name' => '',
1087
+							'address' => $address,
1088
+						];
1089
+					}
1090
+				} else {
1091
+					list($name, $email) = explode('<', $address);
1092
+					$email = trim(str_replace('>', '', $email));
1093
+					if (static::validateAddress($email)) {
1094
+						$addresses[] = [
1095
+							'name' => trim(str_replace(['"', "'"], '', $name)),
1096
+							'address' => $email,
1097
+						];
1098
+					}
1099
+				}
1100
+			}
1101
+		}
1102
+
1103
+		return $addresses;
1104
+	}
1105
+
1106
+	/**
1107
+	 * Set the From and FromName properties.
1108
+	 *
1109
+	 * @param string $address
1110
+	 * @param string $name
1111
+	 * @param bool   $auto    Whether to also set the Sender address, defaults to true
1112
+	 *
1113
+	 * @throws Exception
1114
+	 *
1115
+	 * @return bool
1116
+	 */
1117
+	public function setFrom($address, $name = '', $auto = true)
1118
+	{
1119
+		$address = trim($address);
1120
+		$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
1121
+		// Don't validate now addresses with IDN. Will be done in send().
1122
+		$pos = strrpos($address, '@');
1123
+		if (false === $pos or
1124
+			(!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
1125
+			!static::validateAddress($address)) {
1126
+			$error_message = $this->lang('invalid_address') . " (setFrom) $address";
1127
+			$this->setError($error_message);
1128
+			$this->edebug($error_message);
1129
+			if ($this->exceptions) {
1130
+				throw new Exception($error_message);
1131
+			}
1132
+
1133
+			return false;
1134
+		}
1135
+		$this->From = $address;
1136
+		$this->FromName = $name;
1137
+		if ($auto) {
1138
+			if (empty($this->Sender)) {
1139
+				$this->Sender = $address;
1140
+			}
1141
+		}
1142
+
1143
+		return true;
1144
+	}
1145
+
1146
+	/**
1147
+	 * Return the Message-ID header of the last email.
1148
+	 * Technically this is the value from the last time the headers were created,
1149
+	 * but it's also the message ID of the last sent message except in
1150
+	 * pathological cases.
1151
+	 *
1152
+	 * @return string
1153
+	 */
1154
+	public function getLastMessageID()
1155
+	{
1156
+		return $this->lastMessageID;
1157
+	}
1158
+
1159
+	/**
1160
+	 * Check that a string looks like an email address.
1161
+	 * Validation patterns supported:
1162
+	 * * `auto` Pick best pattern automatically;
1163
+	 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0;
1164
+	 * * `pcre` Use old PCRE implementation;
1165
+	 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
1166
+	 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
1167
+	 * * `noregex` Don't use a regex: super fast, really dumb.
1168
+	 * Alternatively you may pass in a callable to inject your own validator, for example:
1169
+	 *
1170
+	 * ```php
1171
+	 * PHPMailer::validateAddress('[email protected]', function($address) {
1172
+	 *     return (strpos($address, '@') !== false);
1173
+	 * });
1174
+	 * ```
1175
+	 *
1176
+	 * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
1177
+	 *
1178
+	 * @param string          $address       The email address to check
1179
+	 * @param string|callable $patternselect Which pattern to use
1180
+	 *
1181
+	 * @return bool
1182
+	 */
1183
+	public static function validateAddress($address, $patternselect = null)
1184
+	{
1185
+		if (null === $patternselect) {
1186
+			$patternselect = static::$validator;
1187
+		}
1188
+		if (is_callable($patternselect)) {
1189
+			return call_user_func($patternselect, $address);
1190
+		}
1191
+		//Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1192
+		if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1193
+			return false;
1194
+		}
1195
+		switch ($patternselect) {
1196
+			case 'pcre': //Kept for BC
1197
+			case 'pcre8':
1198
+				/*
1199 1199
                  * A more complex and more permissive version of the RFC5322 regex on which FILTER_VALIDATE_EMAIL
1200 1200
                  * is based.
1201 1201
                  * In addition to the addresses allowed by filter_var, also permits:
@@ -1211,3177 +1211,3177 @@  discard block
 block discarded – undo
1211 1211
                  * @copyright 2009-2010 Michael Rushton
1212 1212
                  * Feel free to use and redistribute this code. But please keep this copyright notice.
1213 1213
                  */
1214
-                return (bool) preg_match(
1215
-                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1216
-                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1217
-                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1218
-                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1219
-                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1220
-                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1221
-                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1222
-                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1223
-                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1224
-                    $address
1225
-                );
1226
-            case 'html5':
1227
-                /*
1214
+				return (bool) preg_match(
1215
+					'/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1216
+					'((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1217
+					'(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1218
+					'([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1219
+					'(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1220
+					'(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1221
+					'|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1222
+					'|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1223
+					'|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1224
+					$address
1225
+				);
1226
+			case 'html5':
1227
+				/*
1228 1228
                  * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1229 1229
                  *
1230 1230
                  * @see http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1231 1231
                  */
1232
-                return (bool) preg_match(
1233
-                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1234
-                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1235
-                    $address
1236
-                );
1237
-            case 'php':
1238
-            default:
1239
-                return (bool) filter_var($address, FILTER_VALIDATE_EMAIL);
1240
-        }
1241
-    }
1242
-
1243
-    /**
1244
-     * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1245
-     * `intl` and `mbstring` PHP extensions.
1246
-     *
1247
-     * @return bool `true` if required functions for IDN support are present
1248
-     */
1249
-    public function idnSupported()
1250
-    {
1251
-        return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1252
-    }
1253
-
1254
-    /**
1255
-     * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1256
-     * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1257
-     * This function silently returns unmodified address if:
1258
-     * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1259
-     * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1260
-     *   or fails for any reason (e.g. domain contains characters not allowed in an IDN).
1261
-     *
1262
-     * @see    PHPMailer::$CharSet
1263
-     *
1264
-     * @param string $address The email address to convert
1265
-     *
1266
-     * @return string The encoded address in ASCII form
1267
-     */
1268
-    public function punyencodeAddress($address)
1269
-    {
1270
-        // Verify we have required functions, CharSet, and at-sign.
1271
-        $pos = strrpos($address, '@');
1272
-        if ($this->idnSupported() and
1273
-            !empty($this->CharSet) and
1274
-            false !== $pos
1275
-        ) {
1276
-            $domain = substr($address, ++$pos);
1277
-            // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1278
-            if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1279
-                $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1280
-                //Ignore IDE complaints about this line - method signature changed in PHP 5.4
1281
-                $errorcode = 0;
1282
-                $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_UTS46);
1283
-                if (false !== $punycode) {
1284
-                    return substr($address, 0, $pos) . $punycode;
1285
-                }
1286
-            }
1287
-        }
1288
-
1289
-        return $address;
1290
-    }
1291
-
1292
-    /**
1293
-     * Create a message and send it.
1294
-     * Uses the sending method specified by $Mailer.
1295
-     *
1296
-     * @throws Exception
1297
-     *
1298
-     * @return bool false on error - See the ErrorInfo property for details of the error
1299
-     */
1300
-    public function send()
1301
-    {
1302
-        try {
1303
-            if (!$this->preSend()) {
1304
-                return false;
1305
-            }
1306
-
1307
-            return $this->postSend();
1308
-        } catch (Exception $exc) {
1309
-            $this->mailHeader = '';
1310
-            $this->setError($exc->getMessage());
1311
-            if ($this->exceptions) {
1312
-                throw $exc;
1313
-            }
1314
-
1315
-            return false;
1316
-        }
1317
-    }
1318
-
1319
-    /**
1320
-     * Prepare a message for sending.
1321
-     *
1322
-     * @throws Exception
1323
-     *
1324
-     * @return bool
1325
-     */
1326
-    public function preSend()
1327
-    {
1328
-        if ('smtp' == $this->Mailer or
1329
-            ('mail' == $this->Mailer and stripos(PHP_OS, 'WIN') === 0)
1330
-        ) {
1331
-            //SMTP mandates RFC-compliant line endings
1332
-            //and it's also used with mail() on Windows
1333
-            static::setLE("\r\n");
1334
-        } else {
1335
-            //Maintain backward compatibility with legacy Linux command line mailers
1336
-            static::setLE(PHP_EOL);
1337
-        }
1338
-        //Check for buggy PHP versions that add a header with an incorrect line break
1339
-        if (ini_get('mail.add_x_header') == 1
1340
-            and 'mail' == $this->Mailer
1341
-            and stripos(PHP_OS, 'WIN') === 0
1342
-            and ((version_compare(PHP_VERSION, '7.0.0', '>=')
1343
-                    and version_compare(PHP_VERSION, '7.0.17', '<'))
1344
-                or (version_compare(PHP_VERSION, '7.1.0', '>=')
1345
-                    and version_compare(PHP_VERSION, '7.1.3', '<')))
1346
-        ) {
1347
-            trigger_error(
1348
-                'Your version of PHP is affected by a bug that may result in corrupted messages.' .
1349
-                ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' .
1350
-                ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.',
1351
-                E_USER_WARNING
1352
-            );
1353
-        }
1354
-
1355
-        try {
1356
-            $this->error_count = 0; // Reset errors
1357
-            $this->mailHeader = '';
1358
-
1359
-            // Dequeue recipient and Reply-To addresses with IDN
1360
-            foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1361
-                $params[1] = $this->punyencodeAddress($params[1]);
1362
-                call_user_func_array([$this, 'addAnAddress'], $params);
1363
-            }
1364
-            if (count($this->to) + count($this->cc) + count($this->bcc) < 1) {
1365
-                throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL);
1366
-            }
1367
-
1368
-            // Validate From, Sender, and ConfirmReadingTo addresses
1369
-            foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
1370
-                $this->$address_kind = trim($this->$address_kind);
1371
-                if (empty($this->$address_kind)) {
1372
-                    continue;
1373
-                }
1374
-                $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1375
-                if (!static::validateAddress($this->$address_kind)) {
1376
-                    $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
1377
-                    $this->setError($error_message);
1378
-                    $this->edebug($error_message);
1379
-                    if ($this->exceptions) {
1380
-                        throw new Exception($error_message);
1381
-                    }
1382
-
1383
-                    return false;
1384
-                }
1385
-            }
1386
-
1387
-            // Set whether the message is multipart/alternative
1388
-            if ($this->alternativeExists()) {
1389
-                $this->ContentType = 'multipart/alternative';
1390
-            }
1391
-
1392
-            $this->setMessageType();
1393
-            // Refuse to send an empty message unless we are specifically allowing it
1394
-            if (!$this->AllowEmpty and empty($this->Body)) {
1395
-                throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
1396
-            }
1397
-
1398
-            //Trim subject consistently
1399
-            $this->Subject = trim($this->Subject);
1400
-            // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1401
-            $this->MIMEHeader = '';
1402
-            $this->MIMEBody = $this->createBody();
1403
-            // createBody may have added some headers, so retain them
1404
-            $tempheaders = $this->MIMEHeader;
1405
-            $this->MIMEHeader = $this->createHeader();
1406
-            $this->MIMEHeader .= $tempheaders;
1407
-
1408
-            // To capture the complete message when using mail(), create
1409
-            // an extra header list which createHeader() doesn't fold in
1410
-            if ('mail' == $this->Mailer) {
1411
-                if (count($this->to) > 0) {
1412
-                    $this->mailHeader .= $this->addrAppend('To', $this->to);
1413
-                } else {
1414
-                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1415
-                }
1416
-                $this->mailHeader .= $this->headerLine(
1417
-                    'Subject',
1418
-                    $this->encodeHeader($this->secureHeader($this->Subject))
1419
-                );
1420
-            }
1421
-
1422
-            // Sign with DKIM if enabled
1423
-            if (!empty($this->DKIM_domain)
1424
-                and !empty($this->DKIM_selector)
1425
-                and (!empty($this->DKIM_private_string)
1426
-                    or (!empty($this->DKIM_private) and file_exists($this->DKIM_private))
1427
-                )
1428
-            ) {
1429
-                $header_dkim = $this->DKIM_Add(
1430
-                    $this->MIMEHeader . $this->mailHeader,
1431
-                    $this->encodeHeader($this->secureHeader($this->Subject)),
1432
-                    $this->MIMEBody
1433
-                );
1434
-                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . static::$LE .
1435
-                    static::normalizeBreaks($header_dkim) . static::$LE;
1436
-            }
1437
-
1438
-            return true;
1439
-        } catch (Exception $exc) {
1440
-            $this->setError($exc->getMessage());
1441
-            if ($this->exceptions) {
1442
-                throw $exc;
1443
-            }
1444
-
1445
-            return false;
1446
-        }
1447
-    }
1448
-
1449
-    /**
1450
-     * Actually send a message via the selected mechanism.
1451
-     *
1452
-     * @throws Exception
1453
-     *
1454
-     * @return bool
1455
-     */
1456
-    public function postSend()
1457
-    {
1458
-        try {
1459
-            // Choose the mailer and send through it
1460
-            switch ($this->Mailer) {
1461
-                case 'sendmail':
1462
-                case 'qmail':
1463
-                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1464
-                case 'smtp':
1465
-                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1466
-                case 'mail':
1467
-                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1468
-                default:
1469
-                    $sendMethod = $this->Mailer . 'Send';
1470
-                    if (method_exists($this, $sendMethod)) {
1471
-                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1472
-                    }
1473
-
1474
-                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1475
-            }
1476
-        } catch (Exception $exc) {
1477
-            $this->setError($exc->getMessage());
1478
-            $this->edebug($exc->getMessage());
1479
-            if ($this->exceptions) {
1480
-                throw $exc;
1481
-            }
1482
-        }
1483
-
1484
-        return false;
1485
-    }
1486
-
1487
-    /**
1488
-     * Send mail using the $Sendmail program.
1489
-     *
1490
-     * @see    PHPMailer::$Sendmail
1491
-     *
1492
-     * @param string $header The message headers
1493
-     * @param string $body   The message body
1494
-     *
1495
-     * @throws Exception
1496
-     *
1497
-     * @return bool
1498
-     */
1499
-    protected function sendmailSend($header, $body)
1500
-    {
1501
-        // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1502
-        if (!empty($this->Sender) and self::isShellSafe($this->Sender)) {
1503
-            if ('qmail' == $this->Mailer) {
1504
-                $sendmailFmt = '%s -f%s';
1505
-            } else {
1506
-                $sendmailFmt = '%s -oi -f%s -t';
1507
-            }
1508
-        } else {
1509
-            if ('qmail' == $this->Mailer) {
1510
-                $sendmailFmt = '%s';
1511
-            } else {
1512
-                $sendmailFmt = '%s -oi -t';
1513
-            }
1514
-        }
1515
-
1516
-        $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
1517
-
1518
-        if ($this->SingleTo) {
1519
-            foreach ($this->SingleToArray as $toAddr) {
1520
-                $mail = @popen($sendmail, 'w');
1521
-                if (!$mail) {
1522
-                    throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1523
-                }
1524
-                fwrite($mail, 'To: ' . $toAddr . "\n");
1525
-                fwrite($mail, $header);
1526
-                fwrite($mail, $body);
1527
-                $result = pclose($mail);
1528
-                $this->doCallback(
1529
-                    ($result == 0),
1530
-                    [$toAddr],
1531
-                    $this->cc,
1532
-                    $this->bcc,
1533
-                    $this->Subject,
1534
-                    $body,
1535
-                    $this->From,
1536
-                    []
1537
-                );
1538
-                if (0 !== $result) {
1539
-                    throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1540
-                }
1541
-            }
1542
-        } else {
1543
-            $mail = @popen($sendmail, 'w');
1544
-            if (!$mail) {
1545
-                throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1546
-            }
1547
-            fwrite($mail, $header);
1548
-            fwrite($mail, $body);
1549
-            $result = pclose($mail);
1550
-            $this->doCallback(
1551
-                ($result == 0),
1552
-                $this->to,
1553
-                $this->cc,
1554
-                $this->bcc,
1555
-                $this->Subject,
1556
-                $body,
1557
-                $this->From,
1558
-                []
1559
-            );
1560
-            if (0 !== $result) {
1561
-                throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1562
-            }
1563
-        }
1564
-
1565
-        return true;
1566
-    }
1567
-
1568
-    /**
1569
-     * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
1570
-     * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
1571
-     *
1572
-     * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report
1573
-     *
1574
-     * @param string $string The string to be validated
1575
-     *
1576
-     * @return bool
1577
-     */
1578
-    protected static function isShellSafe($string)
1579
-    {
1580
-        // Future-proof
1581
-        if (escapeshellcmd($string) !== $string
1582
-            or !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])
1583
-        ) {
1584
-            return false;
1585
-        }
1586
-
1587
-        $length = strlen($string);
1588
-
1589
-        for ($i = 0; $i < $length; ++$i) {
1590
-            $c = $string[$i];
1591
-
1592
-            // All other characters have a special meaning in at least one common shell, including = and +.
1593
-            // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
1594
-            // Note that this does permit non-Latin alphanumeric characters based on the current locale.
1595
-            if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
1596
-                return false;
1597
-            }
1598
-        }
1599
-
1600
-        return true;
1601
-    }
1602
-
1603
-    /**
1604
-     * Send mail using the PHP mail() function.
1605
-     *
1606
-     * @see    http://www.php.net/manual/en/book.mail.php
1607
-     *
1608
-     * @param string $header The message headers
1609
-     * @param string $body   The message body
1610
-     *
1611
-     * @throws Exception
1612
-     *
1613
-     * @return bool
1614
-     */
1615
-    protected function mailSend($header, $body)
1616
-    {
1617
-        $toArr = [];
1618
-        foreach ($this->to as $toaddr) {
1619
-            $toArr[] = $this->addrFormat($toaddr);
1620
-        }
1621
-        $to = implode(', ', $toArr);
1622
-
1623
-        $params = null;
1624
-        //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
1625
-        if (!empty($this->Sender) and static::validateAddress($this->Sender)) {
1626
-            //A space after `-f` is optional, but there is a long history of its presence
1627
-            //causing problems, so we don't use one
1628
-            //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
1629
-            //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
1630
-            //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
1631
-            //Example problem: https://www.drupal.org/node/1057954
1632
-            // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1633
-            if (self::isShellSafe($this->Sender)) {
1634
-                $params = sprintf('-f%s', $this->Sender);
1635
-            }
1636
-        }
1637
-        if (!empty($this->Sender) and static::validateAddress($this->Sender)) {
1638
-            $old_from = ini_get('sendmail_from');
1639
-            ini_set('sendmail_from', $this->Sender);
1640
-        }
1641
-        $result = false;
1642
-        if ($this->SingleTo and count($toArr) > 1) {
1643
-            foreach ($toArr as $toAddr) {
1644
-                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1645
-                $this->doCallback($result, [$toAddr], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []);
1646
-            }
1647
-        } else {
1648
-            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1649
-            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []);
1650
-        }
1651
-        if (isset($old_from)) {
1652
-            ini_set('sendmail_from', $old_from);
1653
-        }
1654
-        if (!$result) {
1655
-            throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL);
1656
-        }
1657
-
1658
-        return true;
1659
-    }
1660
-
1661
-    /**
1662
-     * Get an instance to use for SMTP operations.
1663
-     * Override this function to load your own SMTP implementation,
1664
-     * or set one with setSMTPInstance.
1665
-     *
1666
-     * @return SMTP
1667
-     */
1668
-    public function getSMTPInstance()
1669
-    {
1670
-        if (!is_object($this->smtp)) {
1671
-            $this->smtp = new SMTP();
1672
-        }
1673
-
1674
-        return $this->smtp;
1675
-    }
1676
-
1677
-    /**
1678
-     * Provide an instance to use for SMTP operations.
1679
-     *
1680
-     * @param SMTP $smtp
1681
-     *
1682
-     * @return SMTP
1683
-     */
1684
-    public function setSMTPInstance(SMTP $smtp)
1685
-    {
1686
-        $this->smtp = $smtp;
1687
-
1688
-        return $this->smtp;
1689
-    }
1690
-
1691
-    /**
1692
-     * Send mail via SMTP.
1693
-     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1694
-     *
1695
-     * @see PHPMailer::setSMTPInstance() to use a different class.
1696
-     *
1697
-     * @uses \PHPMailer\PHPMailer\SMTP
1698
-     *
1699
-     * @param string $header The message headers
1700
-     * @param string $body   The message body
1701
-     *
1702
-     * @throws Exception
1703
-     *
1704
-     * @return bool
1705
-     */
1706
-    protected function smtpSend($header, $body)
1707
-    {
1708
-        $bad_rcpt = [];
1709
-        if (!$this->smtpConnect($this->SMTPOptions)) {
1710
-            throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1711
-        }
1712
-        //Sender already validated in preSend()
1713
-        if ('' == $this->Sender) {
1714
-            $smtp_from = $this->From;
1715
-        } else {
1716
-            $smtp_from = $this->Sender;
1717
-        }
1718
-        if (!$this->smtp->mail($smtp_from)) {
1719
-            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1720
-            throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
1721
-        }
1722
-
1723
-        $callbacks = [];
1724
-        // Attempt to send to all recipients
1725
-        foreach ([$this->to, $this->cc, $this->bcc] as $togroup) {
1726
-            foreach ($togroup as $to) {
1727
-                if (!$this->smtp->recipient($to[0])) {
1728
-                    $error = $this->smtp->getError();
1729
-                    $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']];
1730
-                    $isSent = false;
1731
-                } else {
1732
-                    $isSent = true;
1733
-                }
1734
-
1735
-                $callbacks[] = ['issent'=>$isSent, 'to'=>$to[0]];
1736
-            }
1737
-        }
1738
-
1739
-        // Only send the DATA command if we have viable recipients
1740
-        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1741
-            throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1742
-        }
1743
-
1744
-        $smtp_transaction_id = $this->smtp->getLastTransactionID();
1745
-
1746
-        if ($this->SMTPKeepAlive) {
1747
-            $this->smtp->reset();
1748
-        } else {
1749
-            $this->smtp->quit();
1750
-            $this->smtp->close();
1751
-        }
1752
-
1753
-        foreach ($callbacks as $cb) {
1754
-            $this->doCallback(
1755
-                $cb['issent'],
1756
-                [$cb['to']],
1757
-                [],
1758
-                [],
1759
-                $this->Subject,
1760
-                $body,
1761
-                $this->From,
1762
-                ['smtp_transaction_id' => $smtp_transaction_id]
1763
-            );
1764
-        }
1765
-
1766
-        //Create error message for any bad addresses
1767
-        if (count($bad_rcpt) > 0) {
1768
-            $errstr = '';
1769
-            foreach ($bad_rcpt as $bad) {
1770
-                $errstr .= $bad['to'] . ': ' . $bad['error'];
1771
-            }
1772
-            throw new Exception(
1773
-                $this->lang('recipients_failed') . $errstr,
1774
-                self::STOP_CONTINUE
1775
-            );
1776
-        }
1777
-
1778
-        return true;
1779
-    }
1780
-
1781
-    /**
1782
-     * Initiate a connection to an SMTP server.
1783
-     * Returns false if the operation failed.
1784
-     *
1785
-     * @param array $options An array of options compatible with stream_context_create()
1786
-     *
1787
-     * @throws Exception
1788
-     *
1789
-     * @uses \PHPMailer\PHPMailer\SMTP
1790
-     *
1791
-     * @return bool
1792
-     */
1793
-    public function smtpConnect($options = null)
1794
-    {
1795
-        if (null === $this->smtp) {
1796
-            $this->smtp = $this->getSMTPInstance();
1797
-        }
1798
-
1799
-        //If no options are provided, use whatever is set in the instance
1800
-        if (null === $options) {
1801
-            $options = $this->SMTPOptions;
1802
-        }
1803
-
1804
-        // Already connected?
1805
-        if ($this->smtp->connected()) {
1806
-            return true;
1807
-        }
1808
-
1809
-        $this->smtp->setTimeout($this->Timeout);
1810
-        $this->smtp->setDebugLevel($this->SMTPDebug);
1811
-        $this->smtp->setDebugOutput($this->Debugoutput);
1812
-        $this->smtp->setVerp($this->do_verp);
1813
-        $hosts = explode(';', $this->Host);
1814
-        $lastexception = null;
1815
-
1816
-        foreach ($hosts as $hostentry) {
1817
-            $hostinfo = [];
1818
-            if (!preg_match(
1819
-                '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/',
1820
-                trim($hostentry),
1821
-                $hostinfo
1822
-            )) {
1823
-                static::edebug($this->lang('connect_host') . ' ' . $hostentry);
1824
-                // Not a valid host entry
1825
-                continue;
1826
-            }
1827
-            // $hostinfo[2]: optional ssl or tls prefix
1828
-            // $hostinfo[3]: the hostname
1829
-            // $hostinfo[4]: optional port number
1830
-            // The host string prefix can temporarily override the current setting for SMTPSecure
1831
-            // If it's not specified, the default value is used
1832
-
1833
-            //Check the host name is a valid name or IP address before trying to use it
1834
-            if (!static::isValidHost($hostinfo[3])) {
1835
-                static::edebug($this->lang('connect_host') . ' ' . $hostentry);
1836
-                continue;
1837
-            }
1838
-            $prefix = '';
1839
-            $secure = $this->SMTPSecure;
1840
-            $tls = ('tls' == $this->SMTPSecure);
1841
-            if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1842
-                $prefix = 'ssl://';
1843
-                $tls = false; // Can't have SSL and TLS at the same time
1844
-                $secure = 'ssl';
1845
-            } elseif ('tls' == $hostinfo[2]) {
1846
-                $tls = true;
1847
-                // tls doesn't use a prefix
1848
-                $secure = 'tls';
1849
-            }
1850
-            //Do we need the OpenSSL extension?
1851
-            $sslext = defined('OPENSSL_ALGO_SHA256');
1852
-            if ('tls' === $secure or 'ssl' === $secure) {
1853
-                //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1854
-                if (!$sslext) {
1855
-                    throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL);
1856
-                }
1857
-            }
1858
-            $host = $hostinfo[3];
1859
-            $port = $this->Port;
1860
-            $tport = (int) $hostinfo[4];
1861
-            if ($tport > 0 and $tport < 65536) {
1862
-                $port = $tport;
1863
-            }
1864
-            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1865
-                try {
1866
-                    if ($this->Helo) {
1867
-                        $hello = $this->Helo;
1868
-                    } else {
1869
-                        $hello = $this->serverHostname();
1870
-                    }
1871
-                    $this->smtp->hello($hello);
1872
-                    //Automatically enable TLS encryption if:
1873
-                    // * it's not disabled
1874
-                    // * we have openssl extension
1875
-                    // * we are not already using SSL
1876
-                    // * the server offers STARTTLS
1877
-                    if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1878
-                        $tls = true;
1879
-                    }
1880
-                    if ($tls) {
1881
-                        if (!$this->smtp->startTLS()) {
1882
-                            throw new Exception($this->lang('connect_host'));
1883
-                        }
1884
-                        // We must resend EHLO after TLS negotiation
1885
-                        $this->smtp->hello($hello);
1886
-                    }
1887
-                    if ($this->SMTPAuth) {
1888
-                        if (!$this->smtp->authenticate(
1889
-                            $this->Username,
1890
-                            $this->Password,
1891
-                            $this->AuthType,
1892
-                            $this->oauth
1893
-                        )
1894
-                        ) {
1895
-                            throw new Exception($this->lang('authenticate'));
1896
-                        }
1897
-                    }
1898
-
1899
-                    return true;
1900
-                } catch (Exception $exc) {
1901
-                    $lastexception = $exc;
1902
-                    $this->edebug($exc->getMessage());
1903
-                    // We must have connected, but then failed TLS or Auth, so close connection nicely
1904
-                    $this->smtp->quit();
1905
-                }
1906
-            }
1907
-        }
1908
-        // If we get here, all connection attempts have failed, so close connection hard
1909
-        $this->smtp->close();
1910
-        // As we've caught all exceptions, just report whatever the last one was
1911
-        if ($this->exceptions and null !== $lastexception) {
1912
-            throw $lastexception;
1913
-        }
1914
-
1915
-        return false;
1916
-    }
1917
-
1918
-    /**
1919
-     * Close the active SMTP session if one exists.
1920
-     */
1921
-    public function smtpClose()
1922
-    {
1923
-        if (null !== $this->smtp) {
1924
-            if ($this->smtp->connected()) {
1925
-                $this->smtp->quit();
1926
-                $this->smtp->close();
1927
-            }
1928
-        }
1929
-    }
1930
-
1931
-    /**
1932
-     * Set the language for error messages.
1933
-     * Returns false if it cannot load the language file.
1934
-     * The default language is English.
1935
-     *
1936
-     * @param string $langcode  ISO 639-1 2-character language code (e.g. French is "fr")
1937
-     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1938
-     *
1939
-     * @return bool
1940
-     */
1941
-    public function setLanguage($langcode = 'en', $lang_path = '')
1942
-    {
1943
-        // Backwards compatibility for renamed language codes
1944
-        $renamed_langcodes = [
1945
-            'br' => 'pt_br',
1946
-            'cz' => 'cs',
1947
-            'dk' => 'da',
1948
-            'no' => 'nb',
1949
-            'se' => 'sv',
1950
-            'sr' => 'rs',
1951
-        ];
1952
-
1953
-        if (isset($renamed_langcodes[$langcode])) {
1954
-            $langcode = $renamed_langcodes[$langcode];
1955
-        }
1956
-
1957
-        // Define full set of translatable strings in English
1958
-        $PHPMAILER_LANG = [
1959
-            'authenticate' => 'SMTP Error: Could not authenticate.',
1960
-            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1961
-            'data_not_accepted' => 'SMTP Error: data not accepted.',
1962
-            'empty_message' => 'Message body empty',
1963
-            'encoding' => 'Unknown encoding: ',
1964
-            'execute' => 'Could not execute: ',
1965
-            'file_access' => 'Could not access file: ',
1966
-            'file_open' => 'File Error: Could not open file: ',
1967
-            'from_failed' => 'The following From address failed: ',
1968
-            'instantiate' => 'Could not instantiate mail function.',
1969
-            'invalid_address' => 'Invalid address: ',
1970
-            'mailer_not_supported' => ' mailer is not supported.',
1971
-            'provide_address' => 'You must provide at least one recipient email address.',
1972
-            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1973
-            'signing' => 'Signing Error: ',
1974
-            'smtp_connect_failed' => 'SMTP connect() failed.',
1975
-            'smtp_error' => 'SMTP server error: ',
1976
-            'variable_set' => 'Cannot set or reset variable: ',
1977
-            'extension_missing' => 'Extension missing: ',
1978
-        ];
1979
-        if (empty($lang_path)) {
1980
-            // Calculate an absolute path so it can work if CWD is not here
1981
-            $lang_path = __DIR__ . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
1982
-        }
1983
-        //Validate $langcode
1984
-        if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
1985
-            $langcode = 'en';
1986
-        }
1987
-        $foundlang = true;
1988
-        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1989
-        // There is no English translation file
1990
-        if ('en' != $langcode) {
1991
-            // Make sure language file path is readable
1992
-            if (!file_exists($lang_file)) {
1993
-                $foundlang = false;
1994
-            } else {
1995
-                // Overwrite language-specific strings.
1996
-                // This way we'll never have missing translation keys.
1997
-                $foundlang = include $lang_file;
1998
-            }
1999
-        }
2000
-        $this->language = $PHPMAILER_LANG;
2001
-
2002
-        return (bool) $foundlang; // Returns false if language not found
2003
-    }
2004
-
2005
-    /**
2006
-     * Get the array of strings for the current language.
2007
-     *
2008
-     * @return array
2009
-     */
2010
-    public function getTranslations()
2011
-    {
2012
-        return $this->language;
2013
-    }
2014
-
2015
-    /**
2016
-     * Create recipient headers.
2017
-     *
2018
-     * @param string $type
2019
-     * @param array  $addr An array of recipients,
2020
-     *                     where each recipient is a 2-element indexed array with element 0 containing an address
2021
-     *                     and element 1 containing a name, like:
2022
-     *                     [['[email protected]', 'Joe User'], ['[email protected]', 'Zoe User']]
2023
-     *
2024
-     * @return string
2025
-     */
2026
-    public function addrAppend($type, $addr)
2027
-    {
2028
-        $addresses = [];
2029
-        foreach ($addr as $address) {
2030
-            $addresses[] = $this->addrFormat($address);
2031
-        }
2032
-
2033
-        return $type . ': ' . implode(', ', $addresses) . static::$LE;
2034
-    }
2035
-
2036
-    /**
2037
-     * Format an address for use in a message header.
2038
-     *
2039
-     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name like
2040
-     *                    ['[email protected]', 'Joe User']
2041
-     *
2042
-     * @return string
2043
-     */
2044
-    public function addrFormat($addr)
2045
-    {
2046
-        if (empty($addr[1])) { // No name provided
2047
-            return $this->secureHeader($addr[0]);
2048
-        }
2049
-
2050
-        return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
2051
-                $addr[0]
2052
-            ) . '>';
2053
-    }
2054
-
2055
-    /**
2056
-     * Word-wrap message.
2057
-     * For use with mailers that do not automatically perform wrapping
2058
-     * and for quoted-printable encoded messages.
2059
-     * Original written by philippe.
2060
-     *
2061
-     * @param string $message The message to wrap
2062
-     * @param int    $length  The line length to wrap to
2063
-     * @param bool   $qp_mode Whether to run in Quoted-Printable mode
2064
-     *
2065
-     * @return string
2066
-     */
2067
-    public function wrapText($message, $length, $qp_mode = false)
2068
-    {
2069
-        if ($qp_mode) {
2070
-            $soft_break = sprintf(' =%s', static::$LE);
2071
-        } else {
2072
-            $soft_break = static::$LE;
2073
-        }
2074
-        // If utf-8 encoding is used, we will need to make sure we don't
2075
-        // split multibyte characters when we wrap
2076
-        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
2077
-        $lelen = strlen(static::$LE);
2078
-        $crlflen = strlen(static::$LE);
2079
-
2080
-        $message = static::normalizeBreaks($message);
2081
-        //Remove a trailing line break
2082
-        if (substr($message, -$lelen) == static::$LE) {
2083
-            $message = substr($message, 0, -$lelen);
2084
-        }
2085
-
2086
-        //Split message into lines
2087
-        $lines = explode(static::$LE, $message);
2088
-        //Message will be rebuilt in here
2089
-        $message = '';
2090
-        foreach ($lines as $line) {
2091
-            $words = explode(' ', $line);
2092
-            $buf = '';
2093
-            $firstword = true;
2094
-            foreach ($words as $word) {
2095
-                if ($qp_mode and (strlen($word) > $length)) {
2096
-                    $space_left = $length - strlen($buf) - $crlflen;
2097
-                    if (!$firstword) {
2098
-                        if ($space_left > 20) {
2099
-                            $len = $space_left;
2100
-                            if ($is_utf8) {
2101
-                                $len = $this->utf8CharBoundary($word, $len);
2102
-                            } elseif (substr($word, $len - 1, 1) == '=') {
2103
-                                --$len;
2104
-                            } elseif (substr($word, $len - 2, 1) == '=') {
2105
-                                $len -= 2;
2106
-                            }
2107
-                            $part = substr($word, 0, $len);
2108
-                            $word = substr($word, $len);
2109
-                            $buf .= ' ' . $part;
2110
-                            $message .= $buf . sprintf('=%s', static::$LE);
2111
-                        } else {
2112
-                            $message .= $buf . $soft_break;
2113
-                        }
2114
-                        $buf = '';
2115
-                    }
2116
-                    while (strlen($word) > 0) {
2117
-                        if ($length <= 0) {
2118
-                            break;
2119
-                        }
2120
-                        $len = $length;
2121
-                        if ($is_utf8) {
2122
-                            $len = $this->utf8CharBoundary($word, $len);
2123
-                        } elseif (substr($word, $len - 1, 1) == '=') {
2124
-                            --$len;
2125
-                        } elseif (substr($word, $len - 2, 1) == '=') {
2126
-                            $len -= 2;
2127
-                        }
2128
-                        $part = substr($word, 0, $len);
2129
-                        $word = substr($word, $len);
2130
-
2131
-                        if (strlen($word) > 0) {
2132
-                            $message .= $part . sprintf('=%s', static::$LE);
2133
-                        } else {
2134
-                            $buf = $part;
2135
-                        }
2136
-                    }
2137
-                } else {
2138
-                    $buf_o = $buf;
2139
-                    if (!$firstword) {
2140
-                        $buf .= ' ';
2141
-                    }
2142
-                    $buf .= $word;
2143
-
2144
-                    if (strlen($buf) > $length and $buf_o != '') {
2145
-                        $message .= $buf_o . $soft_break;
2146
-                        $buf = $word;
2147
-                    }
2148
-                }
2149
-                $firstword = false;
2150
-            }
2151
-            $message .= $buf . static::$LE;
2152
-        }
2153
-
2154
-        return $message;
2155
-    }
2156
-
2157
-    /**
2158
-     * Find the last character boundary prior to $maxLength in a utf-8
2159
-     * quoted-printable encoded string.
2160
-     * Original written by Colin Brown.
2161
-     *
2162
-     * @param string $encodedText utf-8 QP text
2163
-     * @param int    $maxLength   Find the last character boundary prior to this length
2164
-     *
2165
-     * @return int
2166
-     */
2167
-    public function utf8CharBoundary($encodedText, $maxLength)
2168
-    {
2169
-        $foundSplitPos = false;
2170
-        $lookBack = 3;
2171
-        while (!$foundSplitPos) {
2172
-            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
2173
-            $encodedCharPos = strpos($lastChunk, '=');
2174
-            if (false !== $encodedCharPos) {
2175
-                // Found start of encoded character byte within $lookBack block.
2176
-                // Check the encoded byte value (the 2 chars after the '=')
2177
-                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
2178
-                $dec = hexdec($hex);
2179
-                if ($dec < 128) {
2180
-                    // Single byte character.
2181
-                    // If the encoded char was found at pos 0, it will fit
2182
-                    // otherwise reduce maxLength to start of the encoded char
2183
-                    if ($encodedCharPos > 0) {
2184
-                        $maxLength -= $lookBack - $encodedCharPos;
2185
-                    }
2186
-                    $foundSplitPos = true;
2187
-                } elseif ($dec >= 192) {
2188
-                    // First byte of a multi byte character
2189
-                    // Reduce maxLength to split at start of character
2190
-                    $maxLength -= $lookBack - $encodedCharPos;
2191
-                    $foundSplitPos = true;
2192
-                } elseif ($dec < 192) {
2193
-                    // Middle byte of a multi byte character, look further back
2194
-                    $lookBack += 3;
2195
-                }
2196
-            } else {
2197
-                // No encoded character found
2198
-                $foundSplitPos = true;
2199
-            }
2200
-        }
2201
-
2202
-        return $maxLength;
2203
-    }
2204
-
2205
-    /**
2206
-     * Apply word wrapping to the message body.
2207
-     * Wraps the message body to the number of chars set in the WordWrap property.
2208
-     * You should only do this to plain-text bodies as wrapping HTML tags may break them.
2209
-     * This is called automatically by createBody(), so you don't need to call it yourself.
2210
-     */
2211
-    public function setWordWrap()
2212
-    {
2213
-        if ($this->WordWrap < 1) {
2214
-            return;
2215
-        }
2216
-
2217
-        switch ($this->message_type) {
2218
-            case 'alt':
2219
-            case 'alt_inline':
2220
-            case 'alt_attach':
2221
-            case 'alt_inline_attach':
2222
-                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
2223
-                break;
2224
-            default:
2225
-                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
2226
-                break;
2227
-        }
2228
-    }
2229
-
2230
-    /**
2231
-     * Assemble message headers.
2232
-     *
2233
-     * @return string The assembled headers
2234
-     */
2235
-    public function createHeader()
2236
-    {
2237
-        $result = '';
2238
-
2239
-        $result .= $this->headerLine('Date', '' == $this->MessageDate ? self::rfcDate() : $this->MessageDate);
2240
-
2241
-        // To be created automatically by mail()
2242
-        if ($this->SingleTo) {
2243
-            if ('mail' != $this->Mailer) {
2244
-                foreach ($this->to as $toaddr) {
2245
-                    $this->SingleToArray[] = $this->addrFormat($toaddr);
2246
-                }
2247
-            }
2248
-        } else {
2249
-            if (count($this->to) > 0) {
2250
-                if ('mail' != $this->Mailer) {
2251
-                    $result .= $this->addrAppend('To', $this->to);
2252
-                }
2253
-            } elseif (count($this->cc) == 0) {
2254
-                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
2255
-            }
2256
-        }
2257
-
2258
-        $result .= $this->addrAppend('From', [[trim($this->From), $this->FromName]]);
2259
-
2260
-        // sendmail and mail() extract Cc from the header before sending
2261
-        if (count($this->cc) > 0) {
2262
-            $result .= $this->addrAppend('Cc', $this->cc);
2263
-        }
2264
-
2265
-        // sendmail and mail() extract Bcc from the header before sending
2266
-        if ((
2267
-                'sendmail' == $this->Mailer or 'qmail' == $this->Mailer or 'mail' == $this->Mailer
2268
-            )
2269
-            and count($this->bcc) > 0
2270
-        ) {
2271
-            $result .= $this->addrAppend('Bcc', $this->bcc);
2272
-        }
2273
-
2274
-        if (count($this->ReplyTo) > 0) {
2275
-            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
2276
-        }
2277
-
2278
-        // mail() sets the subject itself
2279
-        if ('mail' != $this->Mailer) {
2280
-            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2281
-        }
2282
-
2283
-        // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
2284
-        // https://tools.ietf.org/html/rfc5322#section-3.6.4
2285
-        if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
2286
-            $this->lastMessageID = $this->MessageID;
2287
-        } else {
2288
-            $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
2289
-        }
2290
-        $result .= $this->headerLine('Message-ID', $this->lastMessageID);
2291
-        if (null !== $this->Priority) {
2292
-            $result .= $this->headerLine('X-Priority', $this->Priority);
2293
-        }
2294
-        if ('' == $this->XMailer) {
2295
-            $result .= $this->headerLine(
2296
-                'X-Mailer',
2297
-                'PHPMailer ' . self::VERSION . ' (https://github.com/PHPMailer/PHPMailer)'
2298
-            );
2299
-        } else {
2300
-            $myXmailer = trim($this->XMailer);
2301
-            if ($myXmailer) {
2302
-                $result .= $this->headerLine('X-Mailer', $myXmailer);
2303
-            }
2304
-        }
2305
-
2306
-        if ('' != $this->ConfirmReadingTo) {
2307
-            $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
2308
-        }
2309
-
2310
-        // Add custom headers
2311
-        foreach ($this->CustomHeader as $header) {
2312
-            $result .= $this->headerLine(
2313
-                trim($header[0]),
2314
-                $this->encodeHeader(trim($header[1]))
2315
-            );
2316
-        }
2317
-        if (!$this->sign_key_file) {
2318
-            $result .= $this->headerLine('MIME-Version', '1.0');
2319
-            $result .= $this->getMailMIME();
2320
-        }
2321
-
2322
-        return $result;
2323
-    }
2324
-
2325
-    /**
2326
-     * Get the message MIME type headers.
2327
-     *
2328
-     * @return string
2329
-     */
2330
-    public function getMailMIME()
2331
-    {
2332
-        $result = '';
2333
-        $ismultipart = true;
2334
-        switch ($this->message_type) {
2335
-            case 'inline':
2336
-                $result .= $this->headerLine('Content-Type', 'multipart/related;');
2337
-                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2338
-                break;
2339
-            case 'attach':
2340
-            case 'inline_attach':
2341
-            case 'alt_attach':
2342
-            case 'alt_inline_attach':
2343
-                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2344
-                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2345
-                break;
2346
-            case 'alt':
2347
-            case 'alt_inline':
2348
-                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2349
-                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2350
-                break;
2351
-            default:
2352
-                // Catches case 'plain': and case '':
2353
-                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2354
-                $ismultipart = false;
2355
-                break;
2356
-        }
2357
-        // RFC1341 part 5 says 7bit is assumed if not specified
2358
-        if ('7bit' != $this->Encoding) {
2359
-            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2360
-            if ($ismultipart) {
2361
-                if ('8bit' == $this->Encoding) {
2362
-                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2363
-                }
2364
-                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2365
-            } else {
2366
-                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2367
-            }
2368
-        }
2369
-
2370
-        if ('mail' != $this->Mailer) {
2371
-            $result .= static::$LE;
2372
-        }
2373
-
2374
-        return $result;
2375
-    }
2376
-
2377
-    /**
2378
-     * Returns the whole MIME message.
2379
-     * Includes complete headers and body.
2380
-     * Only valid post preSend().
2381
-     *
2382
-     * @see PHPMailer::preSend()
2383
-     *
2384
-     * @return string
2385
-     */
2386
-    public function getSentMIMEMessage()
2387
-    {
2388
-        return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . static::$LE . static::$LE . $this->MIMEBody;
2389
-    }
2390
-
2391
-    /**
2392
-     * Create a unique ID to use for boundaries.
2393
-     *
2394
-     * @return string
2395
-     */
2396
-    protected function generateId()
2397
-    {
2398
-        $len = 32; //32 bytes = 256 bits
2399
-        if (function_exists('random_bytes')) {
2400
-            $bytes = random_bytes($len);
2401
-        } elseif (function_exists('openssl_random_pseudo_bytes')) {
2402
-            $bytes = openssl_random_pseudo_bytes($len);
2403
-        } else {
2404
-            //Use a hash to force the length to the same as the other methods
2405
-            $bytes = hash('sha256', uniqid((string) mt_rand(), true), true);
2406
-        }
2407
-
2408
-        //We don't care about messing up base64 format here, just want a random string
2409
-        return str_replace(['=', '+', '/'], '', base64_encode(hash('sha256', $bytes, true)));
2410
-    }
2411
-
2412
-    /**
2413
-     * Assemble the message body.
2414
-     * Returns an empty string on failure.
2415
-     *
2416
-     * @throws Exception
2417
-     *
2418
-     * @return string The assembled message body
2419
-     */
2420
-    public function createBody()
2421
-    {
2422
-        $body = '';
2423
-        //Create unique IDs and preset boundaries
2424
-        $this->uniqueid = $this->generateId();
2425
-        $this->boundary[1] = 'b1_' . $this->uniqueid;
2426
-        $this->boundary[2] = 'b2_' . $this->uniqueid;
2427
-        $this->boundary[3] = 'b3_' . $this->uniqueid;
2428
-
2429
-        if ($this->sign_key_file) {
2430
-            $body .= $this->getMailMIME() . static::$LE;
2431
-        }
2432
-
2433
-        $this->setWordWrap();
2434
-
2435
-        $bodyEncoding = $this->Encoding;
2436
-        $bodyCharSet = $this->CharSet;
2437
-        //Can we do a 7-bit downgrade?
2438
-        if ('8bit' == $bodyEncoding and !$this->has8bitChars($this->Body)) {
2439
-            $bodyEncoding = '7bit';
2440
-            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2441
-            $bodyCharSet = 'us-ascii';
2442
-        }
2443
-        //If lines are too long, and we're not already using an encoding that will shorten them,
2444
-        //change to quoted-printable transfer encoding for the body part only
2445
-        if ('base64' != $this->Encoding and static::hasLineLongerThanMax($this->Body)) {
2446
-            $bodyEncoding = 'quoted-printable';
2447
-        }
2448
-
2449
-        $altBodyEncoding = $this->Encoding;
2450
-        $altBodyCharSet = $this->CharSet;
2451
-        //Can we do a 7-bit downgrade?
2452
-        if ('8bit' == $altBodyEncoding and !$this->has8bitChars($this->AltBody)) {
2453
-            $altBodyEncoding = '7bit';
2454
-            //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2455
-            $altBodyCharSet = 'us-ascii';
2456
-        }
2457
-        //If lines are too long, and we're not already using an encoding that will shorten them,
2458
-        //change to quoted-printable transfer encoding for the alt body part only
2459
-        if ('base64' != $altBodyEncoding and static::hasLineLongerThanMax($this->AltBody)) {
2460
-            $altBodyEncoding = 'quoted-printable';
2461
-        }
2462
-        //Use this as a preamble in all multipart message types
2463
-        $mimepre = 'This is a multi-part message in MIME format.' . static::$LE;
2464
-        switch ($this->message_type) {
2465
-            case 'inline':
2466
-                $body .= $mimepre;
2467
-                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2468
-                $body .= $this->encodeString($this->Body, $bodyEncoding);
2469
-                $body .= static::$LE;
2470
-                $body .= $this->attachAll('inline', $this->boundary[1]);
2471
-                break;
2472
-            case 'attach':
2473
-                $body .= $mimepre;
2474
-                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2475
-                $body .= $this->encodeString($this->Body, $bodyEncoding);
2476
-                $body .= static::$LE;
2477
-                $body .= $this->attachAll('attachment', $this->boundary[1]);
2478
-                break;
2479
-            case 'inline_attach':
2480
-                $body .= $mimepre;
2481
-                $body .= $this->textLine('--' . $this->boundary[1]);
2482
-                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2483
-                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2484
-                $body .= static::$LE;
2485
-                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2486
-                $body .= $this->encodeString($this->Body, $bodyEncoding);
2487
-                $body .= static::$LE;
2488
-                $body .= $this->attachAll('inline', $this->boundary[2]);
2489
-                $body .= static::$LE;
2490
-                $body .= $this->attachAll('attachment', $this->boundary[1]);
2491
-                break;
2492
-            case 'alt':
2493
-                $body .= $mimepre;
2494
-                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2495
-                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2496
-                $body .= static::$LE;
2497
-                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2498
-                $body .= $this->encodeString($this->Body, $bodyEncoding);
2499
-                $body .= static::$LE;
2500
-                if (!empty($this->Ical)) {
2501
-                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2502
-                    $body .= $this->encodeString($this->Ical, $this->Encoding);
2503
-                    $body .= static::$LE;
2504
-                }
2505
-                $body .= $this->endBoundary($this->boundary[1]);
2506
-                break;
2507
-            case 'alt_inline':
2508
-                $body .= $mimepre;
2509
-                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2510
-                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2511
-                $body .= static::$LE;
2512
-                $body .= $this->textLine('--' . $this->boundary[1]);
2513
-                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2514
-                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2515
-                $body .= static::$LE;
2516
-                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2517
-                $body .= $this->encodeString($this->Body, $bodyEncoding);
2518
-                $body .= static::$LE;
2519
-                $body .= $this->attachAll('inline', $this->boundary[2]);
2520
-                $body .= static::$LE;
2521
-                $body .= $this->endBoundary($this->boundary[1]);
2522
-                break;
2523
-            case 'alt_attach':
2524
-                $body .= $mimepre;
2525
-                $body .= $this->textLine('--' . $this->boundary[1]);
2526
-                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2527
-                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2528
-                $body .= static::$LE;
2529
-                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2530
-                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2531
-                $body .= static::$LE;
2532
-                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2533
-                $body .= $this->encodeString($this->Body, $bodyEncoding);
2534
-                $body .= static::$LE;
2535
-                if (!empty($this->Ical)) {
2536
-                    $body .= $this->getBoundary($this->boundary[2], '', 'text/calendar; method=REQUEST', '');
2537
-                    $body .= $this->encodeString($this->Ical, $this->Encoding);
2538
-                }
2539
-                $body .= $this->endBoundary($this->boundary[2]);
2540
-                $body .= static::$LE;
2541
-                $body .= $this->attachAll('attachment', $this->boundary[1]);
2542
-                break;
2543
-            case 'alt_inline_attach':
2544
-                $body .= $mimepre;
2545
-                $body .= $this->textLine('--' . $this->boundary[1]);
2546
-                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2547
-                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2548
-                $body .= static::$LE;
2549
-                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2550
-                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2551
-                $body .= static::$LE;
2552
-                $body .= $this->textLine('--' . $this->boundary[2]);
2553
-                $body .= $this->headerLine('Content-Type', 'multipart/related;');
2554
-                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2555
-                $body .= static::$LE;
2556
-                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2557
-                $body .= $this->encodeString($this->Body, $bodyEncoding);
2558
-                $body .= static::$LE;
2559
-                $body .= $this->attachAll('inline', $this->boundary[3]);
2560
-                $body .= static::$LE;
2561
-                $body .= $this->endBoundary($this->boundary[2]);
2562
-                $body .= static::$LE;
2563
-                $body .= $this->attachAll('attachment', $this->boundary[1]);
2564
-                break;
2565
-            default:
2566
-                // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
2567
-                //Reset the `Encoding` property in case we changed it for line length reasons
2568
-                $this->Encoding = $bodyEncoding;
2569
-                $body .= $this->encodeString($this->Body, $this->Encoding);
2570
-                break;
2571
-        }
2572
-
2573
-        if ($this->isError()) {
2574
-            $body = '';
2575
-            if ($this->exceptions) {
2576
-                throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
2577
-            }
2578
-        } elseif ($this->sign_key_file) {
2579
-            try {
2580
-                if (!defined('PKCS7_TEXT')) {
2581
-                    throw new Exception($this->lang('extension_missing') . 'openssl');
2582
-                }
2583
-                // @TODO would be nice to use php://temp streams here
2584
-                $file = tempnam(sys_get_temp_dir(), 'mail');
2585
-                if (false === file_put_contents($file, $body)) {
2586
-                    throw new Exception($this->lang('signing') . ' Could not write temp file');
2587
-                }
2588
-                $signed = tempnam(sys_get_temp_dir(), 'signed');
2589
-                //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2590
-                if (empty($this->sign_extracerts_file)) {
2591
-                    $sign = @openssl_pkcs7_sign(
2592
-                        $file,
2593
-                        $signed,
2594
-                        'file://' . realpath($this->sign_cert_file),
2595
-                        ['file://' . realpath($this->sign_key_file), $this->sign_key_pass],
2596
-                        []
2597
-                    );
2598
-                } else {
2599
-                    $sign = @openssl_pkcs7_sign(
2600
-                        $file,
2601
-                        $signed,
2602
-                        'file://' . realpath($this->sign_cert_file),
2603
-                        ['file://' . realpath($this->sign_key_file), $this->sign_key_pass],
2604
-                        [],
2605
-                        PKCS7_DETACHED,
2606
-                        $this->sign_extracerts_file
2607
-                    );
2608
-                }
2609
-                @unlink($file);
2610
-                if ($sign) {
2611
-                    $body = file_get_contents($signed);
2612
-                    @unlink($signed);
2613
-                    //The message returned by openssl contains both headers and body, so need to split them up
2614
-                    $parts = explode("\n\n", $body, 2);
2615
-                    $this->MIMEHeader .= $parts[0] . static::$LE . static::$LE;
2616
-                    $body = $parts[1];
2617
-                } else {
2618
-                    @unlink($signed);
2619
-                    throw new Exception($this->lang('signing') . openssl_error_string());
2620
-                }
2621
-            } catch (Exception $exc) {
2622
-                $body = '';
2623
-                if ($this->exceptions) {
2624
-                    throw $exc;
2625
-                }
2626
-            }
2627
-        }
2628
-
2629
-        return $body;
2630
-    }
2631
-
2632
-    /**
2633
-     * Return the start of a message boundary.
2634
-     *
2635
-     * @param string $boundary
2636
-     * @param string $charSet
2637
-     * @param string $contentType
2638
-     * @param string $encoding
2639
-     *
2640
-     * @return string
2641
-     */
2642
-    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2643
-    {
2644
-        $result = '';
2645
-        if ('' == $charSet) {
2646
-            $charSet = $this->CharSet;
2647
-        }
2648
-        if ('' == $contentType) {
2649
-            $contentType = $this->ContentType;
2650
-        }
2651
-        if ('' == $encoding) {
2652
-            $encoding = $this->Encoding;
2653
-        }
2654
-        $result .= $this->textLine('--' . $boundary);
2655
-        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2656
-        $result .= static::$LE;
2657
-        // RFC1341 part 5 says 7bit is assumed if not specified
2658
-        if ('7bit' != $encoding) {
2659
-            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2660
-        }
2661
-        $result .= static::$LE;
2662
-
2663
-        return $result;
2664
-    }
2665
-
2666
-    /**
2667
-     * Return the end of a message boundary.
2668
-     *
2669
-     * @param string $boundary
2670
-     *
2671
-     * @return string
2672
-     */
2673
-    protected function endBoundary($boundary)
2674
-    {
2675
-        return static::$LE . '--' . $boundary . '--' . static::$LE;
2676
-    }
2677
-
2678
-    /**
2679
-     * Set the message type.
2680
-     * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2681
-     */
2682
-    protected function setMessageType()
2683
-    {
2684
-        $type = [];
2685
-        if ($this->alternativeExists()) {
2686
-            $type[] = 'alt';
2687
-        }
2688
-        if ($this->inlineImageExists()) {
2689
-            $type[] = 'inline';
2690
-        }
2691
-        if ($this->attachmentExists()) {
2692
-            $type[] = 'attach';
2693
-        }
2694
-        $this->message_type = implode('_', $type);
2695
-        if ('' == $this->message_type) {
2696
-            //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
2697
-            $this->message_type = 'plain';
2698
-        }
2699
-    }
2700
-
2701
-    /**
2702
-     * Format a header line.
2703
-     *
2704
-     * @param string     $name
2705
-     * @param string|int $value
2706
-     *
2707
-     * @return string
2708
-     */
2709
-    public function headerLine($name, $value)
2710
-    {
2711
-        return $name . ': ' . $value . static::$LE;
2712
-    }
2713
-
2714
-    /**
2715
-     * Return a formatted mail line.
2716
-     *
2717
-     * @param string $value
2718
-     *
2719
-     * @return string
2720
-     */
2721
-    public function textLine($value)
2722
-    {
2723
-        return $value . static::$LE;
2724
-    }
2725
-
2726
-    /**
2727
-     * Add an attachment from a path on the filesystem.
2728
-     * Never use a user-supplied path to a file!
2729
-     * Returns false if the file could not be found or read.
2730
-     *
2731
-     * @param string $path        Path to the attachment
2732
-     * @param string $name        Overrides the attachment name
2733
-     * @param string $encoding    File encoding (see $Encoding)
2734
-     * @param string $type        File extension (MIME) type
2735
-     * @param string $disposition Disposition to use
2736
-     *
2737
-     * @throws Exception
2738
-     *
2739
-     * @return bool
2740
-     */
2741
-    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2742
-    {
2743
-        try {
2744
-            if (!@is_file($path)) {
2745
-                throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
2746
-            }
2747
-
2748
-            // If a MIME type is not specified, try to work it out from the file name
2749
-            if ('' == $type) {
2750
-                $type = static::filenameToType($path);
2751
-            }
2752
-
2753
-            $filename = basename($path);
2754
-            if ('' == $name) {
2755
-                $name = $filename;
2756
-            }
2757
-
2758
-            $this->attachment[] = [
2759
-                0 => $path,
2760
-                1 => $filename,
2761
-                2 => $name,
2762
-                3 => $encoding,
2763
-                4 => $type,
2764
-                5 => false, // isStringAttachment
2765
-                6 => $disposition,
2766
-                7 => $name,
2767
-            ];
2768
-        } catch (Exception $exc) {
2769
-            $this->setError($exc->getMessage());
2770
-            $this->edebug($exc->getMessage());
2771
-            if ($this->exceptions) {
2772
-                throw $exc;
2773
-            }
2774
-
2775
-            return false;
2776
-        }
2777
-
2778
-        return true;
2779
-    }
2780
-
2781
-    /**
2782
-     * Return the array of attachments.
2783
-     *
2784
-     * @return array
2785
-     */
2786
-    public function getAttachments()
2787
-    {
2788
-        return $this->attachment;
2789
-    }
2790
-
2791
-    /**
2792
-     * Attach all file, string, and binary attachments to the message.
2793
-     * Returns an empty string on failure.
2794
-     *
2795
-     * @param string $disposition_type
2796
-     * @param string $boundary
2797
-     *
2798
-     * @return string
2799
-     */
2800
-    protected function attachAll($disposition_type, $boundary)
2801
-    {
2802
-        // Return text of body
2803
-        $mime = [];
2804
-        $cidUniq = [];
2805
-        $incl = [];
2806
-
2807
-        // Add all attachments
2808
-        foreach ($this->attachment as $attachment) {
2809
-            // Check if it is a valid disposition_filter
2810
-            if ($attachment[6] == $disposition_type) {
2811
-                // Check for string attachment
2812
-                $string = '';
2813
-                $path = '';
2814
-                $bString = $attachment[5];
2815
-                if ($bString) {
2816
-                    $string = $attachment[0];
2817
-                } else {
2818
-                    $path = $attachment[0];
2819
-                }
2820
-
2821
-                $inclhash = hash('sha256', serialize($attachment));
2822
-                if (in_array($inclhash, $incl)) {
2823
-                    continue;
2824
-                }
2825
-                $incl[] = $inclhash;
2826
-                $name = $attachment[2];
2827
-                $encoding = $attachment[3];
2828
-                $type = $attachment[4];
2829
-                $disposition = $attachment[6];
2830
-                $cid = $attachment[7];
2831
-                if ('inline' == $disposition and array_key_exists($cid, $cidUniq)) {
2832
-                    continue;
2833
-                }
2834
-                $cidUniq[$cid] = true;
2835
-
2836
-                $mime[] = sprintf('--%s%s', $boundary, static::$LE);
2837
-                //Only include a filename property if we have one
2838
-                if (!empty($name)) {
2839
-                    $mime[] = sprintf(
2840
-                        'Content-Type: %s; name="%s"%s',
2841
-                        $type,
2842
-                        $this->encodeHeader($this->secureHeader($name)),
2843
-                        static::$LE
2844
-                    );
2845
-                } else {
2846
-                    $mime[] = sprintf(
2847
-                        'Content-Type: %s%s',
2848
-                        $type,
2849
-                        static::$LE
2850
-                    );
2851
-                }
2852
-                // RFC1341 part 5 says 7bit is assumed if not specified
2853
-                if ('7bit' != $encoding) {
2854
-                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, static::$LE);
2855
-                }
2856
-
2857
-                if (!empty($cid)) {
2858
-                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, static::$LE);
2859
-                }
2860
-
2861
-                // If a filename contains any of these chars, it should be quoted,
2862
-                // but not otherwise: RFC2183 & RFC2045 5.1
2863
-                // Fixes a warning in IETF's msglint MIME checker
2864
-                // Allow for bypassing the Content-Disposition header totally
2865
-                if (!(empty($disposition))) {
2866
-                    $encoded_name = $this->encodeHeader($this->secureHeader($name));
2867
-                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2868
-                        $mime[] = sprintf(
2869
-                            'Content-Disposition: %s; filename="%s"%s',
2870
-                            $disposition,
2871
-                            $encoded_name,
2872
-                            static::$LE . static::$LE
2873
-                        );
2874
-                    } else {
2875
-                        if (!empty($encoded_name)) {
2876
-                            $mime[] = sprintf(
2877
-                                'Content-Disposition: %s; filename=%s%s',
2878
-                                $disposition,
2879
-                                $encoded_name,
2880
-                                static::$LE . static::$LE
2881
-                            );
2882
-                        } else {
2883
-                            $mime[] = sprintf(
2884
-                                'Content-Disposition: %s%s',
2885
-                                $disposition,
2886
-                                static::$LE . static::$LE
2887
-                            );
2888
-                        }
2889
-                    }
2890
-                } else {
2891
-                    $mime[] = static::$LE;
2892
-                }
2893
-
2894
-                // Encode as string attachment
2895
-                if ($bString) {
2896
-                    $mime[] = $this->encodeString($string, $encoding);
2897
-                } else {
2898
-                    $mime[] = $this->encodeFile($path, $encoding);
2899
-                }
2900
-                if ($this->isError()) {
2901
-                    return '';
2902
-                }
2903
-                $mime[] = static::$LE;
2904
-            }
2905
-        }
2906
-
2907
-        $mime[] = sprintf('--%s--%s', $boundary, static::$LE);
2908
-
2909
-        return implode('', $mime);
2910
-    }
2911
-
2912
-    /**
2913
-     * Encode a file attachment in requested format.
2914
-     * Returns an empty string on failure.
2915
-     *
2916
-     * @param string $path     The full path to the file
2917
-     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2918
-     *
2919
-     * @throws Exception
2920
-     *
2921
-     * @return string
2922
-     */
2923
-    protected function encodeFile($path, $encoding = 'base64')
2924
-    {
2925
-        try {
2926
-            if (!file_exists($path)) {
2927
-                throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
2928
-            }
2929
-            $file_buffer = file_get_contents($path);
2930
-            if (false === $file_buffer) {
2931
-                throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
2932
-            }
2933
-            $file_buffer = $this->encodeString($file_buffer, $encoding);
2934
-
2935
-            return $file_buffer;
2936
-        } catch (Exception $exc) {
2937
-            $this->setError($exc->getMessage());
2938
-
2939
-            return '';
2940
-        }
2941
-    }
2942
-
2943
-    /**
2944
-     * Encode a string in requested format.
2945
-     * Returns an empty string on failure.
2946
-     *
2947
-     * @param string $str      The text to encode
2948
-     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable
2949
-     *
2950
-     * @return string
2951
-     */
2952
-    public function encodeString($str, $encoding = 'base64')
2953
-    {
2954
-        $encoded = '';
2955
-        switch (strtolower($encoding)) {
2956
-            case 'base64':
2957
-                $encoded = chunk_split(
2958
-                    base64_encode($str),
2959
-                    static::STD_LINE_LENGTH - strlen(static::$LE),
2960
-                    static::$LE
2961
-                );
2962
-                break;
2963
-            case '7bit':
2964
-            case '8bit':
2965
-                $encoded = static::normalizeBreaks($str);
2966
-                // Make sure it ends with a line break
2967
-                if (substr($encoded, -(strlen(static::$LE))) != static::$LE) {
2968
-                    $encoded .= static::$LE;
2969
-                }
2970
-                break;
2971
-            case 'binary':
2972
-                $encoded = $str;
2973
-                break;
2974
-            case 'quoted-printable':
2975
-                $encoded = $this->encodeQP($str);
2976
-                break;
2977
-            default:
2978
-                $this->setError($this->lang('encoding') . $encoding);
2979
-                break;
2980
-        }
2981
-
2982
-        return $encoded;
2983
-    }
2984
-
2985
-    /**
2986
-     * Encode a header value (not including its label) optimally.
2987
-     * Picks shortest of Q, B, or none. Result includes folding if needed.
2988
-     * See RFC822 definitions for phrase, comment and text positions.
2989
-     *
2990
-     * @param string $str      The header value to encode
2991
-     * @param string $position What context the string will be used in
2992
-     *
2993
-     * @return string
2994
-     */
2995
-    public function encodeHeader($str, $position = 'text')
2996
-    {
2997
-        $matchcount = 0;
2998
-        switch (strtolower($position)) {
2999
-            case 'phrase':
3000
-                if (!preg_match('/[\200-\377]/', $str)) {
3001
-                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
3002
-                    $encoded = addcslashes($str, "\0..\37\177\\\"");
3003
-                    if (($str == $encoded) and !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
3004
-                        return $encoded;
3005
-                    }
3006
-
3007
-                    return "\"$encoded\"";
3008
-                }
3009
-                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
3010
-                break;
3011
-            /* @noinspection PhpMissingBreakStatementInspection */
3012
-            case 'comment':
3013
-                $matchcount = preg_match_all('/[()"]/', $str, $matches);
3014
-            //fallthrough
3015
-            case 'text':
3016
-            default:
3017
-                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
3018
-                break;
3019
-        }
3020
-
3021
-        //RFCs specify a maximum line length of 78 chars, however mail() will sometimes
3022
-        //corrupt messages with headers longer than 65 chars. See #818
3023
-        $lengthsub = 'mail' == $this->Mailer ? 13 : 0;
3024
-        $maxlen = static::STD_LINE_LENGTH - $lengthsub;
3025
-        // Try to select the encoding which should produce the shortest output
3026
-        if ($matchcount > strlen($str) / 3) {
3027
-            // More than a third of the content will need encoding, so B encoding will be most efficient
3028
-            $encoding = 'B';
3029
-            //This calculation is:
3030
-            // max line length
3031
-            // - shorten to avoid mail() corruption
3032
-            // - Q/B encoding char overhead ("` =?<charset>?[QB]?<content>?=`")
3033
-            // - charset name length
3034
-            $maxlen = static::STD_LINE_LENGTH - $lengthsub - 8 - strlen($this->CharSet);
3035
-            if ($this->hasMultiBytes($str)) {
3036
-                // Use a custom function which correctly encodes and wraps long
3037
-                // multibyte strings without breaking lines within a character
3038
-                $encoded = $this->base64EncodeWrapMB($str, "\n");
3039
-            } else {
3040
-                $encoded = base64_encode($str);
3041
-                $maxlen -= $maxlen % 4;
3042
-                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
3043
-            }
3044
-            $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
3045
-        } elseif ($matchcount > 0) {
3046
-            //1 or more chars need encoding, use Q-encode
3047
-            $encoding = 'Q';
3048
-            //Recalc max line length for Q encoding - see comments on B encode
3049
-            $maxlen = static::STD_LINE_LENGTH - $lengthsub - 8 - strlen($this->CharSet);
3050
-            $encoded = $this->encodeQ($str, $position);
3051
-            $encoded = $this->wrapText($encoded, $maxlen, true);
3052
-            $encoded = str_replace('=' . static::$LE, "\n", trim($encoded));
3053
-            $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
3054
-        } elseif (strlen($str) > $maxlen) {
3055
-            //No chars need encoding, but line is too long, so fold it
3056
-            $encoded = trim($this->wrapText($str, $maxlen, false));
3057
-            if ($str == $encoded) {
3058
-                //Wrapping nicely didn't work, wrap hard instead
3059
-                $encoded = trim(chunk_split($str, static::STD_LINE_LENGTH, static::$LE));
3060
-            }
3061
-            $encoded = str_replace(static::$LE, "\n", trim($encoded));
3062
-            $encoded = preg_replace('/^(.*)$/m', ' \\1', $encoded);
3063
-        } else {
3064
-            //No reformatting needed
3065
-            return $str;
3066
-        }
3067
-
3068
-        return trim(static::normalizeBreaks($encoded));
3069
-    }
3070
-
3071
-    /**
3072
-     * Check if a string contains multi-byte characters.
3073
-     *
3074
-     * @param string $str multi-byte text to wrap encode
3075
-     *
3076
-     * @return bool
3077
-     */
3078
-    public function hasMultiBytes($str)
3079
-    {
3080
-        if (function_exists('mb_strlen')) {
3081
-            return strlen($str) > mb_strlen($str, $this->CharSet);
3082
-        }
3083
-
3084
-        // Assume no multibytes (we can't handle without mbstring functions anyway)
3085
-        return false;
3086
-    }
3087
-
3088
-    /**
3089
-     * Does a string contain any 8-bit chars (in any charset)?
3090
-     *
3091
-     * @param string $text
3092
-     *
3093
-     * @return bool
3094
-     */
3095
-    public function has8bitChars($text)
3096
-    {
3097
-        return (bool) preg_match('/[\x80-\xFF]/', $text);
3098
-    }
3099
-
3100
-    /**
3101
-     * Encode and wrap long multibyte strings for mail headers
3102
-     * without breaking lines within a character.
3103
-     * Adapted from a function by paravoid.
3104
-     *
3105
-     * @see http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
3106
-     *
3107
-     * @param string $str       multi-byte text to wrap encode
3108
-     * @param string $linebreak string to use as linefeed/end-of-line
3109
-     *
3110
-     * @return string
3111
-     */
3112
-    public function base64EncodeWrapMB($str, $linebreak = null)
3113
-    {
3114
-        $start = '=?' . $this->CharSet . '?B?';
3115
-        $end = '?=';
3116
-        $encoded = '';
3117
-        if (null === $linebreak) {
3118
-            $linebreak = static::$LE;
3119
-        }
3120
-
3121
-        $mb_length = mb_strlen($str, $this->CharSet);
3122
-        // Each line must have length <= 75, including $start and $end
3123
-        $length = 75 - strlen($start) - strlen($end);
3124
-        // Average multi-byte ratio
3125
-        $ratio = $mb_length / strlen($str);
3126
-        // Base64 has a 4:3 ratio
3127
-        $avgLength = floor($length * $ratio * .75);
3128
-
3129
-        for ($i = 0; $i < $mb_length; $i += $offset) {
3130
-            $lookBack = 0;
3131
-            do {
3132
-                $offset = $avgLength - $lookBack;
3133
-                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
3134
-                $chunk = base64_encode($chunk);
3135
-                ++$lookBack;
3136
-            } while (strlen($chunk) > $length);
3137
-            $encoded .= $chunk . $linebreak;
3138
-        }
3139
-
3140
-        // Chomp the last linefeed
3141
-        return substr($encoded, 0, -strlen($linebreak));
3142
-    }
3143
-
3144
-    /**
3145
-     * Encode a string in quoted-printable format.
3146
-     * According to RFC2045 section 6.7.
3147
-     *
3148
-     * @param string $string The text to encode
3149
-     *
3150
-     * @return string
3151
-     */
3152
-    public function encodeQP($string)
3153
-    {
3154
-        return static::normalizeBreaks(quoted_printable_encode($string));
3155
-    }
3156
-
3157
-    /**
3158
-     * Encode a string using Q encoding.
3159
-     *
3160
-     * @see http://tools.ietf.org/html/rfc2047#section-4.2
3161
-     *
3162
-     * @param string $str      the text to encode
3163
-     * @param string $position Where the text is going to be used, see the RFC for what that means
3164
-     *
3165
-     * @return string
3166
-     */
3167
-    public function encodeQ($str, $position = 'text')
3168
-    {
3169
-        // There should not be any EOL in the string
3170
-        $pattern = '';
3171
-        $encoded = str_replace(["\r", "\n"], '', $str);
3172
-        switch (strtolower($position)) {
3173
-            case 'phrase':
3174
-                // RFC 2047 section 5.3
3175
-                $pattern = '^A-Za-z0-9!*+\/ -';
3176
-                break;
3177
-            /*
1232
+				return (bool) preg_match(
1233
+					'/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1234
+					'[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1235
+					$address
1236
+				);
1237
+			case 'php':
1238
+			default:
1239
+				return (bool) filter_var($address, FILTER_VALIDATE_EMAIL);
1240
+		}
1241
+	}
1242
+
1243
+	/**
1244
+	 * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1245
+	 * `intl` and `mbstring` PHP extensions.
1246
+	 *
1247
+	 * @return bool `true` if required functions for IDN support are present
1248
+	 */
1249
+	public function idnSupported()
1250
+	{
1251
+		return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1252
+	}
1253
+
1254
+	/**
1255
+	 * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1256
+	 * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1257
+	 * This function silently returns unmodified address if:
1258
+	 * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1259
+	 * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1260
+	 *   or fails for any reason (e.g. domain contains characters not allowed in an IDN).
1261
+	 *
1262
+	 * @see    PHPMailer::$CharSet
1263
+	 *
1264
+	 * @param string $address The email address to convert
1265
+	 *
1266
+	 * @return string The encoded address in ASCII form
1267
+	 */
1268
+	public function punyencodeAddress($address)
1269
+	{
1270
+		// Verify we have required functions, CharSet, and at-sign.
1271
+		$pos = strrpos($address, '@');
1272
+		if ($this->idnSupported() and
1273
+			!empty($this->CharSet) and
1274
+			false !== $pos
1275
+		) {
1276
+			$domain = substr($address, ++$pos);
1277
+			// Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1278
+			if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1279
+				$domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1280
+				//Ignore IDE complaints about this line - method signature changed in PHP 5.4
1281
+				$errorcode = 0;
1282
+				$punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_UTS46);
1283
+				if (false !== $punycode) {
1284
+					return substr($address, 0, $pos) . $punycode;
1285
+				}
1286
+			}
1287
+		}
1288
+
1289
+		return $address;
1290
+	}
1291
+
1292
+	/**
1293
+	 * Create a message and send it.
1294
+	 * Uses the sending method specified by $Mailer.
1295
+	 *
1296
+	 * @throws Exception
1297
+	 *
1298
+	 * @return bool false on error - See the ErrorInfo property for details of the error
1299
+	 */
1300
+	public function send()
1301
+	{
1302
+		try {
1303
+			if (!$this->preSend()) {
1304
+				return false;
1305
+			}
1306
+
1307
+			return $this->postSend();
1308
+		} catch (Exception $exc) {
1309
+			$this->mailHeader = '';
1310
+			$this->setError($exc->getMessage());
1311
+			if ($this->exceptions) {
1312
+				throw $exc;
1313
+			}
1314
+
1315
+			return false;
1316
+		}
1317
+	}
1318
+
1319
+	/**
1320
+	 * Prepare a message for sending.
1321
+	 *
1322
+	 * @throws Exception
1323
+	 *
1324
+	 * @return bool
1325
+	 */
1326
+	public function preSend()
1327
+	{
1328
+		if ('smtp' == $this->Mailer or
1329
+			('mail' == $this->Mailer and stripos(PHP_OS, 'WIN') === 0)
1330
+		) {
1331
+			//SMTP mandates RFC-compliant line endings
1332
+			//and it's also used with mail() on Windows
1333
+			static::setLE("\r\n");
1334
+		} else {
1335
+			//Maintain backward compatibility with legacy Linux command line mailers
1336
+			static::setLE(PHP_EOL);
1337
+		}
1338
+		//Check for buggy PHP versions that add a header with an incorrect line break
1339
+		if (ini_get('mail.add_x_header') == 1
1340
+			and 'mail' == $this->Mailer
1341
+			and stripos(PHP_OS, 'WIN') === 0
1342
+			and ((version_compare(PHP_VERSION, '7.0.0', '>=')
1343
+					and version_compare(PHP_VERSION, '7.0.17', '<'))
1344
+				or (version_compare(PHP_VERSION, '7.1.0', '>=')
1345
+					and version_compare(PHP_VERSION, '7.1.3', '<')))
1346
+		) {
1347
+			trigger_error(
1348
+				'Your version of PHP is affected by a bug that may result in corrupted messages.' .
1349
+				' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' .
1350
+				' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.',
1351
+				E_USER_WARNING
1352
+			);
1353
+		}
1354
+
1355
+		try {
1356
+			$this->error_count = 0; // Reset errors
1357
+			$this->mailHeader = '';
1358
+
1359
+			// Dequeue recipient and Reply-To addresses with IDN
1360
+			foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1361
+				$params[1] = $this->punyencodeAddress($params[1]);
1362
+				call_user_func_array([$this, 'addAnAddress'], $params);
1363
+			}
1364
+			if (count($this->to) + count($this->cc) + count($this->bcc) < 1) {
1365
+				throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL);
1366
+			}
1367
+
1368
+			// Validate From, Sender, and ConfirmReadingTo addresses
1369
+			foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
1370
+				$this->$address_kind = trim($this->$address_kind);
1371
+				if (empty($this->$address_kind)) {
1372
+					continue;
1373
+				}
1374
+				$this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1375
+				if (!static::validateAddress($this->$address_kind)) {
1376
+					$error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
1377
+					$this->setError($error_message);
1378
+					$this->edebug($error_message);
1379
+					if ($this->exceptions) {
1380
+						throw new Exception($error_message);
1381
+					}
1382
+
1383
+					return false;
1384
+				}
1385
+			}
1386
+
1387
+			// Set whether the message is multipart/alternative
1388
+			if ($this->alternativeExists()) {
1389
+				$this->ContentType = 'multipart/alternative';
1390
+			}
1391
+
1392
+			$this->setMessageType();
1393
+			// Refuse to send an empty message unless we are specifically allowing it
1394
+			if (!$this->AllowEmpty and empty($this->Body)) {
1395
+				throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
1396
+			}
1397
+
1398
+			//Trim subject consistently
1399
+			$this->Subject = trim($this->Subject);
1400
+			// Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1401
+			$this->MIMEHeader = '';
1402
+			$this->MIMEBody = $this->createBody();
1403
+			// createBody may have added some headers, so retain them
1404
+			$tempheaders = $this->MIMEHeader;
1405
+			$this->MIMEHeader = $this->createHeader();
1406
+			$this->MIMEHeader .= $tempheaders;
1407
+
1408
+			// To capture the complete message when using mail(), create
1409
+			// an extra header list which createHeader() doesn't fold in
1410
+			if ('mail' == $this->Mailer) {
1411
+				if (count($this->to) > 0) {
1412
+					$this->mailHeader .= $this->addrAppend('To', $this->to);
1413
+				} else {
1414
+					$this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1415
+				}
1416
+				$this->mailHeader .= $this->headerLine(
1417
+					'Subject',
1418
+					$this->encodeHeader($this->secureHeader($this->Subject))
1419
+				);
1420
+			}
1421
+
1422
+			// Sign with DKIM if enabled
1423
+			if (!empty($this->DKIM_domain)
1424
+				and !empty($this->DKIM_selector)
1425
+				and (!empty($this->DKIM_private_string)
1426
+					or (!empty($this->DKIM_private) and file_exists($this->DKIM_private))
1427
+				)
1428
+			) {
1429
+				$header_dkim = $this->DKIM_Add(
1430
+					$this->MIMEHeader . $this->mailHeader,
1431
+					$this->encodeHeader($this->secureHeader($this->Subject)),
1432
+					$this->MIMEBody
1433
+				);
1434
+				$this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . static::$LE .
1435
+					static::normalizeBreaks($header_dkim) . static::$LE;
1436
+			}
1437
+
1438
+			return true;
1439
+		} catch (Exception $exc) {
1440
+			$this->setError($exc->getMessage());
1441
+			if ($this->exceptions) {
1442
+				throw $exc;
1443
+			}
1444
+
1445
+			return false;
1446
+		}
1447
+	}
1448
+
1449
+	/**
1450
+	 * Actually send a message via the selected mechanism.
1451
+	 *
1452
+	 * @throws Exception
1453
+	 *
1454
+	 * @return bool
1455
+	 */
1456
+	public function postSend()
1457
+	{
1458
+		try {
1459
+			// Choose the mailer and send through it
1460
+			switch ($this->Mailer) {
1461
+				case 'sendmail':
1462
+				case 'qmail':
1463
+					return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1464
+				case 'smtp':
1465
+					return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1466
+				case 'mail':
1467
+					return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1468
+				default:
1469
+					$sendMethod = $this->Mailer . 'Send';
1470
+					if (method_exists($this, $sendMethod)) {
1471
+						return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1472
+					}
1473
+
1474
+					return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1475
+			}
1476
+		} catch (Exception $exc) {
1477
+			$this->setError($exc->getMessage());
1478
+			$this->edebug($exc->getMessage());
1479
+			if ($this->exceptions) {
1480
+				throw $exc;
1481
+			}
1482
+		}
1483
+
1484
+		return false;
1485
+	}
1486
+
1487
+	/**
1488
+	 * Send mail using the $Sendmail program.
1489
+	 *
1490
+	 * @see    PHPMailer::$Sendmail
1491
+	 *
1492
+	 * @param string $header The message headers
1493
+	 * @param string $body   The message body
1494
+	 *
1495
+	 * @throws Exception
1496
+	 *
1497
+	 * @return bool
1498
+	 */
1499
+	protected function sendmailSend($header, $body)
1500
+	{
1501
+		// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1502
+		if (!empty($this->Sender) and self::isShellSafe($this->Sender)) {
1503
+			if ('qmail' == $this->Mailer) {
1504
+				$sendmailFmt = '%s -f%s';
1505
+			} else {
1506
+				$sendmailFmt = '%s -oi -f%s -t';
1507
+			}
1508
+		} else {
1509
+			if ('qmail' == $this->Mailer) {
1510
+				$sendmailFmt = '%s';
1511
+			} else {
1512
+				$sendmailFmt = '%s -oi -t';
1513
+			}
1514
+		}
1515
+
1516
+		$sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
1517
+
1518
+		if ($this->SingleTo) {
1519
+			foreach ($this->SingleToArray as $toAddr) {
1520
+				$mail = @popen($sendmail, 'w');
1521
+				if (!$mail) {
1522
+					throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1523
+				}
1524
+				fwrite($mail, 'To: ' . $toAddr . "\n");
1525
+				fwrite($mail, $header);
1526
+				fwrite($mail, $body);
1527
+				$result = pclose($mail);
1528
+				$this->doCallback(
1529
+					($result == 0),
1530
+					[$toAddr],
1531
+					$this->cc,
1532
+					$this->bcc,
1533
+					$this->Subject,
1534
+					$body,
1535
+					$this->From,
1536
+					[]
1537
+				);
1538
+				if (0 !== $result) {
1539
+					throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1540
+				}
1541
+			}
1542
+		} else {
1543
+			$mail = @popen($sendmail, 'w');
1544
+			if (!$mail) {
1545
+				throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1546
+			}
1547
+			fwrite($mail, $header);
1548
+			fwrite($mail, $body);
1549
+			$result = pclose($mail);
1550
+			$this->doCallback(
1551
+				($result == 0),
1552
+				$this->to,
1553
+				$this->cc,
1554
+				$this->bcc,
1555
+				$this->Subject,
1556
+				$body,
1557
+				$this->From,
1558
+				[]
1559
+			);
1560
+			if (0 !== $result) {
1561
+				throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1562
+			}
1563
+		}
1564
+
1565
+		return true;
1566
+	}
1567
+
1568
+	/**
1569
+	 * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
1570
+	 * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
1571
+	 *
1572
+	 * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report
1573
+	 *
1574
+	 * @param string $string The string to be validated
1575
+	 *
1576
+	 * @return bool
1577
+	 */
1578
+	protected static function isShellSafe($string)
1579
+	{
1580
+		// Future-proof
1581
+		if (escapeshellcmd($string) !== $string
1582
+			or !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])
1583
+		) {
1584
+			return false;
1585
+		}
1586
+
1587
+		$length = strlen($string);
1588
+
1589
+		for ($i = 0; $i < $length; ++$i) {
1590
+			$c = $string[$i];
1591
+
1592
+			// All other characters have a special meaning in at least one common shell, including = and +.
1593
+			// Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
1594
+			// Note that this does permit non-Latin alphanumeric characters based on the current locale.
1595
+			if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
1596
+				return false;
1597
+			}
1598
+		}
1599
+
1600
+		return true;
1601
+	}
1602
+
1603
+	/**
1604
+	 * Send mail using the PHP mail() function.
1605
+	 *
1606
+	 * @see    http://www.php.net/manual/en/book.mail.php
1607
+	 *
1608
+	 * @param string $header The message headers
1609
+	 * @param string $body   The message body
1610
+	 *
1611
+	 * @throws Exception
1612
+	 *
1613
+	 * @return bool
1614
+	 */
1615
+	protected function mailSend($header, $body)
1616
+	{
1617
+		$toArr = [];
1618
+		foreach ($this->to as $toaddr) {
1619
+			$toArr[] = $this->addrFormat($toaddr);
1620
+		}
1621
+		$to = implode(', ', $toArr);
1622
+
1623
+		$params = null;
1624
+		//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
1625
+		if (!empty($this->Sender) and static::validateAddress($this->Sender)) {
1626
+			//A space after `-f` is optional, but there is a long history of its presence
1627
+			//causing problems, so we don't use one
1628
+			//Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
1629
+			//Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html
1630
+			//Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html
1631
+			//Example problem: https://www.drupal.org/node/1057954
1632
+			// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1633
+			if (self::isShellSafe($this->Sender)) {
1634
+				$params = sprintf('-f%s', $this->Sender);
1635
+			}
1636
+		}
1637
+		if (!empty($this->Sender) and static::validateAddress($this->Sender)) {
1638
+			$old_from = ini_get('sendmail_from');
1639
+			ini_set('sendmail_from', $this->Sender);
1640
+		}
1641
+		$result = false;
1642
+		if ($this->SingleTo and count($toArr) > 1) {
1643
+			foreach ($toArr as $toAddr) {
1644
+				$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1645
+				$this->doCallback($result, [$toAddr], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []);
1646
+			}
1647
+		} else {
1648
+			$result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1649
+			$this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []);
1650
+		}
1651
+		if (isset($old_from)) {
1652
+			ini_set('sendmail_from', $old_from);
1653
+		}
1654
+		if (!$result) {
1655
+			throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL);
1656
+		}
1657
+
1658
+		return true;
1659
+	}
1660
+
1661
+	/**
1662
+	 * Get an instance to use for SMTP operations.
1663
+	 * Override this function to load your own SMTP implementation,
1664
+	 * or set one with setSMTPInstance.
1665
+	 *
1666
+	 * @return SMTP
1667
+	 */
1668
+	public function getSMTPInstance()
1669
+	{
1670
+		if (!is_object($this->smtp)) {
1671
+			$this->smtp = new SMTP();
1672
+		}
1673
+
1674
+		return $this->smtp;
1675
+	}
1676
+
1677
+	/**
1678
+	 * Provide an instance to use for SMTP operations.
1679
+	 *
1680
+	 * @param SMTP $smtp
1681
+	 *
1682
+	 * @return SMTP
1683
+	 */
1684
+	public function setSMTPInstance(SMTP $smtp)
1685
+	{
1686
+		$this->smtp = $smtp;
1687
+
1688
+		return $this->smtp;
1689
+	}
1690
+
1691
+	/**
1692
+	 * Send mail via SMTP.
1693
+	 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1694
+	 *
1695
+	 * @see PHPMailer::setSMTPInstance() to use a different class.
1696
+	 *
1697
+	 * @uses \PHPMailer\PHPMailer\SMTP
1698
+	 *
1699
+	 * @param string $header The message headers
1700
+	 * @param string $body   The message body
1701
+	 *
1702
+	 * @throws Exception
1703
+	 *
1704
+	 * @return bool
1705
+	 */
1706
+	protected function smtpSend($header, $body)
1707
+	{
1708
+		$bad_rcpt = [];
1709
+		if (!$this->smtpConnect($this->SMTPOptions)) {
1710
+			throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1711
+		}
1712
+		//Sender already validated in preSend()
1713
+		if ('' == $this->Sender) {
1714
+			$smtp_from = $this->From;
1715
+		} else {
1716
+			$smtp_from = $this->Sender;
1717
+		}
1718
+		if (!$this->smtp->mail($smtp_from)) {
1719
+			$this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1720
+			throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
1721
+		}
1722
+
1723
+		$callbacks = [];
1724
+		// Attempt to send to all recipients
1725
+		foreach ([$this->to, $this->cc, $this->bcc] as $togroup) {
1726
+			foreach ($togroup as $to) {
1727
+				if (!$this->smtp->recipient($to[0])) {
1728
+					$error = $this->smtp->getError();
1729
+					$bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']];
1730
+					$isSent = false;
1731
+				} else {
1732
+					$isSent = true;
1733
+				}
1734
+
1735
+				$callbacks[] = ['issent'=>$isSent, 'to'=>$to[0]];
1736
+			}
1737
+		}
1738
+
1739
+		// Only send the DATA command if we have viable recipients
1740
+		if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1741
+			throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1742
+		}
1743
+
1744
+		$smtp_transaction_id = $this->smtp->getLastTransactionID();
1745
+
1746
+		if ($this->SMTPKeepAlive) {
1747
+			$this->smtp->reset();
1748
+		} else {
1749
+			$this->smtp->quit();
1750
+			$this->smtp->close();
1751
+		}
1752
+
1753
+		foreach ($callbacks as $cb) {
1754
+			$this->doCallback(
1755
+				$cb['issent'],
1756
+				[$cb['to']],
1757
+				[],
1758
+				[],
1759
+				$this->Subject,
1760
+				$body,
1761
+				$this->From,
1762
+				['smtp_transaction_id' => $smtp_transaction_id]
1763
+			);
1764
+		}
1765
+
1766
+		//Create error message for any bad addresses
1767
+		if (count($bad_rcpt) > 0) {
1768
+			$errstr = '';
1769
+			foreach ($bad_rcpt as $bad) {
1770
+				$errstr .= $bad['to'] . ': ' . $bad['error'];
1771
+			}
1772
+			throw new Exception(
1773
+				$this->lang('recipients_failed') . $errstr,
1774
+				self::STOP_CONTINUE
1775
+			);
1776
+		}
1777
+
1778
+		return true;
1779
+	}
1780
+
1781
+	/**
1782
+	 * Initiate a connection to an SMTP server.
1783
+	 * Returns false if the operation failed.
1784
+	 *
1785
+	 * @param array $options An array of options compatible with stream_context_create()
1786
+	 *
1787
+	 * @throws Exception
1788
+	 *
1789
+	 * @uses \PHPMailer\PHPMailer\SMTP
1790
+	 *
1791
+	 * @return bool
1792
+	 */
1793
+	public function smtpConnect($options = null)
1794
+	{
1795
+		if (null === $this->smtp) {
1796
+			$this->smtp = $this->getSMTPInstance();
1797
+		}
1798
+
1799
+		//If no options are provided, use whatever is set in the instance
1800
+		if (null === $options) {
1801
+			$options = $this->SMTPOptions;
1802
+		}
1803
+
1804
+		// Already connected?
1805
+		if ($this->smtp->connected()) {
1806
+			return true;
1807
+		}
1808
+
1809
+		$this->smtp->setTimeout($this->Timeout);
1810
+		$this->smtp->setDebugLevel($this->SMTPDebug);
1811
+		$this->smtp->setDebugOutput($this->Debugoutput);
1812
+		$this->smtp->setVerp($this->do_verp);
1813
+		$hosts = explode(';', $this->Host);
1814
+		$lastexception = null;
1815
+
1816
+		foreach ($hosts as $hostentry) {
1817
+			$hostinfo = [];
1818
+			if (!preg_match(
1819
+				'/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/',
1820
+				trim($hostentry),
1821
+				$hostinfo
1822
+			)) {
1823
+				static::edebug($this->lang('connect_host') . ' ' . $hostentry);
1824
+				// Not a valid host entry
1825
+				continue;
1826
+			}
1827
+			// $hostinfo[2]: optional ssl or tls prefix
1828
+			// $hostinfo[3]: the hostname
1829
+			// $hostinfo[4]: optional port number
1830
+			// The host string prefix can temporarily override the current setting for SMTPSecure
1831
+			// If it's not specified, the default value is used
1832
+
1833
+			//Check the host name is a valid name or IP address before trying to use it
1834
+			if (!static::isValidHost($hostinfo[3])) {
1835
+				static::edebug($this->lang('connect_host') . ' ' . $hostentry);
1836
+				continue;
1837
+			}
1838
+			$prefix = '';
1839
+			$secure = $this->SMTPSecure;
1840
+			$tls = ('tls' == $this->SMTPSecure);
1841
+			if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1842
+				$prefix = 'ssl://';
1843
+				$tls = false; // Can't have SSL and TLS at the same time
1844
+				$secure = 'ssl';
1845
+			} elseif ('tls' == $hostinfo[2]) {
1846
+				$tls = true;
1847
+				// tls doesn't use a prefix
1848
+				$secure = 'tls';
1849
+			}
1850
+			//Do we need the OpenSSL extension?
1851
+			$sslext = defined('OPENSSL_ALGO_SHA256');
1852
+			if ('tls' === $secure or 'ssl' === $secure) {
1853
+				//Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1854
+				if (!$sslext) {
1855
+					throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL);
1856
+				}
1857
+			}
1858
+			$host = $hostinfo[3];
1859
+			$port = $this->Port;
1860
+			$tport = (int) $hostinfo[4];
1861
+			if ($tport > 0 and $tport < 65536) {
1862
+				$port = $tport;
1863
+			}
1864
+			if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1865
+				try {
1866
+					if ($this->Helo) {
1867
+						$hello = $this->Helo;
1868
+					} else {
1869
+						$hello = $this->serverHostname();
1870
+					}
1871
+					$this->smtp->hello($hello);
1872
+					//Automatically enable TLS encryption if:
1873
+					// * it's not disabled
1874
+					// * we have openssl extension
1875
+					// * we are not already using SSL
1876
+					// * the server offers STARTTLS
1877
+					if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1878
+						$tls = true;
1879
+					}
1880
+					if ($tls) {
1881
+						if (!$this->smtp->startTLS()) {
1882
+							throw new Exception($this->lang('connect_host'));
1883
+						}
1884
+						// We must resend EHLO after TLS negotiation
1885
+						$this->smtp->hello($hello);
1886
+					}
1887
+					if ($this->SMTPAuth) {
1888
+						if (!$this->smtp->authenticate(
1889
+							$this->Username,
1890
+							$this->Password,
1891
+							$this->AuthType,
1892
+							$this->oauth
1893
+						)
1894
+						) {
1895
+							throw new Exception($this->lang('authenticate'));
1896
+						}
1897
+					}
1898
+
1899
+					return true;
1900
+				} catch (Exception $exc) {
1901
+					$lastexception = $exc;
1902
+					$this->edebug($exc->getMessage());
1903
+					// We must have connected, but then failed TLS or Auth, so close connection nicely
1904
+					$this->smtp->quit();
1905
+				}
1906
+			}
1907
+		}
1908
+		// If we get here, all connection attempts have failed, so close connection hard
1909
+		$this->smtp->close();
1910
+		// As we've caught all exceptions, just report whatever the last one was
1911
+		if ($this->exceptions and null !== $lastexception) {
1912
+			throw $lastexception;
1913
+		}
1914
+
1915
+		return false;
1916
+	}
1917
+
1918
+	/**
1919
+	 * Close the active SMTP session if one exists.
1920
+	 */
1921
+	public function smtpClose()
1922
+	{
1923
+		if (null !== $this->smtp) {
1924
+			if ($this->smtp->connected()) {
1925
+				$this->smtp->quit();
1926
+				$this->smtp->close();
1927
+			}
1928
+		}
1929
+	}
1930
+
1931
+	/**
1932
+	 * Set the language for error messages.
1933
+	 * Returns false if it cannot load the language file.
1934
+	 * The default language is English.
1935
+	 *
1936
+	 * @param string $langcode  ISO 639-1 2-character language code (e.g. French is "fr")
1937
+	 * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1938
+	 *
1939
+	 * @return bool
1940
+	 */
1941
+	public function setLanguage($langcode = 'en', $lang_path = '')
1942
+	{
1943
+		// Backwards compatibility for renamed language codes
1944
+		$renamed_langcodes = [
1945
+			'br' => 'pt_br',
1946
+			'cz' => 'cs',
1947
+			'dk' => 'da',
1948
+			'no' => 'nb',
1949
+			'se' => 'sv',
1950
+			'sr' => 'rs',
1951
+		];
1952
+
1953
+		if (isset($renamed_langcodes[$langcode])) {
1954
+			$langcode = $renamed_langcodes[$langcode];
1955
+		}
1956
+
1957
+		// Define full set of translatable strings in English
1958
+		$PHPMAILER_LANG = [
1959
+			'authenticate' => 'SMTP Error: Could not authenticate.',
1960
+			'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1961
+			'data_not_accepted' => 'SMTP Error: data not accepted.',
1962
+			'empty_message' => 'Message body empty',
1963
+			'encoding' => 'Unknown encoding: ',
1964
+			'execute' => 'Could not execute: ',
1965
+			'file_access' => 'Could not access file: ',
1966
+			'file_open' => 'File Error: Could not open file: ',
1967
+			'from_failed' => 'The following From address failed: ',
1968
+			'instantiate' => 'Could not instantiate mail function.',
1969
+			'invalid_address' => 'Invalid address: ',
1970
+			'mailer_not_supported' => ' mailer is not supported.',
1971
+			'provide_address' => 'You must provide at least one recipient email address.',
1972
+			'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1973
+			'signing' => 'Signing Error: ',
1974
+			'smtp_connect_failed' => 'SMTP connect() failed.',
1975
+			'smtp_error' => 'SMTP server error: ',
1976
+			'variable_set' => 'Cannot set or reset variable: ',
1977
+			'extension_missing' => 'Extension missing: ',
1978
+		];
1979
+		if (empty($lang_path)) {
1980
+			// Calculate an absolute path so it can work if CWD is not here
1981
+			$lang_path = __DIR__ . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
1982
+		}
1983
+		//Validate $langcode
1984
+		if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
1985
+			$langcode = 'en';
1986
+		}
1987
+		$foundlang = true;
1988
+		$lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1989
+		// There is no English translation file
1990
+		if ('en' != $langcode) {
1991
+			// Make sure language file path is readable
1992
+			if (!file_exists($lang_file)) {
1993
+				$foundlang = false;
1994
+			} else {
1995
+				// Overwrite language-specific strings.
1996
+				// This way we'll never have missing translation keys.
1997
+				$foundlang = include $lang_file;
1998
+			}
1999
+		}
2000
+		$this->language = $PHPMAILER_LANG;
2001
+
2002
+		return (bool) $foundlang; // Returns false if language not found
2003
+	}
2004
+
2005
+	/**
2006
+	 * Get the array of strings for the current language.
2007
+	 *
2008
+	 * @return array
2009
+	 */
2010
+	public function getTranslations()
2011
+	{
2012
+		return $this->language;
2013
+	}
2014
+
2015
+	/**
2016
+	 * Create recipient headers.
2017
+	 *
2018
+	 * @param string $type
2019
+	 * @param array  $addr An array of recipients,
2020
+	 *                     where each recipient is a 2-element indexed array with element 0 containing an address
2021
+	 *                     and element 1 containing a name, like:
2022
+	 *                     [['[email protected]', 'Joe User'], ['[email protected]', 'Zoe User']]
2023
+	 *
2024
+	 * @return string
2025
+	 */
2026
+	public function addrAppend($type, $addr)
2027
+	{
2028
+		$addresses = [];
2029
+		foreach ($addr as $address) {
2030
+			$addresses[] = $this->addrFormat($address);
2031
+		}
2032
+
2033
+		return $type . ': ' . implode(', ', $addresses) . static::$LE;
2034
+	}
2035
+
2036
+	/**
2037
+	 * Format an address for use in a message header.
2038
+	 *
2039
+	 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name like
2040
+	 *                    ['[email protected]', 'Joe User']
2041
+	 *
2042
+	 * @return string
2043
+	 */
2044
+	public function addrFormat($addr)
2045
+	{
2046
+		if (empty($addr[1])) { // No name provided
2047
+			return $this->secureHeader($addr[0]);
2048
+		}
2049
+
2050
+		return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
2051
+				$addr[0]
2052
+			) . '>';
2053
+	}
2054
+
2055
+	/**
2056
+	 * Word-wrap message.
2057
+	 * For use with mailers that do not automatically perform wrapping
2058
+	 * and for quoted-printable encoded messages.
2059
+	 * Original written by philippe.
2060
+	 *
2061
+	 * @param string $message The message to wrap
2062
+	 * @param int    $length  The line length to wrap to
2063
+	 * @param bool   $qp_mode Whether to run in Quoted-Printable mode
2064
+	 *
2065
+	 * @return string
2066
+	 */
2067
+	public function wrapText($message, $length, $qp_mode = false)
2068
+	{
2069
+		if ($qp_mode) {
2070
+			$soft_break = sprintf(' =%s', static::$LE);
2071
+		} else {
2072
+			$soft_break = static::$LE;
2073
+		}
2074
+		// If utf-8 encoding is used, we will need to make sure we don't
2075
+		// split multibyte characters when we wrap
2076
+		$is_utf8 = (strtolower($this->CharSet) == 'utf-8');
2077
+		$lelen = strlen(static::$LE);
2078
+		$crlflen = strlen(static::$LE);
2079
+
2080
+		$message = static::normalizeBreaks($message);
2081
+		//Remove a trailing line break
2082
+		if (substr($message, -$lelen) == static::$LE) {
2083
+			$message = substr($message, 0, -$lelen);
2084
+		}
2085
+
2086
+		//Split message into lines
2087
+		$lines = explode(static::$LE, $message);
2088
+		//Message will be rebuilt in here
2089
+		$message = '';
2090
+		foreach ($lines as $line) {
2091
+			$words = explode(' ', $line);
2092
+			$buf = '';
2093
+			$firstword = true;
2094
+			foreach ($words as $word) {
2095
+				if ($qp_mode and (strlen($word) > $length)) {
2096
+					$space_left = $length - strlen($buf) - $crlflen;
2097
+					if (!$firstword) {
2098
+						if ($space_left > 20) {
2099
+							$len = $space_left;
2100
+							if ($is_utf8) {
2101
+								$len = $this->utf8CharBoundary($word, $len);
2102
+							} elseif (substr($word, $len - 1, 1) == '=') {
2103
+								--$len;
2104
+							} elseif (substr($word, $len - 2, 1) == '=') {
2105
+								$len -= 2;
2106
+							}
2107
+							$part = substr($word, 0, $len);
2108
+							$word = substr($word, $len);
2109
+							$buf .= ' ' . $part;
2110
+							$message .= $buf . sprintf('=%s', static::$LE);
2111
+						} else {
2112
+							$message .= $buf . $soft_break;
2113
+						}
2114
+						$buf = '';
2115
+					}
2116
+					while (strlen($word) > 0) {
2117
+						if ($length <= 0) {
2118
+							break;
2119
+						}
2120
+						$len = $length;
2121
+						if ($is_utf8) {
2122
+							$len = $this->utf8CharBoundary($word, $len);
2123
+						} elseif (substr($word, $len - 1, 1) == '=') {
2124
+							--$len;
2125
+						} elseif (substr($word, $len - 2, 1) == '=') {
2126
+							$len -= 2;
2127
+						}
2128
+						$part = substr($word, 0, $len);
2129
+						$word = substr($word, $len);
2130
+
2131
+						if (strlen($word) > 0) {
2132
+							$message .= $part . sprintf('=%s', static::$LE);
2133
+						} else {
2134
+							$buf = $part;
2135
+						}
2136
+					}
2137
+				} else {
2138
+					$buf_o = $buf;
2139
+					if (!$firstword) {
2140
+						$buf .= ' ';
2141
+					}
2142
+					$buf .= $word;
2143
+
2144
+					if (strlen($buf) > $length and $buf_o != '') {
2145
+						$message .= $buf_o . $soft_break;
2146
+						$buf = $word;
2147
+					}
2148
+				}
2149
+				$firstword = false;
2150
+			}
2151
+			$message .= $buf . static::$LE;
2152
+		}
2153
+
2154
+		return $message;
2155
+	}
2156
+
2157
+	/**
2158
+	 * Find the last character boundary prior to $maxLength in a utf-8
2159
+	 * quoted-printable encoded string.
2160
+	 * Original written by Colin Brown.
2161
+	 *
2162
+	 * @param string $encodedText utf-8 QP text
2163
+	 * @param int    $maxLength   Find the last character boundary prior to this length
2164
+	 *
2165
+	 * @return int
2166
+	 */
2167
+	public function utf8CharBoundary($encodedText, $maxLength)
2168
+	{
2169
+		$foundSplitPos = false;
2170
+		$lookBack = 3;
2171
+		while (!$foundSplitPos) {
2172
+			$lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
2173
+			$encodedCharPos = strpos($lastChunk, '=');
2174
+			if (false !== $encodedCharPos) {
2175
+				// Found start of encoded character byte within $lookBack block.
2176
+				// Check the encoded byte value (the 2 chars after the '=')
2177
+				$hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
2178
+				$dec = hexdec($hex);
2179
+				if ($dec < 128) {
2180
+					// Single byte character.
2181
+					// If the encoded char was found at pos 0, it will fit
2182
+					// otherwise reduce maxLength to start of the encoded char
2183
+					if ($encodedCharPos > 0) {
2184
+						$maxLength -= $lookBack - $encodedCharPos;
2185
+					}
2186
+					$foundSplitPos = true;
2187
+				} elseif ($dec >= 192) {
2188
+					// First byte of a multi byte character
2189
+					// Reduce maxLength to split at start of character
2190
+					$maxLength -= $lookBack - $encodedCharPos;
2191
+					$foundSplitPos = true;
2192
+				} elseif ($dec < 192) {
2193
+					// Middle byte of a multi byte character, look further back
2194
+					$lookBack += 3;
2195
+				}
2196
+			} else {
2197
+				// No encoded character found
2198
+				$foundSplitPos = true;
2199
+			}
2200
+		}
2201
+
2202
+		return $maxLength;
2203
+	}
2204
+
2205
+	/**
2206
+	 * Apply word wrapping to the message body.
2207
+	 * Wraps the message body to the number of chars set in the WordWrap property.
2208
+	 * You should only do this to plain-text bodies as wrapping HTML tags may break them.
2209
+	 * This is called automatically by createBody(), so you don't need to call it yourself.
2210
+	 */
2211
+	public function setWordWrap()
2212
+	{
2213
+		if ($this->WordWrap < 1) {
2214
+			return;
2215
+		}
2216
+
2217
+		switch ($this->message_type) {
2218
+			case 'alt':
2219
+			case 'alt_inline':
2220
+			case 'alt_attach':
2221
+			case 'alt_inline_attach':
2222
+				$this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
2223
+				break;
2224
+			default:
2225
+				$this->Body = $this->wrapText($this->Body, $this->WordWrap);
2226
+				break;
2227
+		}
2228
+	}
2229
+
2230
+	/**
2231
+	 * Assemble message headers.
2232
+	 *
2233
+	 * @return string The assembled headers
2234
+	 */
2235
+	public function createHeader()
2236
+	{
2237
+		$result = '';
2238
+
2239
+		$result .= $this->headerLine('Date', '' == $this->MessageDate ? self::rfcDate() : $this->MessageDate);
2240
+
2241
+		// To be created automatically by mail()
2242
+		if ($this->SingleTo) {
2243
+			if ('mail' != $this->Mailer) {
2244
+				foreach ($this->to as $toaddr) {
2245
+					$this->SingleToArray[] = $this->addrFormat($toaddr);
2246
+				}
2247
+			}
2248
+		} else {
2249
+			if (count($this->to) > 0) {
2250
+				if ('mail' != $this->Mailer) {
2251
+					$result .= $this->addrAppend('To', $this->to);
2252
+				}
2253
+			} elseif (count($this->cc) == 0) {
2254
+				$result .= $this->headerLine('To', 'undisclosed-recipients:;');
2255
+			}
2256
+		}
2257
+
2258
+		$result .= $this->addrAppend('From', [[trim($this->From), $this->FromName]]);
2259
+
2260
+		// sendmail and mail() extract Cc from the header before sending
2261
+		if (count($this->cc) > 0) {
2262
+			$result .= $this->addrAppend('Cc', $this->cc);
2263
+		}
2264
+
2265
+		// sendmail and mail() extract Bcc from the header before sending
2266
+		if ((
2267
+				'sendmail' == $this->Mailer or 'qmail' == $this->Mailer or 'mail' == $this->Mailer
2268
+			)
2269
+			and count($this->bcc) > 0
2270
+		) {
2271
+			$result .= $this->addrAppend('Bcc', $this->bcc);
2272
+		}
2273
+
2274
+		if (count($this->ReplyTo) > 0) {
2275
+			$result .= $this->addrAppend('Reply-To', $this->ReplyTo);
2276
+		}
2277
+
2278
+		// mail() sets the subject itself
2279
+		if ('mail' != $this->Mailer) {
2280
+			$result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2281
+		}
2282
+
2283
+		// Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
2284
+		// https://tools.ietf.org/html/rfc5322#section-3.6.4
2285
+		if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
2286
+			$this->lastMessageID = $this->MessageID;
2287
+		} else {
2288
+			$this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
2289
+		}
2290
+		$result .= $this->headerLine('Message-ID', $this->lastMessageID);
2291
+		if (null !== $this->Priority) {
2292
+			$result .= $this->headerLine('X-Priority', $this->Priority);
2293
+		}
2294
+		if ('' == $this->XMailer) {
2295
+			$result .= $this->headerLine(
2296
+				'X-Mailer',
2297
+				'PHPMailer ' . self::VERSION . ' (https://github.com/PHPMailer/PHPMailer)'
2298
+			);
2299
+		} else {
2300
+			$myXmailer = trim($this->XMailer);
2301
+			if ($myXmailer) {
2302
+				$result .= $this->headerLine('X-Mailer', $myXmailer);
2303
+			}
2304
+		}
2305
+
2306
+		if ('' != $this->ConfirmReadingTo) {
2307
+			$result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
2308
+		}
2309
+
2310
+		// Add custom headers
2311
+		foreach ($this->CustomHeader as $header) {
2312
+			$result .= $this->headerLine(
2313
+				trim($header[0]),
2314
+				$this->encodeHeader(trim($header[1]))
2315
+			);
2316
+		}
2317
+		if (!$this->sign_key_file) {
2318
+			$result .= $this->headerLine('MIME-Version', '1.0');
2319
+			$result .= $this->getMailMIME();
2320
+		}
2321
+
2322
+		return $result;
2323
+	}
2324
+
2325
+	/**
2326
+	 * Get the message MIME type headers.
2327
+	 *
2328
+	 * @return string
2329
+	 */
2330
+	public function getMailMIME()
2331
+	{
2332
+		$result = '';
2333
+		$ismultipart = true;
2334
+		switch ($this->message_type) {
2335
+			case 'inline':
2336
+				$result .= $this->headerLine('Content-Type', 'multipart/related;');
2337
+				$result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2338
+				break;
2339
+			case 'attach':
2340
+			case 'inline_attach':
2341
+			case 'alt_attach':
2342
+			case 'alt_inline_attach':
2343
+				$result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2344
+				$result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2345
+				break;
2346
+			case 'alt':
2347
+			case 'alt_inline':
2348
+				$result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2349
+				$result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2350
+				break;
2351
+			default:
2352
+				// Catches case 'plain': and case '':
2353
+				$result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2354
+				$ismultipart = false;
2355
+				break;
2356
+		}
2357
+		// RFC1341 part 5 says 7bit is assumed if not specified
2358
+		if ('7bit' != $this->Encoding) {
2359
+			// RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2360
+			if ($ismultipart) {
2361
+				if ('8bit' == $this->Encoding) {
2362
+					$result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2363
+				}
2364
+				// The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2365
+			} else {
2366
+				$result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2367
+			}
2368
+		}
2369
+
2370
+		if ('mail' != $this->Mailer) {
2371
+			$result .= static::$LE;
2372
+		}
2373
+
2374
+		return $result;
2375
+	}
2376
+
2377
+	/**
2378
+	 * Returns the whole MIME message.
2379
+	 * Includes complete headers and body.
2380
+	 * Only valid post preSend().
2381
+	 *
2382
+	 * @see PHPMailer::preSend()
2383
+	 *
2384
+	 * @return string
2385
+	 */
2386
+	public function getSentMIMEMessage()
2387
+	{
2388
+		return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . static::$LE . static::$LE . $this->MIMEBody;
2389
+	}
2390
+
2391
+	/**
2392
+	 * Create a unique ID to use for boundaries.
2393
+	 *
2394
+	 * @return string
2395
+	 */
2396
+	protected function generateId()
2397
+	{
2398
+		$len = 32; //32 bytes = 256 bits
2399
+		if (function_exists('random_bytes')) {
2400
+			$bytes = random_bytes($len);
2401
+		} elseif (function_exists('openssl_random_pseudo_bytes')) {
2402
+			$bytes = openssl_random_pseudo_bytes($len);
2403
+		} else {
2404
+			//Use a hash to force the length to the same as the other methods
2405
+			$bytes = hash('sha256', uniqid((string) mt_rand(), true), true);
2406
+		}
2407
+
2408
+		//We don't care about messing up base64 format here, just want a random string
2409
+		return str_replace(['=', '+', '/'], '', base64_encode(hash('sha256', $bytes, true)));
2410
+	}
2411
+
2412
+	/**
2413
+	 * Assemble the message body.
2414
+	 * Returns an empty string on failure.
2415
+	 *
2416
+	 * @throws Exception
2417
+	 *
2418
+	 * @return string The assembled message body
2419
+	 */
2420
+	public function createBody()
2421
+	{
2422
+		$body = '';
2423
+		//Create unique IDs and preset boundaries
2424
+		$this->uniqueid = $this->generateId();
2425
+		$this->boundary[1] = 'b1_' . $this->uniqueid;
2426
+		$this->boundary[2] = 'b2_' . $this->uniqueid;
2427
+		$this->boundary[3] = 'b3_' . $this->uniqueid;
2428
+
2429
+		if ($this->sign_key_file) {
2430
+			$body .= $this->getMailMIME() . static::$LE;
2431
+		}
2432
+
2433
+		$this->setWordWrap();
2434
+
2435
+		$bodyEncoding = $this->Encoding;
2436
+		$bodyCharSet = $this->CharSet;
2437
+		//Can we do a 7-bit downgrade?
2438
+		if ('8bit' == $bodyEncoding and !$this->has8bitChars($this->Body)) {
2439
+			$bodyEncoding = '7bit';
2440
+			//All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2441
+			$bodyCharSet = 'us-ascii';
2442
+		}
2443
+		//If lines are too long, and we're not already using an encoding that will shorten them,
2444
+		//change to quoted-printable transfer encoding for the body part only
2445
+		if ('base64' != $this->Encoding and static::hasLineLongerThanMax($this->Body)) {
2446
+			$bodyEncoding = 'quoted-printable';
2447
+		}
2448
+
2449
+		$altBodyEncoding = $this->Encoding;
2450
+		$altBodyCharSet = $this->CharSet;
2451
+		//Can we do a 7-bit downgrade?
2452
+		if ('8bit' == $altBodyEncoding and !$this->has8bitChars($this->AltBody)) {
2453
+			$altBodyEncoding = '7bit';
2454
+			//All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2455
+			$altBodyCharSet = 'us-ascii';
2456
+		}
2457
+		//If lines are too long, and we're not already using an encoding that will shorten them,
2458
+		//change to quoted-printable transfer encoding for the alt body part only
2459
+		if ('base64' != $altBodyEncoding and static::hasLineLongerThanMax($this->AltBody)) {
2460
+			$altBodyEncoding = 'quoted-printable';
2461
+		}
2462
+		//Use this as a preamble in all multipart message types
2463
+		$mimepre = 'This is a multi-part message in MIME format.' . static::$LE;
2464
+		switch ($this->message_type) {
2465
+			case 'inline':
2466
+				$body .= $mimepre;
2467
+				$body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2468
+				$body .= $this->encodeString($this->Body, $bodyEncoding);
2469
+				$body .= static::$LE;
2470
+				$body .= $this->attachAll('inline', $this->boundary[1]);
2471
+				break;
2472
+			case 'attach':
2473
+				$body .= $mimepre;
2474
+				$body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2475
+				$body .= $this->encodeString($this->Body, $bodyEncoding);
2476
+				$body .= static::$LE;
2477
+				$body .= $this->attachAll('attachment', $this->boundary[1]);
2478
+				break;
2479
+			case 'inline_attach':
2480
+				$body .= $mimepre;
2481
+				$body .= $this->textLine('--' . $this->boundary[1]);
2482
+				$body .= $this->headerLine('Content-Type', 'multipart/related;');
2483
+				$body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2484
+				$body .= static::$LE;
2485
+				$body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2486
+				$body .= $this->encodeString($this->Body, $bodyEncoding);
2487
+				$body .= static::$LE;
2488
+				$body .= $this->attachAll('inline', $this->boundary[2]);
2489
+				$body .= static::$LE;
2490
+				$body .= $this->attachAll('attachment', $this->boundary[1]);
2491
+				break;
2492
+			case 'alt':
2493
+				$body .= $mimepre;
2494
+				$body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2495
+				$body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2496
+				$body .= static::$LE;
2497
+				$body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2498
+				$body .= $this->encodeString($this->Body, $bodyEncoding);
2499
+				$body .= static::$LE;
2500
+				if (!empty($this->Ical)) {
2501
+					$body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2502
+					$body .= $this->encodeString($this->Ical, $this->Encoding);
2503
+					$body .= static::$LE;
2504
+				}
2505
+				$body .= $this->endBoundary($this->boundary[1]);
2506
+				break;
2507
+			case 'alt_inline':
2508
+				$body .= $mimepre;
2509
+				$body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2510
+				$body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2511
+				$body .= static::$LE;
2512
+				$body .= $this->textLine('--' . $this->boundary[1]);
2513
+				$body .= $this->headerLine('Content-Type', 'multipart/related;');
2514
+				$body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2515
+				$body .= static::$LE;
2516
+				$body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2517
+				$body .= $this->encodeString($this->Body, $bodyEncoding);
2518
+				$body .= static::$LE;
2519
+				$body .= $this->attachAll('inline', $this->boundary[2]);
2520
+				$body .= static::$LE;
2521
+				$body .= $this->endBoundary($this->boundary[1]);
2522
+				break;
2523
+			case 'alt_attach':
2524
+				$body .= $mimepre;
2525
+				$body .= $this->textLine('--' . $this->boundary[1]);
2526
+				$body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2527
+				$body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2528
+				$body .= static::$LE;
2529
+				$body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2530
+				$body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2531
+				$body .= static::$LE;
2532
+				$body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2533
+				$body .= $this->encodeString($this->Body, $bodyEncoding);
2534
+				$body .= static::$LE;
2535
+				if (!empty($this->Ical)) {
2536
+					$body .= $this->getBoundary($this->boundary[2], '', 'text/calendar; method=REQUEST', '');
2537
+					$body .= $this->encodeString($this->Ical, $this->Encoding);
2538
+				}
2539
+				$body .= $this->endBoundary($this->boundary[2]);
2540
+				$body .= static::$LE;
2541
+				$body .= $this->attachAll('attachment', $this->boundary[1]);
2542
+				break;
2543
+			case 'alt_inline_attach':
2544
+				$body .= $mimepre;
2545
+				$body .= $this->textLine('--' . $this->boundary[1]);
2546
+				$body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2547
+				$body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2548
+				$body .= static::$LE;
2549
+				$body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2550
+				$body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2551
+				$body .= static::$LE;
2552
+				$body .= $this->textLine('--' . $this->boundary[2]);
2553
+				$body .= $this->headerLine('Content-Type', 'multipart/related;');
2554
+				$body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2555
+				$body .= static::$LE;
2556
+				$body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2557
+				$body .= $this->encodeString($this->Body, $bodyEncoding);
2558
+				$body .= static::$LE;
2559
+				$body .= $this->attachAll('inline', $this->boundary[3]);
2560
+				$body .= static::$LE;
2561
+				$body .= $this->endBoundary($this->boundary[2]);
2562
+				$body .= static::$LE;
2563
+				$body .= $this->attachAll('attachment', $this->boundary[1]);
2564
+				break;
2565
+			default:
2566
+				// Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
2567
+				//Reset the `Encoding` property in case we changed it for line length reasons
2568
+				$this->Encoding = $bodyEncoding;
2569
+				$body .= $this->encodeString($this->Body, $this->Encoding);
2570
+				break;
2571
+		}
2572
+
2573
+		if ($this->isError()) {
2574
+			$body = '';
2575
+			if ($this->exceptions) {
2576
+				throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
2577
+			}
2578
+		} elseif ($this->sign_key_file) {
2579
+			try {
2580
+				if (!defined('PKCS7_TEXT')) {
2581
+					throw new Exception($this->lang('extension_missing') . 'openssl');
2582
+				}
2583
+				// @TODO would be nice to use php://temp streams here
2584
+				$file = tempnam(sys_get_temp_dir(), 'mail');
2585
+				if (false === file_put_contents($file, $body)) {
2586
+					throw new Exception($this->lang('signing') . ' Could not write temp file');
2587
+				}
2588
+				$signed = tempnam(sys_get_temp_dir(), 'signed');
2589
+				//Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2590
+				if (empty($this->sign_extracerts_file)) {
2591
+					$sign = @openssl_pkcs7_sign(
2592
+						$file,
2593
+						$signed,
2594
+						'file://' . realpath($this->sign_cert_file),
2595
+						['file://' . realpath($this->sign_key_file), $this->sign_key_pass],
2596
+						[]
2597
+					);
2598
+				} else {
2599
+					$sign = @openssl_pkcs7_sign(
2600
+						$file,
2601
+						$signed,
2602
+						'file://' . realpath($this->sign_cert_file),
2603
+						['file://' . realpath($this->sign_key_file), $this->sign_key_pass],
2604
+						[],
2605
+						PKCS7_DETACHED,
2606
+						$this->sign_extracerts_file
2607
+					);
2608
+				}
2609
+				@unlink($file);
2610
+				if ($sign) {
2611
+					$body = file_get_contents($signed);
2612
+					@unlink($signed);
2613
+					//The message returned by openssl contains both headers and body, so need to split them up
2614
+					$parts = explode("\n\n", $body, 2);
2615
+					$this->MIMEHeader .= $parts[0] . static::$LE . static::$LE;
2616
+					$body = $parts[1];
2617
+				} else {
2618
+					@unlink($signed);
2619
+					throw new Exception($this->lang('signing') . openssl_error_string());
2620
+				}
2621
+			} catch (Exception $exc) {
2622
+				$body = '';
2623
+				if ($this->exceptions) {
2624
+					throw $exc;
2625
+				}
2626
+			}
2627
+		}
2628
+
2629
+		return $body;
2630
+	}
2631
+
2632
+	/**
2633
+	 * Return the start of a message boundary.
2634
+	 *
2635
+	 * @param string $boundary
2636
+	 * @param string $charSet
2637
+	 * @param string $contentType
2638
+	 * @param string $encoding
2639
+	 *
2640
+	 * @return string
2641
+	 */
2642
+	protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2643
+	{
2644
+		$result = '';
2645
+		if ('' == $charSet) {
2646
+			$charSet = $this->CharSet;
2647
+		}
2648
+		if ('' == $contentType) {
2649
+			$contentType = $this->ContentType;
2650
+		}
2651
+		if ('' == $encoding) {
2652
+			$encoding = $this->Encoding;
2653
+		}
2654
+		$result .= $this->textLine('--' . $boundary);
2655
+		$result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2656
+		$result .= static::$LE;
2657
+		// RFC1341 part 5 says 7bit is assumed if not specified
2658
+		if ('7bit' != $encoding) {
2659
+			$result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2660
+		}
2661
+		$result .= static::$LE;
2662
+
2663
+		return $result;
2664
+	}
2665
+
2666
+	/**
2667
+	 * Return the end of a message boundary.
2668
+	 *
2669
+	 * @param string $boundary
2670
+	 *
2671
+	 * @return string
2672
+	 */
2673
+	protected function endBoundary($boundary)
2674
+	{
2675
+		return static::$LE . '--' . $boundary . '--' . static::$LE;
2676
+	}
2677
+
2678
+	/**
2679
+	 * Set the message type.
2680
+	 * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2681
+	 */
2682
+	protected function setMessageType()
2683
+	{
2684
+		$type = [];
2685
+		if ($this->alternativeExists()) {
2686
+			$type[] = 'alt';
2687
+		}
2688
+		if ($this->inlineImageExists()) {
2689
+			$type[] = 'inline';
2690
+		}
2691
+		if ($this->attachmentExists()) {
2692
+			$type[] = 'attach';
2693
+		}
2694
+		$this->message_type = implode('_', $type);
2695
+		if ('' == $this->message_type) {
2696
+			//The 'plain' message_type refers to the message having a single body element, not that it is plain-text
2697
+			$this->message_type = 'plain';
2698
+		}
2699
+	}
2700
+
2701
+	/**
2702
+	 * Format a header line.
2703
+	 *
2704
+	 * @param string     $name
2705
+	 * @param string|int $value
2706
+	 *
2707
+	 * @return string
2708
+	 */
2709
+	public function headerLine($name, $value)
2710
+	{
2711
+		return $name . ': ' . $value . static::$LE;
2712
+	}
2713
+
2714
+	/**
2715
+	 * Return a formatted mail line.
2716
+	 *
2717
+	 * @param string $value
2718
+	 *
2719
+	 * @return string
2720
+	 */
2721
+	public function textLine($value)
2722
+	{
2723
+		return $value . static::$LE;
2724
+	}
2725
+
2726
+	/**
2727
+	 * Add an attachment from a path on the filesystem.
2728
+	 * Never use a user-supplied path to a file!
2729
+	 * Returns false if the file could not be found or read.
2730
+	 *
2731
+	 * @param string $path        Path to the attachment
2732
+	 * @param string $name        Overrides the attachment name
2733
+	 * @param string $encoding    File encoding (see $Encoding)
2734
+	 * @param string $type        File extension (MIME) type
2735
+	 * @param string $disposition Disposition to use
2736
+	 *
2737
+	 * @throws Exception
2738
+	 *
2739
+	 * @return bool
2740
+	 */
2741
+	public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2742
+	{
2743
+		try {
2744
+			if (!@is_file($path)) {
2745
+				throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
2746
+			}
2747
+
2748
+			// If a MIME type is not specified, try to work it out from the file name
2749
+			if ('' == $type) {
2750
+				$type = static::filenameToType($path);
2751
+			}
2752
+
2753
+			$filename = basename($path);
2754
+			if ('' == $name) {
2755
+				$name = $filename;
2756
+			}
2757
+
2758
+			$this->attachment[] = [
2759
+				0 => $path,
2760
+				1 => $filename,
2761
+				2 => $name,
2762
+				3 => $encoding,
2763
+				4 => $type,
2764
+				5 => false, // isStringAttachment
2765
+				6 => $disposition,
2766
+				7 => $name,
2767
+			];
2768
+		} catch (Exception $exc) {
2769
+			$this->setError($exc->getMessage());
2770
+			$this->edebug($exc->getMessage());
2771
+			if ($this->exceptions) {
2772
+				throw $exc;
2773
+			}
2774
+
2775
+			return false;
2776
+		}
2777
+
2778
+		return true;
2779
+	}
2780
+
2781
+	/**
2782
+	 * Return the array of attachments.
2783
+	 *
2784
+	 * @return array
2785
+	 */
2786
+	public function getAttachments()
2787
+	{
2788
+		return $this->attachment;
2789
+	}
2790
+
2791
+	/**
2792
+	 * Attach all file, string, and binary attachments to the message.
2793
+	 * Returns an empty string on failure.
2794
+	 *
2795
+	 * @param string $disposition_type
2796
+	 * @param string $boundary
2797
+	 *
2798
+	 * @return string
2799
+	 */
2800
+	protected function attachAll($disposition_type, $boundary)
2801
+	{
2802
+		// Return text of body
2803
+		$mime = [];
2804
+		$cidUniq = [];
2805
+		$incl = [];
2806
+
2807
+		// Add all attachments
2808
+		foreach ($this->attachment as $attachment) {
2809
+			// Check if it is a valid disposition_filter
2810
+			if ($attachment[6] == $disposition_type) {
2811
+				// Check for string attachment
2812
+				$string = '';
2813
+				$path = '';
2814
+				$bString = $attachment[5];
2815
+				if ($bString) {
2816
+					$string = $attachment[0];
2817
+				} else {
2818
+					$path = $attachment[0];
2819
+				}
2820
+
2821
+				$inclhash = hash('sha256', serialize($attachment));
2822
+				if (in_array($inclhash, $incl)) {
2823
+					continue;
2824
+				}
2825
+				$incl[] = $inclhash;
2826
+				$name = $attachment[2];
2827
+				$encoding = $attachment[3];
2828
+				$type = $attachment[4];
2829
+				$disposition = $attachment[6];
2830
+				$cid = $attachment[7];
2831
+				if ('inline' == $disposition and array_key_exists($cid, $cidUniq)) {
2832
+					continue;
2833
+				}
2834
+				$cidUniq[$cid] = true;
2835
+
2836
+				$mime[] = sprintf('--%s%s', $boundary, static::$LE);
2837
+				//Only include a filename property if we have one
2838
+				if (!empty($name)) {
2839
+					$mime[] = sprintf(
2840
+						'Content-Type: %s; name="%s"%s',
2841
+						$type,
2842
+						$this->encodeHeader($this->secureHeader($name)),
2843
+						static::$LE
2844
+					);
2845
+				} else {
2846
+					$mime[] = sprintf(
2847
+						'Content-Type: %s%s',
2848
+						$type,
2849
+						static::$LE
2850
+					);
2851
+				}
2852
+				// RFC1341 part 5 says 7bit is assumed if not specified
2853
+				if ('7bit' != $encoding) {
2854
+					$mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, static::$LE);
2855
+				}
2856
+
2857
+				if (!empty($cid)) {
2858
+					$mime[] = sprintf('Content-ID: <%s>%s', $cid, static::$LE);
2859
+				}
2860
+
2861
+				// If a filename contains any of these chars, it should be quoted,
2862
+				// but not otherwise: RFC2183 & RFC2045 5.1
2863
+				// Fixes a warning in IETF's msglint MIME checker
2864
+				// Allow for bypassing the Content-Disposition header totally
2865
+				if (!(empty($disposition))) {
2866
+					$encoded_name = $this->encodeHeader($this->secureHeader($name));
2867
+					if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2868
+						$mime[] = sprintf(
2869
+							'Content-Disposition: %s; filename="%s"%s',
2870
+							$disposition,
2871
+							$encoded_name,
2872
+							static::$LE . static::$LE
2873
+						);
2874
+					} else {
2875
+						if (!empty($encoded_name)) {
2876
+							$mime[] = sprintf(
2877
+								'Content-Disposition: %s; filename=%s%s',
2878
+								$disposition,
2879
+								$encoded_name,
2880
+								static::$LE . static::$LE
2881
+							);
2882
+						} else {
2883
+							$mime[] = sprintf(
2884
+								'Content-Disposition: %s%s',
2885
+								$disposition,
2886
+								static::$LE . static::$LE
2887
+							);
2888
+						}
2889
+					}
2890
+				} else {
2891
+					$mime[] = static::$LE;
2892
+				}
2893
+
2894
+				// Encode as string attachment
2895
+				if ($bString) {
2896
+					$mime[] = $this->encodeString($string, $encoding);
2897
+				} else {
2898
+					$mime[] = $this->encodeFile($path, $encoding);
2899
+				}
2900
+				if ($this->isError()) {
2901
+					return '';
2902
+				}
2903
+				$mime[] = static::$LE;
2904
+			}
2905
+		}
2906
+
2907
+		$mime[] = sprintf('--%s--%s', $boundary, static::$LE);
2908
+
2909
+		return implode('', $mime);
2910
+	}
2911
+
2912
+	/**
2913
+	 * Encode a file attachment in requested format.
2914
+	 * Returns an empty string on failure.
2915
+	 *
2916
+	 * @param string $path     The full path to the file
2917
+	 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2918
+	 *
2919
+	 * @throws Exception
2920
+	 *
2921
+	 * @return string
2922
+	 */
2923
+	protected function encodeFile($path, $encoding = 'base64')
2924
+	{
2925
+		try {
2926
+			if (!file_exists($path)) {
2927
+				throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
2928
+			}
2929
+			$file_buffer = file_get_contents($path);
2930
+			if (false === $file_buffer) {
2931
+				throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
2932
+			}
2933
+			$file_buffer = $this->encodeString($file_buffer, $encoding);
2934
+
2935
+			return $file_buffer;
2936
+		} catch (Exception $exc) {
2937
+			$this->setError($exc->getMessage());
2938
+
2939
+			return '';
2940
+		}
2941
+	}
2942
+
2943
+	/**
2944
+	 * Encode a string in requested format.
2945
+	 * Returns an empty string on failure.
2946
+	 *
2947
+	 * @param string $str      The text to encode
2948
+	 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable
2949
+	 *
2950
+	 * @return string
2951
+	 */
2952
+	public function encodeString($str, $encoding = 'base64')
2953
+	{
2954
+		$encoded = '';
2955
+		switch (strtolower($encoding)) {
2956
+			case 'base64':
2957
+				$encoded = chunk_split(
2958
+					base64_encode($str),
2959
+					static::STD_LINE_LENGTH - strlen(static::$LE),
2960
+					static::$LE
2961
+				);
2962
+				break;
2963
+			case '7bit':
2964
+			case '8bit':
2965
+				$encoded = static::normalizeBreaks($str);
2966
+				// Make sure it ends with a line break
2967
+				if (substr($encoded, -(strlen(static::$LE))) != static::$LE) {
2968
+					$encoded .= static::$LE;
2969
+				}
2970
+				break;
2971
+			case 'binary':
2972
+				$encoded = $str;
2973
+				break;
2974
+			case 'quoted-printable':
2975
+				$encoded = $this->encodeQP($str);
2976
+				break;
2977
+			default:
2978
+				$this->setError($this->lang('encoding') . $encoding);
2979
+				break;
2980
+		}
2981
+
2982
+		return $encoded;
2983
+	}
2984
+
2985
+	/**
2986
+	 * Encode a header value (not including its label) optimally.
2987
+	 * Picks shortest of Q, B, or none. Result includes folding if needed.
2988
+	 * See RFC822 definitions for phrase, comment and text positions.
2989
+	 *
2990
+	 * @param string $str      The header value to encode
2991
+	 * @param string $position What context the string will be used in
2992
+	 *
2993
+	 * @return string
2994
+	 */
2995
+	public function encodeHeader($str, $position = 'text')
2996
+	{
2997
+		$matchcount = 0;
2998
+		switch (strtolower($position)) {
2999
+			case 'phrase':
3000
+				if (!preg_match('/[\200-\377]/', $str)) {
3001
+					// Can't use addslashes as we don't know the value of magic_quotes_sybase
3002
+					$encoded = addcslashes($str, "\0..\37\177\\\"");
3003
+					if (($str == $encoded) and !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
3004
+						return $encoded;
3005
+					}
3006
+
3007
+					return "\"$encoded\"";
3008
+				}
3009
+				$matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
3010
+				break;
3011
+			/* @noinspection PhpMissingBreakStatementInspection */
3012
+			case 'comment':
3013
+				$matchcount = preg_match_all('/[()"]/', $str, $matches);
3014
+			//fallthrough
3015
+			case 'text':
3016
+			default:
3017
+				$matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
3018
+				break;
3019
+		}
3020
+
3021
+		//RFCs specify a maximum line length of 78 chars, however mail() will sometimes
3022
+		//corrupt messages with headers longer than 65 chars. See #818
3023
+		$lengthsub = 'mail' == $this->Mailer ? 13 : 0;
3024
+		$maxlen = static::STD_LINE_LENGTH - $lengthsub;
3025
+		// Try to select the encoding which should produce the shortest output
3026
+		if ($matchcount > strlen($str) / 3) {
3027
+			// More than a third of the content will need encoding, so B encoding will be most efficient
3028
+			$encoding = 'B';
3029
+			//This calculation is:
3030
+			// max line length
3031
+			// - shorten to avoid mail() corruption
3032
+			// - Q/B encoding char overhead ("` =?<charset>?[QB]?<content>?=`")
3033
+			// - charset name length
3034
+			$maxlen = static::STD_LINE_LENGTH - $lengthsub - 8 - strlen($this->CharSet);
3035
+			if ($this->hasMultiBytes($str)) {
3036
+				// Use a custom function which correctly encodes and wraps long
3037
+				// multibyte strings without breaking lines within a character
3038
+				$encoded = $this->base64EncodeWrapMB($str, "\n");
3039
+			} else {
3040
+				$encoded = base64_encode($str);
3041
+				$maxlen -= $maxlen % 4;
3042
+				$encoded = trim(chunk_split($encoded, $maxlen, "\n"));
3043
+			}
3044
+			$encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
3045
+		} elseif ($matchcount > 0) {
3046
+			//1 or more chars need encoding, use Q-encode
3047
+			$encoding = 'Q';
3048
+			//Recalc max line length for Q encoding - see comments on B encode
3049
+			$maxlen = static::STD_LINE_LENGTH - $lengthsub - 8 - strlen($this->CharSet);
3050
+			$encoded = $this->encodeQ($str, $position);
3051
+			$encoded = $this->wrapText($encoded, $maxlen, true);
3052
+			$encoded = str_replace('=' . static::$LE, "\n", trim($encoded));
3053
+			$encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
3054
+		} elseif (strlen($str) > $maxlen) {
3055
+			//No chars need encoding, but line is too long, so fold it
3056
+			$encoded = trim($this->wrapText($str, $maxlen, false));
3057
+			if ($str == $encoded) {
3058
+				//Wrapping nicely didn't work, wrap hard instead
3059
+				$encoded = trim(chunk_split($str, static::STD_LINE_LENGTH, static::$LE));
3060
+			}
3061
+			$encoded = str_replace(static::$LE, "\n", trim($encoded));
3062
+			$encoded = preg_replace('/^(.*)$/m', ' \\1', $encoded);
3063
+		} else {
3064
+			//No reformatting needed
3065
+			return $str;
3066
+		}
3067
+
3068
+		return trim(static::normalizeBreaks($encoded));
3069
+	}
3070
+
3071
+	/**
3072
+	 * Check if a string contains multi-byte characters.
3073
+	 *
3074
+	 * @param string $str multi-byte text to wrap encode
3075
+	 *
3076
+	 * @return bool
3077
+	 */
3078
+	public function hasMultiBytes($str)
3079
+	{
3080
+		if (function_exists('mb_strlen')) {
3081
+			return strlen($str) > mb_strlen($str, $this->CharSet);
3082
+		}
3083
+
3084
+		// Assume no multibytes (we can't handle without mbstring functions anyway)
3085
+		return false;
3086
+	}
3087
+
3088
+	/**
3089
+	 * Does a string contain any 8-bit chars (in any charset)?
3090
+	 *
3091
+	 * @param string $text
3092
+	 *
3093
+	 * @return bool
3094
+	 */
3095
+	public function has8bitChars($text)
3096
+	{
3097
+		return (bool) preg_match('/[\x80-\xFF]/', $text);
3098
+	}
3099
+
3100
+	/**
3101
+	 * Encode and wrap long multibyte strings for mail headers
3102
+	 * without breaking lines within a character.
3103
+	 * Adapted from a function by paravoid.
3104
+	 *
3105
+	 * @see http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
3106
+	 *
3107
+	 * @param string $str       multi-byte text to wrap encode
3108
+	 * @param string $linebreak string to use as linefeed/end-of-line
3109
+	 *
3110
+	 * @return string
3111
+	 */
3112
+	public function base64EncodeWrapMB($str, $linebreak = null)
3113
+	{
3114
+		$start = '=?' . $this->CharSet . '?B?';
3115
+		$end = '?=';
3116
+		$encoded = '';
3117
+		if (null === $linebreak) {
3118
+			$linebreak = static::$LE;
3119
+		}
3120
+
3121
+		$mb_length = mb_strlen($str, $this->CharSet);
3122
+		// Each line must have length <= 75, including $start and $end
3123
+		$length = 75 - strlen($start) - strlen($end);
3124
+		// Average multi-byte ratio
3125
+		$ratio = $mb_length / strlen($str);
3126
+		// Base64 has a 4:3 ratio
3127
+		$avgLength = floor($length * $ratio * .75);
3128
+
3129
+		for ($i = 0; $i < $mb_length; $i += $offset) {
3130
+			$lookBack = 0;
3131
+			do {
3132
+				$offset = $avgLength - $lookBack;
3133
+				$chunk = mb_substr($str, $i, $offset, $this->CharSet);
3134
+				$chunk = base64_encode($chunk);
3135
+				++$lookBack;
3136
+			} while (strlen($chunk) > $length);
3137
+			$encoded .= $chunk . $linebreak;
3138
+		}
3139
+
3140
+		// Chomp the last linefeed
3141
+		return substr($encoded, 0, -strlen($linebreak));
3142
+	}
3143
+
3144
+	/**
3145
+	 * Encode a string in quoted-printable format.
3146
+	 * According to RFC2045 section 6.7.
3147
+	 *
3148
+	 * @param string $string The text to encode
3149
+	 *
3150
+	 * @return string
3151
+	 */
3152
+	public function encodeQP($string)
3153
+	{
3154
+		return static::normalizeBreaks(quoted_printable_encode($string));
3155
+	}
3156
+
3157
+	/**
3158
+	 * Encode a string using Q encoding.
3159
+	 *
3160
+	 * @see http://tools.ietf.org/html/rfc2047#section-4.2
3161
+	 *
3162
+	 * @param string $str      the text to encode
3163
+	 * @param string $position Where the text is going to be used, see the RFC for what that means
3164
+	 *
3165
+	 * @return string
3166
+	 */
3167
+	public function encodeQ($str, $position = 'text')
3168
+	{
3169
+		// There should not be any EOL in the string
3170
+		$pattern = '';
3171
+		$encoded = str_replace(["\r", "\n"], '', $str);
3172
+		switch (strtolower($position)) {
3173
+			case 'phrase':
3174
+				// RFC 2047 section 5.3
3175
+				$pattern = '^A-Za-z0-9!*+\/ -';
3176
+				break;
3177
+			/*
3178 3178
              * RFC 2047 section 5.2.
3179 3179
              * Build $pattern without including delimiters and []
3180 3180
              */
3181
-            /* @noinspection PhpMissingBreakStatementInspection */
3182
-            case 'comment':
3183
-                $pattern = '\(\)"';
3184
-            /* Intentional fall through */
3185
-            case 'text':
3186
-            default:
3187
-                // RFC 2047 section 5.1
3188
-                // Replace every high ascii, control, =, ? and _ characters
3189
-                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
3190
-                break;
3191
-        }
3192
-        $matches = [];
3193
-        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
3194
-            // If the string contains an '=', make sure it's the first thing we replace
3195
-            // so as to avoid double-encoding
3196
-            $eqkey = array_search('=', $matches[0]);
3197
-            if (false !== $eqkey) {
3198
-                unset($matches[0][$eqkey]);
3199
-                array_unshift($matches[0], '=');
3200
-            }
3201
-            foreach (array_unique($matches[0]) as $char) {
3202
-                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
3203
-            }
3204
-        }
3205
-        // Replace spaces with _ (more readable than =20)
3206
-        // RFC 2047 section 4.2(2)
3207
-        return str_replace(' ', '_', $encoded);
3208
-    }
3209
-
3210
-    /**
3211
-     * Add a string or binary attachment (non-filesystem).
3212
-     * This method can be used to attach ascii or binary data,
3213
-     * such as a BLOB record from a database.
3214
-     *
3215
-     * @param string $string      String attachment data
3216
-     * @param string $filename    Name of the attachment
3217
-     * @param string $encoding    File encoding (see $Encoding)
3218
-     * @param string $type        File extension (MIME) type
3219
-     * @param string $disposition Disposition to use
3220
-     */
3221
-    public function addStringAttachment(
3222
-        $string,
3223
-        $filename,
3224
-        $encoding = 'base64',
3225
-        $type = '',
3226
-        $disposition = 'attachment'
3227
-    ) {
3228
-        // If a MIME type is not specified, try to work it out from the file name
3229
-        if ('' == $type) {
3230
-            $type = static::filenameToType($filename);
3231
-        }
3232
-        // Append to $attachment array
3233
-        $this->attachment[] = [
3234
-            0 => $string,
3235
-            1 => $filename,
3236
-            2 => basename($filename),
3237
-            3 => $encoding,
3238
-            4 => $type,
3239
-            5 => true, // isStringAttachment
3240
-            6 => $disposition,
3241
-            7 => 0,
3242
-        ];
3243
-    }
3244
-
3245
-    /**
3246
-     * Add an embedded (inline) attachment from a file.
3247
-     * This can include images, sounds, and just about any other document type.
3248
-     * These differ from 'regular' attachments in that they are intended to be
3249
-     * displayed inline with the message, not just attached for download.
3250
-     * This is used in HTML messages that embed the images
3251
-     * the HTML refers to using the $cid value.
3252
-     * Never use a user-supplied path to a file!
3253
-     *
3254
-     * @param string $path        Path to the attachment
3255
-     * @param string $cid         Content ID of the attachment; Use this to reference
3256
-     *                            the content when using an embedded image in HTML
3257
-     * @param string $name        Overrides the attachment name
3258
-     * @param string $encoding    File encoding (see $Encoding)
3259
-     * @param string $type        File MIME type
3260
-     * @param string $disposition Disposition to use
3261
-     *
3262
-     * @return bool True on successfully adding an attachment
3263
-     */
3264
-    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
3265
-    {
3266
-        if (!@is_file($path)) {
3267
-            $this->setError($this->lang('file_access') . $path);
3268
-
3269
-            return false;
3270
-        }
3271
-
3272
-        // If a MIME type is not specified, try to work it out from the file name
3273
-        if ('' == $type) {
3274
-            $type = static::filenameToType($path);
3275
-        }
3276
-
3277
-        $filename = basename($path);
3278
-        if ('' == $name) {
3279
-            $name = $filename;
3280
-        }
3281
-
3282
-        // Append to $attachment array
3283
-        $this->attachment[] = [
3284
-            0 => $path,
3285
-            1 => $filename,
3286
-            2 => $name,
3287
-            3 => $encoding,
3288
-            4 => $type,
3289
-            5 => false, // isStringAttachment
3290
-            6 => $disposition,
3291
-            7 => $cid,
3292
-        ];
3293
-
3294
-        return true;
3295
-    }
3296
-
3297
-    /**
3298
-     * Add an embedded stringified attachment.
3299
-     * This can include images, sounds, and just about any other document type.
3300
-     * Be sure to set the $type to an image type for images:
3301
-     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
3302
-     *
3303
-     * @param string $string      The attachment binary data
3304
-     * @param string $cid         Content ID of the attachment; Use this to reference
3305
-     *                            the content when using an embedded image in HTML
3306
-     * @param string $name
3307
-     * @param string $encoding    File encoding (see $Encoding)
3308
-     * @param string $type        MIME type
3309
-     * @param string $disposition Disposition to use
3310
-     *
3311
-     * @return bool True on successfully adding an attachment
3312
-     */
3313
-    public function addStringEmbeddedImage(
3314
-        $string,
3315
-        $cid,
3316
-        $name = '',
3317
-        $encoding = 'base64',
3318
-        $type = '',
3319
-        $disposition = 'inline'
3320
-    ) {
3321
-        // If a MIME type is not specified, try to work it out from the name
3322
-        if ('' == $type and !empty($name)) {
3323
-            $type = static::filenameToType($name);
3324
-        }
3325
-
3326
-        // Append to $attachment array
3327
-        $this->attachment[] = [
3328
-            0 => $string,
3329
-            1 => $name,
3330
-            2 => $name,
3331
-            3 => $encoding,
3332
-            4 => $type,
3333
-            5 => true, // isStringAttachment
3334
-            6 => $disposition,
3335
-            7 => $cid,
3336
-        ];
3337
-
3338
-        return true;
3339
-    }
3340
-
3341
-    /**
3342
-     * Check if an embedded attachment is present with this cid.
3343
-     *
3344
-     * @param string $cid
3345
-     *
3346
-     * @return bool
3347
-     */
3348
-    protected function cidExists($cid)
3349
-    {
3350
-        foreach ($this->attachment as $attachment) {
3351
-            if ('inline' == $attachment[6] and $cid == $attachment[7]) {
3352
-                return true;
3353
-            }
3354
-        }
3355
-
3356
-        return false;
3357
-    }
3358
-
3359
-    /**
3360
-     * Check if an inline attachment is present.
3361
-     *
3362
-     * @return bool
3363
-     */
3364
-    public function inlineImageExists()
3365
-    {
3366
-        foreach ($this->attachment as $attachment) {
3367
-            if ($attachment[6] == 'inline') {
3368
-                return true;
3369
-            }
3370
-        }
3371
-
3372
-        return false;
3373
-    }
3374
-
3375
-    /**
3376
-     * Check if an attachment (non-inline) is present.
3377
-     *
3378
-     * @return bool
3379
-     */
3380
-    public function attachmentExists()
3381
-    {
3382
-        foreach ($this->attachment as $attachment) {
3383
-            if ($attachment[6] == 'attachment') {
3384
-                return true;
3385
-            }
3386
-        }
3387
-
3388
-        return false;
3389
-    }
3390
-
3391
-    /**
3392
-     * Check if this message has an alternative body set.
3393
-     *
3394
-     * @return bool
3395
-     */
3396
-    public function alternativeExists()
3397
-    {
3398
-        return !empty($this->AltBody);
3399
-    }
3400
-
3401
-    /**
3402
-     * Clear queued addresses of given kind.
3403
-     *
3404
-     * @param string $kind 'to', 'cc', or 'bcc'
3405
-     */
3406
-    public function clearQueuedAddresses($kind)
3407
-    {
3408
-        $this->RecipientsQueue = array_filter(
3409
-            $this->RecipientsQueue,
3410
-            function ($params) use ($kind) {
3411
-                return $params[0] != $kind;
3412
-            }
3413
-        );
3414
-    }
3415
-
3416
-    /**
3417
-     * Clear all To recipients.
3418
-     */
3419
-    public function clearAddresses()
3420
-    {
3421
-        foreach ($this->to as $to) {
3422
-            unset($this->all_recipients[strtolower($to[0])]);
3423
-        }
3424
-        $this->to = [];
3425
-        $this->clearQueuedAddresses('to');
3426
-    }
3427
-
3428
-    /**
3429
-     * Clear all CC recipients.
3430
-     */
3431
-    public function clearCCs()
3432
-    {
3433
-        foreach ($this->cc as $cc) {
3434
-            unset($this->all_recipients[strtolower($cc[0])]);
3435
-        }
3436
-        $this->cc = [];
3437
-        $this->clearQueuedAddresses('cc');
3438
-    }
3439
-
3440
-    /**
3441
-     * Clear all BCC recipients.
3442
-     */
3443
-    public function clearBCCs()
3444
-    {
3445
-        foreach ($this->bcc as $bcc) {
3446
-            unset($this->all_recipients[strtolower($bcc[0])]);
3447
-        }
3448
-        $this->bcc = [];
3449
-        $this->clearQueuedAddresses('bcc');
3450
-    }
3451
-
3452
-    /**
3453
-     * Clear all ReplyTo recipients.
3454
-     */
3455
-    public function clearReplyTos()
3456
-    {
3457
-        $this->ReplyTo = [];
3458
-        $this->ReplyToQueue = [];
3459
-    }
3460
-
3461
-    /**
3462
-     * Clear all recipient types.
3463
-     */
3464
-    public function clearAllRecipients()
3465
-    {
3466
-        $this->to = [];
3467
-        $this->cc = [];
3468
-        $this->bcc = [];
3469
-        $this->all_recipients = [];
3470
-        $this->RecipientsQueue = [];
3471
-    }
3472
-
3473
-    /**
3474
-     * Clear all filesystem, string, and binary attachments.
3475
-     */
3476
-    public function clearAttachments()
3477
-    {
3478
-        $this->attachment = [];
3479
-    }
3480
-
3481
-    /**
3482
-     * Clear all custom headers.
3483
-     */
3484
-    public function clearCustomHeaders()
3485
-    {
3486
-        $this->CustomHeader = [];
3487
-    }
3488
-
3489
-    /**
3490
-     * Add an error message to the error container.
3491
-     *
3492
-     * @param string $msg
3493
-     */
3494
-    protected function setError($msg)
3495
-    {
3496
-        ++$this->error_count;
3497
-        if ('smtp' == $this->Mailer and null !== $this->smtp) {
3498
-            $lasterror = $this->smtp->getError();
3499
-            if (!empty($lasterror['error'])) {
3500
-                $msg .= $this->lang('smtp_error') . $lasterror['error'];
3501
-                if (!empty($lasterror['detail'])) {
3502
-                    $msg .= ' Detail: ' . $lasterror['detail'];
3503
-                }
3504
-                if (!empty($lasterror['smtp_code'])) {
3505
-                    $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
3506
-                }
3507
-                if (!empty($lasterror['smtp_code_ex'])) {
3508
-                    $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3509
-                }
3510
-            }
3511
-        }
3512
-        $this->ErrorInfo = $msg;
3513
-    }
3514
-
3515
-    /**
3516
-     * Return an RFC 822 formatted date.
3517
-     *
3518
-     * @return string
3519
-     */
3520
-    public static function rfcDate()
3521
-    {
3522
-        // Set the time zone to whatever the default is to avoid 500 errors
3523
-        // Will default to UTC if it's not set properly in php.ini
3524
-        date_default_timezone_set(@date_default_timezone_get());
3525
-
3526
-        return date('D, j M Y H:i:s O');
3527
-    }
3528
-
3529
-    /**
3530
-     * Get the server hostname.
3531
-     * Returns 'localhost.localdomain' if unknown.
3532
-     *
3533
-     * @return string
3534
-     */
3535
-    protected function serverHostname()
3536
-    {
3537
-        $result = '';
3538
-        if (!empty($this->Hostname)) {
3539
-            $result = $this->Hostname;
3540
-        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER)) {
3541
-            $result = $_SERVER['SERVER_NAME'];
3542
-        } elseif (function_exists('gethostname') and gethostname() !== false) {
3543
-            $result = gethostname();
3544
-        } elseif (php_uname('n') !== false) {
3545
-            $result = php_uname('n');
3546
-        }
3547
-        if (!static::isValidHost($result)) {
3548
-            return 'localhost.localdomain';
3549
-        }
3550
-
3551
-        return $result;
3552
-    }
3553
-
3554
-    /**
3555
-     * Validate whether a string contains a valid value to use as a hostname or IP address.
3556
-     * IPv6 addresses must include [], e.g. `[::1]`, not just `::1`.
3557
-     *
3558
-     * @param string $host The host name or IP address to check
3559
-     *
3560
-     * @return bool
3561
-     */
3562
-    public static function isValidHost($host)
3563
-    {
3564
-        //Simple syntax limits
3565
-        if (empty($host)
3566
-            or !is_string($host)
3567
-            or strlen($host) > 256
3568
-        ) {
3569
-            return false;
3570
-        }
3571
-        //Looks like a bracketed IPv6 address
3572
-        if (trim($host, '[]') != $host) {
3573
-            return (bool) filter_var(trim($host, '[]'), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
3574
-        }
3575
-        //If removing all the dots results in a numeric string, it must be an IPv4 address.
3576
-        //Need to check this first because otherwise things like `999.0.0.0` are considered valid host names
3577
-        if (is_numeric(str_replace('.', '', $host))) {
3578
-            //Is it a valid IPv4 address?
3579
-            return (bool) filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
3580
-        }
3581
-        if (filter_var('http://' . $host, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED)) {
3582
-            //Is it a syntactically valid hostname?
3583
-            return true;
3584
-        }
3585
-
3586
-        return false;
3587
-    }
3588
-
3589
-    /**
3590
-     * Get an error message in the current language.
3591
-     *
3592
-     * @param string $key
3593
-     *
3594
-     * @return string
3595
-     */
3596
-    protected function lang($key)
3597
-    {
3598
-        if (count($this->language) < 1) {
3599
-            $this->setLanguage('en'); // set the default language
3600
-        }
3601
-
3602
-        if (array_key_exists($key, $this->language)) {
3603
-            if ('smtp_connect_failed' == $key) {
3604
-                //Include a link to troubleshooting docs on SMTP connection failure
3605
-                //this is by far the biggest cause of support questions
3606
-                //but it's usually not PHPMailer's fault.
3607
-                return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3608
-            }
3609
-
3610
-            return $this->language[$key];
3611
-        }
3612
-
3613
-        //Return the key as a fallback
3614
-        return $key;
3615
-    }
3616
-
3617
-    /**
3618
-     * Check if an error occurred.
3619
-     *
3620
-     * @return bool True if an error did occur
3621
-     */
3622
-    public function isError()
3623
-    {
3624
-        return $this->error_count > 0;
3625
-    }
3626
-
3627
-    /**
3628
-     * Add a custom header.
3629
-     * $name value can be overloaded to contain
3630
-     * both header name and value (name:value).
3631
-     *
3632
-     * @param string      $name  Custom header name
3633
-     * @param string|null $value Header value
3634
-     */
3635
-    public function addCustomHeader($name, $value = null)
3636
-    {
3637
-        if (null === $value) {
3638
-            // Value passed in as name:value
3639
-            $this->CustomHeader[] = explode(':', $name, 2);
3640
-        } else {
3641
-            $this->CustomHeader[] = [$name, $value];
3642
-        }
3643
-    }
3644
-
3645
-    /**
3646
-     * Returns all custom headers.
3647
-     *
3648
-     * @return array
3649
-     */
3650
-    public function getCustomHeaders()
3651
-    {
3652
-        return $this->CustomHeader;
3653
-    }
3654
-
3655
-    /**
3656
-     * Create a message body from an HTML string.
3657
-     * Automatically inlines images and creates a plain-text version by converting the HTML,
3658
-     * overwriting any existing values in Body and AltBody.
3659
-     * Do not source $message content from user input!
3660
-     * $basedir is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty
3661
-     * will look for an image file in $basedir/images/a.png and convert it to inline.
3662
-     * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email)
3663
-     * Converts data-uri images into embedded attachments.
3664
-     * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
3665
-     *
3666
-     * @param string        $message  HTML message string
3667
-     * @param string        $basedir  Absolute path to a base directory to prepend to relative paths to images
3668
-     * @param bool|callable $advanced Whether to use the internal HTML to text converter
3669
-     *                                or your own custom converter @see PHPMailer::html2text()
3670
-     *
3671
-     * @return string $message The transformed message Body
3672
-     */
3673
-    public function msgHTML($message, $basedir = '', $advanced = false)
3674
-    {
3675
-        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3676
-        if (array_key_exists(2, $images)) {
3677
-            if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3678
-                // Ensure $basedir has a trailing /
3679
-                $basedir .= '/';
3680
-            }
3681
-            foreach ($images[2] as $imgindex => $url) {
3682
-                // Convert data URIs into embedded images
3683
-                //e.g. "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
3684
-                if (preg_match('#^data:(image/(?:jpe?g|gif|png));?(base64)?,(.+)#', $url, $match)) {
3685
-                    if (count($match) == 4 and 'base64' == $match[2]) {
3686
-                        $data = base64_decode($match[3]);
3687
-                    } elseif ('' == $match[2]) {
3688
-                        $data = rawurldecode($match[3]);
3689
-                    } else {
3690
-                        //Not recognised so leave it alone
3691
-                        continue;
3692
-                    }
3693
-                    //Hash the decoded data, not the URL so that the same data-URI image used in multiple places
3694
-                    //will only be embedded once, even if it used a different encoding
3695
-                    $cid = hash('sha256', $data) . '@phpmailer.0'; // RFC2392 S 2
3696
-
3697
-                    if (!$this->cidExists($cid)) {
3698
-                        $this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1]);
3699
-                    }
3700
-                    $message = str_replace(
3701
-                        $images[0][$imgindex],
3702
-                        $images[1][$imgindex] . '="cid:' . $cid . '"',
3703
-                        $message
3704
-                    );
3705
-                    continue;
3706
-                }
3707
-                if (// Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
3708
-                    !empty($basedir)
3709
-                    // Ignore URLs containing parent dir traversal (..)
3710
-                    and (strpos($url, '..') === false)
3711
-                    // Do not change urls that are already inline images
3712
-                    and substr($url, 0, 4) !== 'cid:'
3713
-                    // Do not change absolute URLs, including anonymous protocol
3714
-                    and !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
3715
-                ) {
3716
-                    $filename = basename($url);
3717
-                    $directory = dirname($url);
3718
-                    if ('.' == $directory) {
3719
-                        $directory = '';
3720
-                    }
3721
-                    $cid = hash('sha256', $url) . '@phpmailer.0'; // RFC2392 S 2
3722
-                    if (strlen($basedir) > 1 and substr($basedir, -1) != '/') {
3723
-                        $basedir .= '/';
3724
-                    }
3725
-                    if (strlen($directory) > 1 and substr($directory, -1) != '/') {
3726
-                        $directory .= '/';
3727
-                    }
3728
-                    if ($this->addEmbeddedImage(
3729
-                        $basedir . $directory . $filename,
3730
-                        $cid,
3731
-                        $filename,
3732
-                        'base64',
3733
-                        static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION))
3734
-                    )
3735
-                    ) {
3736
-                        $message = preg_replace(
3737
-                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3738
-                            $images[1][$imgindex] . '="cid:' . $cid . '"',
3739
-                            $message
3740
-                        );
3741
-                    }
3742
-                }
3743
-            }
3744
-        }
3745
-        $this->isHTML(true);
3746
-        // Convert all message body line breaks to LE, makes quoted-printable encoding work much better
3747
-        $this->Body = static::normalizeBreaks($message);
3748
-        $this->AltBody = static::normalizeBreaks($this->html2text($message, $advanced));
3749
-        if (!$this->alternativeExists()) {
3750
-            $this->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.'
3751
-                . static::$LE;
3752
-        }
3753
-
3754
-        return $this->Body;
3755
-    }
3756
-
3757
-    /**
3758
-     * Convert an HTML string into plain text.
3759
-     * This is used by msgHTML().
3760
-     * Note - older versions of this function used a bundled advanced converter
3761
-     * which was removed for license reasons in #232.
3762
-     * Example usage:
3763
-     *
3764
-     * ```php
3765
-     * // Use default conversion
3766
-     * $plain = $mail->html2text($html);
3767
-     * // Use your own custom converter
3768
-     * $plain = $mail->html2text($html, function($html) {
3769
-     *     $converter = new MyHtml2text($html);
3770
-     *     return $converter->get_text();
3771
-     * });
3772
-     * ```
3773
-     *
3774
-     * @param string        $html     The HTML text to convert
3775
-     * @param bool|callable $advanced Any boolean value to use the internal converter,
3776
-     *                                or provide your own callable for custom conversion
3777
-     *
3778
-     * @return string
3779
-     */
3780
-    public function html2text($html, $advanced = false)
3781
-    {
3782
-        if (is_callable($advanced)) {
3783
-            return call_user_func($advanced, $html);
3784
-        }
3785
-
3786
-        return html_entity_decode(
3787
-            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3788
-            ENT_QUOTES,
3789
-            $this->CharSet
3790
-        );
3791
-    }
3792
-
3793
-    /**
3794
-     * Get the MIME type for a file extension.
3795
-     *
3796
-     * @param string $ext File extension
3797
-     *
3798
-     * @return string MIME type of file
3799
-     */
3800
-    public static function _mime_types($ext = '')
3801
-    {
3802
-        $mimes = [
3803
-            'xl' => 'application/excel',
3804
-            'js' => 'application/javascript',
3805
-            'hqx' => 'application/mac-binhex40',
3806
-            'cpt' => 'application/mac-compactpro',
3807
-            'bin' => 'application/macbinary',
3808
-            'doc' => 'application/msword',
3809
-            'word' => 'application/msword',
3810
-            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3811
-            'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3812
-            'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3813
-            'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3814
-            'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3815
-            'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3816
-            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3817
-            'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3818
-            'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
3819
-            'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3820
-            'class' => 'application/octet-stream',
3821
-            'dll' => 'application/octet-stream',
3822
-            'dms' => 'application/octet-stream',
3823
-            'exe' => 'application/octet-stream',
3824
-            'lha' => 'application/octet-stream',
3825
-            'lzh' => 'application/octet-stream',
3826
-            'psd' => 'application/octet-stream',
3827
-            'sea' => 'application/octet-stream',
3828
-            'so' => 'application/octet-stream',
3829
-            'oda' => 'application/oda',
3830
-            'pdf' => 'application/pdf',
3831
-            'ai' => 'application/postscript',
3832
-            'eps' => 'application/postscript',
3833
-            'ps' => 'application/postscript',
3834
-            'smi' => 'application/smil',
3835
-            'smil' => 'application/smil',
3836
-            'mif' => 'application/vnd.mif',
3837
-            'xls' => 'application/vnd.ms-excel',
3838
-            'ppt' => 'application/vnd.ms-powerpoint',
3839
-            'wbxml' => 'application/vnd.wap.wbxml',
3840
-            'wmlc' => 'application/vnd.wap.wmlc',
3841
-            'dcr' => 'application/x-director',
3842
-            'dir' => 'application/x-director',
3843
-            'dxr' => 'application/x-director',
3844
-            'dvi' => 'application/x-dvi',
3845
-            'gtar' => 'application/x-gtar',
3846
-            'php3' => 'application/x-httpd-php',
3847
-            'php4' => 'application/x-httpd-php',
3848
-            'php' => 'application/x-httpd-php',
3849
-            'phtml' => 'application/x-httpd-php',
3850
-            'phps' => 'application/x-httpd-php-source',
3851
-            'swf' => 'application/x-shockwave-flash',
3852
-            'sit' => 'application/x-stuffit',
3853
-            'tar' => 'application/x-tar',
3854
-            'tgz' => 'application/x-tar',
3855
-            'xht' => 'application/xhtml+xml',
3856
-            'xhtml' => 'application/xhtml+xml',
3857
-            'zip' => 'application/zip',
3858
-            'mid' => 'audio/midi',
3859
-            'midi' => 'audio/midi',
3860
-            'mp2' => 'audio/mpeg',
3861
-            'mp3' => 'audio/mpeg',
3862
-            'mpga' => 'audio/mpeg',
3863
-            'aif' => 'audio/x-aiff',
3864
-            'aifc' => 'audio/x-aiff',
3865
-            'aiff' => 'audio/x-aiff',
3866
-            'ram' => 'audio/x-pn-realaudio',
3867
-            'rm' => 'audio/x-pn-realaudio',
3868
-            'rpm' => 'audio/x-pn-realaudio-plugin',
3869
-            'ra' => 'audio/x-realaudio',
3870
-            'wav' => 'audio/x-wav',
3871
-            'bmp' => 'image/bmp',
3872
-            'gif' => 'image/gif',
3873
-            'jpeg' => 'image/jpeg',
3874
-            'jpe' => 'image/jpeg',
3875
-            'jpg' => 'image/jpeg',
3876
-            'png' => 'image/png',
3877
-            'tiff' => 'image/tiff',
3878
-            'tif' => 'image/tiff',
3879
-            'eml' => 'message/rfc822',
3880
-            'css' => 'text/css',
3881
-            'html' => 'text/html',
3882
-            'htm' => 'text/html',
3883
-            'shtml' => 'text/html',
3884
-            'log' => 'text/plain',
3885
-            'text' => 'text/plain',
3886
-            'txt' => 'text/plain',
3887
-            'rtx' => 'text/richtext',
3888
-            'rtf' => 'text/rtf',
3889
-            'vcf' => 'text/vcard',
3890
-            'vcard' => 'text/vcard',
3891
-            'ics' => 'text/calendar',
3892
-            'xml' => 'text/xml',
3893
-            'xsl' => 'text/xml',
3894
-            'mpeg' => 'video/mpeg',
3895
-            'mpe' => 'video/mpeg',
3896
-            'mpg' => 'video/mpeg',
3897
-            'mov' => 'video/quicktime',
3898
-            'qt' => 'video/quicktime',
3899
-            'rv' => 'video/vnd.rn-realvideo',
3900
-            'avi' => 'video/x-msvideo',
3901
-            'movie' => 'video/x-sgi-movie',
3902
-        ];
3903
-        if (array_key_exists(strtolower($ext), $mimes)) {
3904
-            return $mimes[strtolower($ext)];
3905
-        }
3906
-
3907
-        return 'application/octet-stream';
3908
-    }
3909
-
3910
-    /**
3911
-     * Map a file name to a MIME type.
3912
-     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3913
-     *
3914
-     * @param string $filename A file name or full path, does not need to exist as a file
3915
-     *
3916
-     * @return string
3917
-     */
3918
-    public static function filenameToType($filename)
3919
-    {
3920
-        // In case the path is a URL, strip any query string before getting extension
3921
-        $qpos = strpos($filename, '?');
3922
-        if (false !== $qpos) {
3923
-            $filename = substr($filename, 0, $qpos);
3924
-        }
3925
-        $ext = static::mb_pathinfo($filename, PATHINFO_EXTENSION);
3926
-
3927
-        return static::_mime_types($ext);
3928
-    }
3929
-
3930
-    /**
3931
-     * Multi-byte-safe pathinfo replacement.
3932
-     * Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe.
3933
-     *
3934
-     * @see    http://www.php.net/manual/en/function.pathinfo.php#107461
3935
-     *
3936
-     * @param string     $path    A filename or path, does not need to exist as a file
3937
-     * @param int|string $options Either a PATHINFO_* constant,
3938
-     *                            or a string name to return only the specified piece
3939
-     *
3940
-     * @return string|array
3941
-     */
3942
-    public static function mb_pathinfo($path, $options = null)
3943
-    {
3944
-        $ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''];
3945
-        $pathinfo = [];
3946
-        if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$#im', $path, $pathinfo)) {
3947
-            if (array_key_exists(1, $pathinfo)) {
3948
-                $ret['dirname'] = $pathinfo[1];
3949
-            }
3950
-            if (array_key_exists(2, $pathinfo)) {
3951
-                $ret['basename'] = $pathinfo[2];
3952
-            }
3953
-            if (array_key_exists(5, $pathinfo)) {
3954
-                $ret['extension'] = $pathinfo[5];
3955
-            }
3956
-            if (array_key_exists(3, $pathinfo)) {
3957
-                $ret['filename'] = $pathinfo[3];
3958
-            }
3959
-        }
3960
-        switch ($options) {
3961
-            case PATHINFO_DIRNAME:
3962
-            case 'dirname':
3963
-                return $ret['dirname'];
3964
-            case PATHINFO_BASENAME:
3965
-            case 'basename':
3966
-                return $ret['basename'];
3967
-            case PATHINFO_EXTENSION:
3968
-            case 'extension':
3969
-                return $ret['extension'];
3970
-            case PATHINFO_FILENAME:
3971
-            case 'filename':
3972
-                return $ret['filename'];
3973
-            default:
3974
-                return $ret;
3975
-        }
3976
-    }
3977
-
3978
-    /**
3979
-     * Set or reset instance properties.
3980
-     * You should avoid this function - it's more verbose, less efficient, more error-prone and
3981
-     * harder to debug than setting properties directly.
3982
-     * Usage Example:
3983
-     * `$mail->set('SMTPSecure', 'tls');`
3984
-     *   is the same as:
3985
-     * `$mail->SMTPSecure = 'tls';`.
3986
-     *
3987
-     * @param string $name  The property name to set
3988
-     * @param mixed  $value The value to set the property to
3989
-     *
3990
-     * @return bool
3991
-     */
3992
-    public function set($name, $value = '')
3993
-    {
3994
-        if (property_exists($this, $name)) {
3995
-            $this->$name = $value;
3996
-
3997
-            return true;
3998
-        }
3999
-        $this->setError($this->lang('variable_set') . $name);
4000
-
4001
-        return false;
4002
-    }
4003
-
4004
-    /**
4005
-     * Strip newlines to prevent header injection.
4006
-     *
4007
-     * @param string $str
4008
-     *
4009
-     * @return string
4010
-     */
4011
-    public function secureHeader($str)
4012
-    {
4013
-        return trim(str_replace(["\r", "\n"], '', $str));
4014
-    }
4015
-
4016
-    /**
4017
-     * Normalize line breaks in a string.
4018
-     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
4019
-     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
4020
-     *
4021
-     * @param string $text
4022
-     * @param string $breaktype What kind of line break to use; defaults to static::$LE
4023
-     *
4024
-     * @return string
4025
-     */
4026
-    public static function normalizeBreaks($text, $breaktype = null)
4027
-    {
4028
-        if (null === $breaktype) {
4029
-            $breaktype = static::$LE;
4030
-        }
4031
-        // Normalise to \n
4032
-        $text = str_replace(["\r\n", "\r"], "\n", $text);
4033
-        // Now convert LE as needed
4034
-        if ("\n" !== static::$LE) {
4035
-            $text = str_replace("\n", $breaktype, $text);
4036
-        }
4037
-
4038
-        return $text;
4039
-    }
4040
-
4041
-    /**
4042
-     * Return the current line break format string.
4043
-     *
4044
-     * @return string
4045
-     */
4046
-    public static function getLE()
4047
-    {
4048
-        return static::$LE;
4049
-    }
4050
-
4051
-    /**
4052
-     * Set the line break format string, e.g. "\r\n".
4053
-     *
4054
-     * @param string $le
4055
-     */
4056
-    protected static function setLE($le)
4057
-    {
4058
-        static::$LE = $le;
4059
-    }
4060
-
4061
-    /**
4062
-     * Set the public and private key files and password for S/MIME signing.
4063
-     *
4064
-     * @param string $cert_filename
4065
-     * @param string $key_filename
4066
-     * @param string $key_pass            Password for private key
4067
-     * @param string $extracerts_filename Optional path to chain certificate
4068
-     */
4069
-    public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
4070
-    {
4071
-        $this->sign_cert_file = $cert_filename;
4072
-        $this->sign_key_file = $key_filename;
4073
-        $this->sign_key_pass = $key_pass;
4074
-        $this->sign_extracerts_file = $extracerts_filename;
4075
-    }
4076
-
4077
-    /**
4078
-     * Quoted-Printable-encode a DKIM header.
4079
-     *
4080
-     * @param string $txt
4081
-     *
4082
-     * @return string
4083
-     */
4084
-    public function DKIM_QP($txt)
4085
-    {
4086
-        $line = '';
4087
-        $len = strlen($txt);
4088
-        for ($i = 0; $i < $len; ++$i) {
4089
-            $ord = ord($txt[$i]);
4090
-            if (((0x21 <= $ord) and ($ord <= 0x3A)) or $ord == 0x3C or ((0x3E <= $ord) and ($ord <= 0x7E))) {
4091
-                $line .= $txt[$i];
4092
-            } else {
4093
-                $line .= '=' . sprintf('%02X', $ord);
4094
-            }
4095
-        }
4096
-
4097
-        return $line;
4098
-    }
4099
-
4100
-    /**
4101
-     * Generate a DKIM signature.
4102
-     *
4103
-     * @param string $signHeader
4104
-     *
4105
-     * @throws Exception
4106
-     *
4107
-     * @return string The DKIM signature value
4108
-     */
4109
-    public function DKIM_Sign($signHeader)
4110
-    {
4111
-        if (!defined('PKCS7_TEXT')) {
4112
-            if ($this->exceptions) {
4113
-                throw new Exception($this->lang('extension_missing') . 'openssl');
4114
-            }
4115
-
4116
-            return '';
4117
-        }
4118
-        $privKeyStr = !empty($this->DKIM_private_string) ?
4119
-            $this->DKIM_private_string :
4120
-            file_get_contents($this->DKIM_private);
4121
-        if ('' != $this->DKIM_passphrase) {
4122
-            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
4123
-        } else {
4124
-            $privKey = openssl_pkey_get_private($privKeyStr);
4125
-        }
4126
-        if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
4127
-            openssl_pkey_free($privKey);
4128
-
4129
-            return base64_encode($signature);
4130
-        }
4131
-        openssl_pkey_free($privKey);
4132
-
4133
-        return '';
4134
-    }
4135
-
4136
-    /**
4137
-     * Generate a DKIM canonicalization header.
4138
-     * Uses the 'relaxed' algorithm from RFC6376 section 3.4.2.
4139
-     *
4140
-     * @see    https://tools.ietf.org/html/rfc6376#section-3.4.2
4141
-     *
4142
-     * @param string $signHeader Header
4143
-     *
4144
-     * @return string
4145
-     */
4146
-    public function DKIM_HeaderC($signHeader)
4147
-    {
4148
-        //Unfold all header continuation lines
4149
-        //Also collapses folded whitespace.
4150
-        //Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]`
4151
-        //@see https://tools.ietf.org/html/rfc5322#section-2.2
4152
-        //That means this may break if you do something daft like put vertical tabs in your headers.
4153
-        $signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader);
4154
-        $lines = explode("\r\n", $signHeader);
4155
-        foreach ($lines as $key => $line) {
4156
-            //If the header is missing a :, skip it as it's invalid
4157
-            //This is likely to happen because the explode() above will also split
4158
-            //on the trailing LE, leaving an empty line
4159
-            if (strpos($line, ':') === false) {
4160
-                continue;
4161
-            }
4162
-            list($heading, $value) = explode(':', $line, 2);
4163
-            //Lower-case header name
4164
-            $heading = strtolower($heading);
4165
-            //Collapse white space within the value
4166
-            $value = preg_replace('/[ \t]{2,}/', ' ', $value);
4167
-            //RFC6376 is slightly unclear here - it says to delete space at the *end* of each value
4168
-            //But then says to delete space before and after the colon.
4169
-            //Net result is the same as trimming both ends of the value.
4170
-            //by elimination, the same applies to the field name
4171
-            $lines[$key] = trim($heading, " \t") . ':' . trim($value, " \t");
4172
-        }
4173
-
4174
-        return implode(static::$LE, $lines);
4175
-    }
4176
-
4177
-    /**
4178
-     * Generate a DKIM canonicalization body.
4179
-     * Uses the 'simple' algorithm from RFC6376 section 3.4.3.
4180
-     *
4181
-     * @see    https://tools.ietf.org/html/rfc6376#section-3.4.3
4182
-     *
4183
-     * @param string $body Message Body
4184
-     *
4185
-     * @return string
4186
-     */
4187
-    public function DKIM_BodyC($body)
4188
-    {
4189
-        if (empty($body)) {
4190
-            return static::$LE;
4191
-        }
4192
-        // Normalize line endings
4193
-        $body = static::normalizeBreaks($body);
4194
-
4195
-        //Reduce multiple trailing line breaks to a single one
4196
-        return rtrim($body, "\r\n") . static::$LE;
4197
-    }
4198
-
4199
-    /**
4200
-     * Create the DKIM header and body in a new message header.
4201
-     *
4202
-     * @param string $headers_line Header lines
4203
-     * @param string $subject      Subject
4204
-     * @param string $body         Body
4205
-     *
4206
-     * @return string
4207
-     */
4208
-    public function DKIM_Add($headers_line, $subject, $body)
4209
-    {
4210
-        $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
4211
-        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
4212
-        $DKIMquery = 'dns/txt'; // Query method
4213
-        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
4214
-        $subject_header = "Subject: $subject";
4215
-        $headers = explode(static::$LE, $headers_line);
4216
-        $from_header = '';
4217
-        $to_header = '';
4218
-        $date_header = '';
4219
-        $current = '';
4220
-        foreach ($headers as $header) {
4221
-            if (strpos($header, 'From:') === 0) {
4222
-                $from_header = $header;
4223
-                $current = 'from_header';
4224
-            } elseif (strpos($header, 'To:') === 0) {
4225
-                $to_header = $header;
4226
-                $current = 'to_header';
4227
-            } elseif (strpos($header, 'Date:') === 0) {
4228
-                $date_header = $header;
4229
-                $current = 'date_header';
4230
-            } else {
4231
-                if (!empty($$current) and strpos($header, ' =?') === 0) {
4232
-                    $$current .= $header;
4233
-                } else {
4234
-                    $current = '';
4235
-                }
4236
-            }
4237
-        }
4238
-        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
4239
-        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
4240
-        $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
4241
-        $subject = str_replace(
4242
-            '|',
4243
-            '=7C',
4244
-            $this->DKIM_QP($subject_header)
4245
-        ); // Copied header fields (dkim-quoted-printable)
4246
-        $body = $this->DKIM_BodyC($body);
4247
-        $DKIMlen = strlen($body); // Length of body
4248
-        $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
4249
-        if ('' == $this->DKIM_identity) {
4250
-            $ident = '';
4251
-        } else {
4252
-            $ident = ' i=' . $this->DKIM_identity . ';';
4253
-        }
4254
-        $dkimhdrs = 'DKIM-Signature: v=1; a=' .
4255
-            $DKIMsignatureType . '; q=' .
4256
-            $DKIMquery . '; l=' .
4257
-            $DKIMlen . '; s=' .
4258
-            $this->DKIM_selector .
4259
-            ";\r\n" .
4260
-            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
4261
-            "\th=From:To:Date:Subject;\r\n" .
4262
-            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
4263
-            "\tz=$from\r\n" .
4264
-            "\t|$to\r\n" .
4265
-            "\t|$date\r\n" .
4266
-            "\t|$subject;\r\n" .
4267
-            "\tbh=" . $DKIMb64 . ";\r\n" .
4268
-            "\tb=";
4269
-        $toSign = $this->DKIM_HeaderC(
4270
-            $from_header . "\r\n" .
4271
-            $to_header . "\r\n" .
4272
-            $date_header . "\r\n" .
4273
-            $subject_header . "\r\n" .
4274
-            $dkimhdrs
4275
-        );
4276
-        $signed = $this->DKIM_Sign($toSign);
4277
-
4278
-        return static::normalizeBreaks($dkimhdrs . $signed) . static::$LE;
4279
-    }
4280
-
4281
-    /**
4282
-     * Detect if a string contains a line longer than the maximum line length
4283
-     * allowed by RFC 2822 section 2.1.1.
4284
-     *
4285
-     * @param string $str
4286
-     *
4287
-     * @return bool
4288
-     */
4289
-    public static function hasLineLongerThanMax($str)
4290
-    {
4291
-        return (bool) preg_match('/^(.{' . (self::MAX_LINE_LENGTH + strlen(static::$LE)) . ',})/m', $str);
4292
-    }
4293
-
4294
-    /**
4295
-     * Allows for public read access to 'to' property.
4296
-     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4297
-     *
4298
-     * @return array
4299
-     */
4300
-    public function getToAddresses()
4301
-    {
4302
-        return $this->to;
4303
-    }
4304
-
4305
-    /**
4306
-     * Allows for public read access to 'cc' property.
4307
-     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4308
-     *
4309
-     * @return array
4310
-     */
4311
-    public function getCcAddresses()
4312
-    {
4313
-        return $this->cc;
4314
-    }
4315
-
4316
-    /**
4317
-     * Allows for public read access to 'bcc' property.
4318
-     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4319
-     *
4320
-     * @return array
4321
-     */
4322
-    public function getBccAddresses()
4323
-    {
4324
-        return $this->bcc;
4325
-    }
4326
-
4327
-    /**
4328
-     * Allows for public read access to 'ReplyTo' property.
4329
-     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4330
-     *
4331
-     * @return array
4332
-     */
4333
-    public function getReplyToAddresses()
4334
-    {
4335
-        return $this->ReplyTo;
4336
-    }
4337
-
4338
-    /**
4339
-     * Allows for public read access to 'all_recipients' property.
4340
-     * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4341
-     *
4342
-     * @return array
4343
-     */
4344
-    public function getAllRecipientAddresses()
4345
-    {
4346
-        return $this->all_recipients;
4347
-    }
4348
-
4349
-    /**
4350
-     * Perform a callback.
4351
-     *
4352
-     * @param bool   $isSent
4353
-     * @param array  $to
4354
-     * @param array  $cc
4355
-     * @param array  $bcc
4356
-     * @param string $subject
4357
-     * @param string $body
4358
-     * @param string $from
4359
-     * @param array  $extra
4360
-     */
4361
-    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from, $extra)
4362
-    {
4363
-        if (!empty($this->action_function) and is_callable($this->action_function)) {
4364
-            call_user_func_array($this->action_function, [$isSent, $to, $cc, $bcc, $subject, $body, $from, $extra]);
4365
-        }
4366
-    }
4367
-
4368
-    /**
4369
-     * Get the OAuth instance.
4370
-     *
4371
-     * @return OAuth
4372
-     */
4373
-    public function getOAuth()
4374
-    {
4375
-        return $this->oauth;
4376
-    }
4377
-
4378
-    /**
4379
-     * Set an OAuth instance.
4380
-     *
4381
-     * @param OAuth $oauth
4382
-     */
4383
-    public function setOAuth(OAuth $oauth)
4384
-    {
4385
-        $this->oauth = $oauth;
4386
-    }
3181
+			/* @noinspection PhpMissingBreakStatementInspection */
3182
+			case 'comment':
3183
+				$pattern = '\(\)"';
3184
+			/* Intentional fall through */
3185
+			case 'text':
3186
+			default:
3187
+				// RFC 2047 section 5.1
3188
+				// Replace every high ascii, control, =, ? and _ characters
3189
+				$pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
3190
+				break;
3191
+		}
3192
+		$matches = [];
3193
+		if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
3194
+			// If the string contains an '=', make sure it's the first thing we replace
3195
+			// so as to avoid double-encoding
3196
+			$eqkey = array_search('=', $matches[0]);
3197
+			if (false !== $eqkey) {
3198
+				unset($matches[0][$eqkey]);
3199
+				array_unshift($matches[0], '=');
3200
+			}
3201
+			foreach (array_unique($matches[0]) as $char) {
3202
+				$encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
3203
+			}
3204
+		}
3205
+		// Replace spaces with _ (more readable than =20)
3206
+		// RFC 2047 section 4.2(2)
3207
+		return str_replace(' ', '_', $encoded);
3208
+	}
3209
+
3210
+	/**
3211
+	 * Add a string or binary attachment (non-filesystem).
3212
+	 * This method can be used to attach ascii or binary data,
3213
+	 * such as a BLOB record from a database.
3214
+	 *
3215
+	 * @param string $string      String attachment data
3216
+	 * @param string $filename    Name of the attachment
3217
+	 * @param string $encoding    File encoding (see $Encoding)
3218
+	 * @param string $type        File extension (MIME) type
3219
+	 * @param string $disposition Disposition to use
3220
+	 */
3221
+	public function addStringAttachment(
3222
+		$string,
3223
+		$filename,
3224
+		$encoding = 'base64',
3225
+		$type = '',
3226
+		$disposition = 'attachment'
3227
+	) {
3228
+		// If a MIME type is not specified, try to work it out from the file name
3229
+		if ('' == $type) {
3230
+			$type = static::filenameToType($filename);
3231
+		}
3232
+		// Append to $attachment array
3233
+		$this->attachment[] = [
3234
+			0 => $string,
3235
+			1 => $filename,
3236
+			2 => basename($filename),
3237
+			3 => $encoding,
3238
+			4 => $type,
3239
+			5 => true, // isStringAttachment
3240
+			6 => $disposition,
3241
+			7 => 0,
3242
+		];
3243
+	}
3244
+
3245
+	/**
3246
+	 * Add an embedded (inline) attachment from a file.
3247
+	 * This can include images, sounds, and just about any other document type.
3248
+	 * These differ from 'regular' attachments in that they are intended to be
3249
+	 * displayed inline with the message, not just attached for download.
3250
+	 * This is used in HTML messages that embed the images
3251
+	 * the HTML refers to using the $cid value.
3252
+	 * Never use a user-supplied path to a file!
3253
+	 *
3254
+	 * @param string $path        Path to the attachment
3255
+	 * @param string $cid         Content ID of the attachment; Use this to reference
3256
+	 *                            the content when using an embedded image in HTML
3257
+	 * @param string $name        Overrides the attachment name
3258
+	 * @param string $encoding    File encoding (see $Encoding)
3259
+	 * @param string $type        File MIME type
3260
+	 * @param string $disposition Disposition to use
3261
+	 *
3262
+	 * @return bool True on successfully adding an attachment
3263
+	 */
3264
+	public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
3265
+	{
3266
+		if (!@is_file($path)) {
3267
+			$this->setError($this->lang('file_access') . $path);
3268
+
3269
+			return false;
3270
+		}
3271
+
3272
+		// If a MIME type is not specified, try to work it out from the file name
3273
+		if ('' == $type) {
3274
+			$type = static::filenameToType($path);
3275
+		}
3276
+
3277
+		$filename = basename($path);
3278
+		if ('' == $name) {
3279
+			$name = $filename;
3280
+		}
3281
+
3282
+		// Append to $attachment array
3283
+		$this->attachment[] = [
3284
+			0 => $path,
3285
+			1 => $filename,
3286
+			2 => $name,
3287
+			3 => $encoding,
3288
+			4 => $type,
3289
+			5 => false, // isStringAttachment
3290
+			6 => $disposition,
3291
+			7 => $cid,
3292
+		];
3293
+
3294
+		return true;
3295
+	}
3296
+
3297
+	/**
3298
+	 * Add an embedded stringified attachment.
3299
+	 * This can include images, sounds, and just about any other document type.
3300
+	 * Be sure to set the $type to an image type for images:
3301
+	 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
3302
+	 *
3303
+	 * @param string $string      The attachment binary data
3304
+	 * @param string $cid         Content ID of the attachment; Use this to reference
3305
+	 *                            the content when using an embedded image in HTML
3306
+	 * @param string $name
3307
+	 * @param string $encoding    File encoding (see $Encoding)
3308
+	 * @param string $type        MIME type
3309
+	 * @param string $disposition Disposition to use
3310
+	 *
3311
+	 * @return bool True on successfully adding an attachment
3312
+	 */
3313
+	public function addStringEmbeddedImage(
3314
+		$string,
3315
+		$cid,
3316
+		$name = '',
3317
+		$encoding = 'base64',
3318
+		$type = '',
3319
+		$disposition = 'inline'
3320
+	) {
3321
+		// If a MIME type is not specified, try to work it out from the name
3322
+		if ('' == $type and !empty($name)) {
3323
+			$type = static::filenameToType($name);
3324
+		}
3325
+
3326
+		// Append to $attachment array
3327
+		$this->attachment[] = [
3328
+			0 => $string,
3329
+			1 => $name,
3330
+			2 => $name,
3331
+			3 => $encoding,
3332
+			4 => $type,
3333
+			5 => true, // isStringAttachment
3334
+			6 => $disposition,
3335
+			7 => $cid,
3336
+		];
3337
+
3338
+		return true;
3339
+	}
3340
+
3341
+	/**
3342
+	 * Check if an embedded attachment is present with this cid.
3343
+	 *
3344
+	 * @param string $cid
3345
+	 *
3346
+	 * @return bool
3347
+	 */
3348
+	protected function cidExists($cid)
3349
+	{
3350
+		foreach ($this->attachment as $attachment) {
3351
+			if ('inline' == $attachment[6] and $cid == $attachment[7]) {
3352
+				return true;
3353
+			}
3354
+		}
3355
+
3356
+		return false;
3357
+	}
3358
+
3359
+	/**
3360
+	 * Check if an inline attachment is present.
3361
+	 *
3362
+	 * @return bool
3363
+	 */
3364
+	public function inlineImageExists()
3365
+	{
3366
+		foreach ($this->attachment as $attachment) {
3367
+			if ($attachment[6] == 'inline') {
3368
+				return true;
3369
+			}
3370
+		}
3371
+
3372
+		return false;
3373
+	}
3374
+
3375
+	/**
3376
+	 * Check if an attachment (non-inline) is present.
3377
+	 *
3378
+	 * @return bool
3379
+	 */
3380
+	public function attachmentExists()
3381
+	{
3382
+		foreach ($this->attachment as $attachment) {
3383
+			if ($attachment[6] == 'attachment') {
3384
+				return true;
3385
+			}
3386
+		}
3387
+
3388
+		return false;
3389
+	}
3390
+
3391
+	/**
3392
+	 * Check if this message has an alternative body set.
3393
+	 *
3394
+	 * @return bool
3395
+	 */
3396
+	public function alternativeExists()
3397
+	{
3398
+		return !empty($this->AltBody);
3399
+	}
3400
+
3401
+	/**
3402
+	 * Clear queued addresses of given kind.
3403
+	 *
3404
+	 * @param string $kind 'to', 'cc', or 'bcc'
3405
+	 */
3406
+	public function clearQueuedAddresses($kind)
3407
+	{
3408
+		$this->RecipientsQueue = array_filter(
3409
+			$this->RecipientsQueue,
3410
+			function ($params) use ($kind) {
3411
+				return $params[0] != $kind;
3412
+			}
3413
+		);
3414
+	}
3415
+
3416
+	/**
3417
+	 * Clear all To recipients.
3418
+	 */
3419
+	public function clearAddresses()
3420
+	{
3421
+		foreach ($this->to as $to) {
3422
+			unset($this->all_recipients[strtolower($to[0])]);
3423
+		}
3424
+		$this->to = [];
3425
+		$this->clearQueuedAddresses('to');
3426
+	}
3427
+
3428
+	/**
3429
+	 * Clear all CC recipients.
3430
+	 */
3431
+	public function clearCCs()
3432
+	{
3433
+		foreach ($this->cc as $cc) {
3434
+			unset($this->all_recipients[strtolower($cc[0])]);
3435
+		}
3436
+		$this->cc = [];
3437
+		$this->clearQueuedAddresses('cc');
3438
+	}
3439
+
3440
+	/**
3441
+	 * Clear all BCC recipients.
3442
+	 */
3443
+	public function clearBCCs()
3444
+	{
3445
+		foreach ($this->bcc as $bcc) {
3446
+			unset($this->all_recipients[strtolower($bcc[0])]);
3447
+		}
3448
+		$this->bcc = [];
3449
+		$this->clearQueuedAddresses('bcc');
3450
+	}
3451
+
3452
+	/**
3453
+	 * Clear all ReplyTo recipients.
3454
+	 */
3455
+	public function clearReplyTos()
3456
+	{
3457
+		$this->ReplyTo = [];
3458
+		$this->ReplyToQueue = [];
3459
+	}
3460
+
3461
+	/**
3462
+	 * Clear all recipient types.
3463
+	 */
3464
+	public function clearAllRecipients()
3465
+	{
3466
+		$this->to = [];
3467
+		$this->cc = [];
3468
+		$this->bcc = [];
3469
+		$this->all_recipients = [];
3470
+		$this->RecipientsQueue = [];
3471
+	}
3472
+
3473
+	/**
3474
+	 * Clear all filesystem, string, and binary attachments.
3475
+	 */
3476
+	public function clearAttachments()
3477
+	{
3478
+		$this->attachment = [];
3479
+	}
3480
+
3481
+	/**
3482
+	 * Clear all custom headers.
3483
+	 */
3484
+	public function clearCustomHeaders()
3485
+	{
3486
+		$this->CustomHeader = [];
3487
+	}
3488
+
3489
+	/**
3490
+	 * Add an error message to the error container.
3491
+	 *
3492
+	 * @param string $msg
3493
+	 */
3494
+	protected function setError($msg)
3495
+	{
3496
+		++$this->error_count;
3497
+		if ('smtp' == $this->Mailer and null !== $this->smtp) {
3498
+			$lasterror = $this->smtp->getError();
3499
+			if (!empty($lasterror['error'])) {
3500
+				$msg .= $this->lang('smtp_error') . $lasterror['error'];
3501
+				if (!empty($lasterror['detail'])) {
3502
+					$msg .= ' Detail: ' . $lasterror['detail'];
3503
+				}
3504
+				if (!empty($lasterror['smtp_code'])) {
3505
+					$msg .= ' SMTP code: ' . $lasterror['smtp_code'];
3506
+				}
3507
+				if (!empty($lasterror['smtp_code_ex'])) {
3508
+					$msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
3509
+				}
3510
+			}
3511
+		}
3512
+		$this->ErrorInfo = $msg;
3513
+	}
3514
+
3515
+	/**
3516
+	 * Return an RFC 822 formatted date.
3517
+	 *
3518
+	 * @return string
3519
+	 */
3520
+	public static function rfcDate()
3521
+	{
3522
+		// Set the time zone to whatever the default is to avoid 500 errors
3523
+		// Will default to UTC if it's not set properly in php.ini
3524
+		date_default_timezone_set(@date_default_timezone_get());
3525
+
3526
+		return date('D, j M Y H:i:s O');
3527
+	}
3528
+
3529
+	/**
3530
+	 * Get the server hostname.
3531
+	 * Returns 'localhost.localdomain' if unknown.
3532
+	 *
3533
+	 * @return string
3534
+	 */
3535
+	protected function serverHostname()
3536
+	{
3537
+		$result = '';
3538
+		if (!empty($this->Hostname)) {
3539
+			$result = $this->Hostname;
3540
+		} elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER)) {
3541
+			$result = $_SERVER['SERVER_NAME'];
3542
+		} elseif (function_exists('gethostname') and gethostname() !== false) {
3543
+			$result = gethostname();
3544
+		} elseif (php_uname('n') !== false) {
3545
+			$result = php_uname('n');
3546
+		}
3547
+		if (!static::isValidHost($result)) {
3548
+			return 'localhost.localdomain';
3549
+		}
3550
+
3551
+		return $result;
3552
+	}
3553
+
3554
+	/**
3555
+	 * Validate whether a string contains a valid value to use as a hostname or IP address.
3556
+	 * IPv6 addresses must include [], e.g. `[::1]`, not just `::1`.
3557
+	 *
3558
+	 * @param string $host The host name or IP address to check
3559
+	 *
3560
+	 * @return bool
3561
+	 */
3562
+	public static function isValidHost($host)
3563
+	{
3564
+		//Simple syntax limits
3565
+		if (empty($host)
3566
+			or !is_string($host)
3567
+			or strlen($host) > 256
3568
+		) {
3569
+			return false;
3570
+		}
3571
+		//Looks like a bracketed IPv6 address
3572
+		if (trim($host, '[]') != $host) {
3573
+			return (bool) filter_var(trim($host, '[]'), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
3574
+		}
3575
+		//If removing all the dots results in a numeric string, it must be an IPv4 address.
3576
+		//Need to check this first because otherwise things like `999.0.0.0` are considered valid host names
3577
+		if (is_numeric(str_replace('.', '', $host))) {
3578
+			//Is it a valid IPv4 address?
3579
+			return (bool) filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
3580
+		}
3581
+		if (filter_var('http://' . $host, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED)) {
3582
+			//Is it a syntactically valid hostname?
3583
+			return true;
3584
+		}
3585
+
3586
+		return false;
3587
+	}
3588
+
3589
+	/**
3590
+	 * Get an error message in the current language.
3591
+	 *
3592
+	 * @param string $key
3593
+	 *
3594
+	 * @return string
3595
+	 */
3596
+	protected function lang($key)
3597
+	{
3598
+		if (count($this->language) < 1) {
3599
+			$this->setLanguage('en'); // set the default language
3600
+		}
3601
+
3602
+		if (array_key_exists($key, $this->language)) {
3603
+			if ('smtp_connect_failed' == $key) {
3604
+				//Include a link to troubleshooting docs on SMTP connection failure
3605
+				//this is by far the biggest cause of support questions
3606
+				//but it's usually not PHPMailer's fault.
3607
+				return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3608
+			}
3609
+
3610
+			return $this->language[$key];
3611
+		}
3612
+
3613
+		//Return the key as a fallback
3614
+		return $key;
3615
+	}
3616
+
3617
+	/**
3618
+	 * Check if an error occurred.
3619
+	 *
3620
+	 * @return bool True if an error did occur
3621
+	 */
3622
+	public function isError()
3623
+	{
3624
+		return $this->error_count > 0;
3625
+	}
3626
+
3627
+	/**
3628
+	 * Add a custom header.
3629
+	 * $name value can be overloaded to contain
3630
+	 * both header name and value (name:value).
3631
+	 *
3632
+	 * @param string      $name  Custom header name
3633
+	 * @param string|null $value Header value
3634
+	 */
3635
+	public function addCustomHeader($name, $value = null)
3636
+	{
3637
+		if (null === $value) {
3638
+			// Value passed in as name:value
3639
+			$this->CustomHeader[] = explode(':', $name, 2);
3640
+		} else {
3641
+			$this->CustomHeader[] = [$name, $value];
3642
+		}
3643
+	}
3644
+
3645
+	/**
3646
+	 * Returns all custom headers.
3647
+	 *
3648
+	 * @return array
3649
+	 */
3650
+	public function getCustomHeaders()
3651
+	{
3652
+		return $this->CustomHeader;
3653
+	}
3654
+
3655
+	/**
3656
+	 * Create a message body from an HTML string.
3657
+	 * Automatically inlines images and creates a plain-text version by converting the HTML,
3658
+	 * overwriting any existing values in Body and AltBody.
3659
+	 * Do not source $message content from user input!
3660
+	 * $basedir is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty
3661
+	 * will look for an image file in $basedir/images/a.png and convert it to inline.
3662
+	 * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email)
3663
+	 * Converts data-uri images into embedded attachments.
3664
+	 * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
3665
+	 *
3666
+	 * @param string        $message  HTML message string
3667
+	 * @param string        $basedir  Absolute path to a base directory to prepend to relative paths to images
3668
+	 * @param bool|callable $advanced Whether to use the internal HTML to text converter
3669
+	 *                                or your own custom converter @see PHPMailer::html2text()
3670
+	 *
3671
+	 * @return string $message The transformed message Body
3672
+	 */
3673
+	public function msgHTML($message, $basedir = '', $advanced = false)
3674
+	{
3675
+		preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3676
+		if (array_key_exists(2, $images)) {
3677
+			if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3678
+				// Ensure $basedir has a trailing /
3679
+				$basedir .= '/';
3680
+			}
3681
+			foreach ($images[2] as $imgindex => $url) {
3682
+				// Convert data URIs into embedded images
3683
+				//e.g. "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
3684
+				if (preg_match('#^data:(image/(?:jpe?g|gif|png));?(base64)?,(.+)#', $url, $match)) {
3685
+					if (count($match) == 4 and 'base64' == $match[2]) {
3686
+						$data = base64_decode($match[3]);
3687
+					} elseif ('' == $match[2]) {
3688
+						$data = rawurldecode($match[3]);
3689
+					} else {
3690
+						//Not recognised so leave it alone
3691
+						continue;
3692
+					}
3693
+					//Hash the decoded data, not the URL so that the same data-URI image used in multiple places
3694
+					//will only be embedded once, even if it used a different encoding
3695
+					$cid = hash('sha256', $data) . '@phpmailer.0'; // RFC2392 S 2
3696
+
3697
+					if (!$this->cidExists($cid)) {
3698
+						$this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1]);
3699
+					}
3700
+					$message = str_replace(
3701
+						$images[0][$imgindex],
3702
+						$images[1][$imgindex] . '="cid:' . $cid . '"',
3703
+						$message
3704
+					);
3705
+					continue;
3706
+				}
3707
+				if (// Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
3708
+					!empty($basedir)
3709
+					// Ignore URLs containing parent dir traversal (..)
3710
+					and (strpos($url, '..') === false)
3711
+					// Do not change urls that are already inline images
3712
+					and substr($url, 0, 4) !== 'cid:'
3713
+					// Do not change absolute URLs, including anonymous protocol
3714
+					and !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
3715
+				) {
3716
+					$filename = basename($url);
3717
+					$directory = dirname($url);
3718
+					if ('.' == $directory) {
3719
+						$directory = '';
3720
+					}
3721
+					$cid = hash('sha256', $url) . '@phpmailer.0'; // RFC2392 S 2
3722
+					if (strlen($basedir) > 1 and substr($basedir, -1) != '/') {
3723
+						$basedir .= '/';
3724
+					}
3725
+					if (strlen($directory) > 1 and substr($directory, -1) != '/') {
3726
+						$directory .= '/';
3727
+					}
3728
+					if ($this->addEmbeddedImage(
3729
+						$basedir . $directory . $filename,
3730
+						$cid,
3731
+						$filename,
3732
+						'base64',
3733
+						static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION))
3734
+					)
3735
+					) {
3736
+						$message = preg_replace(
3737
+							'/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3738
+							$images[1][$imgindex] . '="cid:' . $cid . '"',
3739
+							$message
3740
+						);
3741
+					}
3742
+				}
3743
+			}
3744
+		}
3745
+		$this->isHTML(true);
3746
+		// Convert all message body line breaks to LE, makes quoted-printable encoding work much better
3747
+		$this->Body = static::normalizeBreaks($message);
3748
+		$this->AltBody = static::normalizeBreaks($this->html2text($message, $advanced));
3749
+		if (!$this->alternativeExists()) {
3750
+			$this->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.'
3751
+				. static::$LE;
3752
+		}
3753
+
3754
+		return $this->Body;
3755
+	}
3756
+
3757
+	/**
3758
+	 * Convert an HTML string into plain text.
3759
+	 * This is used by msgHTML().
3760
+	 * Note - older versions of this function used a bundled advanced converter
3761
+	 * which was removed for license reasons in #232.
3762
+	 * Example usage:
3763
+	 *
3764
+	 * ```php
3765
+	 * // Use default conversion
3766
+	 * $plain = $mail->html2text($html);
3767
+	 * // Use your own custom converter
3768
+	 * $plain = $mail->html2text($html, function($html) {
3769
+	 *     $converter = new MyHtml2text($html);
3770
+	 *     return $converter->get_text();
3771
+	 * });
3772
+	 * ```
3773
+	 *
3774
+	 * @param string        $html     The HTML text to convert
3775
+	 * @param bool|callable $advanced Any boolean value to use the internal converter,
3776
+	 *                                or provide your own callable for custom conversion
3777
+	 *
3778
+	 * @return string
3779
+	 */
3780
+	public function html2text($html, $advanced = false)
3781
+	{
3782
+		if (is_callable($advanced)) {
3783
+			return call_user_func($advanced, $html);
3784
+		}
3785
+
3786
+		return html_entity_decode(
3787
+			trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3788
+			ENT_QUOTES,
3789
+			$this->CharSet
3790
+		);
3791
+	}
3792
+
3793
+	/**
3794
+	 * Get the MIME type for a file extension.
3795
+	 *
3796
+	 * @param string $ext File extension
3797
+	 *
3798
+	 * @return string MIME type of file
3799
+	 */
3800
+	public static function _mime_types($ext = '')
3801
+	{
3802
+		$mimes = [
3803
+			'xl' => 'application/excel',
3804
+			'js' => 'application/javascript',
3805
+			'hqx' => 'application/mac-binhex40',
3806
+			'cpt' => 'application/mac-compactpro',
3807
+			'bin' => 'application/macbinary',
3808
+			'doc' => 'application/msword',
3809
+			'word' => 'application/msword',
3810
+			'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3811
+			'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3812
+			'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3813
+			'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3814
+			'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3815
+			'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3816
+			'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3817
+			'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3818
+			'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
3819
+			'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3820
+			'class' => 'application/octet-stream',
3821
+			'dll' => 'application/octet-stream',
3822
+			'dms' => 'application/octet-stream',
3823
+			'exe' => 'application/octet-stream',
3824
+			'lha' => 'application/octet-stream',
3825
+			'lzh' => 'application/octet-stream',
3826
+			'psd' => 'application/octet-stream',
3827
+			'sea' => 'application/octet-stream',
3828
+			'so' => 'application/octet-stream',
3829
+			'oda' => 'application/oda',
3830
+			'pdf' => 'application/pdf',
3831
+			'ai' => 'application/postscript',
3832
+			'eps' => 'application/postscript',
3833
+			'ps' => 'application/postscript',
3834
+			'smi' => 'application/smil',
3835
+			'smil' => 'application/smil',
3836
+			'mif' => 'application/vnd.mif',
3837
+			'xls' => 'application/vnd.ms-excel',
3838
+			'ppt' => 'application/vnd.ms-powerpoint',
3839
+			'wbxml' => 'application/vnd.wap.wbxml',
3840
+			'wmlc' => 'application/vnd.wap.wmlc',
3841
+			'dcr' => 'application/x-director',
3842
+			'dir' => 'application/x-director',
3843
+			'dxr' => 'application/x-director',
3844
+			'dvi' => 'application/x-dvi',
3845
+			'gtar' => 'application/x-gtar',
3846
+			'php3' => 'application/x-httpd-php',
3847
+			'php4' => 'application/x-httpd-php',
3848
+			'php' => 'application/x-httpd-php',
3849
+			'phtml' => 'application/x-httpd-php',
3850
+			'phps' => 'application/x-httpd-php-source',
3851
+			'swf' => 'application/x-shockwave-flash',
3852
+			'sit' => 'application/x-stuffit',
3853
+			'tar' => 'application/x-tar',
3854
+			'tgz' => 'application/x-tar',
3855
+			'xht' => 'application/xhtml+xml',
3856
+			'xhtml' => 'application/xhtml+xml',
3857
+			'zip' => 'application/zip',
3858
+			'mid' => 'audio/midi',
3859
+			'midi' => 'audio/midi',
3860
+			'mp2' => 'audio/mpeg',
3861
+			'mp3' => 'audio/mpeg',
3862
+			'mpga' => 'audio/mpeg',
3863
+			'aif' => 'audio/x-aiff',
3864
+			'aifc' => 'audio/x-aiff',
3865
+			'aiff' => 'audio/x-aiff',
3866
+			'ram' => 'audio/x-pn-realaudio',
3867
+			'rm' => 'audio/x-pn-realaudio',
3868
+			'rpm' => 'audio/x-pn-realaudio-plugin',
3869
+			'ra' => 'audio/x-realaudio',
3870
+			'wav' => 'audio/x-wav',
3871
+			'bmp' => 'image/bmp',
3872
+			'gif' => 'image/gif',
3873
+			'jpeg' => 'image/jpeg',
3874
+			'jpe' => 'image/jpeg',
3875
+			'jpg' => 'image/jpeg',
3876
+			'png' => 'image/png',
3877
+			'tiff' => 'image/tiff',
3878
+			'tif' => 'image/tiff',
3879
+			'eml' => 'message/rfc822',
3880
+			'css' => 'text/css',
3881
+			'html' => 'text/html',
3882
+			'htm' => 'text/html',
3883
+			'shtml' => 'text/html',
3884
+			'log' => 'text/plain',
3885
+			'text' => 'text/plain',
3886
+			'txt' => 'text/plain',
3887
+			'rtx' => 'text/richtext',
3888
+			'rtf' => 'text/rtf',
3889
+			'vcf' => 'text/vcard',
3890
+			'vcard' => 'text/vcard',
3891
+			'ics' => 'text/calendar',
3892
+			'xml' => 'text/xml',
3893
+			'xsl' => 'text/xml',
3894
+			'mpeg' => 'video/mpeg',
3895
+			'mpe' => 'video/mpeg',
3896
+			'mpg' => 'video/mpeg',
3897
+			'mov' => 'video/quicktime',
3898
+			'qt' => 'video/quicktime',
3899
+			'rv' => 'video/vnd.rn-realvideo',
3900
+			'avi' => 'video/x-msvideo',
3901
+			'movie' => 'video/x-sgi-movie',
3902
+		];
3903
+		if (array_key_exists(strtolower($ext), $mimes)) {
3904
+			return $mimes[strtolower($ext)];
3905
+		}
3906
+
3907
+		return 'application/octet-stream';
3908
+	}
3909
+
3910
+	/**
3911
+	 * Map a file name to a MIME type.
3912
+	 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3913
+	 *
3914
+	 * @param string $filename A file name or full path, does not need to exist as a file
3915
+	 *
3916
+	 * @return string
3917
+	 */
3918
+	public static function filenameToType($filename)
3919
+	{
3920
+		// In case the path is a URL, strip any query string before getting extension
3921
+		$qpos = strpos($filename, '?');
3922
+		if (false !== $qpos) {
3923
+			$filename = substr($filename, 0, $qpos);
3924
+		}
3925
+		$ext = static::mb_pathinfo($filename, PATHINFO_EXTENSION);
3926
+
3927
+		return static::_mime_types($ext);
3928
+	}
3929
+
3930
+	/**
3931
+	 * Multi-byte-safe pathinfo replacement.
3932
+	 * Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe.
3933
+	 *
3934
+	 * @see    http://www.php.net/manual/en/function.pathinfo.php#107461
3935
+	 *
3936
+	 * @param string     $path    A filename or path, does not need to exist as a file
3937
+	 * @param int|string $options Either a PATHINFO_* constant,
3938
+	 *                            or a string name to return only the specified piece
3939
+	 *
3940
+	 * @return string|array
3941
+	 */
3942
+	public static function mb_pathinfo($path, $options = null)
3943
+	{
3944
+		$ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''];
3945
+		$pathinfo = [];
3946
+		if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$#im', $path, $pathinfo)) {
3947
+			if (array_key_exists(1, $pathinfo)) {
3948
+				$ret['dirname'] = $pathinfo[1];
3949
+			}
3950
+			if (array_key_exists(2, $pathinfo)) {
3951
+				$ret['basename'] = $pathinfo[2];
3952
+			}
3953
+			if (array_key_exists(5, $pathinfo)) {
3954
+				$ret['extension'] = $pathinfo[5];
3955
+			}
3956
+			if (array_key_exists(3, $pathinfo)) {
3957
+				$ret['filename'] = $pathinfo[3];
3958
+			}
3959
+		}
3960
+		switch ($options) {
3961
+			case PATHINFO_DIRNAME:
3962
+			case 'dirname':
3963
+				return $ret['dirname'];
3964
+			case PATHINFO_BASENAME:
3965
+			case 'basename':
3966
+				return $ret['basename'];
3967
+			case PATHINFO_EXTENSION:
3968
+			case 'extension':
3969
+				return $ret['extension'];
3970
+			case PATHINFO_FILENAME:
3971
+			case 'filename':
3972
+				return $ret['filename'];
3973
+			default:
3974
+				return $ret;
3975
+		}
3976
+	}
3977
+
3978
+	/**
3979
+	 * Set or reset instance properties.
3980
+	 * You should avoid this function - it's more verbose, less efficient, more error-prone and
3981
+	 * harder to debug than setting properties directly.
3982
+	 * Usage Example:
3983
+	 * `$mail->set('SMTPSecure', 'tls');`
3984
+	 *   is the same as:
3985
+	 * `$mail->SMTPSecure = 'tls';`.
3986
+	 *
3987
+	 * @param string $name  The property name to set
3988
+	 * @param mixed  $value The value to set the property to
3989
+	 *
3990
+	 * @return bool
3991
+	 */
3992
+	public function set($name, $value = '')
3993
+	{
3994
+		if (property_exists($this, $name)) {
3995
+			$this->$name = $value;
3996
+
3997
+			return true;
3998
+		}
3999
+		$this->setError($this->lang('variable_set') . $name);
4000
+
4001
+		return false;
4002
+	}
4003
+
4004
+	/**
4005
+	 * Strip newlines to prevent header injection.
4006
+	 *
4007
+	 * @param string $str
4008
+	 *
4009
+	 * @return string
4010
+	 */
4011
+	public function secureHeader($str)
4012
+	{
4013
+		return trim(str_replace(["\r", "\n"], '', $str));
4014
+	}
4015
+
4016
+	/**
4017
+	 * Normalize line breaks in a string.
4018
+	 * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
4019
+	 * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
4020
+	 *
4021
+	 * @param string $text
4022
+	 * @param string $breaktype What kind of line break to use; defaults to static::$LE
4023
+	 *
4024
+	 * @return string
4025
+	 */
4026
+	public static function normalizeBreaks($text, $breaktype = null)
4027
+	{
4028
+		if (null === $breaktype) {
4029
+			$breaktype = static::$LE;
4030
+		}
4031
+		// Normalise to \n
4032
+		$text = str_replace(["\r\n", "\r"], "\n", $text);
4033
+		// Now convert LE as needed
4034
+		if ("\n" !== static::$LE) {
4035
+			$text = str_replace("\n", $breaktype, $text);
4036
+		}
4037
+
4038
+		return $text;
4039
+	}
4040
+
4041
+	/**
4042
+	 * Return the current line break format string.
4043
+	 *
4044
+	 * @return string
4045
+	 */
4046
+	public static function getLE()
4047
+	{
4048
+		return static::$LE;
4049
+	}
4050
+
4051
+	/**
4052
+	 * Set the line break format string, e.g. "\r\n".
4053
+	 *
4054
+	 * @param string $le
4055
+	 */
4056
+	protected static function setLE($le)
4057
+	{
4058
+		static::$LE = $le;
4059
+	}
4060
+
4061
+	/**
4062
+	 * Set the public and private key files and password for S/MIME signing.
4063
+	 *
4064
+	 * @param string $cert_filename
4065
+	 * @param string $key_filename
4066
+	 * @param string $key_pass            Password for private key
4067
+	 * @param string $extracerts_filename Optional path to chain certificate
4068
+	 */
4069
+	public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
4070
+	{
4071
+		$this->sign_cert_file = $cert_filename;
4072
+		$this->sign_key_file = $key_filename;
4073
+		$this->sign_key_pass = $key_pass;
4074
+		$this->sign_extracerts_file = $extracerts_filename;
4075
+	}
4076
+
4077
+	/**
4078
+	 * Quoted-Printable-encode a DKIM header.
4079
+	 *
4080
+	 * @param string $txt
4081
+	 *
4082
+	 * @return string
4083
+	 */
4084
+	public function DKIM_QP($txt)
4085
+	{
4086
+		$line = '';
4087
+		$len = strlen($txt);
4088
+		for ($i = 0; $i < $len; ++$i) {
4089
+			$ord = ord($txt[$i]);
4090
+			if (((0x21 <= $ord) and ($ord <= 0x3A)) or $ord == 0x3C or ((0x3E <= $ord) and ($ord <= 0x7E))) {
4091
+				$line .= $txt[$i];
4092
+			} else {
4093
+				$line .= '=' . sprintf('%02X', $ord);
4094
+			}
4095
+		}
4096
+
4097
+		return $line;
4098
+	}
4099
+
4100
+	/**
4101
+	 * Generate a DKIM signature.
4102
+	 *
4103
+	 * @param string $signHeader
4104
+	 *
4105
+	 * @throws Exception
4106
+	 *
4107
+	 * @return string The DKIM signature value
4108
+	 */
4109
+	public function DKIM_Sign($signHeader)
4110
+	{
4111
+		if (!defined('PKCS7_TEXT')) {
4112
+			if ($this->exceptions) {
4113
+				throw new Exception($this->lang('extension_missing') . 'openssl');
4114
+			}
4115
+
4116
+			return '';
4117
+		}
4118
+		$privKeyStr = !empty($this->DKIM_private_string) ?
4119
+			$this->DKIM_private_string :
4120
+			file_get_contents($this->DKIM_private);
4121
+		if ('' != $this->DKIM_passphrase) {
4122
+			$privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
4123
+		} else {
4124
+			$privKey = openssl_pkey_get_private($privKeyStr);
4125
+		}
4126
+		if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
4127
+			openssl_pkey_free($privKey);
4128
+
4129
+			return base64_encode($signature);
4130
+		}
4131
+		openssl_pkey_free($privKey);
4132
+
4133
+		return '';
4134
+	}
4135
+
4136
+	/**
4137
+	 * Generate a DKIM canonicalization header.
4138
+	 * Uses the 'relaxed' algorithm from RFC6376 section 3.4.2.
4139
+	 *
4140
+	 * @see    https://tools.ietf.org/html/rfc6376#section-3.4.2
4141
+	 *
4142
+	 * @param string $signHeader Header
4143
+	 *
4144
+	 * @return string
4145
+	 */
4146
+	public function DKIM_HeaderC($signHeader)
4147
+	{
4148
+		//Unfold all header continuation lines
4149
+		//Also collapses folded whitespace.
4150
+		//Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]`
4151
+		//@see https://tools.ietf.org/html/rfc5322#section-2.2
4152
+		//That means this may break if you do something daft like put vertical tabs in your headers.
4153
+		$signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader);
4154
+		$lines = explode("\r\n", $signHeader);
4155
+		foreach ($lines as $key => $line) {
4156
+			//If the header is missing a :, skip it as it's invalid
4157
+			//This is likely to happen because the explode() above will also split
4158
+			//on the trailing LE, leaving an empty line
4159
+			if (strpos($line, ':') === false) {
4160
+				continue;
4161
+			}
4162
+			list($heading, $value) = explode(':', $line, 2);
4163
+			//Lower-case header name
4164
+			$heading = strtolower($heading);
4165
+			//Collapse white space within the value
4166
+			$value = preg_replace('/[ \t]{2,}/', ' ', $value);
4167
+			//RFC6376 is slightly unclear here - it says to delete space at the *end* of each value
4168
+			//But then says to delete space before and after the colon.
4169
+			//Net result is the same as trimming both ends of the value.
4170
+			//by elimination, the same applies to the field name
4171
+			$lines[$key] = trim($heading, " \t") . ':' . trim($value, " \t");
4172
+		}
4173
+
4174
+		return implode(static::$LE, $lines);
4175
+	}
4176
+
4177
+	/**
4178
+	 * Generate a DKIM canonicalization body.
4179
+	 * Uses the 'simple' algorithm from RFC6376 section 3.4.3.
4180
+	 *
4181
+	 * @see    https://tools.ietf.org/html/rfc6376#section-3.4.3
4182
+	 *
4183
+	 * @param string $body Message Body
4184
+	 *
4185
+	 * @return string
4186
+	 */
4187
+	public function DKIM_BodyC($body)
4188
+	{
4189
+		if (empty($body)) {
4190
+			return static::$LE;
4191
+		}
4192
+		// Normalize line endings
4193
+		$body = static::normalizeBreaks($body);
4194
+
4195
+		//Reduce multiple trailing line breaks to a single one
4196
+		return rtrim($body, "\r\n") . static::$LE;
4197
+	}
4198
+
4199
+	/**
4200
+	 * Create the DKIM header and body in a new message header.
4201
+	 *
4202
+	 * @param string $headers_line Header lines
4203
+	 * @param string $subject      Subject
4204
+	 * @param string $body         Body
4205
+	 *
4206
+	 * @return string
4207
+	 */
4208
+	public function DKIM_Add($headers_line, $subject, $body)
4209
+	{
4210
+		$DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
4211
+		$DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
4212
+		$DKIMquery = 'dns/txt'; // Query method
4213
+		$DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
4214
+		$subject_header = "Subject: $subject";
4215
+		$headers = explode(static::$LE, $headers_line);
4216
+		$from_header = '';
4217
+		$to_header = '';
4218
+		$date_header = '';
4219
+		$current = '';
4220
+		foreach ($headers as $header) {
4221
+			if (strpos($header, 'From:') === 0) {
4222
+				$from_header = $header;
4223
+				$current = 'from_header';
4224
+			} elseif (strpos($header, 'To:') === 0) {
4225
+				$to_header = $header;
4226
+				$current = 'to_header';
4227
+			} elseif (strpos($header, 'Date:') === 0) {
4228
+				$date_header = $header;
4229
+				$current = 'date_header';
4230
+			} else {
4231
+				if (!empty($$current) and strpos($header, ' =?') === 0) {
4232
+					$$current .= $header;
4233
+				} else {
4234
+					$current = '';
4235
+				}
4236
+			}
4237
+		}
4238
+		$from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
4239
+		$to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
4240
+		$date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
4241
+		$subject = str_replace(
4242
+			'|',
4243
+			'=7C',
4244
+			$this->DKIM_QP($subject_header)
4245
+		); // Copied header fields (dkim-quoted-printable)
4246
+		$body = $this->DKIM_BodyC($body);
4247
+		$DKIMlen = strlen($body); // Length of body
4248
+		$DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
4249
+		if ('' == $this->DKIM_identity) {
4250
+			$ident = '';
4251
+		} else {
4252
+			$ident = ' i=' . $this->DKIM_identity . ';';
4253
+		}
4254
+		$dkimhdrs = 'DKIM-Signature: v=1; a=' .
4255
+			$DKIMsignatureType . '; q=' .
4256
+			$DKIMquery . '; l=' .
4257
+			$DKIMlen . '; s=' .
4258
+			$this->DKIM_selector .
4259
+			";\r\n" .
4260
+			"\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
4261
+			"\th=From:To:Date:Subject;\r\n" .
4262
+			"\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
4263
+			"\tz=$from\r\n" .
4264
+			"\t|$to\r\n" .
4265
+			"\t|$date\r\n" .
4266
+			"\t|$subject;\r\n" .
4267
+			"\tbh=" . $DKIMb64 . ";\r\n" .
4268
+			"\tb=";
4269
+		$toSign = $this->DKIM_HeaderC(
4270
+			$from_header . "\r\n" .
4271
+			$to_header . "\r\n" .
4272
+			$date_header . "\r\n" .
4273
+			$subject_header . "\r\n" .
4274
+			$dkimhdrs
4275
+		);
4276
+		$signed = $this->DKIM_Sign($toSign);
4277
+
4278
+		return static::normalizeBreaks($dkimhdrs . $signed) . static::$LE;
4279
+	}
4280
+
4281
+	/**
4282
+	 * Detect if a string contains a line longer than the maximum line length
4283
+	 * allowed by RFC 2822 section 2.1.1.
4284
+	 *
4285
+	 * @param string $str
4286
+	 *
4287
+	 * @return bool
4288
+	 */
4289
+	public static function hasLineLongerThanMax($str)
4290
+	{
4291
+		return (bool) preg_match('/^(.{' . (self::MAX_LINE_LENGTH + strlen(static::$LE)) . ',})/m', $str);
4292
+	}
4293
+
4294
+	/**
4295
+	 * Allows for public read access to 'to' property.
4296
+	 * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4297
+	 *
4298
+	 * @return array
4299
+	 */
4300
+	public function getToAddresses()
4301
+	{
4302
+		return $this->to;
4303
+	}
4304
+
4305
+	/**
4306
+	 * Allows for public read access to 'cc' property.
4307
+	 * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4308
+	 *
4309
+	 * @return array
4310
+	 */
4311
+	public function getCcAddresses()
4312
+	{
4313
+		return $this->cc;
4314
+	}
4315
+
4316
+	/**
4317
+	 * Allows for public read access to 'bcc' property.
4318
+	 * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4319
+	 *
4320
+	 * @return array
4321
+	 */
4322
+	public function getBccAddresses()
4323
+	{
4324
+		return $this->bcc;
4325
+	}
4326
+
4327
+	/**
4328
+	 * Allows for public read access to 'ReplyTo' property.
4329
+	 * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4330
+	 *
4331
+	 * @return array
4332
+	 */
4333
+	public function getReplyToAddresses()
4334
+	{
4335
+		return $this->ReplyTo;
4336
+	}
4337
+
4338
+	/**
4339
+	 * Allows for public read access to 'all_recipients' property.
4340
+	 * Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4341
+	 *
4342
+	 * @return array
4343
+	 */
4344
+	public function getAllRecipientAddresses()
4345
+	{
4346
+		return $this->all_recipients;
4347
+	}
4348
+
4349
+	/**
4350
+	 * Perform a callback.
4351
+	 *
4352
+	 * @param bool   $isSent
4353
+	 * @param array  $to
4354
+	 * @param array  $cc
4355
+	 * @param array  $bcc
4356
+	 * @param string $subject
4357
+	 * @param string $body
4358
+	 * @param string $from
4359
+	 * @param array  $extra
4360
+	 */
4361
+	protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from, $extra)
4362
+	{
4363
+		if (!empty($this->action_function) and is_callable($this->action_function)) {
4364
+			call_user_func_array($this->action_function, [$isSent, $to, $cc, $bcc, $subject, $body, $from, $extra]);
4365
+		}
4366
+	}
4367
+
4368
+	/**
4369
+	 * Get the OAuth instance.
4370
+	 *
4371
+	 * @return OAuth
4372
+	 */
4373
+	public function getOAuth()
4374
+	{
4375
+		return $this->oauth;
4376
+	}
4377
+
4378
+	/**
4379
+	 * Set an OAuth instance.
4380
+	 *
4381
+	 * @param OAuth $oauth
4382
+	 */
4383
+	public function setOAuth(OAuth $oauth)
4384
+	{
4385
+		$this->oauth = $oauth;
4386
+	}
4387 4387
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -3 removed lines patch added patch discarded remove patch
@@ -3407,7 +3407,7 @@  discard block
 block discarded – undo
3407 3407
     {
3408 3408
         $this->RecipientsQueue = array_filter(
3409 3409
             $this->RecipientsQueue,
3410
-            function ($params) use ($kind) {
3410
+            function($params) use ($kind) {
3411 3411
                 return $params[0] != $kind;
3412 3412
             }
3413 3413
         );
@@ -4116,8 +4116,7 @@  discard block
 block discarded – undo
4116 4116
             return '';
4117 4117
         }
4118 4118
         $privKeyStr = !empty($this->DKIM_private_string) ?
4119
-            $this->DKIM_private_string :
4120
-            file_get_contents($this->DKIM_private);
4119
+            $this->DKIM_private_string : file_get_contents($this->DKIM_private);
4121 4120
         if ('' != $this->DKIM_passphrase) {
4122 4121
             $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
4123 4122
         } else {
Please login to merge, or discard this patch.
www/vendor/PHPMailer/PHPMailer/Exception.php 1 patch
Indentation   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -27,13 +27,13 @@
 block discarded – undo
27 27
  */
28 28
 class Exception extends \Exception
29 29
 {
30
-    /**
31
-     * Prettify error message output.
32
-     *
33
-     * @return string
34
-     */
35
-    public function errorMessage()
36
-    {
37
-        return '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n";
38
-    }
30
+	/**
31
+	 * Prettify error message output.
32
+	 *
33
+	 * @return string
34
+	 */
35
+	public function errorMessage()
36
+	{
37
+		return '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n";
38
+	}
39 39
 }
Please login to merge, or discard this patch.
www/vendor/PHPMailer/PHPMailer/OAuth.php 1 patch
Indentation   +91 added lines, -91 removed lines patch added patch discarded remove patch
@@ -34,105 +34,105 @@
 block discarded – undo
34 34
  */
35 35
 class OAuth
36 36
 {
37
-    /**
38
-     * An instance of the League OAuth Client Provider.
39
-     *
40
-     * @var AbstractProvider
41
-     */
42
-    protected $provider = null;
37
+	/**
38
+	 * An instance of the League OAuth Client Provider.
39
+	 *
40
+	 * @var AbstractProvider
41
+	 */
42
+	protected $provider = null;
43 43
 
44
-    /**
45
-     * The current OAuth access token.
46
-     *
47
-     * @var AccessToken
48
-     */
49
-    protected $oauthToken = null;
44
+	/**
45
+	 * The current OAuth access token.
46
+	 *
47
+	 * @var AccessToken
48
+	 */
49
+	protected $oauthToken = null;
50 50
 
51
-    /**
52
-     * The user's email address, usually used as the login ID
53
-     * and also the from address when sending email.
54
-     *
55
-     * @var string
56
-     */
57
-    protected $oauthUserEmail = '';
51
+	/**
52
+	 * The user's email address, usually used as the login ID
53
+	 * and also the from address when sending email.
54
+	 *
55
+	 * @var string
56
+	 */
57
+	protected $oauthUserEmail = '';
58 58
 
59
-    /**
60
-     * The client secret, generated in the app definition of the service you're connecting to.
61
-     *
62
-     * @var string
63
-     */
64
-    protected $oauthClientSecret = '';
59
+	/**
60
+	 * The client secret, generated in the app definition of the service you're connecting to.
61
+	 *
62
+	 * @var string
63
+	 */
64
+	protected $oauthClientSecret = '';
65 65
 
66
-    /**
67
-     * The client ID, generated in the app definition of the service you're connecting to.
68
-     *
69
-     * @var string
70
-     */
71
-    protected $oauthClientId = '';
66
+	/**
67
+	 * The client ID, generated in the app definition of the service you're connecting to.
68
+	 *
69
+	 * @var string
70
+	 */
71
+	protected $oauthClientId = '';
72 72
 
73
-    /**
74
-     * The refresh token, used to obtain new AccessTokens.
75
-     *
76
-     * @var string
77
-     */
78
-    protected $oauthRefreshToken = '';
73
+	/**
74
+	 * The refresh token, used to obtain new AccessTokens.
75
+	 *
76
+	 * @var string
77
+	 */
78
+	protected $oauthRefreshToken = '';
79 79
 
80
-    /**
81
-     * OAuth constructor.
82
-     *
83
-     * @param array $options Associative array containing
84
-     *                       `provider`, `userName`, `clientSecret`, `clientId` and `refreshToken` elements
85
-     */
86
-    public function __construct($options)
87
-    {
88
-        $this->provider = $options['provider'];
89
-        $this->oauthUserEmail = $options['userName'];
90
-        $this->oauthClientSecret = $options['clientSecret'];
91
-        $this->oauthClientId = $options['clientId'];
92
-        $this->oauthRefreshToken = $options['refreshToken'];
93
-    }
80
+	/**
81
+	 * OAuth constructor.
82
+	 *
83
+	 * @param array $options Associative array containing
84
+	 *                       `provider`, `userName`, `clientSecret`, `clientId` and `refreshToken` elements
85
+	 */
86
+	public function __construct($options)
87
+	{
88
+		$this->provider = $options['provider'];
89
+		$this->oauthUserEmail = $options['userName'];
90
+		$this->oauthClientSecret = $options['clientSecret'];
91
+		$this->oauthClientId = $options['clientId'];
92
+		$this->oauthRefreshToken = $options['refreshToken'];
93
+	}
94 94
 
95
-    /**
96
-     * Get a new RefreshToken.
97
-     *
98
-     * @return RefreshToken
99
-     */
100
-    protected function getGrant()
101
-    {
102
-        return new RefreshToken();
103
-    }
95
+	/**
96
+	 * Get a new RefreshToken.
97
+	 *
98
+	 * @return RefreshToken
99
+	 */
100
+	protected function getGrant()
101
+	{
102
+		return new RefreshToken();
103
+	}
104 104
 
105
-    /**
106
-     * Get a new AccessToken.
107
-     *
108
-     * @return AccessToken
109
-     */
110
-    protected function getToken()
111
-    {
112
-        return $this->provider->getAccessToken(
113
-            $this->getGrant(),
114
-            ['refresh_token' => $this->oauthRefreshToken]
115
-        );
116
-    }
105
+	/**
106
+	 * Get a new AccessToken.
107
+	 *
108
+	 * @return AccessToken
109
+	 */
110
+	protected function getToken()
111
+	{
112
+		return $this->provider->getAccessToken(
113
+			$this->getGrant(),
114
+			['refresh_token' => $this->oauthRefreshToken]
115
+		);
116
+	}
117 117
 
118
-    /**
119
-     * Generate a base64-encoded OAuth token.
120
-     *
121
-     * @return string
122
-     */
123
-    public function getOauth64()
124
-    {
125
-        // Get a new token if it's not available or has expired
126
-        if (null === $this->oauthToken or $this->oauthToken->hasExpired()) {
127
-            $this->oauthToken = $this->getToken();
128
-        }
118
+	/**
119
+	 * Generate a base64-encoded OAuth token.
120
+	 *
121
+	 * @return string
122
+	 */
123
+	public function getOauth64()
124
+	{
125
+		// Get a new token if it's not available or has expired
126
+		if (null === $this->oauthToken or $this->oauthToken->hasExpired()) {
127
+			$this->oauthToken = $this->getToken();
128
+		}
129 129
 
130
-        return base64_encode(
131
-            'user=' .
132
-            $this->oauthUserEmail .
133
-            "\001auth=Bearer " .
134
-            $this->oauthToken .
135
-            "\001\001"
136
-        );
137
-    }
130
+		return base64_encode(
131
+			'user=' .
132
+			$this->oauthUserEmail .
133
+			"\001auth=Bearer " .
134
+			$this->oauthToken .
135
+			"\001\001"
136
+		);
137
+	}
138 138
 }
Please login to merge, or discard this patch.