thomascube
2010-09-21 cb2bc809ef29f349d38c89e202d821e67bb4c947
program/steps/mail/func.inc
@@ -26,8 +26,8 @@
$EMAIL_ADDRESS_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9][a-z0-9\-\.]*\\.[a-z]{2,5})';
// actions that do not require imap connection
$NOIMAP_ACTIONS = array('addcontact', 'autocomplete', 'upload', 'display-attachment', 'remove-attachment');
// actions that do not require imap connection here
$NOIMAP_ACTIONS = array('addcontact', 'autocomplete', 'upload', 'display-attachment', 'remove-attachment', 'get');
// always instantiate imap object (but not yet connect to server)
$RCMAIL->imap_init();
@@ -130,7 +130,7 @@
  if (!$OUTPUT->ajax_call)
    $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash',
      'movingmessage', 'copyingmessage', 'copy', 'move');
      'movingmessage', 'copyingmessage', 'copy', 'move', 'quota');
  $OUTPUT->set_pagetitle(rcmail_localize_foldername($mbox_name));
  }
@@ -171,7 +171,9 @@
      && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
    $a_show_cols[$f] = 'to';
  // make sure 'threads' column is present
  // make sure 'threads' and 'subject' columns are present
  if (!in_array('subject', $a_show_cols))
    array_unshift($a_show_cols, 'subject');
  if (!in_array('threads', $a_show_cols))
    array_unshift($a_show_cols, 'threads');
@@ -223,28 +225,41 @@
/**
 * return javascript commands to add rows to the message list
 * or to replace the whole list (IE only)
 */
