thomascube
2009-03-02 f54a3a6d41e5700c45120091a57f2c73b804ae25
program/steps/mail/func.inc
@@ -5,7 +5,7 @@
 | program/steps/mail/func.inc                                           |
 |                                                                       |
 | This file is part of the RoundCube Webmail client                     |
 | Copyright (C) 2005-2008, RoundCube Dev. - Switzerland                 |
 | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                 |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 | PURPOSE:                                                              |
@@ -19,25 +19,34 @@
*/
require_once('lib/enriched.inc');
require_once('include/rcube_smtp.inc');
$EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i';
if (empty($_SESSION['mbox']))
  $_SESSION['mbox'] = $IMAP->get_mailbox_name();
// actions that do not require imap connection
$NOIMAP_ACTIONS = array('spell', 'addcontact', 'autocomplete', 'upload', 'display-attachment', 'remove-attachment');
// log in to imap server
if (!in_array($RCMAIL->action, $NOIMAP_ACTIONS) && !$RCMAIL->imap_connect()) {
  $RCMAIL->kill_session();
  if ($OUTPUT->ajax_call)
    $OUTPUT->redirect(array(), 2000);
  $OUTPUT->set_env('task', 'login');
  $OUTPUT->send('login');
}
// set imap properties and session vars
if ($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC))
  $IMAP->set_mailbox(($_SESSION['mbox'] = $mbox));
else
  $_SESSION['mbox'] = $IMAP->get_mailbox_name();
if (!empty($_GET['_page']))
  $IMAP->set_page(($_SESSION['page'] = intval($_GET['_page'])));
// set mailbox to INBOX if not set
if (empty($_SESSION['mbox']))
  $_SESSION['mbox'] = $IMAP->get_mailbox_name();
// set default sort col/order to session
if (!isset($_SESSION['sort_col']))
@@ -56,8 +65,26 @@
// set main env variables, labels and page title
if (empty($RCMAIL->action) || $RCMAIL->action == 'list')
  {
  $mbox_name = $IMAP->get_mailbox_name();
  if (empty($RCMAIL->action))
    {
    // initialize searching result if search_filter is used
    if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL')
      {
      $search_request = md5($mbox_name.$_SESSION['search_filter']);
      $IMAP->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, $_SESSION['sort_col']);
      $_SESSION['search'][$search_request] = $IMAP->get_search_set();
      $OUTPUT->set_env('search_request', $search_request);
      }
      // make sure the message count is refreshed (for default view)
      $IMAP->messagecount($mbox_name, 'ALL', true);
    }
  // set current mailbox in client environment
  $OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
  $OUTPUT->set_env('mailbox', $mbox_name);
  $OUTPUT->set_env('quota', $IMAP->get_capability('quota'));
  $OUTPUT->set_env('delimiter', $IMAP->get_hierarchy_delimiter());
@@ -71,7 +98,7 @@
  if (!$OUTPUT->ajax_call)
    $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash', 'movingmessage');
  $OUTPUT->set_pagetitle(rcmail_localize_foldername($IMAP->get_mailbox_name()));
  $OUTPUT->set_pagetitle(rcmail_localize_foldername($mbox_name));
  }
@@ -104,8 +131,16 @@
  $out = '<table' . $attrib_str . ">\n";
  // define list of cols to be displayed
  $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
  // define list of cols to be displayed based on parameter or config
  if (empty($attrib['columns']))
      $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
  else
      $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns']));
  // store column list in a session-variable
  $_SESSION['list_columns'] = $a_show_cols;
  // define sortable columns
  $a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
  $mbox = $IMAP->get_mailbox_name();
@@ -120,9 +155,8 @@
  $out .= '<col class="icon" />';
  foreach ($a_show_cols as $col)
    $out .= sprintf('<col class="%s" />', $col);
    $out .= ($col!='attachment') ? sprintf('<col class="%s" />', $col) : '<col class="icon" />';
  $out .= '<col class="icon" />';
  $out .= "</colgroup>\n";
  // add table title
@@ -132,7 +166,17 @@
  foreach ($a_show_cols as $col)
    {
    // get column name
    $col_name = $col != 'flag' ? Q(rcube_label($col)) : sprintf($image_tag, $skin_path, $attrib['unflaggedicon'], '');
    switch ($col)
      {
      case 'flag':
        $col_name = sprintf($image_tag, $skin_path, $attrib['unflaggedicon'], '');
        break;
      case 'attachment':
        $col_name = sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '');
        break;
      default:
        $col_name = Q(rcube_label($col));
    }
    // make sort links
    $sort = '';
@@ -180,10 +224,12 @@
    $sort_class = $col==$sort_col ? " sorted$sort_order" : '';
    // put it all together
    $out .= '<td class="'.$col.$sort_class.'" id="rcmHead'.$col.'">' . "$col_name$sort</td>\n";
    if ($col!='attachment')
      $out .= '<td class="'.$col.$sort_class.'" id="rcm'.$col.'">' . "$col_name$sort</td>\n";
    else
      $out .= '<td class="icon" id="rcm'.$col.'">' . "$col_name$sort</td>\n";
    }
  $out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '&nbsp;')."</td>\n";
  $out .= "</tr></thead>\n<tbody>\n";
  // no messages in this mailbox
@@ -235,7 +281,7 @@
      $flagged_icon = $attrib['unflaggedicon'];
    
    // set attachment icon
    if ($attrib['attachmenticon'] && preg_match("/multipart\/[mr]/i", $header->ctype))
    if ($attrib['attachmenticon'] && preg_match("/multipart\/m/i", $header->ctype))
      $attach_icon = $attrib['attachmenticon'];
        
    $out .= sprintf('<tr id="rcmrow%d" class="message%s%s%s%s">'."\n",
@@ -248,8 +294,7 @@
    $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
    if (!empty($header->charset))
      $IMAP->set_charset($header->charset);
    $IMAP->set_charset(!empty($header->charset) ? $header->charset : $CONFIG['default_charset']);
  
    // format each col
    foreach ($a_show_cols as $col)
@@ -273,10 +318,12 @@
      else
        $cont = Q($header->$col);
        
      $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
      if ($col!='attachment')
        $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
      else
        $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '&nbsp;');
      }
    $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
    $out .= "</tr>\n";
    
    if (sizeof($js_row_arr))
@@ -332,7 +379,11 @@
  {
  global $CONFIG, $IMAP, $OUTPUT;
  $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
  if (empty($_SESSION['list_columns']))
    $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
  else
    $a_show_cols = $_SESSION['list_columns'];
  $mbox = $IMAP->get_mailbox_name();
  // show 'to' instead of from in sent messages
@@ -351,8 +402,13 @@
    if (empty($header))
      continue;
    if (!empty($header->charset))
      $IMAP->set_charset($header->charset);
    $IMAP->set_charset(!empty($header->charset) ? $header->charset : $CONFIG['default_charset']);
    // remove 'attachment' and 'flag' columns, we don't need them here
    if(($key = array_search('attachment', $a_show_cols)) !== FALSE)
      unset($a_show_cols[$key]);
    if(($key = array_search('flag', $a_show_cols)) !== FALSE)
      unset($a_show_cols[$key]);
    // format each col; similar as in rcmail_message_list()
    foreach ($a_show_cols as $col)
@@ -554,22 +610,129 @@
    return rcmail_localize_foldername($RCMAIL->imap->get_mailbox_name());
}
/**
 * Sets message is_safe flag according to 'show_images' option value
 *
 * @param object rcube_message Message
 */
function rcmail_check_safe(&$message)
{
  global $RCMAIL;
  $show_images = $RCMAIL->config->get('show_images');
  if (!$message->is_safe
    && !empty($show_images)
    && $message->has_html_part())
  {
    switch($show_images) {
      case '1': // known senders only
        $CONTACTS = new rcube_contacts($RCMAIL->db, $_SESSION['user_id']);
        if ($CONTACTS->search('email', $message->sender['mailto'], true, false)->count) {
          $message->set_safe(true);
        }
      break;
      case '2': // always
        $message->set_safe(true);
      break;
    }
  }
}
/**
 * Cleans up the given message HTML Body (for displaying)
 *
 * @param string HTML
 * @param array  Display parameters
 * @param array  CID map replaces (inline images)
 * @return string Clean HTML
 */
function rcmail_wash_html($html, $p = array(), $cid_replaces)
{
  global $REMOTE_OBJECTS;
  $p += array('safe' => false, 'inline_html' => true);
  // special replacements (not properly handled by washtml class)
  $html_search = array(
    '/(<\/nobr>)(\s+)(<nobr>)/i',   // space(s) between <NOBR>
    '/(<[\/]*st1:[^>]+>)/i',      // Microsoft's Smart Tags <ST1>
    '/<\/?rte_text>/i',         // Rich Text Editor tags (#1485647)
    '/<title>.*<\/title>/i',      // PHP bug #32547 workaround: remove title tag
    '/<html[^>]*>/im',         // malformed html: remove html tags (#1485139)
    '/<\/html>/i',         // malformed html: remove html tags (#1485139)
    '/^[\xFE\xFF\xBB\xBF\x00]+((?:<\!doctype|\<html))/im',   // remove byte-order mark (only outlook?)
  );
  $html_replace = array(
    '\\1'.' &nbsp; '.'\\3',
    '',
    '',
    '',
    '',
    '',
    '\\1',
  );
  $html = preg_replace($html_search, $html_replace, $html);
  // charset was converted to UTF-8 in rcube_imap::get_message_part() -> change charset specification in HTML accordingly
  $charset_pattern = '/(\s+content=[\'"]?\w+\/\w+;\s*charset)=([a-z0-9-_]+)/i';
  if (preg_match($charset_pattern, $html)) {
    $html = preg_replace($charset_pattern, '\\1='.RCMAIL_CHARSET, $html);
  }
  else {
    // add head for malformed messages, washtml cannot work without that
    if (!preg_match('/<head[^>]*>(.*)<\/head>/Uims', $html))
      $html = '<head></head>'. $html;
    $html = substr_replace($html, '<meta http-equiv="content-type" content="text/html; charset='.RCMAIL_CHARSET.'" />', intval(stripos($html, '<head>')+6), 0);
  }
  // turn relative into absolute urls
  $html = rcmail_resolve_base($html);
  // clean HTML with washhtml by Frederic Motte
  $wash_opts = array(
    'show_washed' => false,
    'allow_remote' => $p['safe'],
    'blocked_src' => "./program/blocked.gif",
    'charset' => RCMAIL_CHARSET,
    'cid_map' => $cid_replaces,
    'html_elements' => array('body'),
  );
  if (!$p['inline_html']) {
    $wash_opts['html_elements'] = array('html','head','title','body');
  }
  if ($p['safe']) {
    $wash_opts['html_elements'][] = 'link';
    $wash_opts['html_attribs'] = array('rel','type');
  }
  $washer = new washtml($wash_opts);
  $washer->add_callback('a', 'rcmail_washtml_callback');
  $washer->add_callback('form', 'rcmail_washtml_callback');
  if ($p['safe']) {  // allow CSS styles, will be sanitized by rcmail_washtml_callback()
    $washer->add_callback('style', 'rcmail_washtml_callback');
  }
  $html = $washer->wash($html);
  $REMOTE_OBJECTS = $washer->extlinks;
  return $html;
}
/**
 * Convert the given message part to proper HTML
 * which can be displayed the message view
 *
 * @param object rcube_message_part Message part
 * @param bool  True if external objects (ie. images ) are allowed
 * @param bool  True if part should be converted to plaintext
 * @param array  Display parameters array
 * @return string Formatted HTML string
 */
