From e4acbbd8cc02b960bd5240538016b2c69c33d6bd Mon Sep 17 00:00:00 2001 From: alecpl Date: Wed, 14 Oct 2009 10:52:27 +0000 Subject: [PATCH] - Added server-side e-mail address validation with 'email_dns_check' option (#1485857) --- CHANGELOG | 1 + config/main.inc.php.dist | 3 ++ plugins/managesieve/managesieve.php | 3 ++ program/include/main.inc | 52 +++++++++++++++++++++++++ program/localization/en_US/messages.inc | 1 + program/localization/pl_PL/messages.inc | 1 + program/steps/mail/sendmail.inc | 19 ++++++++- 7 files changed, 79 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 86238b245..02e6a4c60 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG RoundCube Webmail =========================== +- Added server-side e-mail address validation with 'email_dns_check' option (#1485857) - Fix login page loading into an iframe when session expires (#1485952) - added option 'force_https_port' in 'force_https' plugin (#1486091) - Option 'force_https' replaced by 'force_https' plugin diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index e184078a9..7fbf97132 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -364,6 +364,9 @@ $rcmail_config['delete_always'] = false; // Must be less than 'session_lifetime' $rcmail_config['min_keep_alive'] = 60; +// Enable DNS checking for e-mail address validation +$rcmail_config['email_dns_check'] = false; + /***** these settings can be overwritten by user's preferences *****/ // skin name: folder from skins/ diff --git a/plugins/managesieve/managesieve.php b/plugins/managesieve/managesieve.php index 9a5e721c1..0c8c492e4 100644 --- a/plugins/managesieve/managesieve.php +++ b/plugins/managesieve/managesieve.php @@ -818,6 +818,9 @@ class managesieve extends rcube_plugin private function check_email($email) { + if (function_exists('check_email')); + return check_email($email); + // Check for invalid characters if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $email)) return false; diff --git a/program/include/main.inc b/program/include/main.inc index e96840003..06d7780d1 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -1400,6 +1400,58 @@ function rcube_html_editor($mode='') } +/** + * E-mail address validation + */ +function check_email($email) +{ + // Check for invalid characters + if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $email)) + return false; + + // Check that there's one @ symbol, and that the lengths are right + if (!preg_match('/^([^@]{1,64})@([^@]{1,255})$/', $email, $email_array)) + return false; + + // Check local part + $local_array = explode('.', $email_array[1]); + foreach ($local_array as $local_part) + if (!preg_match('/^(([A-Za-z0-9!#$%&\'*+\/=?^_`{|}~-]+)|("[^"]+"))$/', $local_part)) + return false; + + // Check domain part + if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}$/', $email_array[2]) + || preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]$/', $email_array[2])) + return true; // If an IP address + else { + // If not an IP address + $domain_array = explode('.', $email_array[2]); + if (sizeof($domain_array) < 2) + return false; // Not enough parts to be a valid domain + + foreach ($domain_array as $domain_part) + if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/', $domain_part)) + return false; + + if (!rcmail::get_instance()->config->get('email_dns_check')) + return true; + + if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && version_compare(PHP_VERSION, '5.3.0', '<')) + return true; + + // find MX record(s) + if (getmxrr($email_array[2], $mx_records)) + return true; + + // find any DNS record + if (checkdnsrr($email_array[2], 'ANY')) + return true; + } + + return false; +} + + /** * Helper class to turn relative urls into absolute ones * using a predefined base diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc index 6fc7be862..b0a812ce7 100644 --- a/program/localization/en_US/messages.inc +++ b/program/localization/en_US/messages.inc @@ -106,5 +106,6 @@ $messages['smtpfromerror'] = 'SMTP Error ($code): Failed to set sender "$from"'; $messages['smtptoerror'] = 'SMTP Error ($code): Failed to add recipient "$to"'; $messages['smtprecipientserror'] = 'SMTP Error: Unable to parse recipients list'; $messages['smtperror'] = 'SMTP Error: $msg'; +$messages['emailformaterror'] = 'Incorrect e-mail address: $email'; ?> diff --git a/program/localization/pl_PL/messages.inc b/program/localization/pl_PL/messages.inc index 69b9b4285..1fc765982 100644 --- a/program/localization/pl_PL/messages.inc +++ b/program/localization/pl_PL/messages.inc @@ -110,5 +110,6 @@ $messages['smtptoerror'] = 'Błąd SMTP ($code): Nie można dodać odbiorcy "$to $messages['smtprecipientserror'] = 'Błąd SMTP: Parsowanie listy odbiorców nie powiodło się'; $messages['smtperror'] = 'Błąd SMTP: $msg'; $messages['invalidrequest'] = 'Błędne żądanie! Nie zapisano danych.'; +$messages['emailformaterror'] = 'Błędny adres e-mail: $email'; ?> diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index e8ef47a55..e8445aa4c 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -151,6 +151,8 @@ function rcmail_attach_emoticons(&$mime_message) // parse email address input function rcmail_email_input_format($mailto) { + global $EMAIL_FORMAT_ERROR; + $regexp = array('/[,;]\s*[\r\n]+/', '/[\r\n]+/', '/[,;]\s*$/m', '/;/', '/(\S{1})(<\S+@\S+>)/U'); $replace = array(', ', ', ', '', ',', '\\1 \\2'); @@ -181,8 +183,16 @@ function rcmail_email_input_format($mailto) $address = '<'.$address.'>'; $result[] = $name.' '.$address; + $item = $address; } else if (trim($item)) { - // @TODO: handle errors + continue; + } + + // check address format + $item = trim($item, '<>'); + if ($item && !check_email($item)) { + $EMAIL_FORMAT_ERROR = $item; + return; } } @@ -200,10 +210,17 @@ $message_id = sprintf('<%s@%s>', md5(uniqid('rcmail'.mt_rand(),true)), $RCMAIL-> $input_charset = $OUTPUT->get_charset(); $message_charset = isset($_POST['_charset']) ? $_POST['_charset'] : $input_charset; +$EMAIL_FORMAT_ERROR = NULL; + $mailto = rcmail_email_input_format(get_input_value('_to', RCUBE_INPUT_POST, TRUE, $message_charset)); $mailcc = rcmail_email_input_format(get_input_value('_cc', RCUBE_INPUT_POST, TRUE, $message_charset)); $mailbcc = rcmail_email_input_format(get_input_value('_bcc', RCUBE_INPUT_POST, TRUE, $message_charset)); +if ($EMAIL_FORMAT_ERROR) { + $OUTPUT->show_message('emailformaterror', 'error', array('email' => $EMAIL_FORMAT_ERROR)); + $OUTPUT->send('iframe'); +} + if (empty($mailto) && !empty($mailcc)) { $mailto = $mailcc; $mailcc = null;