root/ext/standard/mail.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. PHP_FUNCTION
  2. PHP_FUNCTION
  3. php_mail_log_crlf_to_spaces
  4. php_mail_log_to_syslog
  5. php_mail_log_to_file
  6. php_mail_detect_multiple_crlf
  7. php_mail
  8. PHP_MINFO_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */