Fix GH-8086: Introduce mail.mixed_lf_and_crlf INI

When this INI option is enabled, it reverts the line separator for
headers and message to LF which was a non conformant behavior in PHP 7.
It is done because some non conformant MTAs fail to parse CRLF line
separator for headers and body.

This is used for mail and mb_send_mail functions.
This commit is contained in:
Jakub Zelenka 2022-12-30 15:09:48 +00:00
parent 4d8f981818
commit cc931af35d
No known key found for this signature in database
GPG Key ID: 1C0779DC5C0A9DE4
10 changed files with 83 additions and 11 deletions

3
NEWS
View File

@ -8,6 +8,9 @@ PHP NEWS
- GMP:
. Properly implement GMP::__construct(). (nielsdos)
- Standard:
- Fixed bug GH-8086 (Introduce mail.mixed_lf_and_crlf INI). (Jakub Zelenka)
02 Feb 2023, PHP 8.2.2
- Core:

View File

@ -230,6 +230,8 @@ PHP 8.2 UPGRADE NOTES
- Standard
. unserialize() now performs a stricter validation of the structure of serialized
objects.
. mail() function reverts back to the mixed LF and CRLF new lines (behavior
before PHP 8.0) if mail.mixed_lf_and_crlf INI is on.
- XML
. xml_parser_set_option() now actually returns false when attempting to set a

View File

@ -4128,7 +4128,9 @@ PHP_FUNCTION(mb_send_mail)
|| orig_str.encoding->no_encoding == mbfl_no_encoding_pass) {
orig_str.encoding = mbfl_identify_encoding(&orig_str, MBSTRG(current_detect_order_list), MBSTRG(current_detect_order_list_size), MBSTRG(strict_detection));
}
pstr = mbfl_mime_header_encode(&orig_str, &conv_str, tran_cs, head_enc, CRLF, sizeof("Subject: [PHP-jp nnnnnnnn]" CRLF) - 1);
const char *line_sep = PG(mail_mixed_lf_and_crlf) ? "\n" : CRLF;
size_t line_sep_len = strlen(line_sep);
pstr = mbfl_mime_header_encode(&orig_str, &conv_str, tran_cs, head_enc, line_sep, strlen("Subject: [PHP-jp nnnnnnnn]") + line_sep_len);
if (pstr != NULL) {
subject_buf = subject = (char *)pstr->val;
}
@ -4167,14 +4169,14 @@ PHP_FUNCTION(mb_send_mail)
n = ZSTR_LEN(str_headers);
mbfl_memory_device_strncat(&device, p, n);
if (n > 0 && p[n - 1] != '\n') {
mbfl_memory_device_strncat(&device, CRLF, sizeof(CRLF)-1);
mbfl_memory_device_strncat(&device, line_sep, line_sep_len);
}
zend_string_release_ex(str_headers, 0);
}
if (!zend_hash_str_exists(&ht_headers, "mime-version", sizeof("mime-version") - 1)) {
mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER1, sizeof(PHP_MBSTR_MAIL_MIME_HEADER1) - 1);
mbfl_memory_device_strncat(&device, CRLF, sizeof(CRLF)-1);
mbfl_memory_device_strncat(&device, line_sep, line_sep_len);
}
if (!suppressed_hdrs.cnt_type) {
@ -4185,7 +4187,7 @@ PHP_FUNCTION(mb_send_mail)
mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER3, sizeof(PHP_MBSTR_MAIL_MIME_HEADER3) - 1);
mbfl_memory_device_strcat(&device, p);
}
mbfl_memory_device_strncat(&device, CRLF, sizeof(CRLF)-1);
mbfl_memory_device_strncat(&device, line_sep, line_sep_len);
}
if (!suppressed_hdrs.cnt_trans_enc) {
mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER4, sizeof(PHP_MBSTR_MAIL_MIME_HEADER4) - 1);
@ -4194,10 +4196,12 @@ PHP_FUNCTION(mb_send_mail)
p = "7bit";
}
mbfl_memory_device_strcat(&device, p);
mbfl_memory_device_strncat(&device, CRLF, sizeof(CRLF)-1);
mbfl_memory_device_strncat(&device, line_sep, line_sep_len);
}
mbfl_memory_device_unput(&device);
if (!PG(mail_mixed_lf_and_crlf)) {
mbfl_memory_device_unput(&device);
}
mbfl_memory_device_unput(&device);
mbfl_memory_device_output('\0', &device);
str_headers = zend_string_init((char *)device.buffer, strlen((char *)device.buffer), 0);

View File

