Download | Plain Text | No Line Numbers


  1. --- a/ext/standard/mail.c 2026-10-19 13:07:05.112769445 +0200
  2. +++ b/ext/standard/mail.c 2026-10-19 13:08:13.637882043 +0200
  3. @@ -55,6 +55,13 @@
  4. } \
  5. continue; \
  6. } \
  7. + else if (str[pos] == '\n' && (str[pos + 1] == ' ' || str[pos + 1] == '\t')) { \
  8. + pos += 1; \
  9. + while (str[pos + 1] == ' ' || str[pos + 1] == '\t') { \
  10. + pos++; \
  11. + } \
  12. + continue; \
  13. + } \
  14.  
  15. extern zend_long php_getuid(void);
  16.  
  17. @@ -260,6 +267,47 @@
  18. }
  19.  
  20.  
  21. +static long
  22. +count_recipients(const char *str, int len, int skip_field)
  23. +{
  24. + long recipients = 0;
  25. + int got_field, i;
  26. +
  27. + if (str == NULL || len <= 0)
  28. + return 0;
  29. +
  30. + got_field = skip_field;
  31. + for (i = 0; str[i]; i++) {
  32. + /* search for mime-fields
  33. + * either at beginning or after '\n' of the string
  34. + */
  35. + if (!got_field &&
  36. + (!strncasecmp(&str[i], "To: ", strlen("To: ")) ||
  37. + !strncasecmp(&str[i], "Cc: ", strlen("Cc: ")) ||
  38. + !strncasecmp(&str[i], "Bcc: ", strlen("Bcc: "))
  39. + )) {
  40. + if (i == 0 || (i > 0 && str[i - 1] == '\n'))
  41. + got_field = 1;
  42. + }
  43. +
  44. + /* search for every '@', don't stop at long headers */
  45. + if (got_field) {
  46. + if (str[i] == '@')
  47. + recipients++;
  48. + else if (str[i] == '\n')
  49. + if (i == len - 1 || (str[i + 1] != ' ' && str[i + 1] != '\t'))
  50. + got_field = 0;
  51. + }
  52. +
  53. + /* message body starts here */
  54. + if (i > 0 && str[i - 1] == '\n' && str[i] == '\n')
  55. + break;
  56. + }
  57. +
  58. + return recipients;
  59. +}
  60. +
  61. +
  62. /* {{{ Send an email message */
  63. PHP_FUNCTION(mail)
  64. {
  65. @@ -270,7 +318,9 @@
  66. HashTable *headers_ht = NULL;
  67. size_t to_len, message_len;
  68. size_t subject_len, i;
  69. - char *to_r, *subject_r;
  70. + char *to_r = NULL, *subject_r = NULL;
  71. + long recipients = 0;
  72. + long max_recipients = zend_ini_long_literal("sendmail_max_recipients");
  73.  
  74. ZEND_PARSE_PARAMETERS_START(3, 5)
  75. Z_PARAM_PATH(to, to_len)
  76. @@ -309,7 +359,9 @@
  77. * To prevent these separators from being replaced with a space, we use the
  78. * SKIP_LONG_HEADER_SEP to skip over them. */
  79. SKIP_LONG_HEADER_SEP(to_r, i);
  80. - to_r[i] = ' ';
  81. + php_error_docref(NULL, E_WARNING, "Disallowed characters in mail parameters, mail not sent.");
  82. + RETVAL_FALSE;
  83. + goto end;
  84. }
  85. }
  86. } else {
  87. @@ -327,7 +379,9 @@
  88. for (i = 0; subject_r[i]; i++) {
  89. if (iscntrl((unsigned char) subject_r[i])) {
  90. SKIP_LONG_HEADER_SEP(subject_r, i);
  91. - subject_r[i] = ' ';
  92. + php_error_docref(NULL, E_WARNING, "Disallowed characters in mail parameters, mail not sent.");
  93. + RETVAL_FALSE;
  94. + goto end;
  95. }
  96. }
  97. } else {
  98. @@ -341,12 +395,32 @@
  99. extra_cmd = php_escape_shell_cmd(extra_cmd);
  100. }
  101.  
  102. + /* count recipients */
  103. + if (max_recipients > 0) {
  104. + recipients += count_recipients(to, to_len, 1);
  105. + if (headers_str) {
  106. + recipients += count_recipients(ZSTR_VAL(headers_str), ZSTR_LEN(headers_str), 0);
  107. + }
  108. + if (extra_cmd) {
  109. + recipients += count_recipients(ZSTR_VAL(extra_cmd), ZSTR_LEN(extra_cmd), 1);
  110. + if (zend_string_starts_with_literal(extra_cmd, "-f")) {
  111. + recipients--;
  112. + }
  113. + }
  114. + if (recipients > max_recipients) {
  115. + php_error_docref(NULL, E_WARNING, "Max recipients reached, mail not sent.");
  116. + RETVAL_FALSE;
  117. + goto end;
  118. + }
  119. + }
  120. +
  121. if (php_mail(to_r, subject_r, message, headers_str && ZSTR_LEN(headers_str) ? ZSTR_VAL(headers_str) : NULL, extra_cmd)) {
  122. RETVAL_TRUE;
  123. } else {
  124. RETVAL_FALSE;
  125. }
  126.  
  127. +end:
  128. if (headers_str) {
  129. zend_string_release_ex(headers_str, 0);
  130. }
  131. @@ -450,10 +524,23 @@
  132. } \
  133. return val; \
  134.  
  135. + zval *zhttphost;
  136. + char *httphost = NULL;
  137. + if ((mail_log && ZSTR_LEN(mail_log)) || PG(mail_x_header)) {
  138. + if ((Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY || zend_is_auto_global_str(ZEND_STRL("_SERVER"))) &&
  139. + (zhttphost = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), ZEND_STRL("HTTP_HOST"))) != NULL &&
  140. + Z_TYPE_P(zhttphost) == IS_STRING &&
  141. + Z_STRLEN_P(zhttphost) > 0) {
  142. + httphost = Z_STRVAL_P(zhttphost);
  143. + } else {
  144. + httphost = "";
  145. + }
  146. + }
  147. +
  148. if (mail_log && ZSTR_LEN(mail_log)) {
  149. char *logline;
  150.  
  151. - spprintf(&logline, 0, "mail() on [%s:%d]: To: %s -- Headers: %s -- Subject: %s", zend_get_executed_filename(), zend_get_executed_lineno(), to, hdr ? hdr : "", subject);
  152. + spprintf(&logline, 0, "mail() on [%s:%d]: To: %s -- HTTP-Host: %s -- Headers: %s -- Subject: %s", zend_get_executed_filename(), zend_get_executed_lineno(), to, httphost, hdr ? hdr : "", subject);
  153.  
  154. if (hdr) {
  155. php_mail_log_crlf_to_spaces(logline);
  156. @@ -515,9 +602,9 @@
  157. f = php_basename(tmp, strlen(tmp), NULL, 0);
  158.  
  159. if (headers != NULL && *headers) {
  160. - spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s%s%s", php_getuid(), ZSTR_VAL(f), line_sep, headers);
  161. + spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s%sX-PHP-HTTP-Host: %s%s%s", php_getuid(), tmp, line_sep, httphost, line_sep, headers);
  162. } else {
  163. - spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s", php_getuid(), ZSTR_VAL(f));
  164. + spprintf(&ahdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s\r\nX-PHP-HTTP-Host: %s", php_getuid(), tmp, httphost);
  165. }
  166. hdr = ahdr;
  167. zend_string_release_ex(f, 0);
  168. --- a/main/main.c 2026-10-19 13:07:05.192114390 +0200
  169. +++ b/main/main.c 2026-10-19 13:07:55.722199502 +0200
  170. @@ -879,6 +879,7 @@
  171. PHP_INI_ENTRY("precision", "14", PHP_INI_ALL, OnSetPrecision)
  172. PHP_INI_ENTRY("sendmail_from", NULL, PHP_INI_ALL, NULL)
  173. PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL)
  174. + PHP_INI_ENTRY("sendmail_max_recipients", "5", PHP_INI_ALL, NULL)
  175. PHP_INI_ENTRY("mail.force_extra_parameters",NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnChangeMailForceExtra)
  176. PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL)
  177. PHP_INI_ENTRY("max_file_uploads", "20", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL)
  178.