Skip to content

Commit cc931af

Browse files
committedJan 19, 2023
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.
1 parent 4d8f981 commit cc931af

File tree

10 files changed

+83
-11
lines changed

10 files changed

+83
-11
lines changed
 

‎NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ PHP NEWS
88
- GMP:
99
. Properly implement GMP::__construct(). (nielsdos)
1010

11+
- Standard:
12+
- Fixed bug GH-8086 (Introduce mail.mixed_lf_and_crlf INI). (Jakub Zelenka)
13+
1114
02 Feb 2023, PHP 8.2.2
1215

1316
- Core:

‎UPGRADING

+2
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ PHP 8.2 UPGRADE NOTES
230230
- Standard
231231
. unserialize() now performs a stricter validation of the structure of serialized
232232
objects.
233+
. mail() function reverts back to the mixed LF and CRLF new lines (behavior
234+
before PHP 8.0) if mail.mixed_lf_and_crlf INI is on.
233235

234236
- XML
235237
. xml_parser_set_option() now actually returns false when attempting to set a

‎ext/mbstring/mbstring.c

+10-6
Original file line numberDiff line numberDiff line change
@@ -4128,7 +4128,9 @@ PHP_FUNCTION(mb_send_mail)
41284128
|| orig_str.encoding->no_encoding == mbfl_no_encoding_pass) {
41294129
orig_str.encoding = mbfl_identify_encoding(&orig_str, MBSTRG(current_detect_order_list), MBSTRG(current_detect_order_list_size), MBSTRG(strict_detection));
41304130
}
4131-
pstr = mbfl_mime_header_encode(&orig_str, &conv_str, tran_cs, head_enc, CRLF, sizeof("Subject: [PHP-jp nnnnnnnn]" CRLF) - 1);
4131+
const char *line_sep = PG(mail_mixed_lf_and_crlf) ? "\n" : CRLF;
4132+
size_t line_sep_len = strlen(line_sep);
4133+
pstr = mbfl_mime_header_encode(&orig_str, &conv_str, tran_cs, head_enc, line_sep, strlen("Subject: [PHP-jp nnnnnnnn]") + line_sep_len);
41324134
if (pstr != NULL) {
41334135
subject_buf = subject = (char *)pstr->val;
41344136
}
@@ -4167,14 +4169,14 @@ PHP_FUNCTION(mb_send_mail)
41674169
n = ZSTR_LEN(str_headers);
41684170
mbfl_memory_device_strncat(&device, p, n);
41694171
if (n > 0 && p[n - 1] != '\n') {
4170-
mbfl_memory_device_strncat(&device, CRLF, sizeof(CRLF)-1);
4172+
mbfl_memory_device_strncat(&device, line_sep, line_sep_len);
41714173
}
41724174
zend_string_release_ex(str_headers, 0);
41734175
}
41744176

41754177
if (!zend_hash_str_exists(&ht_headers, "mime-version", sizeof("mime-version") - 1)) {
41764178
mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER1, sizeof(PHP_MBSTR_MAIL_MIME_HEADER1) - 1);
4177-
mbfl_memory_device_strncat(&device, CRLF, sizeof(CRLF)-1);
4179+
mbfl_memory_device_strncat(&device, line_sep, line_sep_len);
41784180
}
41794181

41804182
if (!suppressed_hdrs.cnt_type) {
@@ -4185,7 +4187,7 @@ PHP_FUNCTION(mb_send_mail)
41854187
mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER3, sizeof(PHP_MBSTR_MAIL_MIME_HEADER3) - 1);
41864188
mbfl_memory_device_strcat(&device, p);
41874189
}
4188-
mbfl_memory_device_strncat(&device, CRLF, sizeof(CRLF)-1);
4190+
mbfl_memory_device_strncat(&device, line_sep, line_sep_len);
41894191
}
41904192
if (!suppressed_hdrs.cnt_trans_enc) {
41914193
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)
41944196
p = "7bit";
41954197
}
41964198
mbfl_memory_device_strcat(&device, p);
4197-
mbfl_memory_device_strncat(&device, CRLF, sizeof(CRLF)-1);
4199+
mbfl_memory_device_strncat(&device, line_sep, line_sep_len);
41984200
}
41994201

4200-
mbfl_memory_device_unput(&device);
4202+
if (!PG(mail_mixed_lf_and_crlf)) {
4203+
mbfl_memory_device_unput(&device);
4204+
}
42014205
mbfl_memory_device_unput(&device);
42024206
mbfl_memory_device_output('\0', &device);
42034207
str_headers = zend_string_init((char *)device.buffer, strlen((char *)device.buffer), 0);

‎ext/mbstring/tests/gh8086.phpt

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
GH-8086 (mb_send_mail() function not working correctly in PHP 8.x)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("mbstring")) die("skip mbstring extension not available");
6+
?>
7+
--INI--
8+
sendmail_path={MAIL:{PWD}/gh8086.eml}
9+
mail.mixed_lf_and_crlf=on
10+
--FILE--
11+
<?php
12+
mb_internal_encoding("UTF-8");
13+
mb_language("uni");
14+
$to = "omittedvalidaddress@example.com";
15+
$subject = "test mail";
16+
$message = "body of testing php mail";
17+
$header["Mime-Version"] = "1.0";
18+
$header["Content-Type"] = "text/html; charset=UTF-8";
19+
$header["From"] = "omittedvalidaddress2@example.com";
20+
$header["X-Mailer"] = "PHP/" . phpversion();
21+
mb_send_mail($to, $subject, $message, $header);
22+
23+
$stream = fopen(__DIR__ . "/gh8086.eml", "rb");
24+
$eml = stream_get_contents($stream);
25+
fclose($stream);
26+
var_dump(preg_match_all('/(?<!\r)\n/', $eml));
27+
?>
28+
--CLEAN--
29+
<?php
30+
@unlink(__DIR__ . "/gh8086.eml");
31+
?>
32+
--EXPECT--
33+
int(6)

