diff options
Diffstat (limited to 'ext/standard/mail.c')
| -rw-r--r-- | ext/standard/mail.c | 243 |
1 files changed, 223 insertions, 20 deletions
diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 26272cd76d..3762bb8bda 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -27,6 +27,7 @@ #include "ext/standard/php_string.h" #include "ext/standard/basic_functions.h" #include "ext/date/php_date.h" +#include "zend_smart_str.h" #if HAVE_SYSEXITS_H #include <sysexits.h> @@ -51,11 +52,6 @@ #include "win32/sendmail.h" #endif -#ifdef NETWARE -#define EX_OK 0 /* successful termination */ -#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ -#endif - #define SKIP_LONG_HEADER_SEP(str, pos) \ if (str[pos] == '\r' && str[pos + 1] == '\n' && (str[pos + 2] == ' ' || str[pos + 2] == '\t')) { \ pos += 2; \ @@ -82,9 +78,9 @@ PHP_FUNCTION(ezmlm_hash) unsigned int h = 5381; size_t j, str_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len) == FAILURE) { - return; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(str, str_len) + ZEND_PARSE_PARAMETERS_END(); for (j = 0; j < str_len; j++) { h = (h + (h << 5)) ^ (zend_ulong) (unsigned char) tolower(str[j]); @@ -96,30 +92,237 @@ PHP_FUNCTION(ezmlm_hash) } /* }}} */ + +static zend_bool php_mail_build_headers_check_field_value(zval *val) +{ + size_t len = 0; + zend_string *value = Z_STR_P(val); + + /* https://tools.ietf.org/html/rfc2822#section-2.2.1 */ + /* https://tools.ietf.org/html/rfc2822#section-2.2.3 */ + while (len < value->len) { + if (*(value->val+len) == '\r') { + if (value->len - len >= 3 + && *(value->val+len+1) == '\n' + && (*(value->val+len+2) == ' ' || *(value->val+len+2) == '\t')) { + len += 3; + continue; + } + return FAILURE; + } + if (*(value->val+len) == '\0') { + return FAILURE; + } + len++; + } + return SUCCESS; +} + + +static zend_bool php_mail_build_headers_check_field_name(zend_string *key) +{ + size_t len = 0; + + /* https://tools.ietf.org/html/rfc2822#section-2.2 */ + while (len < key->len) { + if (*(key->val+len) < 33 || *(key->val+len) > 126 || *(key->val+len) == ':') { + return FAILURE; + } + len++; + } + return SUCCESS; +} + + +static void php_mail_build_headers_elems(smart_str *s, zend_string *key, zval *val); + +static void php_mail_build_headers_elem(smart_str *s, zend_string *key, zval *val) +{ + switch(Z_TYPE_P(val)) { + case IS_STRING: + if (php_mail_build_headers_check_field_name(key) != SUCCESS) { + php_error_docref(NULL, E_WARNING, "Header field name (%s) contains invalid chars", ZSTR_VAL(key)); + return; + } + if (php_mail_build_headers_check_field_value(val) != SUCCESS) { + php_error_docref(NULL, E_WARNING, "Header field value (%s => %s) contains invalid chars or format", ZSTR_VAL(key), Z_STRVAL_P(val)); + return; + } + smart_str_append(s, key); + smart_str_appendl(s, ": ", 2); + smart_str_appends(s, Z_STRVAL_P(val)); + smart_str_appendl(s, "\r\n", 2); + break; + case IS_ARRAY: + php_mail_build_headers_elems(s, key, val); + break; + default: + php_error_docref(NULL, E_WARNING, "headers array elements must be string or array (%s)", ZSTR_VAL(key)); + } +} + + +static void php_mail_build_headers_elems(smart_str *s, zend_string *key, zval *val) +{ + zend_ulong idx; + zend_string *tmp_key; + zval *tmp_val; + + (void)(idx); + ZEND_HASH_FOREACH_KEY_VAL(HASH_OF(val), idx, tmp_key, tmp_val) { + if (tmp_key) { + php_error_docref(NULL, E_WARNING, "Multiple header key must be numeric index (%s)", ZSTR_VAL(tmp_key)); + continue; + } + if (Z_TYPE_P(tmp_val) != IS_STRING) { + php_error_docref(NULL, E_WARNING, "Multiple header values must be string (%s)", ZSTR_VAL(key)); + continue; + } + php_mail_build_headers_elem(s, key, tmp_val); + } ZEND_HASH_FOREACH_END(); +} + + +PHPAPI zend_string *php_mail_build_headers(zval *headers) +{ + zend_ulong idx; + zend_string *key; + zval *val; + smart_str s = {0}; + + ZEND_ASSERT(Z_TYPE_P(headers) == IS_ARRAY); + + ZEND_HASH_FOREACH_KEY_VAL(HASH_OF(headers), idx, key, val) { + if (!key) { + php_error_docref(NULL, E_WARNING, "Found numeric header (" ZEND_LONG_FMT ")", idx); + continue; + } + /* https://tools.ietf.org/html/rfc2822#section-3.6 */ + switch(ZSTR_LEN(key)) { + case sizeof("orig-date")-1: + if (!strncasecmp("orig-date", ZSTR_VAL(key), ZSTR_LEN(key))) { + PHP_MAIL_BUILD_HEADER_CHECK("orig-date", s, key, val); + } else { + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + } + break; + case sizeof("from")-1: + if (!strncasecmp("from", ZSTR_VAL(key), ZSTR_LEN(key))) { + PHP_MAIL_BUILD_HEADER_CHECK("from", s, key, val); + } else { + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + } + break; + case sizeof("sender")-1: + if (!strncasecmp("sender", ZSTR_VAL(key), ZSTR_LEN(key))) { + PHP_MAIL_BUILD_HEADER_CHECK("sender", s, key, val); + } else { + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + } + break; + case sizeof("reply-to")-1: + if (!strncasecmp("reply-to", ZSTR_VAL(key), ZSTR_LEN(key))) { + PHP_MAIL_BUILD_HEADER_CHECK("reply-to", s, key, val); + } else { + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + } + break; + case sizeof("to")-1: /* "to", "cc" */ + if (!strncasecmp("to", ZSTR_VAL(key), ZSTR_LEN(key))) { + php_error_docref(NULL, E_WARNING, "Extra header cannot contain 'To' header"); + continue; + } + if (!strncasecmp("cc", ZSTR_VAL(key), ZSTR_LEN(key))) { + PHP_MAIL_BUILD_HEADER_CHECK("cc", s, key, val); + } else { + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + } + break; + case sizeof("bcc")-1: + if (!strncasecmp("bcc", ZSTR_VAL(key), ZSTR_LEN(key))) { + PHP_MAIL_BUILD_HEADER_CHECK("bcc", s, key, val); + } else { + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + } + break; + case sizeof("message-id")-1: /* "references" */ + if (!strncasecmp("message-id", ZSTR_VAL(key), ZSTR_LEN(key))) { + PHP_MAIL_BUILD_HEADER_CHECK("message-id", s, key, val); + } else if (!strncasecmp("references", ZSTR_VAL(key), ZSTR_LEN(key))) { + PHP_MAIL_BUILD_HEADER_CHECK("references", s, key, val); + } else { + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + } + break; + case sizeof("in-reply-to")-1: + if (!strncasecmp("in-reply-to", ZSTR_VAL(key), ZSTR_LEN(key))) { + PHP_MAIL_BUILD_HEADER_CHECK("in-reply-to", s, key, val); + } else { + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + } + break; + case sizeof("subject")-1: + if (!strncasecmp("subject", ZSTR_VAL(key), ZSTR_LEN(key))) { + php_error_docref(NULL, E_WARNING, "Extra header cannot contain 'Subject' header"); + continue; + } + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + break; + default: + PHP_MAIL_BUILD_HEADER_DEFAULT(s, key, val); + } + } ZEND_HASH_FOREACH_END(); + + /* Remove the last \r\n */ + if (s.s) s.s->len -= 2; + smart_str_0(&s); + + return s.s; +} + + /* {{{ proto int mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]]) Send an email message */ PHP_FUNCTION(mail) { char *to=NULL, *message=NULL; char *subject=NULL; - zend_string *extra_cmd=NULL, *headers=NULL, *headers_trimmed=NULL; + zend_string *extra_cmd=NULL, *str_headers=NULL, *tmp_headers; + zval *headers = NULL; size_t to_len, message_len; size_t subject_len, i; char *force_extra_parameters = INI_STR("mail.force_extra_parameters"); char *to_r, *subject_r; char *p, *e; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|SS", &to, &to_len, &subject, &subject_len, &message, &message_len, &headers, &extra_cmd) == FAILURE) { - return; - } + ZEND_PARSE_PARAMETERS_START(3, 5) + Z_PARAM_STRING(to, to_len) + Z_PARAM_STRING(subject, subject_len) + Z_PARAM_STRING(message, message_len) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_DEREF(headers) + Z_PARAM_STR(extra_cmd) + ZEND_PARSE_PARAMETERS_END(); /* ASCIIZ check */ MAIL_ASCIIZ_CHECK(to, to_len); MAIL_ASCIIZ_CHECK(subject, subject_len); MAIL_ASCIIZ_CHECK(message, message_len); if (headers) { - MAIL_ASCIIZ_CHECK(ZSTR_VAL(headers), ZSTR_LEN(headers)); - headers_trimmed = php_trim(headers, NULL, 0, 2); + switch(Z_TYPE_P(headers)) { + case IS_STRING: + tmp_headers = zend_string_init(Z_STRVAL_P(headers), Z_STRLEN_P(headers), 0); + MAIL_ASCIIZ_CHECK(ZSTR_VAL(tmp_headers), ZSTR_LEN(tmp_headers)); + str_headers = php_trim(tmp_headers, NULL, 0, 2); + zend_string_release(tmp_headers); + break; + case IS_ARRAY: + str_headers = php_mail_build_headers(headers); + break; + default: + php_error_docref(NULL, E_WARNING, "headers parameter must be string or array"); + RETURN_FALSE; + } } if (extra_cmd) { MAIL_ASCIIZ_CHECK(ZSTR_VAL(extra_cmd), ZSTR_LEN(extra_cmd)); @@ -171,14 +374,14 @@ PHP_FUNCTION(mail) extra_cmd = php_escape_shell_cmd(ZSTR_VAL(extra_cmd)); } - if (php_mail(to_r, subject_r, message, headers_trimmed ? ZSTR_VAL(headers_trimmed) : NULL, extra_cmd ? ZSTR_VAL(extra_cmd) : NULL)) { + if (php_mail(to_r, subject_r, message, str_headers ? ZSTR_VAL(str_headers) : NULL, extra_cmd ? ZSTR_VAL(extra_cmd) : NULL)) { RETVAL_TRUE; } else { RETVAL_FALSE; } - if (headers_trimmed) { - zend_string_release(headers_trimmed); + if (str_headers) { + zend_string_release(str_headers); } if (extra_cmd) { @@ -215,7 +418,7 @@ void php_mail_log_to_syslog(char *message) { void php_mail_log_to_file(char *filename, char *message, size_t message_size) { /* Write 'message' to the given file. */ - uint flags = IGNORE_URL_WIN | REPORT_ERRORS | STREAM_DISABLE_OPEN_BASEDIR; + uint32_t flags = IGNORE_URL_WIN | REPORT_ERRORS | STREAM_DISABLE_OPEN_BASEDIR; php_stream *stream = php_stream_open_wrapper(filename, "a", flags, NULL); if (stream) { php_stream_write(stream, message, message_size); @@ -265,7 +468,7 @@ static int php_mail_detect_multiple_crlf(char *hdr) { */ PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char *extra_cmd) { -#if (defined PHP_WIN32 || defined NETWARE) +#ifdef PHP_WIN32 int tsm_err; char *tsm_errmsg = NULL; #endif @@ -337,7 +540,7 @@ PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char } if (!sendmail_path) { -#if (defined PHP_WIN32 || defined NETWARE) +#ifdef PHP_WIN32 /* handle old style win smtp sending */ if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, hdr, subject, to, message, NULL, NULL, NULL) == FAILURE) { if (tsm_errmsg) { |
