- Add Internationalized Domain Name (IDNA) support (#1483894)

release-0.6
alecpl 14 years ago
parent d7f9eb573b
commit e99991996d

@ -3,6 +3,7 @@ CHANGELOG Roundcube Webmail
- Messages caching: performance improvements, fixed syncing, fixes related with #1486748
- Add link to identities in compose window (#1486729)
- Add Internationalized Domain Name (IDNA) support (#1483894)
RELEASE 0.4.1
-------------

@ -15,6 +15,7 @@ $optional_php_exts = array(
'Multibyte' => 'mbstring',
'OpenSSL' => 'openssl',
'Mcrypt' => 'mcrypt',
'Intl' => 'intl',
);
$required_libs = array(
@ -44,19 +45,20 @@ $optional_checks = array(
);
$source_urls = array(
'Sockets' => 'http://www.php.net/manual/en/book.sockets.php',
'Session' => 'http://www.php.net/manual/en/book.session.php',
'PCRE' => 'http://www.php.net/manual/en/book.pcre.php',
'FileInfo' => 'http://www.php.net/manual/en/book.fileinfo.php',
'Libiconv' => 'http://www.php.net/manual/en/book.iconv.php',
'Sockets' => 'http://www.php.net/manual/en/book.sockets.php',
'Session' => 'http://www.php.net/manual/en/book.session.php',
'PCRE' => 'http://www.php.net/manual/en/book.pcre.php',
'FileInfo' => 'http://www.php.net/manual/en/book.fileinfo.php',
'Libiconv' => 'http://www.php.net/manual/en/book.iconv.php',
'Multibyte' => 'http://www.php.net/manual/en/book.mbstring.php',
'Mcrypt' => 'http://www.php.net/manual/en/book.mcrypt.php',
'OpenSSL' => 'http://www.php.net/manual/en/book.openssl.php',
'JSON' => 'http://www.php.net/manual/en/book.json.php',
'DOM' => 'http://www.php.net/manual/en/book.dom.php',
'PEAR' => 'http://pear.php.net',
'MDB2' => 'http://pear.php.net/package/MDB2',
'Net_SMTP' => 'http://pear.php.net/package/Net_SMTP',
'Mcrypt' => 'http://www.php.net/manual/en/book.mcrypt.php',
'OpenSSL' => 'http://www.php.net/manual/en/book.openssl.php',
'JSON' => 'http://www.php.net/manual/en/book.json.php',
'DOM' => 'http://www.php.net/manual/en/book.dom.php',
'Intl' => 'http://www.php.net/manual/en/book.intl.php',
'PEAR' => 'http://pear.php.net',
'MDB2' => 'http://pear.php.net/package/MDB2',
'Net_SMTP' => 'http://pear.php.net/package/Net_SMTP',
'Mail_mime' => 'http://pear.php.net/package/Mail_mime',
);

@ -249,18 +249,21 @@ if (isset($_POST['sendmail'])) {
echo '<p>Trying to send email...<br />';
if (preg_match('/^' . $RCI->email_pattern . '$/i', trim($_POST['_from'])) &&
preg_match('/^' . $RCI->email_pattern . '$/i', trim($_POST['_to']))) {
$from = idn_to_ascii(trim($_POST['_from']));
$to = idn_to_ascii(trim($_POST['_to']));
if (preg_match('/^' . $RCI->email_pattern . '$/i', $from) &&
preg_match('/^' . $RCI->email_pattern . '$/i', $to)
) {
$headers = array(
'From' => trim($_POST['_from']),
'To' => trim($_POST['_to']),
'From' => $from,
'To' => $to,
'Subject' => 'Test message from Roundcube',
);
$body = 'This is a test to confirm that Roundcube can send email.';
$smtp_response = array();
// send mail using configured SMTP server
if ($RCI->getprop('smtp_server')) {
$CONFIG = $RCI->config;
@ -383,9 +386,12 @@ if (isset($_POST['imaptest']) && !empty($_POST['_host']) && !empty($_POST['_user
$imap_host = trim($_POST['_host']);
$imap_port = $RCI->getprop('default_port');
}
$imap_host = idn_to_ascii($imap_host);
$imap_user = idn_to_ascii($_POST['_user']);
$imap = new rcube_imap(null);
if ($imap->connect($imap_host, $_POST['_user'], $_POST['_pass'], $imap_port, $imap_ssl)) {
if ($imap->connect($imap_host, $imap_user, $_POST['_pass'], $imap_port, $imap_ssl)) {
$RCI->pass('IMAP connect', 'SORT capability: ' . ($imap->get_capability('SORT') ? 'yes' : 'no'));
$imap->close();
}

@ -1764,4 +1764,3 @@ function log_bug($arg_arr)
flush();
}
}

@ -587,7 +587,7 @@ class rcmail
if ($a_host['host']) {
$host = $a_host['host'];
$imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
if(!empty($a_host['port']))
if (!empty($a_host['port']))
$imap_port = $a_host['port'];
else if ($imap_ssl && $imap_ssl != 'tls' && (!$config['default_port'] || $config['default_port'] == 143))
$imap_port = 993;
@ -618,6 +618,12 @@ class rcmail
if (!$this->imap)
$this->imap_init();
// Here we need IDNA ASCII
// Only rcube_contacts class is using domain names in Unicode
$host = idn_to_ascii($host);
if (strpos($username, '@'))
$username = idn_to_ascii($username);
// try IMAP login
if (!($imap_login = $this->imap->connect($host, $username, $pass, $imap_port, $imap_ssl))) {
// lowercase username if it's an e-mail address (#1484473)

@ -274,10 +274,11 @@ class rcube_config
/**
* Return the mail domain configured for the given host
*
* @param string IMAP host
* @param string IMAP host
* @param boolean If true, domain name will be converted to IDN ASCII
* @return string Resolved SMTP host
*/
public function mail_domain($host)
public function mail_domain($host, $encode=true)
{
$domain = $host;
@ -288,6 +289,9 @@ class rcube_config
else if (!empty($this->prop['mail_domain']))
$domain = rcube_parse_host($this->prop['mail_domain']);
if ($encode)
$domain = idn_to_ascii($domain);
return $domain;
}

@ -3505,7 +3505,7 @@ class rcube_imap
$address = trim($val['address']);
$name = trim($val['name']);
if (preg_match('/^[\'"]/', $name) && preg_match('/[\'"]$/', $name))
if ($name && preg_match('/^[\'"]/', $name) && preg_match('/[\'"]$/', $name))
$name = trim($name, '\'"');
if ($name && $address && $name != $address)
@ -3515,7 +3515,8 @@ class rcube_imap
else if ($name)
$string = $name;
$out[$j] = array('name' => $name,
$out[$j] = array(
'name' => $name,
'mailto' => $address,
'string' => $string
);
@ -3912,9 +3913,9 @@ class rcube_imap
$result[$key]['name'] .= (empty($result[$key]['name'])?'':' ').str_replace("\"",'',stripslashes($v));
}
if (empty($result[$key]['name']))
$result[$key]['name'] = $result[$key]['address'];
elseif (empty($result[$key]['address']))
// if (empty($result[$key]['name']))
// $result[$key]['name'] = $result[$key]['address'];
if (empty($result[$key]['address']))
$result[$key]['address'] = $result[$key]['name'];
}

@ -99,7 +99,7 @@ class rcube_ldap extends rcube_addressbook
foreach ($this->prop['hosts'] as $host)
{
$host = rcube_parse_host($host);
$host = idn_to_ascii(rcube_parse_host($host));
$this->_debug("C: Connect [$host".($this->prop['port'] ? ':'.$this->prop['port'] : '')."]");
if ($lc = @ldap_connect($host, $this->prop['port']))

@ -680,3 +680,51 @@ if (!extension_loaded('mbstring'))
}
}
/**
* intl replacement functions
*/
if (!function_exists('idn_to_utf8'))
{
function idn_to_utf8($domain, $flags=null)
{
static $idn, $loaded;
if (!$loaded) {
$idn = new Net_IDNA2();
$loaded = true;
}
if ($idn && $domain && preg_match('/(^|@|\.)xn--/i', $domain)) {
try {
$domain = $idn->decode($domain);
}
catch (Exception $e) {
}
}
return $domain;
}
}
if (!function_exists('idn_to_ascii'))
{
function idn_to_ascii($domain, $flags=null)
{
static $idn, $loaded;
if (!$loaded) {
$idn = new Net_IDNA2();
$loaded = true;
}
if ($idn && $domain && preg_match('/[^\x20-\x7E]/', $domain)) {
try {
$domain = $idn->encode($domain);
}
catch (Exception $e) {
}
}
return $domain;
}
}

@ -98,6 +98,9 @@ class rcube_smtp
else
$helo_host = 'localhost';
// IDNA Support
$smtp_host = idn_to_ascii($smtp_host);
$this->conn = new Net_SMTP($smtp_host, $smtp_port, $helo_host);
if($RCMAIL->config->get('smtp_debug'))
@ -116,10 +119,14 @@ class rcube_smtp
$smtp_user = str_replace('%u', $_SESSION['username'], $CONFIG['smtp_user']);
$smtp_pass = str_replace('%p', $RCMAIL->decrypt($_SESSION['password']), $CONFIG['smtp_pass']);
$smtp_auth_type = empty($CONFIG['smtp_auth_type']) ? NULL : $CONFIG['smtp_auth_type'];
// attempt to authenticate to the SMTP server
if ($smtp_user && $smtp_pass)
{
// IDNA Support
if (strpos($smtp_user, '@'))
$smtp_user = idn_to_ascii($smtp_user);
$result = $this->conn->auth($smtp_user, $smtp_pass, $smtp_auth_type, $use_tls);
if (PEAR::isError($result))

@ -994,15 +994,19 @@ class rcube_template extends rcube_html_page
return $username;
}
// Current username is an e-mail address
if (strpos($_SESSION['username'], '@')) {
$username = $_SESSION['username'];
}
// get e-mail address from default identity
if ($sql_arr = $this->app->user->get_identity()) {
else if ($sql_arr = $this->app->user->get_identity()) {
$username = $sql_arr['email'];
}
else {
$username = $this->app->user->get_username();
}
return $username;
return idn_to_utf8($username);
}

@ -44,7 +44,7 @@ class rcube_user
function __construct($id = null, $sql_arr = null)
{
$this->db = rcmail::get_instance()->get_dbh();
if ($id && !$sql_arr) {
$sql_result = $this->db->query(
"SELECT * FROM ".get_table_name('users')." WHERE user_id = ?", $id);
@ -121,14 +121,14 @@ class rcube_user
{
if (!$this->ID)
return false;
$config = rcmail::get_instance()->config;
$old_prefs = (array)$this->get_prefs();
// merge (partial) prefs array with existing settings
$save_prefs = $a_user_prefs + $old_prefs;
unset($save_prefs['language']);
// don't save prefs with default values if they haven't been changed yet
foreach ($a_user_prefs as $key => $value) {
if (!isset($old_prefs[$key]) && ($value == $config->get($key)))
@ -186,11 +186,11 @@ class rcube_user
($sql_add ? " ".$sql_add : "").
" ORDER BY ".$this->db->quoteIdentifier('standard')." DESC, name ASC, identity_id ASC",
$this->ID);
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
$result[] = $sql_arr;
}
return $result;
}
@ -208,7 +208,7 @@ class rcube_user
return false;
$query_cols = $query_params = array();
foreach ((array)$data as $col => $value) {
$query_cols[] = $this->db->quoteIdentifier($col) . ' = ?';
$query_params[] = $value;
@ -224,11 +224,11 @@ class rcube_user
call_user_func_array(array($this->db, 'query'),
array_merge(array($sql), $query_params));
return $this->db->affected_rows();
}
/**
* Create a new identity record linked with this user
*
@ -259,8 +259,8 @@ class rcube_user
return $this->db->insert_id('identities');
}
/**
* Mark the given identity as deleted
*
@ -282,7 +282,7 @@ class rcube_user
// we'll not delete last identity
if ($sql_arr['ident_count'] <= 1)
return false;
$this->db->query(
"UPDATE ".get_table_name('identities').
" SET del = 1, changed = ".$this->db->now().
@ -293,8 +293,8 @@ class rcube_user
return $this->db->affected_rows();
}
/**
* Make this identity the default one for this user
*
@ -313,8 +313,8 @@ class rcube_user
$iid);
}
}
/**
* Update user's last_login timestamp
*/
@ -328,8 +328,8 @@ class rcube_user
$this->ID);
}
}
/**
* Clear the saved object state
*/
@ -338,8 +338,8 @@ class rcube_user
$this->ID = null;
$this->data = null;
}
/**
* Find a user record matching the given name and host
*
@ -350,25 +350,25 @@ class rcube_user
static function query($user, $host)
{
$dbh = rcmail::get_instance()->get_dbh();
// query for matching user name
$query = "SELECT * FROM ".get_table_name('users')." WHERE mail_host = ? AND %s = ?";
$sql_result = $dbh->query(sprintf($query, 'username'), $host, $user);
// query for matching alias
if (!($sql_arr = $dbh->fetch_assoc($sql_result))) {
$sql_result = $dbh->query(sprintf($query, 'alias'), $host, $user);
$sql_arr = $dbh->fetch_assoc($sql_result);
}
// user already registered -> overwrite username
if ($sql_arr)
return new rcube_user($sql_arr['user_id'], $sql_arr);
else
return false;
}
/**
* Create a new user record and return a rcube_user instance
*
@ -448,7 +448,7 @@ class rcube_user
$plugin = $rcmail->plugins->exec_hook('identity_create',
array('login' => true, 'record' => $record));
if (!$plugin['abort'] && $plugin['record']['email']) {
$rcmail->user->insert_identity($plugin['record']);
}
@ -463,11 +463,11 @@ class rcube_user
'file' => __FILE__,
'message' => "Failed to create new user"), true, false);
}
return $user_id ? $user_instance : false;
}
/**
* Resolve username using a virtuser plugins
*

@ -484,21 +484,26 @@ function rcube_layer(id, attributes)
function rcube_check_email(input, inline)
{
if (input && window.RegExp) {
var qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
var dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
var atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
var quoted_pair = '\\x5c[\\x00-\\x7f]';
var domain_literal = '\\x5b('+dtext+'|'+quoted_pair+')*\\x5d';
var quoted_string = '\\x22('+qtext+'|'+quoted_pair+')*\\x22';
var sub_domain = '('+atom+'|'+domain_literal+')';
var word = '('+atom+'|'+quoted_string+')';
var domain = sub_domain+'(\\x2e'+sub_domain+')*';
var local_part = word+'(\\x2e'+word+')*';
var addr_spec = local_part+'\\x40'+domain;
var delim = '[,;\s\n]';
var reg1 = inline ? new RegExp('(^|<|'+delim+')'+addr_spec+'($|>|'+delim+')', 'i') : new RegExp('^'+addr_spec+'$', 'i');
var qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]',
dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]',
atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+',
quoted_pair = '\\x5c[\\x00-\\x7f]',
quoted_string = '\\x22('+qtext+'|'+quoted_pair+')*\\x22',
// Use simplified domain matching, because we need to allow Unicode characters here
// So, e-mail address should be validated also on server side after idn_to_ascii() use
sub_domain = '[^@]+',
//domain_literal = '\\x5b('+dtext+'|'+quoted_pair+')*\\x5d',
//sub_domain = '('+atom+'|'+domain_literal+')',
word = '('+atom+'|'+quoted_string+')',
delim = '[,;\s\n]',
domain = sub_domain+'(\\x2e'+sub_domain+')*',
local_part = word+'(\\x2e'+word+')*',
addr_spec = local_part+'\\x40'+domain,
reg1 = inline ? new RegExp('(^|<|'+delim+')'+addr_spec+'($|>|'+delim+')', 'i') : new RegExp('^'+addr_spec+'$', 'i');
return reg1.test(input) ? true : false;
}
return false;
};

File diff suppressed because it is too large Load Diff

@ -0,0 +1,3 @@
<?php
class Net_IDNA2_Exception extends Exception {
}

@ -0,0 +1,5 @@
<?php
require_once 'Net/IDNA2/Exception.php';
class Net_IDNA2_Exception_Nameprep extends Net_IDNA2_Exception {
}

@ -36,7 +36,9 @@ if (!empty($_POST['_address']) && is_object($CONTACTS))
'email' => $contact_arr[1]['mailto'],
'name' => $contact_arr[1]['name']
);
$contact['email'] = idn_to_utf8($contact['email']);
// use email address part for name
if (empty($contact['name']) || $contact['name'] == $contact['email'])
$contact['name'] = ucfirst(preg_replace('/[\.\-]/', ' ', substr($contact['email'], 0, strpos($contact['email'], '@'))));

@ -227,14 +227,12 @@ function rcmail_compose_headers($attrib)
$fvalue = urldecode($_SESSION['mailto'][$mailto_id]);
case 'cc':
if (!$fname)
{
if (!$fname) {
$fname = '_cc';
$header = $param = 'cc';
}
case 'bcc':
if (!$fname)
{
if (!$fname) {
$fname = '_bcc';
$header = $param = 'bcc';
}
@ -251,7 +249,7 @@ function rcmail_compose_headers($attrib)
$field_type = 'html_inputfield';
break;
}
if ($fname && !empty($_POST[$fname])) {
$fvalue = get_input_value($fname, RCUBE_INPUT_POST, TRUE);
}
@ -262,13 +260,10 @@ function rcmail_compose_headers($attrib)
// get recipent address(es) out of the message headers
if ($header=='to' && !empty($MESSAGE->headers->replyto))
$fvalue = $MESSAGE->headers->replyto;
else if ($header=='to' && !empty($MESSAGE->headers->from))
$fvalue = $MESSAGE->headers->from;
// add recipent of original message if reply to all
else if ($header=='cc' && !empty($MESSAGE->reply_all))
{
else if ($header=='cc' && !empty($MESSAGE->reply_all)) {
if ($v = $MESSAGE->headers->to)
$fvalue .= $v;
@ -277,39 +272,58 @@ function rcmail_compose_headers($attrib)
}
// split recipients and put them back together in a unique way
if (!empty($fvalue))
{
if (!empty($fvalue)) {
$to_addresses = $IMAP->decode_address_list($fvalue);
$fvalue = '';
foreach ($to_addresses as $addr_part)
{
if (!empty($addr_part['mailto'])
&& !in_array($addr_part['mailto'], $sa_recipients)
foreach ($to_addresses as $addr_part) {
if (empty($addr_part['mailto']))
continue;
$mailto = idn_to_utf8($addr_part['mailto']);
if (!in_array($mailto, $sa_recipients)
&& (!$MESSAGE->compose_from
|| !in_array_nocase($addr_part['mailto'], $MESSAGE->compose_from)
|| (count($to_addresses)==1 && $header=='to'))) // allow reply to yourself
{
$fvalue .= (strlen($fvalue) ? ', ':'').$addr_part['string'];
|| !in_array_nocase($mailto, $MESSAGE->compose_from)
|| (count($to_addresses)==1 && $header=='to')) // allow reply to yourself
) {
if ($addr_part['name'] && $addr_part['mailto'] != $addr_part['name'])
$string = format_email_recipient($mailto, $addr_part['name']);
else
$string = $mailto;
$fvalue .= (strlen($fvalue) ? ', ':'') . $string;
$sa_recipients[] = $addr_part['mailto'];
}
}
}
}
else if ($header && in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT)))
{
else if ($header && in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
// get drafted headers
if ($header=='to' && !empty($MESSAGE->headers->to))
$fvalue = $MESSAGE->get_header('to');
if ($header=='cc' && !empty($MESSAGE->headers->cc))
else if ($header=='cc' && !empty($MESSAGE->headers->cc))
$fvalue = $MESSAGE->get_header('cc');
if ($header=='bcc' && !empty($MESSAGE->headers->bcc))
else if ($header=='bcc' && !empty($MESSAGE->headers->bcc))
$fvalue = $MESSAGE->get_header('bcc');
$addresses = $IMAP->decode_address_list($fvalue);
$fvalue = '';
foreach ($addresses as $addr_part) {
if (empty($addr_part['mailto']))
continue;
$mailto = idn_to_utf8($addr_part['mailto']);
if ($addr_part['name'] && $addr_part['mailto'] != $addr_part['name'])
$string = format_email_recipient($mailto, $addr_part['name']);
else
$string = $mailto;
$fvalue .= (strlen($fvalue) ? ', ':'') . $string;
}
}
if ($fname && $field_type)
{
// pass the following attributes to the form class
@ -326,7 +340,7 @@ function rcmail_compose_headers($attrib)
if ($form_start)
$out = $form_start.$out;
return $out;
return $out;
}
@ -350,7 +364,7 @@ function rcmail_compose_header_from($attrib)
foreach ($a_to as $addr)
{
if (!empty($addr['mailto']))
$a_recipients[] = mb_strtolower($addr['mailto']);
$a_recipients[] = mb_strtolower(idn_to_utf8($addr['mailto']));
}
if (!empty($MESSAGE->headers->cc))
@ -359,7 +373,7 @@ function rcmail_compose_header_from($attrib)
foreach ($a_cc as $addr)
{
if (!empty($addr['mailto']))
$a_recipients[] = mb_strtolower($addr['mailto']);
$a_recipients[] = mb_strtolower(idn_to_utf8($addr['mailto']));
}
}
}
@ -377,6 +391,7 @@ function rcmail_compose_header_from($attrib)
foreach ($user_identities as $sql_arr)
{
$sql_arr['email'] = mb_strtolower(idn_to_utf8($sql_arr['email']));
$identity_id = $sql_arr['identity_id'];
$select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $identity_id);
@ -401,7 +416,7 @@ function rcmail_compose_header_from($attrib)
if ($compose_mode == RCUBE_COMPOSE_DRAFT && strstr($MESSAGE->headers->from, $sql_arr['email']))
$from_id = $sql_arr['identity_id'];
// set identity if it's one of the reply-message recipients (with prio for default identity)
else if (in_array(mb_strtolower($sql_arr['email']), $a_recipients) && (empty($from_id) || $sql_arr['standard']))
else if (in_array($sql_arr['email'], $a_recipients) && (empty($from_id) || $sql_arr['standard']))
$from_id = $sql_arr['identity_id'];
}
}
@ -925,7 +940,7 @@ function rcmail_compose_subject($attrib)
$out = $form_start ? "$form_start\n" : '';
$out .= $textfield->show($subject);
$out .= $form_end ? "\n$form_end" : '';
return $out;
}

@ -24,7 +24,9 @@ $SENT_MBOX = $RCMAIL->config->get('sent_mbox');
$DRAFTS_MBOX = $RCMAIL->config->get('drafts_mbox');
$SEARCH_MODS_DEFAULT = array('*' => array('subject'=>1, 'from'=>1), $SENT_MBOX => array('subject'=>1, 'to'=>1), $DRAFTS_MBOX => array('subject'=>1, 'to'=>1));
$EMAIL_ADDRESS_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9][a-z0-9\-\.]*\\.[a-z]{2,5})';
// Simplified for IDN in Unicode
//$EMAIL_ADDRESS_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9][a-z0-9\-\.]*\\.[a-z]{2,5})';
$EMAIL_ADDRESS_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^.].*\\.[a-z]{2,5})';
// actions that do not require imap connection here
$NOIMAP_ACTIONS = array('addcontact', 'autocomplete', 'upload', 'display-attachment', 'remove-attachment', 'get');
@ -1241,7 +1243,7 @@ function rcmail_alter_html_link($matches)
*/
function rcmail_address_string($input, $max=null, $linked=false, $addicon=null)
{
global $IMAP, $RCMAIL, $PRINT_MODE, $CONFIG, $OUTPUT, $EMAIL_ADDRESS_PATTERN;
global $IMAP, $RCMAIL, $PRINT_MODE, $CONFIG;
static $got_writable_abook = null;
$a_parts = $IMAP->decode_address_list($input);
@ -1259,27 +1261,40 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null)
foreach ($a_parts as $part) {
$j++;
$name = $part['name'];
$mailto = $part['mailto'];
$string = $part['string'];
// IDNA ASCII to Unicode
if ($name == $mailto)
$name = idn_to_utf8($name);
if ($string == $mailto)
$string = idn_to_utf8($string);
$mailto = idn_to_utf8($mailto);
if ($PRINT_MODE) {
$out .= sprintf('%s &lt;%s&gt;', Q($part['name']), $part['mailto']);
$out .= sprintf('%s &lt;%s&gt;', Q($name), $mailto);
}
else if (check_email($part['mailto'], false)) {
if ($linked) {
$out .= html::a(array(
'href' => 'mailto:'.$part['mailto'],
'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($part['mailto'])),
'title' => $part['mailto'],
'href' => 'mailto:'.$mailto,
'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)),
'title' => $mailto,
'class' => "rcmContactAddress",
),
Q($part['name']));
Q($name ? $name : $mailto));
}
else {
$out .= html::span(array('title' => $part['mailto'], 'class' => "rcmContactAddress"), Q($part['name']));
$out .= html::span(array('title' => $mailto, 'class' => "rcmContactAddress"),
Q($name ? $name : $mailto));
}
if ($addicon && $got_writable_abook) {
$out .= '&nbsp;' . html::a(array(
'href' => "#add",
'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, urlencode($part['string'])),
'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, urlencode($string)),
'title' => rcube_label('addtoaddressbook'),
),
html::img(array(
@ -1289,10 +1304,10 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null)
}
}
else {
if ($part['name'])
$out .= Q($part['name']);
if ($part['mailto'])
$out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($part['mailto']));
if ($name)
$out .= Q($name);
if ($mailto)
$out .= (strlen($out) ? ' ' : '') . sprintf('&lt;%s&gt;', Q($mailto));
}
if ($c>$j)
@ -1375,7 +1390,7 @@ function rcmail_draftinfo_decode($str)
function rcmail_message_part_controls()
{
{
global $MESSAGE;
$part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC));
@ -1397,12 +1412,12 @@ function rcmail_message_part_controls()
}
return $table->show($attrib);
}
}
function rcmail_message_part_frame($attrib)
{
{
global $MESSAGE;
$part = $MESSAGE->mime_parts[asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))];
@ -1411,21 +1426,21 @@ function rcmail_message_part_frame($attrib)
$attrib['src'] = './?' . str_replace('_frame=', ($ctype_primary=='text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']);
return html::iframe($attrib);
}
}
/**
* clear message composing settings
*/
function rcmail_compose_cleanup()
{
{
if (!isset($_SESSION['compose']))
return;
$rcmail = rcmail::get_instance();
$rcmail->plugins->exec_hook('attachments_cleanup', array());
$rcmail->session->remove('compose');
}
}
/**

@ -60,8 +60,7 @@ if (!$savedraft) {
function rcmail_encrypt_header($what)
{
global $CONFIG, $RCMAIL;
if (!$CONFIG['http_received_header_encrypt'])
{
if (!$CONFIG['http_received_header_encrypt']) {
return $what;
}
return $RCMAIL->encrypt($what);
@ -69,30 +68,21 @@ function rcmail_encrypt_header($what)
// get identity record
function rcmail_get_identity($id)
{
{
global $USER, $OUTPUT;
if ($sql_arr = $USER->get_identity($id))
{
if ($sql_arr = $USER->get_identity($id)) {
$out = $sql_arr;
$out['mailto'] = $sql_arr['email'];
// Special chars as defined by RFC 822 need to in quoted string (or escaped).
if (preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $sql_arr['name']))
$name = '"' . addcslashes($sql_arr['name'], '"') . '"';
else
$name = $sql_arr['name'];
$out['string'] = rcube_charset_convert($name, RCMAIL_CHARSET, $OUTPUT->get_charset());
if ($sql_arr['email'])
$out['string'] .= ' <' . $sql_arr['email'] . '>';
$out['string'] = format_email_recipient($sql_arr['email'],
rcube_charset_convert($sql_arr['name'], RCMAIL_CHARSET, $OUTPUT->get_charset()));
return $out;
}
return FALSE;
}
return FALSE;
}
/**
* go from this:
* <img src="http[s]://.../tiny_mce/plugins/emotions/images/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
@ -146,7 +136,7 @@ function rcmail_fix_emoticon_paths(&$mime_message)
}
// parse email address input (and count addresses)
function rcmail_email_input_format($mailto, $count=false)
function rcmail_email_input_format($mailto, $count=false, $check=true)
{
global $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT;
@ -163,9 +153,11 @@ function rcmail_email_input_format($mailto, $count=false)
$item = trim($item);
// address in brackets without name (do nothing)
if (preg_match('/^<\S+@\S+>$/', $item)) {
$item = idn_to_ascii($item);
$result[] = $item;
// address without brackets and without name (add brackets)
} else if (preg_match('/^\S+@\S+$/', $item)) {
$item = idn_to_ascii($item);
$result[] = '<'.$item.'>';
// address with name (handle name)
} else if (preg_match('/\S+@\S+>*$/', $item, $matches)) {
@ -176,6 +168,7 @@ function rcmail_email_input_format($mailto, $count=false)
&& preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name)) {
$name = '"'.addcslashes($name, '"').'"';
}
$address = idn_to_ascii($address);
if (!preg_match('/^<\S+@\S+>$/', $address))
$address = '<'.$address.'>';
@ -187,7 +180,7 @@ function rcmail_email_input_format($mailto, $count=false)
// check address format
$item = trim($item, '<>');
if ($item && !check_email($item)) {
if ($item && $check && !check_email($item)) {
$EMAIL_FORMAT_ERROR = $item;
return;
}
@ -297,7 +290,7 @@ if (!empty($mailcc))
if (!empty($mailbcc))
$headers['Bcc'] = $mailbcc;
if (!empty($identity_arr['bcc'])) {
$headers['Bcc'] = ($headers['Bcc'] ? $headers['Bcc'].', ' : '') . $identity_arr['bcc'];
$RECIPIENT_COUNT ++;
@ -319,11 +312,11 @@ if (!empty($identity_arr['organization']))
if (!empty($_POST['_replyto']))
$headers['Reply-To'] = rcmail_email_input_format(get_input_value('_replyto', RCUBE_INPUT_POST, TRUE, $message_charset));
else if (!empty($identity_arr['reply-to']))
$headers['Reply-To'] = $identity_arr['reply-to'];
$headers['Reply-To'] = rcmail_email_input_format($identity_arr['reply-to'], false, true);
if (!empty($_SESSION['compose']['reply_msgid']))
$headers['In-Reply-To'] = $_SESSION['compose']['reply_msgid'];
// remember reply/forward UIDs in special headers
if (!empty($_SESSION['compose']['reply_uid']) && $savedraft)
$headers['X-Draft-Info'] = array('type' => 'reply', 'uid' => $_SESSION['compose']['reply_uid']);

@ -94,6 +94,10 @@ function rcube_identity_form($attrib)
$form['adressing']['content']['email']['class'] = 'disabled';
}
$IDENTITY_RECORD['email'] = idn_to_utf8($IDENTITY_RECORD['email']);
$IDENTITY_RECORD['reply-to'] = idn_to_utf8($IDENTITY_RECORD['reply-to']);
$IDENTITY_RECORD['bcc'] = idn_to_utf8($IDENTITY_RECORD['bcc']);
// Allow plugins to modify identity form content
$plugin = $RCMAIL->plugins->exec_hook('identity_form', array(
'form' => $form, 'record' => $IDENTITY_RECORD));

@ -72,7 +72,7 @@ function rcmail_identities_list($attrib)
// get identities list and define 'mail' column
$list = $USER->list_identities();
foreach ($list as $idx => $row)
$list[$idx]['mail'] = trim($row['name'] . ' <' . $row['email'] .'>');
$list[$idx]['mail'] = trim($row['name'] . ' <' . idn_to_utf8($row['email']) .'>');
// get all identites from DB and define list of cols to be displayed
$plugin = $RCMAIL->plugins->exec_hook('identities_list', array(

@ -56,6 +56,18 @@ foreach ($a_boolean_cols as $col)
if (IDENTITIES_LEVEL == 1 || IDENTITIES_LEVEL == 3)
unset($save_data['email']);
// Validate e-mail addresses
foreach (array('email', 'reply-to', 'bcc') as $item) {
if ($email = $save_data[$item]) {
$ascii_email = idn_to_ascii($email);
if (!check_email($ascii_email, false)) {
// show error message
$OUTPUT->show_message('emailformaterror', 'error', array('email' => $email), false);
rcmail_overwrite_action('edit-identity');
return;
}
}
}
// update an existing contact
if ($_POST['_iid'])
@ -64,6 +76,13 @@ if ($_POST['_iid'])
$plugin = $RCMAIL->plugins->exec_hook('identity_update', array('id' => $iid, 'record' => $save_data));
$save_data = $plugin['record'];
if ($save_data['email'])
$save_data['email'] = idn_to_ascii($save_data['email']);
if ($save_data['bcc'])
$save_data['bcc'] = idn_to_ascii($save_data['bcc']);
if ($save_data['reply-to'])
$save_data['reply-to'] = idn_to_ascii($save_data['reply-to']);
if (!$plugin['abort'] && ($updated = $USER->update_identity($iid, $save_data)))
{
$OUTPUT->show_message('successfullysaved', 'confirmation');
@ -74,7 +93,7 @@ if ($_POST['_iid'])
if ($_POST['_framed'])
{
// update the changed col in list
// ...
// ...
}
}
else if ($plugin['abort'] || $DB->is_error())
@ -95,6 +114,10 @@ else if (IDENTITIES_LEVEL < 2)
$plugin = $RCMAIL->plugins->exec_hook('identity_create', array('record' => $save_data));
$save_data = $plugin['record'];
$save_data['email'] = idn_to_ascii($save_data['email']);
$save_data['bcc'] = idn_to_ascii($save_data['bcc']);
$save_data['reply-to'] = idn_to_ascii($save_data['reply-to']);
if (!$plugin['abort'] && $save_data['email'] && ($insert_id = $USER->insert_identity($save_data)))
{
$OUTPUT->show_message('successfullysaved', 'confirmation', null, false);

Loading…
Cancel
Save