diff -Naur php-7.0.17.orig/ext/standard/mail.c php-7.0.17/ext/standard/mail.c --- php-7.0.17.orig/ext/standard/mail.c 2017-03-14 12:26:10.000000000 +0100 +++ php-7.0.17/ext/standard/mail.c 2017-04-11 00:04:56.203579871 +0200 @@ -64,6 +64,13 @@ } \ continue; \ } \ + else if (str[pos] == '\n' && (str[pos + 1] == ' ' || str[pos + 1] == '\t')) { \ + pos += 1; \ + while (str[pos + 1] == ' ' || str[pos + 1] == '\t') { \ + pos++; \ + } \ + continue; \ + } \ #define MAIL_ASCIIZ_CHECK(str, len) \ p = str; \ @@ -96,6 +103,46 @@ } /* }}} */ +static long +count_recipients(const char *str, int len, int skip_field) +{ + long recipients = 0; + int got_field, i; + + if (str == NULL || len <= 0) + return 0; + + got_field = skip_field; + for (i = 0; str[i]; i++) { + /* search for mime-fields + * either at beginning or after '\n' of the string + */ + if (!got_field && + (!strncasecmp(&str[i], "To: ", strlen("To: ")) || + !strncasecmp(&str[i], "Cc: ", strlen("Cc: ")) || + !strncasecmp(&str[i], "Bcc: ", strlen("Bcc: ")) + )) { + if (i == 0 || (i > 0 && str[i - 1] == '\n')) + got_field = 1; + } + + /* search for every '@', don't stop at long headers */ + if (got_field) { + if (str[i] == '@') + recipients++; + else if (str[i] == '\n') + if (i == len - 1 || (str[i + 1] != ' ' && str[i + 1] != '\t')) + got_field = 0; + } + + /* message body starts here */ + if (i > 0 && str[i - 1] == '\n' && str[i] == '\n') + break; + } + + return recipients; +} + /* {{{ proto int mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]]) Send an email message */ PHP_FUNCTION(mail) @@ -106,8 +153,10 @@ size_t to_len, message_len; size_t subject_len, i; char *force_extra_parameters = INI_STR("mail.force_extra_parameters"); - char *to_r, *subject_r; + char *to_r=NULL, *subject_r=NULL; char *p, *e; + long recipients = 0; + long max_recipients = INI_INT("sendmail_max_recipients"); if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|SS", &to, &to_len, &subject, &subject_len, &message, &message_len, &headers, &extra_cmd) == FAILURE) { return; @@ -125,6 +174,19 @@ MAIL_ASCIIZ_CHECK(ZSTR_VAL(extra_cmd), ZSTR_LEN(extra_cmd)); } + /* count recipients */ + if (max_recipients > 0) { + recipients += count_recipients(to, to_len, 1); + if (headers) { + recipients += count_recipients(ZSTR_VAL(headers), ZSTR_LEN(headers), 0); + } + if (recipients > max_recipients) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Max recipients reached, mail not sent."); + RETVAL_FALSE; + goto end; + } + } + if (to_len > 0) { to_r = estrndup(to, to_len); for (; to_len; to_len--) { @@ -140,7 +202,9 @@ * To prevent these separators from being replaced with a space, we use the * SKIP_LONG_HEADER_SEP to skip over them. */ SKIP_LONG_HEADER_SEP(to_r, i); - to_r[i] = ' '; + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Disallowed characters in mail parameters, mail not sent."); + RETVAL_FALSE; + goto end; } } } else { @@ -158,7 +222,9 @@ for (i = 0; subject_r[i]; i++) { if (iscntrl((unsigned char) subject_r[i])) { SKIP_LONG_HEADER_SEP(subject_r, i); - subject_r[i] = ' '; + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Disallowed characters in mail parameters, mail not sent."); + RETVAL_FALSE; + goto end; } } } else { @@ -177,6 +243,7 @@ RETVAL_FALSE; } +end: if (headers_trimmed) { zend_string_release(headers_trimmed); } @@ -285,6 +352,19 @@ } \ return val; \ + zval *zhttphost; + char *httphost = NULL; + if ((mail_log && *mail_log) || PG(mail_x_header)) { + if ((Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY || zend_is_auto_global_str(ZEND_STRL("_SERVER"))) && + (zhttphost = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), ZEND_STRL("HTTP_HOST"))) != NULL && + Z_TYPE_P(zhttphost) == IS_STRING && + Z_STRLEN_P(zhttphost) > 0) { + httphost = Z_STRVAL_P(zhttphost); + } else { + httphost = ""; + } + } + if (mail_log && *mail_log) { char *tmp; time_t curtime; @@ -294,7 +374,7 @@ time(&curtime); date_str = php_format_date("d-M-Y H:i:s e", 13, curtime, 1); - l = spprintf(&tmp, 0, "[%s] mail() on [%s:%d]: To: %s -- Headers: %s\n", ZSTR_VAL(date_str), zend_get_executed_filename(), zend_get_executed_lineno(), to, hdr ? hdr : ""); + l = spprintf(&tmp, 0, "[%s] mail() on [%s:%d]: To: %s -- HTTP-Host: %s -- Headers: %s\n", ZSTR_VAL(date_str), zend_get_executed_filename(), zend_get_executed_lineno(), to, httphost, hdr ? hdr : ""); zend_string_free(date_str); @@ -318,16 +398,12 @@ 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(&hdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s\n%s", php_getuid(), ZSTR_VAL(f), headers); + spprintf(&hdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s\nX-PHP-HTTP-Host: %s\n%s", php_getuid(), tmp, httphost, headers); } else { - spprintf(&hdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s", php_getuid(), ZSTR_VAL(f)); + spprintf(&hdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s\nX-PHP-HTTP-Host: %s", php_getuid(), tmp, httphost); } - zend_string_release(f); } if (hdr && php_mail_detect_multiple_crlf(hdr)) { diff -Naur php-7.0.17.orig/main/main.c php-7.0.17/main/main.c --- php-7.0.17.orig/main/main.c 2017-03-14 12:26:16.000000000 +0100 +++ php-7.0.17/main/main.c 2017-04-11 00:04:29.047890385 +0200 @@ -558,6 +558,7 @@ PHP_INI_ENTRY("precision", "14", PHP_INI_ALL, OnSetPrecision) PHP_INI_ENTRY("sendmail_from", NULL, PHP_INI_ALL, NULL) PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("sendmail_max_recipients", "5", PHP_INI_ALL, NULL) PHP_INI_ENTRY("mail.force_extra_parameters",NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnChangeMailForceExtra) PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL) PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL)