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