Skip to content

Commit

Permalink
Fix GH-8086: Introduce mail.mixed_lf_and_crlf INI
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bukka committed Jan 19, 2023
1 parent 4d8f981 commit cc931af
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 11 deletions.
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 10 additions & 6 deletions ext/mbstring/mbstring.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand All @@ -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);
Expand Down
33 changes: 33 additions & 0 deletions ext/mbstring/tests/gh8086.phpt
Original file line number Diff line number Diff line change
@@ -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 = "[email protected]";
$subject = "test mail";
$message = "body of testing php mail";
$header["Mime-Version"] = "1.0";
$header["Content-Type"] = "text/html; charset=UTF-8";
$header["From"] = "[email protected]";
$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)
12 changes: 7 additions & 5 deletions ext/standard/mail.c
Original file line number Diff line number Diff line change
Expand Up @@ -429,14 +429,16 @@ 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;

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));
}
Expand Down Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions ext/standard/tests/mail/gh8086.phpt
Original file line number Diff line number Diff line change
@@ -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('[email protected]', '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)
1 change: 1 addition & 0 deletions main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions main/php_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions php.ini-development
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
4 changes: 4 additions & 0 deletions php.ini-production
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down

0 comments on commit cc931af

Please sign in to comment.