function rcmail_js_message_list($a_headers, $insert_top=FALSE, $head_replace=FALSE)
function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null)
{
  global $CONFIG, $IMAP, $RCMAIL, $OUTPUT;
  if (!empty($_SESSION['list_attrib']['columns']))
    $a_show_cols = $_SESSION['list_attrib']['columns'];
  else
    $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
  if (empty($a_show_cols)) {
    if (!empty($_SESSION['list_attrib']['columns']))
      $a_show_cols = $_SESSION['list_attrib']['columns'];
    else
      $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
  }
  else {
    if (!is_array($a_show_cols))
      $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($a_show_cols));
    $head_replace = true;
  }
  $mbox = $IMAP->get_mailbox_name();
  $delim = $IMAP->get_hierarchy_delimiter();
  // make sure 'threads' and 'subject' columns are present
  if (!in_array('subject', $a_show_cols))
    array_unshift($a_show_cols, 'subject');
  if (!in_array('threads', $a_show_cols))
    array_unshift($a_show_cols, 'threads');
  $_SESSION['list_attrib']['columns'] = $a_show_cols;
  // show 'to' instead of 'from' in sent/draft messages
  if ((strpos($mbox.$delim, $CONFIG['sent_mbox'].$delim)===0 || strpos($mbox.$delim, $CONFIG['drafts_mbox'].$delim)===0)
      && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
    $a_show_cols[$f] = 'to';
  // make sure 'threads' column is present
  if (!in_array('threads', $a_show_cols))
    array_unshift($a_show_cols, 'threads');
  // Make sure there are no duplicated columns (#1486999)
  $a_show_cols = array_unique($a_show_cols);
  // Plugins may set header's list_cols/list_flags and other rcube_mail_header variables
  // and list columns
@@ -321,7 +336,7 @@
      $a_msg_flags['forwarded'] = 1;
    if ($header->flagged)
      $a_msg_flags['flagged'] = 1;
    if(preg_match("/multipart\/m/i", $header->ctype))
    if (preg_match("/(application\/|multipart\/m)/i", $header->ctype))
      $a_msg_flags['attachment'] = 1;
    $a_msg_flags['mbox'] = $mbox;
@@ -407,14 +422,15 @@
 */
function rcmail_messagecontent_frame($attrib)
  {
  global $OUTPUT;
  global $OUTPUT, $RCMAIL;
  if (empty($attrib['id']))
    $attrib['id'] = 'rcmailcontentwindow';
  $attrib['name'] = $attrib['id'];
  $OUTPUT->set_env('contentframe', $attrib['id']);
  if ($RCMAIL->config->get('preview_pane'))
    $OUTPUT->set_env('contentframe', $attrib['id']);
  $OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
  return html::iframe($attrib);
@@ -448,13 +464,10 @@
  $quota = rcmail_quota_content($attrib);
  if (is_array($quota)) {
    $OUTPUT->add_script('$(document).ready(function(){
  $OUTPUT->add_script('$(document).ready(function(){
   rcmail.set_quota('.json_serialize($quota).')});', 'foot');
    $quota = '';
    }
  return html::span($attrib, $quota);
  return html::span($attrib, '');
  }
@@ -462,39 +475,37 @@
  {
  global $COMM_PATH, $RCMAIL;
  $display = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : '';
  $quota = $RCMAIL->imap->get_quota();
  $quota = $RCMAIL->plugins->exec_hook('quota', $quota);
  if (!$quota['total'] && $RCMAIL->config->get('quota_zero_as_unlimited'))
    return rcube_label('unlimited');
  $quota_result = (array) $quota;
  $quota_result['type'] = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : '';
  if ($quota['total'])
    {
  if (!$quota['total'] && $RCMAIL->config->get('quota_zero_as_unlimited')) {
    $quota_result['title'] = rcube_label('unlimited');
    $quota_result['percent'] = 0;
    }
  else if ($quota['total']) {
    if (!isset($quota['percent']))
      $quota['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100));
      $quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100));
    $quota_result = sprintf('%s / %s (%.0f%%)',
    $title = sprintf('%s / %s (%.0f%%)',
        show_bytes($quota['used'] * 1024), show_bytes($quota['total'] * 1024),
        $quota['percent']);
        $quota_result['percent']);
    if ($display == 'image') {
      $quota_result = array(
       'percent'    => $quota['percent'],
        'title'      => $quota_result,
     );
    $quota_result['title'] = $title;
      if ($attrib['width'])
        $quota_result['width'] = $attrib['width'];
      if ($attrib['height'])
        $quota_result['height']   = $attrib['height'];
      }
      return $quota_result;
    if ($attrib['width'])
      $quota_result['width'] = $attrib['width'];
    if ($attrib['height'])
      $quota_result['height']   = $attrib['height'];
    }
  else {
    $quota_result['title'] = rcube_label('unknown');
    $quota_result['percent'] = 0;
    }
  return rcube_label('unknown');
  return $quota_result;
  }
@@ -551,12 +562,16 @@
}
function rcmail_send_unread_count($mbox_name, $force=false)
function rcmail_send_unread_count($mbox_name, $force=false, $count=null)
{
  global $RCMAIL;
  $old_unseen = $_SESSION['unseen_count'][$mbox_name];
  $unseen = $RCMAIL->imap->messagecount($mbox_name, 'UNSEEN', $force);
  if ($count === null)
    $unseen = $RCMAIL->imap->messagecount($mbox_name, 'UNSEEN', $force);
  else
    $unseen = $count;
  if ($unseen != $old_unseen || ($mbox_name == 'INBOX'))
    $RCMAIL->output->command('set_unread_count', $mbox_name, $unseen, ($mbox_name == 'INBOX'));
@@ -625,6 +640,21 @@
    '<html>',
  );
  $html = preg_replace($html_search, $html_replace, $html);
  // PCRE errors handling (#1486856), should we use something like for every preg_* use?
  if ($html === null && ($preg_error = preg_last_error()) != PREG_NO_ERROR) {
    $errstr = "Could not clean up HTML message! PCRE Error: $preg_error.";
    if ($preg_error == PREG_BACKTRACK_LIMIT_ERROR)
      $errstr .= " Consider raising pcre.backtrack_limit!";
    if ($preg_error == PREG_RECURSION_LIMIT_ERROR)
      $errstr .= " Consider raising pcre.recursion_limit!";
    raise_error(array('code' => 600, 'type' => 'php',
        'line' => __LINE__, 'file' => __FILE__,
        'message' => $errstr), true, false);
    return '';
  }
  // fix (unknown/malformed) HTML tags before "wash"
  $html = preg_replace_callback('/(<[\/]*)([^\s>]+)/', 'rcmail_html_tag_callback', $html);