function rcmail_print_body($part, $p = array())
{
  global $REMOTE_OBJECTS;
  $p += array('safe' => false, 'plain' => false, 'inline_html' => true);
  // convert html to text/plain
  if ($part->ctype_secondary == 'html' && $p['plain']) {
    $txt = new html2text($part->body, false, true);
@@ -578,64 +741,12 @@
  }
  // text/html
  else if ($part->ctype_secondary == 'html') {
    $html = $part->body;
    // special replacements (not properly handled by washtml class)
    $html_search = array(
   '/(<\/nobr>)(\s+)(<nobr>)/i',   // space(s) between <NOBR>
   '/(<[\/]*st1:[^>]+>)/i',   // Microsoft's Smart Tags <ST1>
   '/<title>.*<\/title>/i',   // PHP bug #32547 workaround: remove title tag
   '/<html[^>]*>/im',      // malformed html: remove html tags (#1485139)
   '/<\/html>/i',         // malformed html: remove html tags (#1485139)
    );
    $html_replace = array(
   '\\1'.' &nbsp; '.'\\3',
   '',
   '',
   '',
   '',
    );
    $html = preg_replace($html_search, $html_replace, $html);
    // charset was converted to UTF-8 in rcube_imap::get_message_part() -> change charset specification in HTML accordingly
    if (preg_match('/(\s+content=[\'"]\w+\/\w+;\s*charset)=([a-z0-9-_]+)/i', $html))
      $html = preg_replace('/(\s+content=[\'"]\w+\/\w+;\s*charset)=([a-z0-9-_]+)/i', '\\1='.RCMAIL_CHARSET, $html);
    else {
      // add head for malformed messages, washtml cannot work without that
      if (!preg_match('/<head[^>]*>(.*)<\/head>/Uims', $html))
        $html = '<head></head>'. $html;
      $html = substr_replace($html, '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />', intval(stripos($html, '</head>')), 0);
    }
    // clean HTML with washhtml by Frederic Motte
    $wash_opts = array(
      'show_washed' => false,
      'allow_remote' => $p['safe'],
      'blocked_src' => "./program/blocked.gif",
      'charset' => RCMAIL_CHARSET,
      'cid_map' => $part->replaces,
      'html_elements' => array('body'),
    );
    if (!$p['inline_html']) {
      $wash_opts['html_elements'] = array('html','head','title','body');
    }
    $washer = new washtml($wash_opts);
    $washer->add_callback('form', 'rcmail_washtml_callback');
    if ($p['safe']) {  // allow CSS styles, will be sanitized by rcmail_washtml_callback()
      $washer->add_callback('style', 'rcmail_washtml_callback');
    }
    $body = $washer->wash($html);
    $REMOTE_OBJECTS = $washer->extlinks;
    return $body;
    return rcmail_wash_html($part->body, $p, $part->replaces);
  }
  // text/enriched
  else if ($part->ctype_secondary=='enriched') {
    $part->ctype_secondary = 'html';
    require_once('lib/enriched.inc');
    return Q(enriched_to_html($part->body), 'show');
  }
  else
@@ -645,22 +756,15 @@
  /**** assert plaintext ****/
  // make links and email-addresses clickable
  $convert_patterns = $convert_replaces = $replace_strings = array();
  $replacements = new rcube_string_replacer;
  
  $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
  $url_chars_within = '\?\.~,!';
  $convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
  $convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)";
  $convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
  $convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)";
  $convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie';
  $convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return ".JS_OBJECT_NAME.".command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)";
  
  // search for patterns like links and e-mail addresses
  $body = preg_replace($convert_patterns, $convert_replaces, $body);
  $body = preg_replace_callback("/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body);
  $body = preg_replace_callback("/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body);
  $body = preg_replace_callback('/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i', array($replacements, 'mailto_callback'), $body);
  // split body into single lines
  $a_lines = preg_split('/\r?\n/', $body);
@@ -689,10 +793,11 @@
  }
  // insert the links for urls and mailtos
  $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines));
  return "<div class=\"pre\">".$body."\n</div>";
  $body = $replacements->resolve(join("\n", $a_lines));
  return html::tag('pre', array(), $body);
}
/**
 * add a string to the replacement array and return a replacement string
@@ -713,6 +818,11 @@
  switch ($tagname) {
    case 'form':
      $out = html::div('form', $content);
      break;
    case 'a':
      if ($attrib) $attrib .= ' target="_blank"';
      $out = '<a'.$attrib.'>' . $content . '</a>';
      break;
      
    case 'style':
@@ -762,7 +872,7 @@
  $out = '<table' . $attrib_str . ">\n";
  // show these headers
  $standard_headers = array('subject', 'from', 'organization', 'to', 'cc', 'bcc', 'replyto', 'date');
  $standard_headers = array('subject', 'from', 'to', 'cc', 'bcc', 'replyto', 'date');
  foreach ($standard_headers as $hkey)
    {
@@ -859,7 +969,7 @@
      }
    }
  else
    $out .= html::div('message-part', html::div('pre', Q($MESSAGE->body)));
    $out .= html::div('message-part', html::tag('pre', array(), Q($MESSAGE->body)));
  $ctype_primary = strtolower($MESSAGE->structure->ctype_primary);
@@ -891,19 +1001,30 @@
  }
/**
 * Convert all relative URLs according to a <base> in HTML
 */
