1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 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 SKIP_LONG_HEADER_SEP(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 75extern long php_getuid(TSRMLS_D); 76 77/* {{{ proto int ezmlm_hash(string addr) 78 Calculate EZMLM list hash value. */ 79PHP_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 */ 101PHP_FUNCTION(mail) 102{ 103 char *to=NULL, *message=NULL, *headers=NULL, *headers_trimmed=NULL; 104 char *subject=NULL, *extra_cmd=NULL; 105 int to_len, message_len, headers_len = 0; 106 int subject_len, extra_cmd_len = 0, i; 107 char *force_extra_parameters = 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 196void 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 207void 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 215void 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/* {{{ php_mail 227 */ 228PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char *extra_cmd TSRMLS_DC) 229{ 230#if (defined PHP_WIN32 || defined NETWARE) 231 int tsm_err; 232 char *tsm_errmsg = NULL; 233#endif 234 FILE *sendmail; 235 int ret; 236 char *sendmail_path = INI_STR("sendmail_path"); 237 char *sendmail_cmd = NULL; 238 char *mail_log = INI_STR("mail.log"); 239 char *hdr = headers; 240#if PHP_SIGCHILD 241 void (*sig_handler)() = NULL; 242#endif 243 244#define MAIL_RET(val) \ 245 if (hdr != headers) { \ 246 efree(hdr); \ 247 } \ 248 return val; \ 249 250 if (mail_log && *mail_log) { 251 char *tmp, *date_str; 252 time_t curtime; 253 int l; 254 255 time(&curtime); 256 date_str = php_format_date("d-M-Y H:i:s e", 13, curtime, 1 TSRMLS_CC); 257 258 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 : ""); 259 260 efree(date_str); 261 262 if (hdr) { 263 php_mail_log_crlf_to_spaces(tmp); 264 } 265 266 if (!strcmp(mail_log, "syslog")) { 267 /* Drop the final space when logging to syslog. */ 268 tmp[l - 1] = 0; 269 php_mail_log_to_syslog(tmp); 270 } 271 else { 272 /* Convert the final space to a newline when logging to file. */ 273 tmp[l - 1] = '\n'; 274 php_mail_log_to_file(mail_log, tmp, l TSRMLS_CC); 275 } 276 277 efree(tmp); 278 } 279 if (PG(mail_x_header)) { 280 const char *tmp = zend_get_executed_filename(TSRMLS_C); 281 char *f; 282 size_t f_len; 283 284 php_basename(tmp, strlen(tmp), NULL, 0,&f, &f_len TSRMLS_CC); 285 286 if (headers != NULL) { 287 spprintf(&hdr, 0, "X-PHP-Originating-Script: %ld:%s\n%s", php_getuid(TSRMLS_C), f, headers); 288 } else { 289 spprintf(&hdr, 0, "X-PHP-Originating-Script: %ld:%s\n", php_getuid(TSRMLS_C), f); 290 } 291 efree(f); 292 } 293 294 if (!sendmail_path) { 295#if (defined PHP_WIN32 || defined NETWARE) 296 /* handle old style win smtp sending */ 297 if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message, NULL, NULL, NULL TSRMLS_CC) == FAILURE) { 298 if (tsm_errmsg) { 299 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", tsm_errmsg); 300 efree(tsm_errmsg); 301 } else { 302 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", GetSMErrorText(tsm_err)); 303 } 304 MAIL_RET(0); 305 } 306 MAIL_RET(1); 307#else 308 MAIL_RET(0); 309#endif 310 } 311 if (extra_cmd != NULL) { 312 spprintf(&sendmail_cmd, 0, "%s %s", sendmail_path, extra_cmd); 313 } else { 314 sendmail_cmd = sendmail_path; 315 } 316 317#if PHP_SIGCHILD 318 /* Set signal handler of SIGCHLD to default to prevent other signal handlers 319 * from being called and reaping the return code when our child exits. 320 * The original handler needs to be restored after pclose() */ 321 sig_handler = (void *)signal(SIGCHLD, SIG_DFL); 322 if (sig_handler == SIG_ERR) { 323 sig_handler = NULL; 324 } 325#endif 326 327#ifdef PHP_WIN32 328 sendmail = popen_ex(sendmail_cmd, "wb", NULL, NULL TSRMLS_CC); 329#else 330 /* Since popen() doesn't indicate if the internal fork() doesn't work 331 * (e.g. the shell can't be executed) we explicitely set it to 0 to be 332 * sure we don't catch any older errno value. */ 333 errno = 0; 334 sendmail = popen(sendmail_cmd, "w"); 335#endif 336 if (extra_cmd != NULL) { 337 efree (sendmail_cmd); 338 } 339 340 if (sendmail) { 341#ifndef PHP_WIN32 342 if (EACCES == errno) { 343 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Permission denied: unable to execute shell to run mail delivery binary '%s'", sendmail_path); 344 pclose(sendmail); 345#if PHP_SIGCHILD 346 /* Restore handler in case of error on Windows 347 Not sure if this applicable on Win but just in case. */ 348 if (sig_handler) { 349 signal(SIGCHLD, sig_handler); 350 } 351#endif 352 MAIL_RET(0); 353 } 354#endif 355 fprintf(sendmail, "To: %s\n", to); 356 fprintf(sendmail, "Subject: %s\n", subject); 357 if (hdr != NULL) { 358 fprintf(sendmail, "%s\n", hdr); 359 } 360 fprintf(sendmail, "\n%s\n", message); 361 ret = pclose(sendmail); 362 363#if PHP_SIGCHILD 364 if (sig_handler) { 365 signal(SIGCHLD, sig_handler); 366 } 367#endif 368 369#ifdef PHP_WIN32 370 if (ret == -1) 371#else 372#if defined(EX_TEMPFAIL) 373 if ((ret != EX_OK)&&(ret != EX_TEMPFAIL)) 374#elif defined(EX_OK) 375 if (ret != EX_OK) 376#else 377 if (ret != 0) 378#endif 379#endif 380 { 381 MAIL_RET(0); 382 } else { 383 MAIL_RET(1); 384 } 385 } else { 386 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute mail delivery program '%s'", sendmail_path); 387#if PHP_SIGCHILD 388 if (sig_handler) { 389 signal(SIGCHLD, sig_handler); 390 } 391#endif 392 MAIL_RET(0); 393 } 394 395 MAIL_RET(1); /* never reached */ 396} 397/* }}} */ 398 399/* {{{ PHP_MINFO_FUNCTION 400 */ 401PHP_MINFO_FUNCTION(mail) 402{ 403 char *sendmail_path = INI_STR("sendmail_path"); 404 405#ifdef PHP_WIN32 406 if (!sendmail_path) { 407 php_info_print_table_row(2, "Internal Sendmail Support for Windows", "enabled"); 408 } else { 409 php_info_print_table_row(2, "Path to sendmail", sendmail_path); 410 } 411#else 412 php_info_print_table_row(2, "Path to sendmail", sendmail_path); 413#endif 414} 415/* }}} */ 416 417/* 418 * Local variables: 419 * tab-width: 4 420 * c-basic-offset: 4 421 * End: 422 * vim600: sw=4 ts=4 fdm=marker 423 * vim<600: sw=4 ts=4 424 */ 425