@@ -720,7 +750,7 @@
  // plaintext postprocessing
  if ($part->ctype_secondary == 'plain')
    $body = rcmail_plain_body($body);
    $body = rcmail_plain_body($body, $part->ctype_parameters['format'] == 'flowed');
  // allow post-processing of the message body
  $data = $RCMAIL->plugins->exec_hook('message_part_after', array('type' => $part->ctype_secondary, 'body' => $body) + $data);
@@ -732,75 +762,106 @@
/**
 * Handle links and citation marks in plain text message
 *
 * @param string  Plain text string
 * @param string  Plain text string
 * @param boolean Text uses format=flowed
 *
 * @return string Formatted HTML string
 */
function rcmail_plain_body($body)
function rcmail_plain_body($body, $flowed=false)
{
  global $RCMAIL;
  // make links and email-addresses clickable
  $replacements = new rcube_string_replacer;
  $replacer = new rcube_string_replacer;
  // search for patterns like links and e-mail addresses
  $body = preg_replace_callback($replacements->link_pattern, array($replacements, 'link_callback'), $body);
  $body = preg_replace_callback($replacements->mailto_pattern, array($replacements, 'mailto_callback'), $body);
  $body = preg_replace_callback($replacer->link_pattern, array($replacer, 'link_callback'), $body);
  $body = preg_replace_callback($replacer->mailto_pattern, array($replacer, 'mailto_callback'), $body);
  // split body into single lines
  $a_lines = preg_split('/\r?\n/', $body);
  $q_lines = array();
  $quote_level = 0;
  $last = -1;
  // find/mark quoted lines...
  for ($n=0, $cnt=count($a_lines); $n < $cnt; $n++) {
    $q = 0;
    if ($a_lines[$n][0] == '>' && preg_match('/^(>+\s*)+/', $a_lines[$n], $regs)) {
      $q = strlen(preg_replace('/\s/', '', $regs[0]));
        $a_lines[$n] = substr($a_lines[$n], strlen($regs[0]));
      $a_lines[$n] = substr($a_lines[$n], strlen($regs[0]));
      if ($q > $quote_level)
        $q_lines[$n]['quote'] = $q - $quote_level;
        $a_lines[$n] = $replacer->get_replacement($replacer->add(
          str_repeat('<blockquote>', $q - $quote_level))) . $a_lines[$n];
      else if ($q < $quote_level)
        $q_lines[$n]['endquote'] = $quote_level - $q;
        $a_lines[$n] = $replacer->get_replacement($replacer->add(
          str_repeat('</blockquote>', $quote_level - $q))) . $a_lines[$n];
      else if ($flowed) {
        // previous line is flowed
        if (isset($a_lines[$last]) && $a_lines[$n]
          && $a_lines[$last][strlen($a_lines[$last])-1] == ' ') {
          // merge lines
          $a_lines[$last] .= $a_lines[$n];
          unset($a_lines[$n]);
        }
        else
          $last = $n;
      }
    }
    else if ($quote_level > 0)
      $q_lines[$n]['endquote'] = $quote_level;
    else {
      $q = 0;
      if ($flowed) {
        // sig separator - line is fixed
        if ($a_lines[$n] == '-- ') {
          $last = $n;
        }
        else {
          // remove space-stuffing
          if ($a_lines[$n][0] == ' ')
            $a_lines[$n] = substr($a_lines[$n], 1);
          // previous line is flowed?
          if (isset($a_lines[$last]) && $a_lines[$n]
            && $a_lines[$last] != '-- '
            && $a_lines[$last][strlen($a_lines[$last])-1] == ' '
          ) {
            $a_lines[$last] .= $a_lines[$n];
            unset($a_lines[$n]);
          }
          else {
            $last = $n;
          }
        }
        if ($quote_level > 0)
          $a_lines[$last] = $replacer->get_replacement($replacer->add(
            str_repeat('</blockquote>', $quote_level))) . $a_lines[$last];
      }
      else if ($quote_level > 0)
        $a_lines[$n] = $replacer->get_replacement($replacer->add(
          str_repeat('</blockquote>', $quote_level))) . $a_lines[$n];
    }
    $quote_level = $q;
  }
  // quote plain text
  $body = Q(join("\n", $a_lines), 'replace', false);
  $body = Q(join("\n", $a_lines), '', false);
  // colorize signature
  if (($sp = strrpos($body, '-- ')) !== false)
    if (($sp == 0 || $body[$sp-1] == "\n") && $body[$sp+3] == "\n") {
      $body = substr($body, 0, max(0, $sp))
   .'<span class="sig">'.substr($body, $sp).'</span>';
  $len = strlen($body);
  while (($sp = strrpos($body, "-- \n", $sp ? -$len+$sp-1 : 0)) !== false) {
    if ($sp == 0 || $body[$sp-1] == "\n") {
      // do not touch blocks with more that X lines
      if (substr_count($body, "\n", $sp) < $RCMAIL->config->get('sig_max_lines', 15))
        $body = substr($body, 0, max(0, $sp))
          .'<span class="sig">'.substr($body, $sp).'</span>';
      break;
    }
  }
  // colorize quoted lines
  $a_lines = preg_split('/\n/', $body);
  foreach ($q_lines as $i => $q)
    if ($q['quote'])
      $a_lines[$i] = str_repeat('<blockquote>', $q['quote']) . $a_lines[$i];
    else if ($q['endquote'])
      $a_lines[$i] = str_repeat('</blockquote>', $q['endquote']) . $a_lines[$i];
  // insert url/mailto links and citation tags
  $body = $replacer->resolve($body);
  // insert the links for urls and mailtos
  $body = $replacements->resolve(join("\n", $a_lines));
  return $body;
}
/**
 * add a string to the replacement array and return a replacement string
 */
function rcmail_str_replacement($str, &$rep)
{
  static $count = 0;
  $rep[$count] = stripslashes($str);
  return "##string_replacement{".($count++)."}##";
}
@@ -929,7 +990,7 @@
 */
function rcmail_message_body($attrib)
  {
  global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $REMOTE_OBJECTS;
  global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $RCMAIL, $REMOTE_OBJECTS;
  if (!is_array($MESSAGE->parts) && empty($MESSAGE->body))
    return '';
@@ -960,22 +1021,42 @@
        if (!isset($part->body))
          $part->body = $MESSAGE->get_part_content($part->mime_id);
        // re-format format=flowed content
        if ($part->ctype_secondary == "plain" && $part->ctype_parameters['format'] == "flowed")
          $part->body = rcube_message::unfold_flowed($part->body);
        // message is cached but not exists (#1485443), or other error
        if ($part->body === false) {
          rcmail_message_error($MESSAGE->uid);
        }
        $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
          'part' => $part, 'prefix' => ''));
        $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$CONFIG['prefer_html']));
        if ($part->ctype_secondary == 'html')
          $out .= html::div('message-htmlpart', rcmail_html4inline($body, $attrib['id'], 'div.rcmBody'));
        if ($part->ctype_secondary == 'html') {
          $body = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs);
          $div_attr = array('class' => 'message-htmlpart');
          $style = array();
          if (!empty($attrs)) {
            foreach ($attrs as $a_idx => $a_val)
              $style[] = $a_idx . ': ' . $a_val;
            if (!empty($style))
              $div_attr['style'] = implode('; ', $style);
          }
          $out .= html::div($div_attr, $plugin['prefix'] . $body);
        }
        else
          $out .= html::div('message-part', $body);
          $out .= html::div('message-part', $plugin['prefix'] . $body);
        }
      }
    }
  else
    $out .= html::div('message-part', html::tag('pre', array(),
  else {
    $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
      'part' => $MESSAGE, 'prefix' => ''));
    $out .= html::div('message-part', $plugin['prefix'] . html::tag('pre', array(),
      rcmail_plain_body(Q($MESSAGE->body, 'strict', false))));
    }
  $ctype_primary = strtolower($MESSAGE->structure->ctype_primary);
  $ctype_secondary = strtolower($MESSAGE->structure->ctype_secondary);