function rcmail_resolve_base($body)
{
  // check for <base href=...>
  if (preg_match('!(<base.*href=["\']?)([hftps]{3,5}://[a-z0-9/.%-]+)!i', $body, $regs)) {
    $replacer = new rcube_base_replacer($regs[2]);
    // replace all relative paths
    $body = preg_replace_callback('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Ui', array($replacer, 'callback'), $body);
    $body = preg_replace_callback('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Ui', array($replacer, 'callback'), $body);
  }
  return $body;
}
/**
 * modify a HTML message that it can be displayed inside a HTML page
 */
function rcmail_html4inline($body, $container_id)
  {
  $base_url = "";
  $last_style_pos = 0;
  $body_lc = strtolower($body);
  // check for <base href>
  if (preg_match(($base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i'), $body, $base_regs))
    $base_url = $base_regs[2];
  
  // find STYLE tags
  while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
@@ -911,21 +1032,13 @@
    $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, $base_url);
    $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id);
    $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
    $body_lc = strtolower($body);
    $last_style_pos = $pos2;
    }
  // resolve <base href>
  if ($base_url)
    {
    $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
    $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
    $body = preg_replace($base_reg, '', $body);
    }
  // modify HTML links to open a new window if clicked
  $body = preg_replace('/<(a|link)\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1','\\2', '$container_id');", $body);
@@ -1208,8 +1321,7 @@
    unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
    
    if ($CONFIG['smtp_log'])
      write_log('sendmail', sprintf("[%s] User: %s on %s; Message for %s; %s",
      date("d-M-Y H:i:s O", mktime()),
      write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s",
      $RCMAIL->user->get_username(),
      $_SERVER['REMOTE_ADDR'],
      $mailto,
@@ -1229,7 +1341,8 @@
  $message = new rcube_message($uid);
  
  if ($message->headers->mdn_to && !$message->headers->mdn_sent && $IMAP->check_permflag('MDNSENT'))
  if ($message->headers->mdn_to && !$message->headers->mdn_sent &&
    ($IMAP->check_permflag('MDNSENT') || $IMAP->check_permflag('*')))
  {
    $identity = $RCMAIL->user->get_identity();
    $sender = format_email_recipient($identity['email'], $identity['name']);
@@ -1293,6 +1406,36 @@
}
function rcmail_search_filter($attrib)
{
  global $OUTPUT;
  if (!strlen($attrib['id']))
    $attrib['id'] = 'rcmlistfilter';
  $attrib['onchange'] = JS_OBJECT_NAME.'.filter_mailbox(this.value)';
  /*
    RFC3501 (6.4.4): 'ALL', 'RECENT',
    'ANSWERED', 'DELETED', 'FLAGGED', 'SEEN',
    'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNSEEN',
    'NEW', // = (RECENT UNSEEN)
    'OLD' // = NOT RECENT
  */
  $select_filter = new html_select($attrib);
  $select_filter->add(rcube_label('all'), 'ALL');
  $select_filter->add(rcube_label('unread'), 'UNSEEN');
  $select_filter->add(rcube_label('flagged'), 'FLAGGED');
  $select_filter->add(rcube_label('unanswered'), 'UNANSWERED');
  $out = $select_filter->show($_SESSION['search_filter']);
  $OUTPUT->add_gui_object('search_filter', $attrib['id']);
  return $out;
}
// register UI objects
$OUTPUT->add_handlers(array(
  'mailboxlist' => 'rcmail_mailbox_list',
@@ -1305,6 +1448,7 @@
  'messagecontentframe' => 'rcmail_messagecontent_frame',
  'messagepartframe' => 'rcmail_message_part_frame',
  'messagepartcontrols' => 'rcmail_message_part_controls',
  'searchfilter' => 'rcmail_search_filter',
  'searchform' => array($OUTPUT, 'search_form'),
));