summaryrefslogtreecommitdiff
path: root/ext/standard/mail.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/standard/mail.c')
-rw-r--r--ext/standard/mail.c243
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) {