‎ext/standard/mail.c

+7-5
Original file line numberDiff line numberDiff line change
@@ -429,14 +429,16 @@ PHPAPI int php_mail(const char *to, const char *subject, const char *message, co
429429
MAIL_RET(0);
430430
}
431431

432+
char *line_sep = PG(mail_mixed_lf_and_crlf) ? "\n" : "\r\n";
433+
432434
if (PG(mail_x_header)) {
433435
const char *tmp = zend_get_executed_filename();
434436
zend_string *f;
435437

436438
f = php_basename(tmp, strlen(tmp), NULL, 0);
437439

438440
if (headers != NULL && *headers) {
439-
spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s\r\n%s", php_getuid(), ZSTR_VAL(f), headers);
441+
spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s%s%s", php_getuid(), ZSTR_VAL(f), line_sep, headers);
440442
} else {
441443
spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s", php_getuid(), ZSTR_VAL(f));
442444
}
@@ -510,12 +512,12 @@ PHPAPI int php_mail(const char *to, const char *subject, const char *message, co
510512
MAIL_RET(0);
511513
}
512514
#endif
513-
fprintf(sendmail, "To: %s\r\n", to);
514-
fprintf(sendmail, "Subject: %s\r\n", subject);
515+
fprintf(sendmail, "To: %s%s", to, line_sep);
516+
fprintf(sendmail, "Subject: %s%s", subject, line_sep);
515517
if (hdr != NULL) {
516-
fprintf(sendmail, "%s\r\n", hdr);
518+
fprintf(sendmail, "%s%s", hdr, line_sep);
517519
}
518-
fprintf(sendmail, "\r\n%s\r\n", message);
520+
fprintf(sendmail, "%s%s%s", line_sep, message, line_sep);
519521
ret = pclose(sendmail);
520522

521523
#if PHP_SIGCHILD

‎ext/standard/tests/mail/gh8086.phpt

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
GH-8086 (Mail() function not working correctly in PHP 8.x)
3+
--INI--
4+
sendmail_path={MAIL:gh8086.out}
5+
mail.mixed_lf_and_crlf=on
6+
--FILE--
7+
<?php
8+
var_dump(mail('user@example.com', 'Test Subject', 'A Message', 'KHeaders'));
9+
$mail = file_get_contents('gh8086.out');
10+
var_dump(preg_match_all('/(?<!\r)\n/', $mail));
11+
?>
12+
--CLEAN--
13+
<?php
14+
unlink('gh8086.out');
15+
?>
16+
--EXPECT--
17+
bool(true)
18+
int(5)

‎main/main.c

+1
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ PHP_INI_BEGIN()
737737
PHP_INI_ENTRY("SMTP", "localhost",PHP_INI_ALL, NULL)
738738
PHP_INI_ENTRY("smtp_port", "25", PHP_INI_ALL, NULL)
739739
STD_PHP_INI_BOOLEAN("mail.add_x_header", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, mail_x_header, php_core_globals, core_globals)
740+
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)
740741
STD_PHP_INI_ENTRY("mail.log", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateMailLog, mail_log, php_core_globals, core_globals)
741742
PHP_INI_ENTRY("browscap", NULL, PHP_INI_SYSTEM, OnChangeBrowscap)
742743
PHP_INI_ENTRY("memory_limit", "128M", PHP_INI_ALL, OnChangeMemoryLimit)

‎main/php_globals.h

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ struct _php_core_globals {
153153
char *request_order;
154154

155155
bool mail_x_header;
156+
bool mail_mixed_lf_and_crlf;
156157
char *mail_log;
157158

158159
bool in_error_log;

‎php.ini-development

+4
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,10 @@ smtp_port = 25
10951095
; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename
10961096
mail.add_x_header = Off
10971097

1098+
; Use mixed LF and CRLF line separators to keep compatibility with some
1099+
; RFC 2822 non conformant MTA.
1100+
mail.mixed_lf_and_crlf = Off
1101+
10981102
; The path to a log file that will log all mail() calls. Log entries include
10991103
; the full path of the script, line number, To address and headers.
11001104
;mail.log =

‎php.ini-production

+4
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,10 @@ smtp_port = 25
10971097
; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename
10981098
mail.add_x_header = Off
10991099

1100+
; Use mixed LF and CRLF line separators to keep compatibility with some
1101+
; RFC 2822 non conformant MTA.
1102+
mail.mixed_lf_and_crlf = Off
1103+
11001104
; The path to a log file that will log all mail() calls. Log entries include
11011105
; the full path of the script, line number, To address and headers.
11021106
;mail.log =

0 commit comments

Comments
 (0)