xref: /PHP-7.4/ext/standard/mail.c (revision ae215069)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Rasmus Lerdorf <rasmus@php.net>                              |
16    +----------------------------------------------------------------------+
17  */
18 
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <time.h>
23 #include "php.h"
24 #include "ext/standard/info.h"
25 #include "ext/standard/php_string.h"
26 #include "ext/standard/basic_functions.h"
27 #include "ext/date/php_date.h"
28 #include "zend_smart_str.h"
29 
30 #if HAVE_SYSEXITS_H
31 #include <sysexits.h>
32 #endif
33 #if HAVE_SYS_SYSEXITS_H
34 #include <sys/sysexits.h>
35 #endif
36 
37 #if PHP_SIGCHILD
38 #include <signal.h>
39 #endif
40 
41 #include "php_syslog.h"
42 #include "php_mail.h"
43 #include "php_ini.h"
44 #include "php_string.h"
45 #include "exec.h"
46 
47 #ifdef PHP_WIN32
48 #include "win32/sendmail.h"
49 #endif
50 
51 #define SKIP_LONG_HEADER_SEP(str, pos)																	\
52 	if (str[pos] == '\r' && str[pos + 1] == '\n' && (str[pos + 2] == ' ' || str[pos + 2] == '\t')) {	\
53 		pos += 2;																						\
54 		while (str[pos + 1] == ' ' || str[pos + 1] == '\t') {											\
55 			pos++;																						\
56 		}																								\
57 		continue;																						\
58 	}																									\
59 
60 #define MAIL_ASCIIZ_CHECK(str, len)				\
61 	p = str;									\
62 	e = p + len;								\
63 	while ((p = memchr(p, '\0', (e - p)))) {	\
64 		*p = ' ';								\
65 	}											\
66 
67 extern zend_long php_getuid(void);
68 
69 /* {{{ proto int ezmlm_hash(string addr)
70    Calculate EZMLM list hash value. */
PHP_FUNCTION(ezmlm_hash)71 PHP_FUNCTION(ezmlm_hash)
72 {
73 	char *str = NULL;
74 	unsigned int h = 5381;
75 	size_t j, str_len;
76 
77 	ZEND_PARSE_PARAMETERS_START(1, 1)
78 		Z_PARAM_STRING(str, str_len)
79 	ZEND_PARSE_PARAMETERS_END();
80 
81 	for (j = 0; j < str_len; j++) {
82 		h = (h + (h << 5)) ^ (zend_ulong) (unsigned char) tolower(str[j]);
83 	}
84 
85 	h = (h % 53);
86 
87 	RETURN_LONG((zend_long) h);
88 }
89 /* }}} */
90 
91 
php_mail_build_headers_check_field_value(zval * val)92 static zend_bool php_mail_build_headers_check_field_value(zval *val)
93 {
94 	size_t len = 0;
95 	zend_string *value = Z_STR_P(val);
96 
97 	/* https://tools.ietf.org/html/rfc2822#section-2.2.1 */
98 	/* https://tools.ietf.org/html/rfc2822#section-2.2.3 */
99 	while (len < value->len) {
100 		if (*(value->val+len) == '\r') {
101 			if (value->len - len >= 3
102 				&&  *(value->val+len+1) == '\n'
103 				&& (*(value->val+len+2) == ' '  || *(value->val+len+2) == '\t')) {
104 				len += 3;
105 				continue;
106 			}
107 			return FAILURE;
108 		}
109 		if (*(value->val+len) == '\0') {
110 			return FAILURE;
111 		}
112 		len++;
113 	}
114 	return SUCCESS;
115 }
116 
117 
php_mail_build_headers_check_field_name(zend_string * key)118 static zend_bool php_mail_build_headers_check_field_name(zend_string *key)
119 {
120 	size_t len = 0;
121 
122 	/* https://tools.ietf.org/html/rfc2822#section-2.2 */
123 	while (len < key->len) {
124 		if (*(key->val+len) < 33 || *(key->val+len) > 126 || *(key->val+len) == ':') {
125 			return FAILURE;
126 		}
127 		len++;
128 	}
129 	return SUCCESS;
130 }
131 
132 
133 static void php_mail_build_headers_elems(smart_str *s, zend_string *key, zval *val);
134 
php_mail_build_headers_elem(smart_str * s,zend_string * key,zval * val)135 static void php_mail_build_headers_elem(smart_str *s, zend_string *key, zval *val)
136 {
137 	switch(Z_TYPE_P(val)) {
138 		case IS_STRING:
139 			if (php_mail_build_headers_check_field_name(key) != SUCCESS) {
140 				php_error_docref(NULL, E_WARNING, "Header field name (%s) contains invalid chars", ZSTR_VAL(key));
141 				return;
142 			}
143 			if (php_mail_build_headers_check_field_value(val) != SUCCESS) {
144 				php_error_docref(NULL, E_WARNING, "Header field value (%s => %s) contains invalid chars or format", ZSTR_VAL(key), Z_STRVAL_P(val));
145 				return;
146 			}
147 			smart_str_append(s, key);
148 			smart_str_appendl(s, ": ", 2);
149 			smart_str_appends(s, Z_STRVAL_P(val));
150 			smart_str_appendl(s, "\r\n", 2);
151 			break;
152 		case IS_ARRAY:
153 			php_mail_build_headers_elems(s, key, val);
154 			break;
155 		default:
156 			php_error_docref(NULL, E_WARNING, "headers array elements must be string or array (%s)", ZSTR_VAL(key));
157 	}
158 }
159 
160 
php_mail_build_headers_elems(smart_str * s,zend_string * key,zval * val)161 static void php_mail_build_headers_elems(smart_str *s, zend_string *key, zval *val)
162 {
163 	zend_string *tmp_key;
164 	zval *tmp_val;
165 
166 	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(val), tmp_key, tmp_val) {
167 		if (tmp_key) {
168 			php_error_docref(NULL, E_WARNING, "Multiple header key must be numeric index (%s)", ZSTR_VAL(tmp_key));
169 			continue;
170 		}
171 		if (Z_TYPE_P(tmp_val) != IS_STRING) {
172 			php_error_docref(NULL, E_WARNING, "Multiple header values must be string (%s)", ZSTR_VAL(key));
173 			continue;
174 		}
175 		php_mail_build_headers_elem(s, key, tmp_val);
176 	} ZEND_HASH_FOREACH_END();
177 }
178 
179 
php_mail_build_headers(zval * headers)180 PHPAPI zend_string *php_mail_build_headers(zval *headers)
181 {
182 	zend_ulong idx;
183 	zend_string *key;
184 	zval *val;
185 	smart_str s = {0};
186 
187 	ZEND_ASSERT(Z_TYPE_P(headers) == IS_ARRAY);
188 
189 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(headers), idx, key, val) {
190 		if (!key) {
191 			php_error_docref(NULL, E_WARNING, "Found numeric header (" ZEND_LONG_FMT ")", idx);
192 			continue;
193 		}
194 		/* https://tools.ietf.org/html/rfc2822#section-3.6 */
195 		switch(ZSTR_LEN(key)) {
196 			case sizeof("orig-date")-1:
197 				if (!strncasecmp("orig-date", ZSTR_VAL(key), ZSTR_LEN(key))) {
198 					PHP_MAIL_BUILD_HEADER_CHECK("orig-date", s, key, val);
199 				} else {
200 					PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
201 				}
202 				break;
203 			case sizeof("from")-1:
204 				if (!strncasecmp("from", ZSTR_VAL(key), ZSTR_LEN(key))) {
205 					PHP_MAIL_BUILD_HEADER_CHECK("from", s, key, val);
206 				} else {
207 					PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
208 				}
209 				break;
210 			case sizeof("sender")-1:
211 				if (!strncasecmp("sender", ZSTR_VAL(key), ZSTR_LEN(key))) {
212 					PHP_MAIL_BUILD_HEADER_CHECK("sender", s, key, val);
213 				} else {
214 					PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
215 				}
216 				break;
217 			case sizeof("reply-to")-1:
218 				if (!strncasecmp("reply-to", ZSTR_VAL(key), ZSTR_LEN(key))) {
219 					PHP_MAIL_BUILD_HEADER_CHECK("reply-to", s, key, val);
220 				} else {
221 					PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
222 				}
223 				break;
224 			case sizeof("to")-1: /* "to", "cc" */
225 				if (!strncasecmp("to", ZSTR_VAL(key), ZSTR_LEN(key))) {
226 					php_error_docref(NULL, E_WARNING, "Extra header cannot contain 'To' header");
227 					continue;
228 				}
229 				if (!strncasecmp("cc", ZSTR_VAL(key), ZSTR_LEN(key))) {
230 					PHP_MAIL_BUILD_HEADER_CHECK("cc", s, key, val);
231 				} else {
232 					PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
233 				}
234 				break;
235 			case sizeof("bcc")-1:
236 				if (!strncasecmp("bcc", ZSTR_VAL(key), ZSTR_LEN(key))) {
237 					PHP_MAIL_BUILD_HEADER_CHECK("bcc", s, key, val);
238 				} else {
239 					PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
240 				}
241 				break;
242 			case sizeof("message-id")-1: /* "references" */
243 				if (!strncasecmp("message-id", ZSTR_VAL(key), ZSTR_LEN(key))) {
244 					PHP_MAIL_BUILD_HEADER_CHECK("message-id", s, key, val);
245 				} else if (!strncasecmp("references", ZSTR_VAL(key), ZSTR_LEN(key))) {
246 					PHP_MAIL_BUILD_HEADER_CHECK("references", s, key, val);
247 				} else {
248 					PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
249 				}
250 				break;
251 			case sizeof("in-reply-to")-1:
252 				if (!strncasecmp("in-reply-to", ZSTR_VAL(key), ZSTR_LEN(key))) {
253 					PHP_MAIL_BUILD_HEADER_CHECK("in-reply-to", s, key, val);
254 				} else {
255 					PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
256 				}
257 				break;
258 			case sizeof("subject")-1:
259 				if (!strncasecmp("subject", ZSTR_VAL(key), ZSTR_LEN(key))) {
260 					php_error_docref(NULL, E_WARNING, "Extra header cannot contain 'Subject' header");
261 					continue;
262 				}
263 				PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
264 				break;
265 			default:
266 				PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val);
267 		}
268 	} ZEND_HASH_FOREACH_END();
269 
270 	/* Remove the last \r\n */
271 	if (s.s) s.s->len -= 2;
272 	smart_str_0(&s);
273 
274 	return s.s;
275 }
276 
277 
278 /* {{{ proto int mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]])
279    Send an email message */
PHP_FUNCTION(mail)280 PHP_FUNCTION(mail)
281 {
282 	char *to=NULL, *message=NULL;
283 	char *subject=NULL;
284 	zend_string *extra_cmd=NULL, *str_headers=NULL, *tmp_headers;
285 	zval *headers = NULL;
286 	size_t to_len, message_len;
287 	size_t subject_len, i;
288 	char *force_extra_parameters = INI_STR("mail.force_extra_parameters");
289 	char *to_r, *subject_r;
290 	char *p, *e;
291 
292 	ZEND_PARSE_PARAMETERS_START(3, 5)
293 		Z_PARAM_STRING(to, to_len)
294 		Z_PARAM_STRING(subject, subject_len)
295 		Z_PARAM_STRING(message, message_len)
296 		Z_PARAM_OPTIONAL
297 		Z_PARAM_ZVAL(headers)
298 		Z_PARAM_STR(extra_cmd)
299 	ZEND_PARSE_PARAMETERS_END();
300 
301 	/* ASCIIZ check */
302 	MAIL_ASCIIZ_CHECK(to, to_len);
303 	MAIL_ASCIIZ_CHECK(subject, subject_len);
304 	MAIL_ASCIIZ_CHECK(message, message_len);
305 	if (headers) {
306 		switch(Z_TYPE_P(headers)) {
307 			case IS_STRING:
308 				tmp_headers = zend_string_init(Z_STRVAL_P(headers), Z_STRLEN_P(headers), 0);
309 				MAIL_ASCIIZ_CHECK(ZSTR_VAL(tmp_headers), ZSTR_LEN(tmp_headers));
310 				str_headers = php_trim(tmp_headers, NULL, 0, 2);
311 				zend_string_release_ex(tmp_headers, 0);
312 				break;
313 			case IS_ARRAY:
314 				str_headers = php_mail_build_headers(headers);
315 				break;
316 			default:
317 				php_error_docref(NULL, E_WARNING, "headers parameter must be string or array");
318 				RETURN_FALSE;
319 		}
320 	}
321 	if (extra_cmd) {
322 		MAIL_ASCIIZ_CHECK(ZSTR_VAL(extra_cmd), ZSTR_LEN(extra_cmd));
323 	}
324 
325 	if (to_len > 0) {
326 		to_r = estrndup(to, to_len);
327 		for (; to_len; to_len--) {
328 			if (!isspace((unsigned char) to_r[to_len - 1])) {
329 				break;
330 			}
331 			to_r[to_len - 1] = '\0';
332 		}
333 		for (i = 0; to_r[i]; i++) {
334 			if (iscntrl((unsigned char) to_r[i])) {
335 				/* According to RFC 822, section 3.1.1 long headers may be separated into
336 				 * parts using CRLF followed at least one linear-white-space character ('\t' or ' ').
337 				 * To prevent these separators from being replaced with a space, we use the
338 				 * SKIP_LONG_HEADER_SEP to skip over them. */
339 				SKIP_LONG_HEADER_SEP(to_r, i);
340 				to_r[i] = ' ';
341 			}
342 		}
343 	} else {
344 		to_r = to;
345 	}
346 
347 	if (subject_len > 0) {
348 		subject_r = estrndup(subject, subject_len);
349 		for (; subject_len; subject_len--) {
350 			if (!isspace((unsigned char) subject_r[subject_len - 1])) {
351 				break;
352 			}
353 			subject_r[subject_len - 1] = '\0';
354 		}
355 		for (i = 0; subject_r[i]; i++) {
356 			if (iscntrl((unsigned char) subject_r[i])) {
357 				SKIP_LONG_HEADER_SEP(subject_r, i);
358 				subject_r[i] = ' ';
359 			}
360 		}
361 	} else {
362 		subject_r = subject;
363 	}
364 
365 	if (force_extra_parameters) {
366 		extra_cmd = php_escape_shell_cmd(force_extra_parameters);
367 	} else if (extra_cmd) {
368 		extra_cmd = php_escape_shell_cmd(ZSTR_VAL(extra_cmd));
369 	}
370 
371 	if (php_mail(to_r, subject_r, message, str_headers && ZSTR_LEN(str_headers) ? ZSTR_VAL(str_headers) : NULL, extra_cmd ? ZSTR_VAL(extra_cmd) : NULL)) {
372 		RETVAL_TRUE;
373 	} else {
374 		RETVAL_FALSE;
375 	}
376 
377 	if (str_headers) {
378 		zend_string_release_ex(str_headers, 0);
379 	}
380 
381 	if (extra_cmd) {
382 		zend_string_release_ex(extra_cmd, 0);
383 	}
384 	if (to_r != to) {
385 		efree(to_r);
386 	}
387 	if (subject_r != subject) {
388 		efree(subject_r);
389 	}
390 }
391 /* }}} */
392 
393 
php_mail_log_crlf_to_spaces(char * message)394 void php_mail_log_crlf_to_spaces(char *message) {
395 	/* Find all instances of carriage returns or line feeds and
396 	 * replace them with spaces. Thus, a log line is always one line
397 	 * long
398 	 */
399 	char *p = message;
400 	while ((p = strpbrk(p, "\r\n"))) {
401 		*p = ' ';
402 	}
403 }
404 
php_mail_log_to_syslog(char * message)405 void php_mail_log_to_syslog(char *message) {
406 	/* Write 'message' to syslog. */
407 #ifdef HAVE_SYSLOG_H
408 	php_syslog(LOG_NOTICE, "%s", message);
409 #endif
410 }
411 
412 
php_mail_log_to_file(char * filename,char * message,size_t message_size)413 void php_mail_log_to_file(char *filename, char *message, size_t message_size) {
414 	/* Write 'message' to the given file. */
415 	uint32_t flags = IGNORE_URL_WIN | REPORT_ERRORS | STREAM_DISABLE_OPEN_BASEDIR;
416 	php_stream *stream = php_stream_open_wrapper(filename, "a", flags, NULL);
417 	if (stream) {
418 		php_stream_write(stream, message, message_size);
419 		php_stream_close(stream);
420 	}
421 }
422 
423 
php_mail_detect_multiple_crlf(char * hdr)424 static int php_mail_detect_multiple_crlf(char *hdr) {
425 	/* This function detects multiple/malformed multiple newlines. */
426 
427 	if (!hdr || !strlen(hdr)) {
428 		return 0;
429 	}
430 
431 	/* Should not have any newlines at the beginning. */
432 	/* RFC 2822 2.2. Header Fields */
433 	if (*hdr < 33 || *hdr > 126 || *hdr == ':') {
434 		return 1;
435 	}
436 
437 	while(*hdr) {
438 		if (*hdr == '\r') {
439 			if (*(hdr+1) == '\0' || *(hdr+1) == '\r' || (*(hdr+1) == '\n' && (*(hdr+2) == '\0' || *(hdr+2) == '\n' || *(hdr+2) == '\r'))) {
440 				/* Malformed or multiple newlines. */
441 				return 1;
442 			} else {
443 				hdr += 2;
444 			}
445 		} else if (*hdr == '\n') {
446 			if (*(hdr+1) == '\0' || *(hdr+1) == '\r' || *(hdr+1) == '\n') {
447 				/* Malformed or multiple newlines. */
448 				return 1;
449 			} else {
450 				hdr += 2;
451 			}
452 		} else {
453 			hdr++;
454 		}
455 	}
456 
457 	return 0;
458 }
459 
460 
461 /* {{{ php_mail
462  */
php_mail(char * to,char * subject,char * message,char * headers,char * extra_cmd)463 PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char *extra_cmd)
464 {
465 #ifdef PHP_WIN32
466 	int tsm_err;
467 	char *tsm_errmsg = NULL;
468 #endif
469 	FILE *sendmail;
470 	int ret;
471 	char *sendmail_path = INI_STR("sendmail_path");
472 	char *sendmail_cmd = NULL;
473 	char *mail_log = INI_STR("mail.log");
474 	char *hdr = headers;
475 #if PHP_SIGCHILD
476 	void (*sig_handler)() = NULL;
477 #endif
478 
479 #define MAIL_RET(val) \
480 	if (hdr != headers) {	\
481 		efree(hdr);	\
482 	}	\
483 	return val;	\
484 
485 	if (mail_log && *mail_log) {
486 		char *logline;
487 
488 		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);
489 
490 		if (hdr) {
491 			php_mail_log_crlf_to_spaces(logline);
492 		}
493 
494 		if (!strcmp(mail_log, "syslog")) {
495 			php_mail_log_to_syslog(logline);
496 		} else {
497 			/* Add date when logging to file */
498 			char *tmp;
499 			time_t curtime;
500 			zend_string *date_str;
501 			size_t len;
502 
503 
504 			time(&curtime);
505 			date_str = php_format_date("d-M-Y H:i:s e", 13, curtime, 1);
506 			len = spprintf(&tmp, 0, "[%s] %s%s", date_str->val, logline, PHP_EOL);
507 
508 			php_mail_log_to_file(mail_log, tmp, len);
509 
510 			zend_string_free(date_str);
511 			efree(tmp);
512 		}
513 
514 		efree(logline);
515 	}
516 
517 	if (PG(mail_x_header)) {
518 		const char *tmp = zend_get_executed_filename();
519 		zend_string *f;
520 
521 		f = php_basename(tmp, strlen(tmp), NULL, 0);
522 
523 		if (headers != NULL && *headers) {
524 			spprintf(&hdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s\n%s", php_getuid(), ZSTR_VAL(f), headers);
525 		} else {
526 			spprintf(&hdr, 0, "X-PHP-Originating-Script: " ZEND_LONG_FMT ":%s", php_getuid(), ZSTR_VAL(f));
527 		}
528 		zend_string_release_ex(f, 0);
529 	}
530 
531 	if (hdr && php_mail_detect_multiple_crlf(hdr)) {
532 		php_error_docref(NULL, E_WARNING, "Multiple or malformed newlines found in additional_header");
533 		MAIL_RET(0);
534 	}
535 
536 	if (!sendmail_path) {
537 #ifdef PHP_WIN32
538 		/* handle old style win smtp sending */
539 		if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message, NULL, NULL, NULL) == FAILURE) {
540 			if (tsm_errmsg) {
541 				php_error_docref(NULL, E_WARNING, "%s", tsm_errmsg);
542 				efree(tsm_errmsg);
543 			} else {
544 				php_error_docref(NULL, E_WARNING, "%s", GetSMErrorText(tsm_err));
545 			}
546 			MAIL_RET(0);
547 		}
548 		MAIL_RET(1);
549 #else
550 		MAIL_RET(0);
551 #endif
552 	}
553 	if (extra_cmd != NULL) {
554 		spprintf(&sendmail_cmd, 0, "%s %s", sendmail_path, extra_cmd);
555 	} else {
556 		sendmail_cmd = sendmail_path;
557 	}
558 
559 #if PHP_SIGCHILD
560 	/* Set signal handler of SIGCHLD to default to prevent other signal handlers
561 	 * from being called and reaping the return code when our child exits.
562 	 * The original handler needs to be restored after pclose() */
563 	sig_handler = (void *)signal(SIGCHLD, SIG_DFL);
564 	if (sig_handler == SIG_ERR) {
565 		sig_handler = NULL;
566 	}
567 #endif
568 
569 #ifdef PHP_WIN32
570 	sendmail = popen_ex(sendmail_cmd, "wb", NULL, NULL);
571 #else
572 	/* Since popen() doesn't indicate if the internal fork() doesn't work
573 	 * (e.g. the shell can't be executed) we explicitly set it to 0 to be
574 	 * sure we don't catch any older errno value. */
575 	errno = 0;
576 	sendmail = popen(sendmail_cmd, "w");
577 #endif
578 	if (extra_cmd != NULL) {
579 		efree (sendmail_cmd);
580 	}
581 
582 	if (sendmail) {
583 #ifndef PHP_WIN32
584 		if (EACCES == errno) {
585 			php_error_docref(NULL, E_WARNING, "Permission denied: unable to execute shell to run mail delivery binary '%s'", sendmail_path);
586 			pclose(sendmail);
587 #if PHP_SIGCHILD
588 			/* Restore handler in case of error on Windows
589 			   Not sure if this applicable on Win but just in case. */
590 			if (sig_handler) {
591 				signal(SIGCHLD, sig_handler);
592 			}
593 #endif
594 			MAIL_RET(0);
595 		}
596 #endif
597 		fprintf(sendmail, "To: %s\n", to);
598 		fprintf(sendmail, "Subject: %s\n", subject);
599 		if (hdr != NULL) {
600 			fprintf(sendmail, "%s\n", hdr);
601 		}
602 		fprintf(sendmail, "\n%s\n", message);
603 		ret = pclose(sendmail);
604 
605 #if PHP_SIGCHILD
606 		if (sig_handler) {
607 			signal(SIGCHLD, sig_handler);
608 		}
609 #endif
610 
611 #ifdef PHP_WIN32
612 		if (ret == -1)
613 #else
614 #if defined(EX_TEMPFAIL)
615 		if ((ret != EX_OK)&&(ret != EX_TEMPFAIL))
616 #elif defined(EX_OK)
617 		if (ret != EX_OK)
618 #else
619 		if (ret != 0)
620 #endif
621 #endif
622 		{
623 			MAIL_RET(0);
624 		} else {
625 			MAIL_RET(1);
626 		}
627 	} else {
628 		php_error_docref(NULL, E_WARNING, "Could not execute mail delivery program '%s'", sendmail_path);
629 #if PHP_SIGCHILD
630 		if (sig_handler) {
631 			signal(SIGCHLD, sig_handler);
632 		}
633 #endif
634 		MAIL_RET(0);
635 	}
636 
637 	MAIL_RET(1); /* never reached */
638 }
639 /* }}} */
640 
641 /* {{{ PHP_MINFO_FUNCTION
642  */
PHP_MINFO_FUNCTION(mail)643 PHP_MINFO_FUNCTION(mail)
644 {
645 	char *sendmail_path = INI_STR("sendmail_path");
646 
647 #ifdef PHP_WIN32
648 	if (!sendmail_path) {
649 		php_info_print_table_row(2, "Internal Sendmail Support for Windows", "enabled");
650 	} else {
651 		php_info_print_table_row(2, "Path to sendmail", sendmail_path);
652 	}
653 #else
654 	php_info_print_table_row(2, "Path to sendmail", sendmail_path);
655 #endif
656 }
657 /* }}} */
658