@ -0,0 +1,33 @@
--TEST--
GH-8086 (mb_send_mail() function not working correctly in PHP 8.x)
--SKIPIF--
<?php
if (!extension_loaded("mbstring")) die("skip mbstring extension not available");
?>
--INI--
sendmail_path={MAIL:{PWD}/gh8086.eml}
mail.mixed_lf_and_crlf=on
--FILE--
<?php
mb_internal_encoding("UTF-8");
mb_language("uni");
$to = "omittedvalidaddress@example.com";
$subject = "test mail";
$message = "body of testing php mail";
$header["Mime-Version"] = "1.0";
$header["Content-Type"] = "text/html; charset=UTF-8";
$header["From"] = "omittedvalidaddress2@example.com";
$header["X-Mailer"] = "PHP/" . phpversion();
mb_send_mail($to, $subject, $message, $header);
$stream = fopen(__DIR__ . "/gh8086.eml", "rb");
$eml = stream_get_contents($stream);
fclose($stream);
var_dump(preg_match_all('/(?<!\r)\n/', $eml));
?>
--CLEAN--
<?php
@unlink(__DIR__ . "/gh8086.eml");
?>
--EXPECT--
int(6)

View File

@ -429,6 +429,8 @@ PHPAPI int php_mail(const char *to, const char *subject, const char *message, co
MAIL_RET(0);
}
char *line_sep = PG(mail_mixed_lf_and_crlf) ? "\n" : "\r\n";
if (PG(mail_x_header)) {
const char *tmp = zend_get_executed_filename();
zend_string *f;
@ -436,7 +438,7 @@ PHPAPI int php_mail(const char *to, const char *subject, const char *message, co
f = php_basename(tmp, strlen(tmp), NULL, 0);
if (headers != NULL && *headers) {
spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s\r\n%s", php_getuid(), ZSTR_VAL(f), headers);
spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s%s%s", php_getuid(), ZSTR_VAL(f), line_sep, headers);
} else {
spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s", php_getuid(), ZSTR_VAL(f));
}
@ -510,12 +512,12 @@ PHPAPI int php_mail(const char *to, const char *subject, const char *message, co
MAIL_RET(0);
}
#endif
fprintf(sendmail, "To: %s\r\n", to);
fprintf(sendmail, "Subject: %s\r\n", subject);
fprintf(sendmail, "To: %s%s", to, line_sep);
fprintf(sendmail, "Subject: %s%s", subject, line_sep);
if (hdr != NULL) {
fprintf(sendmail, "%s\r\n", hdr);
fprintf(sendmail, "%s%s", hdr, line_sep);
}
fprintf(sendmail, "\r\n%s\r\n", message);
fprintf(sendmail, "%s%s%s", line_sep, message, line_sep);
ret = pclose(sendmail);
#if PHP_SIGCHILD

View File

@ -0,0 +1,18 @@
--TEST--
GH-8086 (Mail() function not working correctly in PHP 8.x)
--INI--
sendmail_path={MAIL:gh8086.out}
mail.mixed_lf_and_crlf=on
--FILE--
<?php
var_dump(mail('user@example.com', 'Test Subject', 'A Message', 'KHeaders'));
$mail = file_get_contents('gh8086.out');
var_dump(preg_match_all('/(?<!\r)\n/', $mail));
?>
--CLEAN--
<?php
unlink('gh8086.out');
?>
--EXPECT--
bool(true)
int(5)

View File

@ -737,6 +737,7 @@ PHP_INI_BEGIN()
PHP_INI_ENTRY("SMTP", "localhost",PHP_INI_ALL, NULL)
PHP_INI_ENTRY("smtp_port", "25", PHP_INI_ALL, NULL)
STD_PHP_INI_BOOLEAN("mail.add_x_header", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, mail_x_header, php_core_globals, core_globals)
STD_PHP_INI_BOOLEAN("mail.mixed_lf_and_crlf", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, mail_mixed_lf_and_crlf, php_core_globals, core_globals)
STD_PHP_INI_ENTRY("mail.log", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateMailLog, mail_log, php_core_globals, core_globals)
PHP_INI_ENTRY("browscap", NULL, PHP_INI_SYSTEM, OnChangeBrowscap)
PHP_INI_ENTRY("memory_limit", "128M", PHP_INI_ALL, OnChangeMemoryLimit)

View File

@ -153,6 +153,7 @@ struct _php_core_globals {
char *request_order;
bool mail_x_header;
bool mail_mixed_lf_and_crlf;
char *mail_log;
bool in_error_log;

View File

@ -1095,6 +1095,10 @@ smtp_port = 25
; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename
mail.add_x_header = Off
; Use mixed LF and CRLF line separators to keep compatibility with some
; RFC 2822 non conformant MTA.
mail.mixed_lf_and_crlf = Off
; The path to a log file that will log all mail() calls. Log entries include
; the full path of the script, line number, To address and headers.
;mail.log =

View File

@ -1097,6 +1097,10 @@ smtp_port = 25
; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename
mail.add_x_header = Off
; Use mixed LF and CRLF line separators to keep compatibility with some
; RFC 2822 non conformant MTA.
mail.mixed_lf_and_crlf = Off
; The path to a log file that will log all mail() calls. Log entries include
; the full path of the script, line number, To address and headers.
;mail.log =