@@ -983,11 +1064,16 @@
  // list images after mail body
  if ($CONFIG['inline_images']
      && $ctype_primary == 'multipart'
      && !empty($MESSAGE->attachments)
      && !strstr($message_body, '<html'))
      && !empty($MESSAGE->attachments))
    {
    foreach ($MESSAGE->attachments as $attach_prop) {
      if (strpos($attach_prop->mimetype, 'image/') === 0) {
      // Content-Type: image/*...
      if (preg_match('/^image\//i', $attach_prop->mimetype) ||
        // ...or known file extension: many clients are using application/octet-stream
        ($attach_prop->filename &&
          preg_match('/^application\/octet-stream$/i', $attach_prop->mimetype) &&
          preg_match('/\.(jpg|jpeg|png|gif|bmp)$/i', $attach_prop->filename))
      ) {
        $out .= html::tag('hr') . html::p(array('align' => "center"),
          html::img(array(
            'src' => $MESSAGE->get_part_url($attach_prop->mime_id),
@@ -1026,30 +1112,32 @@
/**
 * modify a HTML message that it can be displayed inside a HTML page
 */
function rcmail_html4inline($body, $container_id, $body_id='')
  {
function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null)
{
  $last_style_pos = 0;
  $body_lc = strtolower($body);
  $cont_id = $container_id.($body_id ? ' div.'.$body_id : '');
  // find STYLE tags
  while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
    {
  {
    $pos = strpos($body_lc, '>', $pos)+1;
    // replace all css definitions with #container [def]
    $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id.($body_id ? ' '.$body_id : ''));
    $styles = rcmail_mod_css_styles(
      substr($body, $pos, $pos2-$pos), $cont_id);
    $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
    $body_lc = strtolower($body);
    $last_style_pos = $pos2;
    }
  }
  // modify HTML links to open a new window if clicked
  $GLOBALS['rcmail_html_container_id'] = $container_id;
  $body = preg_replace_callback('/<(a|link)\s+([^>]+)>/Ui', 'rcmail_alter_html_link', $body);
  unset($GLOBALS['rcmail_html_container_id']);
  $out = preg_replace(array(
  $body = preg_replace(array(
      // add comments arround html and other tags
      '/(<!DOCTYPE[^>]*>)/i',
      '/(<\?xml[^>]*>)/i',
@@ -1073,18 +1161,48 @@
      '<!--\\1-->',
      '&lt;?',
      '?&gt;',
      '<div class="rcmBody"\\1>',
      '<div class="'.$body_id.'"\\1>',
      '</div>',
      ),
    $body);
  $attributes = array();
  // Handle body attributes that doesn't play nicely with div elements
  if (preg_match('/<div class="' . preg_quote($body_id, '/') . '" ([^>]+)/', $body, $m)) {
    $attrs = $m[0];
    // Get bgcolor, we'll set it as background-color of the message container
    if (preg_match('/bgcolor=["\']*([a-z0-9#]+)["\']*/', $attrs, $mb)) {
      $attributes['background-color'] = $mb[1];
      $attrs = preg_replace('/bgcolor=["\']*([a-z0-9#]+)["\']*/', '', $attrs);
    }
    // Get background, we'll set it as background-image of the message container
    if (preg_match('/background=["\']*([^"\'>\s]+)["\']*/', $attrs, $mb)) {
      $attributes['background-image'] = 'url('.$mb[1].')';
      $attrs = preg_replace('/background=["\']*([^"\'>\s]+)["\']*/', '', $attrs);
    }
    if (!empty($attributes))
      $body = preg_replace('/<div class="' . preg_quote($body_id, '/') . '" [^>]+/', rtrim($attrs), $body, 1);
    // handle body styles related to background image
    if ($attributes['background-image']) {
      // get body style
      if (preg_match('/#'.preg_quote($cont_id, '/').'\s+\{([^}]+)}/i', $body, $m)) {
        // get background related style
        if (preg_match_all('/(background-position|background-repeat)\s*:\s*([^;]+);/i', $m[1], $ma, PREG_SET_ORDER)) {
          foreach ($ma as $style)
            $attributes[$style[1]] = $style[2];
        }
      }
    }
  }
  // make sure there's 'rcmBody' div, we need it for proper css modification
  // its name is hardcoded in rcmail_message_body() also
  if (!preg_match('/<div class="rcmBody"/', $out))
    $out = '<div class="rcmBody">' . $out . '</div>';
  else
    $body = '<div class="' . $body_id . '">' . $body . '</div>';
  return $out;
  }
  return $body;
}
/**
@@ -1305,7 +1423,7 @@
    return;
  $rcmail = rcmail::get_instance();
  $rcmail->plugins->exec_hook('cleanup_attachments',array());
  $rcmail->plugins->exec_hook('attachments_cleanup', array());
  $rcmail->session->remove('compose');
  }
@@ -1369,10 +1487,6 @@
    $smtp_response = $RCMAIL->smtp->get_response();
    $smtp_error = $RCMAIL->smtp->get_error();
    if (is_resource($msg_body)) {
      fclose($msg_body);
    }
    // log error
    if (!$sent)
      raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__,
@@ -1428,6 +1542,14 @@
    // remove MDN headers after sending
    unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
    // get all recipients
    if ($headers['Cc'])
      $mailto .= $headers['Cc'];
    if ($headers['Bcc'])
      $mailto .= $headers['Bcc'];
    if (preg_match_all('/<([^@]+@[^>]+)>/', $mailto, $m))
      $mailto = implode(', ', array_unique($m[1]));
    if ($CONFIG['smtp_log']) {
      write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s",
        $RCMAIL->user->get_username(),
@@ -1435,6 +1557,10 @@
        $mailto,
        !empty($smtp_response) ? join('; ', $smtp_response) : ''));
    }
  }
  if (is_resource($msg_body)) {
    fclose($msg_body);
  }
  $message->_headers = array();
@@ -1469,11 +1595,11 @@
    // compose headers array
    $headers = array(
      'Date' => date('r'),
      'Date' => rcmail_user_date(),
      'From' => $sender,
      'To'   => $message->headers->mdn_to,
      'Subject' => rcube_label('receiptread') . ': ' . $message->subject,
      'Message-ID' => sprintf('<%s@%s>', md5(uniqid('rcmail'.mt_rand(),true)), $RCMAIL->config->mail_domain($_SESSION['imap_host'])),
      'Message-ID' => rcmail_gen_message_id(),
      'X-Sender' => $identity['email'],
      'References' => trim($message->headers->references . ' ' . $message->headers->messageID),
    );
@@ -1514,6 +1640,53 @@
  return false;
}
// Returns unique Message-ID
function rcmail_gen_message_id()
{
  global $RCMAIL;
  $local_part  = md5(uniqid('rcmail'.mt_rand(),true));
  $domain_part = $RCMAIL->user->get_username('domain');
  // Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924)
  if (!preg_match('/\.[a-z]+$/i', $domain_part)) {
    if (($host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST']))
      && preg_match('/\.[a-z]+$/i', $host)) {
        $domain_part = $host;
    }
    else if (($host = preg_replace('/:[0-9]+$/', '', $_SERVER['SERVER_NAME']))
      && preg_match('/\.[a-z]+$/i', $host)) {
        $domain_part = $host;
    }
  }
  return sprintf('<%s@%s>', $local_part, $domain_part);
}
// Returns RFC2822 formatted current date in user's timezone
function rcmail_user_date()
{
  global $CONFIG;
  // get user's timezone
  if ($CONFIG['timezone'] === 'auto') {
    $tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z')/3600;
  }
  else {
    $tz = $CONFIG['timezone'];
    if ($CONFIG['dst_active'])
      $tz++;
  }
  $date = time() + $tz * 60 * 60;
  $date = gmdate('r', $date);
  $tz   = sprintf('%+05d', intval($tz) * 100 + ($tz - intval($tz)) * 60);
  $date = preg_replace('/[+-][0-9]{4}$/', $tz, $date);
  return $date;
}
function rcmail_search_filter($attrib)
{
  global $OUTPUT, $CONFIG;
@@ -1546,6 +1719,21 @@
  return $out;
}
function rcmail_message_error($uid=null)
{
  global $RCMAIL;
  // Set env variables for messageerror.html template
  if ($RCMAIL->action == 'show') {
    $mbox_name = $RCMAIL->imap->get_mailbox_name();
    $RCMAIL->output->set_env('mailbox', $mbox_name);
    $RCMAIL->output->set_env('uid', null);
  }
  // display error message
  $RCMAIL->output->show_message('messageopenerror', 'error');
  // ... display message error page
  $RCMAIL->output->send('messageerror');
}
// register UI objects
$OUTPUT->add_handlers(array(
@@ -1563,4 +1751,4 @@
  'searchform' => array($OUTPUT, 'search_form'),
));
?>