Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Builtin
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
2 / 2
7
100.00% covered (success)
100.00%
1 / 1
 send
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
6
 stripNewlines
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * MUA (Mail User Agent) implementation using the PHP built-in mail() function.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Matteo Scaramuccia <matteo@phpmyfaq.de>
12 * @copyright 2009-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2009-09-11
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Mail;
21
22/**
23 * Class Builtin
24 *
25 * @package phpMyFAQ\Mail
26 */
27class Builtin implements MailUserAgentInterface
28{
29    /**
30     * Send the message using an email through the PHP built-in mail() function.
31     *
32     * @param string $recipients Recipients of the e-mail as a comma-separated list of RFC 2822 compliant items
33     * @param array<string, int|string|null> $headers Headers of the e-mail
34     * @param string $body Body of the e-mail
35     */
36    public function send(string $recipients, array $headers, string $body): int
37    {
38        // Get the subject of the e-mail, RFC 2047 compliant. Strip CR/LF defensively so an
39        // untrusted value can never inject additional headers, independent of upstream validation.
40        $subject = self::stripNewlines((string) $headers['Subject']);
41        $headers['Subject'] = null;
42        unset($headers['Subject']);
43
44        $sender = '';
45        if (
46            'WIN' !== strtoupper(substr(PHP_OS, offset: 0, length: 3))
47            && !ini_get('safe_mode')
48            && array_key_exists('Return-Path', $headers)
49        ) {
50            $sender = str_replace(['<', '>'], replace: '', subject: (string) $headers['Return-Path']);
51            unset($headers['Return-Path']);
52        }
53
54        // Prepare the headers for the email
55        $mailHeaders = '';
56        foreach ($headers as $key => $value) {
57            $mailHeaders .= self::stripNewlines((string) $key) . ': ' . self::stripNewlines((string) $value) . PHP_EOL;
58        }
59
60        // Send the email
61        if ($sender === '') {
62            return (int) mail($recipients, $subject, $body, $mailHeaders);
63        }
64
65        return (int) mail($recipients, $subject, $body, $mailHeaders, '-f' . $sender);
66    }
67
68    /**
69     * Removes CR and LF characters to prevent e-mail header (CRLF) injection.
70     */
71    private static function stripNewlines(string $value): string
72    {
73        return str_replace(["\r", "\n"], replace: '', subject: $value);
74    }
75}