mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix GH-19188: Add support for new INI mail.cr_lf_mode (#19238)
This commit is contained in:
committed by
GitHub
parent
4432083f38
commit
ae7def78fb
2
NEWS
2
NEWS
@@ -57,6 +57,8 @@ PHP NEWS
|
|||||||
(Girgias)
|
(Girgias)
|
||||||
. Fixed bug GH-19577 (Avoid integer overflow when using a small offset
|
. Fixed bug GH-19577 (Avoid integer overflow when using a small offset
|
||||||
and PHP_INT_MAX with LimitIterator). (alexandre-daubois)
|
and PHP_INT_MAX with LimitIterator). (alexandre-daubois)
|
||||||
|
. Implement GH-19188: Add support for new INI mail.cr_lf_mode.
|
||||||
|
(alexandre-daubois)
|
||||||
|
|
||||||
- Streams:
|
- Streams:
|
||||||
. Fixed bug GH-14506 (Closing a userspace stream inside a userspace handler
|
. Fixed bug GH-14506 (Closing a userspace stream inside a userspace handler
|
||||||
|
|||||||
@@ -494,7 +494,27 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c
|
|||||||
MAIL_RET(false);
|
MAIL_RET(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *line_sep = PG(mail_mixed_lf_and_crlf) ? "\n" : "\r\n";
|
char *line_sep;
|
||||||
|
const char *cr_lf_mode = PG(mail_cr_lf_mode);
|
||||||
|
|
||||||
|
if (cr_lf_mode && strcmp(cr_lf_mode, "crlf") != 0) {
|
||||||
|
if (strcmp(cr_lf_mode, "lf") == 0) {
|
||||||
|
line_sep = "\n";
|
||||||
|
} else if (strcmp(cr_lf_mode, "mixed") == 0) {
|
||||||
|
line_sep = "\n";
|
||||||
|
} else if (strcmp(cr_lf_mode, "os") == 0) {
|
||||||
|
#ifdef PHP_WIN32
|
||||||
|
line_sep = "\r\n";
|
||||||
|
#else
|
||||||
|
line_sep = "\n";
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
ZEND_ASSERT(0 && "Unexpected cr_lf_mode value");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* CRLF is default mode, but respect mail.mixed_lf_and_crlf for backward compatibility */
|
||||||
|
line_sep = PG(mail_mixed_lf_and_crlf) ? "\n" : "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
if (PG(mail_x_header)) {
|
if (PG(mail_x_header)) {
|
||||||
const char *tmp = zend_get_executed_filename();
|
const char *tmp = zend_get_executed_filename();
|
||||||
@@ -586,7 +606,43 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c
|
|||||||
if (hdr != NULL) {
|
if (hdr != NULL) {
|
||||||
fprintf(sendmail, "%s%s", hdr, line_sep);
|
fprintf(sendmail, "%s%s", hdr, line_sep);
|
||||||
}
|
}
|
||||||
fprintf(sendmail, "%s%s%s", line_sep, message, line_sep);
|
|
||||||
|
fprintf(sendmail, "%s", line_sep);
|
||||||
|
|
||||||
|
if (cr_lf_mode && strcmp(cr_lf_mode, "lf") == 0) {
|
||||||
|
char *converted_message = NULL;
|
||||||
|
size_t msg_len = strlen(message);
|
||||||
|
size_t new_len = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < msg_len - 1; ++i) {
|
||||||
|
if (message[i] == '\r' && message[i + 1] == '\n') {
|
||||||
|
++new_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_len == 0) {
|
||||||
|
fprintf(sendmail, "%s", message);
|
||||||
|
} else {
|
||||||
|
converted_message = emalloc(msg_len - new_len + 1);
|
||||||
|
size_t j = 0;
|
||||||
|
for (size_t i = 0; i < msg_len; ++i) {
|
||||||
|
if (i < msg_len - 1 && message[i] == '\r' && message[i + 1] == '\n') {
|
||||||
|
converted_message[j++] = '\n';
|
||||||
|
++i; /* skip LF part */
|
||||||
|
} else {
|
||||||
|
converted_message[j++] = message[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
converted_message[j] = '\0';
|
||||||
|
fprintf(sendmail, "%s", converted_message);
|
||||||
|
efree(converted_message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(sendmail, "%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(sendmail, "%s", line_sep);
|
||||||
#ifdef PHP_WIN32
|
#ifdef PHP_WIN32
|
||||||
ret = pclose(sendmail);
|
ret = pclose(sendmail);
|
||||||
|
|
||||||
|
|||||||
23
ext/standard/tests/mail/gh19188_cr_lf_mode.phpt
Normal file
23
ext/standard/tests/mail/gh19188_cr_lf_mode.phpt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19188: new INI mail.cr_lf_mode
|
||||||
|
--INI--
|
||||||
|
sendmail_path={MAIL:gh19188_cr_lf_mode.out}
|
||||||
|
mail.cr_lf_mode=crlf
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
var_dump(mail('user@example.com', 'Test Subject', 'A Message', 'X-Test: crlf'));
|
||||||
|
$mail = file_get_contents('gh19188_cr_lf_mode.out');
|
||||||
|
echo "CRLF mode:\n";
|
||||||
|
var_dump(preg_match_all('/\r\n/', $mail));
|
||||||
|
var_dump(preg_match_all('/(?<!\r)\n/', $mail));
|
||||||
|
?>
|
||||||
|
--CLEAN--
|
||||||
|
<?php
|
||||||
|
@unlink('gh19188_cr_lf_mode.out');
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
|
CRLF mode:
|
||||||
|
int(5)
|
||||||
|
int(0)
|
||||||
26
ext/standard/tests/mail/gh19188_invalid_mode.phpt
Normal file
26
ext/standard/tests/mail/gh19188_invalid_mode.phpt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19188: mail.cr_lf_mode runtime changes should fail
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
var_dump(ini_set('mail.cr_lf_mode', 'lf'));
|
||||||
|
var_dump(ini_get('mail.cr_lf_mode'));
|
||||||
|
|
||||||
|
var_dump(ini_set('mail.cr_lf_mode', 'mixed'));
|
||||||
|
var_dump(ini_get('mail.cr_lf_mode'));
|
||||||
|
|
||||||
|
var_dump(ini_set('mail.cr_lf_mode', 'os'));
|
||||||
|
var_dump(ini_get('mail.cr_lf_mode'));
|
||||||
|
|
||||||
|
var_dump(ini_set('mail.cr_lf_mode', 'invalid'));
|
||||||
|
var_dump(ini_get('mail.cr_lf_mode'));
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(false)
|
||||||
|
string(4) "crlf"
|
||||||
|
bool(false)
|
||||||
|
string(4) "crlf"
|
||||||
|
bool(false)
|
||||||
|
string(4) "crlf"
|
||||||
|
bool(false)
|
||||||
|
string(4) "crlf"
|
||||||
23
ext/standard/tests/mail/gh19188_lf_mode.phpt
Normal file
23
ext/standard/tests/mail/gh19188_lf_mode.phpt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19188: mail.cr_lf_mode=lf
|
||||||
|
--INI--
|
||||||
|
sendmail_path={MAIL:gh19188_lf_mode.out}
|
||||||
|
mail.cr_lf_mode=lf
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
var_dump(mail('user@example.com', 'Test Subject', "A Message\r\nWith CRLF", 'X-Test: lf'));
|
||||||
|
$mail = file_get_contents('gh19188_lf_mode.out');
|
||||||
|
echo "LF mode:\n";
|
||||||
|
var_dump(preg_match_all('/\r\n/', $mail));
|
||||||
|
var_dump(preg_match_all('/(?<!\r)\n/', $mail));
|
||||||
|
?>
|
||||||
|
--CLEAN--
|
||||||
|
<?php
|
||||||
|
@unlink('gh19188_lf_mode.out');
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
|
LF mode:
|
||||||
|
int(0)
|
||||||
|
int(6)
|
||||||
20
ext/standard/tests/mail/gh19188_mixed_mode.phpt
Normal file
20
ext/standard/tests/mail/gh19188_mixed_mode.phpt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19188: mail.cr_lf_mode=mixed
|
||||||
|
--INI--
|
||||||
|
sendmail_path={MAIL:gh19188_mixed_mode.out}
|
||||||
|
mail.cr_lf_mode=mixed
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
var_dump(mail('user@example.com', 'Test Subject', 'A Message', 'X-Test: mixed'));
|
||||||
|
$mail = file_get_contents('gh19188_mixed_mode.out');
|
||||||
|
echo "Mixed mode:\n";
|
||||||
|
var_dump(preg_match_all('/(?<!\r)\n/', $mail));
|
||||||
|
?>
|
||||||
|
--CLEAN--
|
||||||
|
<?php
|
||||||
|
@unlink('gh19188_mixed_mode.out');
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
|
Mixed mode:
|
||||||
|
int(5)
|
||||||
30
ext/standard/tests/mail/gh19188_os_mode_unix.phpt
Normal file
30
ext/standard/tests/mail/gh19188_os_mode_unix.phpt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19188: mail.cr_lf_mode=os (Unix)
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (PHP_OS_FAMILY === 'Windows') die("skip Non-Windows only");
|
||||||
|
?>
|
||||||
|
--INI--
|
||||||
|
sendmail_path={MAIL:gh19188_os_mode.out}
|
||||||
|
mail.cr_lf_mode=os
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
var_dump(mail('user@example.com', 'Test Subject', 'A Message', 'X-Test: os'));
|
||||||
|
$mail = file_get_contents('gh19188_os_mode.out');
|
||||||
|
echo "OS mode:\n";
|
||||||
|
$crlf_count = preg_match_all('/\r\n/', $mail);
|
||||||
|
$lf_only_count = preg_match_all('/(?<!\r)\n/', $mail);
|
||||||
|
echo "CRLF count: ";
|
||||||
|
var_dump($crlf_count);
|
||||||
|
echo "LF-only count: ";
|
||||||
|
var_dump($lf_only_count);
|
||||||
|
?>
|
||||||
|
--CLEAN--
|
||||||
|
<?php
|
||||||
|
@unlink('gh19188_os_mode.out');
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
|
OS mode:
|
||||||
|
CRLF count: int(0)
|
||||||
|
LF-only count: int(5)
|
||||||
31
ext/standard/tests/mail/gh19188_os_mode_windows.phpt
Normal file
31
ext/standard/tests/mail/gh19188_os_mode_windows.phpt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19188: mail.cr_lf_mode=os (Windows)
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (PHP_OS_FAMILY !== 'Windows') die("skip Windows only");
|
||||||
|
?>
|
||||||
|
--INI--
|
||||||
|
sendmail_path={MAIL:gh19188_os_mode.out}
|
||||||
|
mail.cr_lf_mode=os
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
var_dump(mail('user@example.com', 'Test Subject', 'A Message', 'X-Test: os'));
|
||||||
|
$mail = file_get_contents('gh19188_os_mode.out');
|
||||||
|
echo "OS mode:\n";
|
||||||
|
$crlf_count = preg_match_all('/\r\n/', $mail);
|
||||||
|
$lf_only_count = preg_match_all('/(?<!\r)\n/', $mail);
|
||||||
|
echo "CRLF count: ";
|
||||||
|
var_dump($crlf_count);
|
||||||
|
echo "LF-only count: ";
|
||||||
|
var_dump($lf_only_count);
|
||||||
|
?>
|
||||||
|
--CLEAN--
|
||||||
|
<?php
|
||||||
|
@unlink('gh19188_os_mode.out');
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
|
OS mode:
|
||||||
|
CRLF count: int(5)
|
||||||
|
LF-only count: int(0)
|
||||||
31
main/main.c
31
main/main.c
@@ -721,6 +721,36 @@ static PHP_INI_MH(OnUpdateMailLog)
|
|||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ PHP_INI_MH */
|
||||||
|
static PHP_INI_MH(OnUpdateMailCrLfMode)
|
||||||
|
{
|
||||||
|
if (new_value) {
|
||||||
|
const char *val = ZSTR_VAL(new_value);
|
||||||
|
if (ZSTR_LEN(new_value) > 0 &&
|
||||||
|
strcmp(val, "crlf") != 0 &&
|
||||||
|
strcmp(val, "lf") != 0 &&
|
||||||
|
strcmp(val, "mixed") != 0 &&
|
||||||
|
strcmp(val, "os") != 0) {
|
||||||
|
int err_type;
|
||||||
|
|
||||||
|
if (stage == ZEND_INI_STAGE_RUNTIME) {
|
||||||
|
err_type = E_WARNING;
|
||||||
|
} else {
|
||||||
|
err_type = E_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage != ZEND_INI_STAGE_DEACTIVATE) {
|
||||||
|
php_error_docref(NULL, err_type, "Invalid value \"%s\" for mail.cr_lf_mode. Must be one of: \"crlf\", \"lf\", \"mixed\", \"os\"", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ PHP_INI_MH */
|
/* {{{ PHP_INI_MH */
|
||||||
static PHP_INI_MH(OnChangeMailForceExtra)
|
static PHP_INI_MH(OnChangeMailForceExtra)
|
||||||
{
|
{
|
||||||
@@ -826,6 +856,7 @@ PHP_INI_BEGIN()
|
|||||||
PHP_INI_ENTRY("smtp_port", "25", 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.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_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.cr_lf_mode", "crlf", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateMailCrLfMode, mail_cr_lf_mode, 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)
|
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("browscap", NULL, PHP_INI_SYSTEM, OnChangeBrowscap)
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ struct _php_core_globals {
|
|||||||
char *mail_log;
|
char *mail_log;
|
||||||
bool mail_x_header;
|
bool mail_x_header;
|
||||||
bool mail_mixed_lf_and_crlf;
|
bool mail_mixed_lf_and_crlf;
|
||||||
|
char *mail_cr_lf_mode;
|
||||||
|
|
||||||
bool in_error_log;
|
bool in_error_log;
|
||||||
|
|
||||||
|
|||||||
@@ -1086,6 +1086,14 @@ mail.add_x_header = Off
|
|||||||
; RFC 2822 non conformant MTA.
|
; RFC 2822 non conformant MTA.
|
||||||
mail.mixed_lf_and_crlf = Off
|
mail.mixed_lf_and_crlf = Off
|
||||||
|
|
||||||
|
; Control line ending mode for mail messages and headers.
|
||||||
|
; Possible values: "crlf" (default), "lf", "mixed", "os"
|
||||||
|
; - crlf: Use CRLF line endings
|
||||||
|
; - lf: Use LF line endings only (converts CRLF in message to LF)
|
||||||
|
; - mixed: Same as mail.mixed_lf_and_crlf = On
|
||||||
|
; - os: Use CRLF on Windows, LF on other systems
|
||||||
|
mail.cr_lf_mode = crlf
|
||||||
|
|
||||||
; The path to a log file that will log all mail() calls. Log entries include
|
; 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.
|
; the full path of the script, line number, To address and headers.
|
||||||
;mail.log =
|
;mail.log =
|
||||||
|
|||||||
@@ -1088,6 +1088,14 @@ mail.add_x_header = Off
|
|||||||
; RFC 2822 non conformant MTA.
|
; RFC 2822 non conformant MTA.
|
||||||
mail.mixed_lf_and_crlf = Off
|
mail.mixed_lf_and_crlf = Off
|
||||||
|
|
||||||
|
; Control line ending mode for mail messages and headers.
|
||||||
|
; Possible values: "crlf" (default), "lf", "mixed", "os"
|
||||||
|
; - crlf: Use CRLF line endings
|
||||||
|
; - lf: Use LF line endings only (converts CRLF in message to LF)
|
||||||
|
; - mixed: Same as mail.mixed_lf_and_crlf = On
|
||||||
|
; - os: Use CRLF on Windows, LF on other systems
|
||||||
|
mail.cr_lf_mode = crlf
|
||||||
|
|
||||||
; The path to a log file that will log all mail() calls. Log entries include
|
; 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.
|
; the full path of the script, line number, To address and headers.
|
||||||
;mail.log =
|
;mail.log =
|
||||||
|
|||||||
Reference in New Issue
Block a user