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)
|
||||
. Fixed bug GH-19577 (Avoid integer overflow when using a small offset
|
||||
and PHP_INT_MAX with LimitIterator). (alexandre-daubois)
|
||||
. Implement GH-19188: Add support for new INI mail.cr_lf_mode.
|
||||
(alexandre-daubois)
|
||||
|
||||
- Streams:
|
||||
. 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);
|
||||
}
|
||||
|
||||
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)) {
|
||||
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) {
|
||||
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
|
||||
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 */
|
||||
static PHP_INI_MH(OnChangeMailForceExtra)
|
||||
{
|
||||
@@ -826,6 +856,7 @@ PHP_INI_BEGIN()
|
||||
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.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)
|
||||
PHP_INI_ENTRY("browscap", NULL, PHP_INI_SYSTEM, OnChangeBrowscap)
|
||||
|
||||
|
||||
@@ -154,6 +154,7 @@ struct _php_core_globals {
|
||||
char *mail_log;
|
||||
bool mail_x_header;
|
||||
bool mail_mixed_lf_and_crlf;
|
||||
char *mail_cr_lf_mode;
|
||||
|
||||
bool in_error_log;
|
||||
|
||||
|
||||
@@ -1086,6 +1086,14 @@ mail.add_x_header = Off
|
||||
; RFC 2822 non conformant MTA.
|
||||
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 full path of the script, line number, To address and headers.
|
||||
;mail.log =
|
||||
|
||||
@@ -1088,6 +1088,14 @@ mail.add_x_header = Off
|
||||
; RFC 2822 non conformant MTA.
|
||||
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 full path of the script, line number, To address and headers.
|
||||
;mail.log =
|
||||
|
||||
Reference in New Issue
Block a user