1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 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 | /* $Id$ */ |
20 | |
21 | #include <stdlib.h> |
22 | #include <ctype.h> |
23 | #include <stdio.h> |
24 | #include <time.h> |
25 | #include "php.h" |
26 | #include "ext/standard/info.h" |
27 | #include "ext/standard/php_string.h" |
28 | #include "ext/standard/basic_functions.h" |
29 | #include "ext/date/php_date.h" |
30 | |
31 | #if HAVE_SYSEXITS_H |
32 | #include <sysexits.h> |
33 | #endif |
34 | #if HAVE_SYS_SYSEXITS_H |
35 | #include <sys/sysexits.h> |
36 | #endif |
37 | |
38 | #if PHP_SIGCHILD |
39 | #if HAVE_SIGNAL_H |
40 | #include <signal.h> |
41 | #endif |
42 | #endif |
43 | |
44 | #include "php_syslog.h" |
45 | #include "php_mail.h" |
46 | #include "php_ini.h" |
47 | #include "php_string.h" |
48 | #include "exec.h" |
49 | |
50 | #ifdef PHP_WIN32 |
51 | #include "win32/sendmail.h" |
52 | #endif |
53 | |
54 | #ifdef NETWARE |
55 | #define EX_OK 0 /* successful termination */ |
56 | #define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ |
57 | #endif |
58 | |
59 | #define (str, pos) \ |
60 | if (str[pos] == '\r' && str[pos + 1] == '\n' && (str[pos + 2] == ' ' || str[pos + 2] == '\t')) { \ |
61 | pos += 2; \ |
62 | while (str[pos + 1] == ' ' || str[pos + 1] == '\t') { \ |
63 | pos++; \ |
64 | } \ |
65 | continue; \ |
66 | } \ |
67 | |
68 | #define MAIL_ASCIIZ_CHECK(str, len) \ |
69 | p = str; \ |
70 | e = p + len; \ |
71 | while ((p = memchr(p, '\0', (e - p)))) { \ |
72 | *p = ' '; \ |
73 | } \ |
74 | |
75 | extern long php_getuid(TSRMLS_D); |
76 | |
77 | /* {{{ proto int ezmlm_hash(string addr) |
78 | Calculate EZMLM list hash value. */ |
79 | PHP_FUNCTION(ezmlm_hash) |
80 | { |
81 | char *str = NULL; |
82 | unsigned int h = 5381; |
83 | int j, str_len; |
84 | |
85 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s" , &str, &str_len) == FAILURE) { |
86 | return; |
87 | } |
88 | |
89 | for (j = 0; j < str_len; j++) { |
90 | h = (h + (h << 5)) ^ (unsigned long) (unsigned char) tolower(str[j]); |
91 | } |
92 | |
93 | h = (h % 53); |
94 | |
95 | RETURN_LONG((int) h); |
96 | } |
97 | /* }}} */ |
98 | |
99 | /* {{{ proto int mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]]) |
100 | Send an email message */ |
101 | PHP_FUNCTION(mail) |
102 | { |
103 | char *to=NULL, *message=NULL, *=NULL, *=NULL; |
104 | char *subject=NULL, *=NULL; |
105 | int to_len, message_len, = 0; |
106 | int subject_len, = 0, i; |
107 | char * = INI_STR("mail.force_extra_parameters" ); |
108 | char *to_r, *subject_r; |
109 | char *p, *e; |
110 | |
111 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|ss" , &to, &to_len, &subject, &subject_len, &message, &message_len, &headers, &headers_len, &extra_cmd, &extra_cmd_len) == FAILURE) { |
112 | return; |
113 | } |
114 | |
115 | /* ASCIIZ check */ |
116 | MAIL_ASCIIZ_CHECK(to, to_len); |
117 | MAIL_ASCIIZ_CHECK(subject, subject_len); |
118 | MAIL_ASCIIZ_CHECK(message, message_len); |
119 | if (headers) { |
120 | MAIL_ASCIIZ_CHECK(headers, headers_len); |
121 | headers_trimmed = php_trim(headers, headers_len, NULL, 0, NULL, 2 TSRMLS_CC); |
122 | } |
123 | if (extra_cmd) { |
124 | MAIL_ASCIIZ_CHECK(extra_cmd, extra_cmd_len); |
125 | } |
126 | |
127 | if (to_len > 0) { |
128 | to_r = estrndup(to, to_len); |
129 | for (; to_len; to_len--) { |
130 | if (!isspace((unsigned char) to_r[to_len - 1])) { |
131 | break; |
132 | } |
133 | to_r[to_len - 1] = '\0'; |
134 | } |
135 | for (i = 0; to_r[i]; i++) { |
136 | if (iscntrl((unsigned char) to_r[i])) { |
137 | /* According to RFC 822, section 3.1.1 long headers may be separated into |
138 | * parts using CRLF followed at least one linear-white-space character ('\t' or ' '). |
139 | * To prevent these separators from being replaced with a space, we use the |
140 | * SKIP_LONG_HEADER_SEP to skip over them. */ |
141 | SKIP_LONG_HEADER_SEP(to_r, i); |
142 | to_r[i] = ' '; |
143 | } |
144 | } |
145 | } else { |
146 | to_r = to; |
147 | } |
148 | |
149 | if (subject_len > 0) { |
150 | subject_r = estrndup(subject, subject_len); |
151 | for (; subject_len; subject_len--) { |
152 | if (!isspace((unsigned char) subject_r[subject_len - 1])) { |
153 | break; |
154 | } |
155 | subject_r[subject_len - 1] = '\0'; |
156 | } |
157 | for (i = 0; subject_r[i]; i++) { |
158 | if (iscntrl((unsigned char) subject_r[i])) { |
159 | SKIP_LONG_HEADER_SEP(subject_r, i); |
160 | subject_r[i] = ' '; |
161 | } |
162 | } |
163 | } else { |
164 | subject_r = subject; |
165 | } |
166 | |
167 | if (force_extra_parameters) { |
168 | extra_cmd = php_escape_shell_cmd(force_extra_parameters); |
169 | } else if (extra_cmd) { |
170 | extra_cmd = php_escape_shell_cmd(extra_cmd); |
171 | } |
172 | |
173 | if (php_mail(to_r, subject_r, message, headers_trimmed, extra_cmd TSRMLS_CC)) { |
174 | RETVAL_TRUE; |
175 | } else { |
176 | RETVAL_FALSE; |
177 | } |
178 | |
179 | if (headers_trimmed) { |
180 | efree(headers_trimmed); |
181 | } |
182 | |
183 | if (extra_cmd) { |
184 | efree (extra_cmd); |
185 | } |
186 | if (to_r != to) { |
187 | efree(to_r); |
188 | } |
189 | if (subject_r != subject) { |
190 | efree(subject_r); |
191 | } |
192 | } |
193 | /* }}} */ |
194 | |
195 | |
196 | void php_mail_log_crlf_to_spaces(char *message) { |
197 | /* Find all instances of carriage returns or line feeds and |
198 | * replace them with spaces. Thus, a log line is always one line |
199 | * long |
200 | */ |
201 | char *p = message; |
202 | while ((p = strpbrk(p, "\r\n" ))) { |
203 | *p = ' '; |
204 | } |
205 | } |
206 | |
207 | void php_mail_log_to_syslog(char *message) { |
208 | /* Write 'message' to syslog. */ |
209 | #ifdef HAVE_SYSLOG_H |
210 | php_syslog(LOG_NOTICE, "%s" , message); |
211 | #endif |
212 | } |
213 | |
214 | |
215 | void php_mail_log_to_file(char *filename, char *message, size_t message_size TSRMLS_DC) { |
216 | /* Write 'message' to the given file. */ |
217 | uint flags = IGNORE_URL_WIN | REPORT_ERRORS | STREAM_DISABLE_OPEN_BASEDIR; |
218 | php_stream *stream = php_stream_open_wrapper(filename, "a" , flags, NULL); |
219 | if (stream) { |
220 | php_stream_write(stream, message, message_size); |
221 | php_stream_close(stream); |
222 | } |
223 | } |
224 | |
225 | |
226 | static int php_mail_detect_multiple_crlf(char *hdr) { |
227 | /* This function detects multiple/malformed multiple newlines. */ |
228 | |
229 | if (!hdr || !strlen(hdr)) { |
230 | return 0; |
231 | } |
232 | |
233 | /* Should not have any newlines at the beginning. */ |
234 | /* RFC 2822 2.2. Header Fields */ |
235 | if (*hdr < 33 || *hdr > 126 || *hdr == ':') { |
236 | return 1; |
237 | } |
238 | |
239 | while(*hdr) { |
240 | if (*hdr == '\r') { |
241 | if (*(hdr+1) == '\0' || *(hdr+1) == '\r' || (*(hdr+1) == '\n' && (*(hdr+2) == '\0' || *(hdr+2) == '\n' || *(hdr+2) == '\r'))) { |
242 | /* Malformed or multiple newlines. */ |
243 | return 1; |
244 | } else { |
245 | hdr += 2; |
246 | } |
247 | } else if (*hdr == '\n') { |
248 | if (*(hdr+1) == '\0' || *(hdr+1) == '\r' || *(hdr+1) == '\n') { |
249 | /* Malformed or multiple newlines. */ |
250 | return 1; |
251 | } else { |
252 | hdr += 2; |
253 | } |
254 | } else { |
255 | hdr++; |
256 | } |
257 | } |
258 | |
259 | return 0; |
260 | } |
261 | |
262 | |
263 | /* {{{ php_mail |
264 | */ |
265 | PHPAPI int php_mail(char *to, char *subject, char *message, char *, char * TSRMLS_DC) |
266 | { |
267 | #if (defined PHP_WIN32 || defined NETWARE) |
268 | int tsm_err; |
269 | char *tsm_errmsg = NULL; |
270 | #endif |
271 | FILE *sendmail; |
272 | int ret; |
273 | char *sendmail_path = INI_STR("sendmail_path" ); |
274 | char *sendmail_cmd = NULL; |
275 | char *mail_log = INI_STR("mail.log" ); |
276 | char *hdr = headers; |
277 | #if PHP_SIGCHILD |
278 | void (*sig_handler)() = NULL; |
279 | #endif |
280 | |
281 | #define MAIL_RET(val) \ |
282 | if (hdr != headers) { \ |
283 | efree(hdr); \ |
284 | } \ |
285 | return val; \ |
286 | |
287 | if (mail_log && *mail_log) { |
288 | char *tmp, *date_str; |
289 | time_t curtime; |
290 | int l; |
291 | |
292 | time(&curtime); |
293 | date_str = php_format_date("d-M-Y H:i:s e" , 13, curtime, 1 TSRMLS_CC); |
294 | |
295 | l = spprintf(&tmp, 0, "[%s] mail() on [%s:%d]: To: %s -- Headers: %s\n" , date_str, zend_get_executed_filename(TSRMLS_C), zend_get_executed_lineno(TSRMLS_C), to, hdr ? hdr : "" ); |
296 | |
297 | efree(date_str); |
298 | |
299 | if (hdr) { |
300 | php_mail_log_crlf_to_spaces(tmp); |
301 | } |
302 | |
303 | if (!strcmp(mail_log, "syslog" )) { |
304 | /* Drop the final space when logging to syslog. */ |
305 | tmp[l - 1] = 0; |
306 | php_mail_log_to_syslog(tmp); |
307 | } |
308 | else { |
309 | /* Convert the final space to a newline when logging to file. */ |
310 | tmp[l - 1] = '\n'; |
311 | php_mail_log_to_file(mail_log, tmp, l TSRMLS_CC); |
312 | } |
313 | |
314 | efree(tmp); |
315 | } |
316 | |
317 | if (PG(mail_x_header)) { |
318 | const char *tmp = zend_get_executed_filename(TSRMLS_C); |
319 | char *f; |
320 | size_t f_len; |
321 | |
322 | php_basename(tmp, strlen(tmp), NULL, 0,&f, &f_len TSRMLS_CC); |
323 | |
324 | if (headers != NULL && *headers) { |
325 | spprintf(&hdr, 0, "X-PHP-Originating-Script: %ld:%s\n%s" , php_getuid(TSRMLS_C), f, headers); |
326 | } else { |
327 | spprintf(&hdr, 0, "X-PHP-Originating-Script: %ld:%s" , php_getuid(TSRMLS_C), f); |
328 | } |
329 | efree(f); |
330 | } |
331 | |
332 | if (hdr && php_mail_detect_multiple_crlf(hdr)) { |
333 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Multiple or malformed newlines found in additional_header" ); |
334 | MAIL_RET(0); |
335 | } |
336 | |
337 | if (!sendmail_path) { |
338 | #if (defined PHP_WIN32 || defined NETWARE) |
339 | /* handle old style win smtp sending */ |
340 | if (TSendMail(INI_STR("SMTP" ), &tsm_err, &tsm_errmsg, hdr, subject, to, message, NULL, NULL, NULL TSRMLS_CC) == FAILURE) { |
341 | if (tsm_errmsg) { |
342 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s" , tsm_errmsg); |
343 | efree(tsm_errmsg); |
344 | } else { |
345 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s" , GetSMErrorText(tsm_err)); |
346 | } |
347 | MAIL_RET(0); |
348 | } |
349 | MAIL_RET(1); |
350 | #else |
351 | MAIL_RET(0); |
352 | #endif |
353 | } |
354 | if (extra_cmd != NULL) { |
355 | spprintf(&sendmail_cmd, 0, "%s %s" , sendmail_path, extra_cmd); |
356 | } else { |
357 | sendmail_cmd = sendmail_path; |
358 | } |
359 | |
360 | #if PHP_SIGCHILD |
361 | /* Set signal handler of SIGCHLD to default to prevent other signal handlers |
362 | * from being called and reaping the return code when our child exits. |
363 | * The original handler needs to be restored after pclose() */ |
364 | sig_handler = (void *)signal(SIGCHLD, SIG_DFL); |
365 | if (sig_handler == SIG_ERR) { |
366 | sig_handler = NULL; |
367 | } |
368 | #endif |
369 | |
370 | #ifdef PHP_WIN32 |
371 | sendmail = popen_ex(sendmail_cmd, "wb" , NULL, NULL TSRMLS_CC); |
372 | #else |
373 | /* Since popen() doesn't indicate if the internal fork() doesn't work |
374 | * (e.g. the shell can't be executed) we explicitly set it to 0 to be |
375 | * sure we don't catch any older errno value. */ |
376 | errno = 0; |
377 | sendmail = popen(sendmail_cmd, "w" ); |
378 | #endif |
379 | if (extra_cmd != NULL) { |
380 | efree (sendmail_cmd); |
381 | } |
382 | |
383 | if (sendmail) { |
384 | #ifndef PHP_WIN32 |
385 | if (EACCES == errno) { |
386 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Permission denied: unable to execute shell to run mail delivery binary '%s'" , sendmail_path); |
387 | pclose(sendmail); |
388 | #if PHP_SIGCHILD |
389 | /* Restore handler in case of error on Windows |
390 | Not sure if this applicable on Win but just in case. */ |
391 | if (sig_handler) { |
392 | signal(SIGCHLD, sig_handler); |
393 | } |
394 | #endif |
395 | MAIL_RET(0); |
396 | } |
397 | #endif |
398 | fprintf(sendmail, "To: %s\n" , to); |
399 | fprintf(sendmail, "Subject: %s\n" , subject); |
400 | if (hdr != NULL) { |
401 | fprintf(sendmail, "%s\n" , hdr); |
402 | } |
403 | fprintf(sendmail, "\n%s\n" , message); |
404 | ret = pclose(sendmail); |
405 | |
406 | #if PHP_SIGCHILD |
407 | if (sig_handler) { |
408 | signal(SIGCHLD, sig_handler); |
409 | } |
410 | #endif |
411 | |
412 | #ifdef PHP_WIN32 |
413 | if (ret == -1) |
414 | #else |
415 | #if defined(EX_TEMPFAIL) |
416 | if ((ret != EX_OK)&&(ret != EX_TEMPFAIL)) |
417 | #elif defined(EX_OK) |
418 | if (ret != EX_OK) |
419 | #else |
420 | if (ret != 0) |
421 | #endif |
422 | #endif |
423 | { |
424 | MAIL_RET(0); |
425 | } else { |
426 | MAIL_RET(1); |
427 | } |
428 | } else { |
429 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute mail delivery program '%s'" , sendmail_path); |
430 | #if PHP_SIGCHILD |
431 | if (sig_handler) { |
432 | signal(SIGCHLD, sig_handler); |
433 | } |
434 | #endif |
435 | MAIL_RET(0); |
436 | } |
437 | |
438 | MAIL_RET(1); /* never reached */ |
439 | } |
440 | /* }}} */ |
441 | |
442 | /* {{{ PHP_MINFO_FUNCTION |
443 | */ |
444 | PHP_MINFO_FUNCTION(mail) |
445 | { |
446 | char *sendmail_path = INI_STR("sendmail_path" ); |
447 | |
448 | #ifdef PHP_WIN32 |
449 | if (!sendmail_path) { |
450 | php_info_print_table_row(2, "Internal Sendmail Support for Windows" , "enabled" ); |
451 | } else { |
452 | php_info_print_table_row(2, "Path to sendmail" , sendmail_path); |
453 | } |
454 | #else |
455 | php_info_print_table_row(2, "Path to sendmail" , sendmail_path); |
456 | #endif |
457 | } |
458 | /* }}} */ |
459 | |
460 | /* |
461 | * Local variables: |
462 | * tab-width: 4 |
463 | * c-basic-offset: 4 |
464 | * End: |
465 | * vim600: sw=4 ts=4 fdm=marker |
466 | * vim<600: sw=4 ts=4 |
467 | */ |
468 | |