alecpl
2009-04-23 7a723522945b8954681171aa012b7ee1431a45cd
program/steps/mail/func.inc
@@ -21,7 +21,7 @@
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';
$EMAIL_ADDRESS_PATTERN = '([a-z0-9][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('spell', 'addcontact', 'autocomplete', 'upload', 'display-attachment', 'remove-attachment');
@@ -374,8 +374,9 @@
/**
 * 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)
function rcmail_js_message_list($a_headers, $insert_top=FALSE, $replace=TRUE)
  {
  global $CONFIG, $IMAP, $OUTPUT;
@@ -391,7 +392,11 @@
      && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
    $a_show_cols[$f] = 'to';
  $browser = new rcube_browser;
  $OUTPUT->command('set_message_coltypes', $a_show_cols);
  if ($browser->ie && $replace)
    $OUTPUT->command('offline_message_list', true);
  // loop through message headers
  foreach ($a_headers as $n => $header)
@@ -433,11 +438,16 @@
      $a_msg_cols[$col] = $cont;
      }
    $a_msg_flags['deleted'] = $header->deleted ? 1 : 0;
    $a_msg_flags['unread'] = $header->seen ? 0 : 1;
    $a_msg_flags['replied'] = $header->answered ? 1 : 0;
    $a_msg_flags['forwarded'] = $header->forwarded ? 1 : 0;
    $a_msg_flags['flagged'] = $header->flagged ? 1 : 0;
    if ($header->deleted)
      $a_msg_flags['deleted'] = 1;
    if (!$header->seen)
      $a_msg_flags['unread'] = 1;
    if ($header->answered)
      $a_msg_flags['replied'] = 1;
    if ($header->forwarded)
      $a_msg_flags['forwarded'] = 1;
    if ($header->flagged)
      $a_msg_flags['flagged'] = 1;
    
    $OUTPUT->command('add_message_row',
      $header->uid,
@@ -446,6 +456,9 @@
      preg_match("/multipart\/m/i", $header->ctype),
      $insert_top);
    }
    if ($browser->ie && $replace)
      $OUTPUT->command('offline_message_list', false);
  }
@@ -626,7 +639,7 @@
  {
    switch($show_images) {
      case '1': // known senders only
        $CONTACTS = new rcube_contacts($DB, $_SESSION['user_id']);
        $CONTACTS = new rcube_contacts($RCMAIL->db, $_SESSION['user_id']);
        if ($CONTACTS->search('email', $message->sender['mailto'], true, false)->count) {
          $message->set_safe(true);
        }
@@ -660,7 +673,7 @@
    '/<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?)
    '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/',   // byte-order mark (only outlook?)
  );
  $html_replace = array(
    '\\1'.' &nbsp; '.'\\3',
@@ -669,7 +682,7 @@
    '',
    '',
    '',
    '\\1',
    '',
  );
  $html = preg_replace($html_search, $html_replace, $html);
@@ -730,71 +743,86 @@
 */
function rcmail_print_body($part, $p = array())
{
  $p += array('safe' => false, 'plain' => false, 'inline_html' => true);
  global $RCMAIL;
  // trigger plugin hook
  $data = $RCMAIL->plugins->exec_hook('message_part_before',
    array('type' => $part->ctype_secondary, 'body' => $part->body) + $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);
  if ($data['type'] == 'html' && $data['plain']) {
    $txt = new html2text($data['body'], false, true);
    $body = $txt->get_text();
    $part->ctype_secondary = 'plain';
  }
  // text/html
  else if ($part->ctype_secondary == 'html') {
    return rcmail_wash_html($part->body, $p, $part->replaces);
  else if ($data['type'] == 'html') {
    $body = rcmail_wash_html($data['body'], $data, $part->replaces);
    $part->ctype_secondary = $data['type'];
  }
  // text/enriched
  else if ($part->ctype_secondary=='enriched') {
  else if ($data['type'] == 'enriched') {
    $part->ctype_secondary = 'html';
    require_once('lib/enriched.inc');
    return Q(enriched_to_html($part->body), 'show');
    $body = Q(enriched_to_html($data['body']), 'show');
  }
  else
  else {
    // assert plaintext
    $body = $part->body;
  /**** assert plaintext ****/
  // make links and email-addresses clickable
  $replacements = new rcube_string_replacer;
  $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
  $url_chars_within = '\?\.~,!';
  // search for patterns like links and e-mail addresses
  $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);
  $quote_level = 0;
  // colorize quoted parts
  for ($n=0; $n < sizeof($a_lines); $n++) {
    $line = $a_lines[$n];
    $quotation = '';
    $q = 0;
    if (preg_match('/^(>+\s*)+/', $line, $regs)) {
      $q    = strlen(preg_replace('/\s/', '', $regs[0]));
      $line = substr($line, strlen($regs[0]));
      if ($q > $quote_level)
        $quotation = str_repeat('<blockquote>', $q - $quote_level);
      else if ($q < $quote_level)
        $quotation = str_repeat("</blockquote>", $quote_level - $q);
    }
    else if ($quote_level > 0)
      $quotation = str_repeat("</blockquote>", $quote_level);
    $quote_level = $q;
    $a_lines[$n] = $quotation . Q($line, 'replace', false);  // htmlquote plaintext
    $part->ctype_secondary = $data['type'] = 'plain';
  }
  // free some memory (hopefully)
  unset($data['body']);
  // insert the links for urls and mailtos
  $body = $replacements->resolve(join("\n", $a_lines));
  return html::tag('pre', array(), $body);
  // plaintext postprocessing
  if ($part->ctype_secondary == 'plain') {
    // make links and email-addresses clickable
    $replacements = new rcube_string_replacer;
    $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
    $url_chars_within = '\?\.~,!';
    // search for patterns like links and e-mail addresses
    $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);
    $quote_level = 0;
    // colorize quoted parts
    for ($n=0; $n < count($a_lines); $n++) {
      $line = $a_lines[$n];
      $quotation = '';
      $q = 0;
      if (preg_match('/^(>+\s*)+/', $line, $regs)) {
        $q    = strlen(preg_replace('/\s/', '', $regs[0]));
        $line = substr($line, strlen($regs[0]));
        if ($q > $quote_level)
          $quotation = str_repeat('<blockquote>', $q - $quote_level);
        else if ($q < $quote_level)
          $quotation = str_repeat("</blockquote>", $quote_level - $q);
      }
      else if ($quote_level > 0)
        $quotation = str_repeat("</blockquote>", $quote_level);
      $quote_level = $q;
      $a_lines[$n] = $quotation . Q($line, 'replace', false);  // htmlquote plaintext
    }
    // insert the links for urls and mailtos
    $body = $replacements->resolve(join("\n", $a_lines));
  }
  // allow post-processing of the message body
  $data = $RCMAIL->plugins->exec_hook('message_part_after', array('type' => $part->ctype_secondary, 'body' => $body) + $data);
  return $data['type'] == 'html' ? $data['body'] : html::tag('pre', array(), $data['body']);
}
@@ -842,7 +870,7 @@
 */
function rcmail_message_headers($attrib, $headers=NULL)
  {
  global $IMAP, $OUTPUT, $MESSAGE, $PRINT_MODE, $CONFIG;
  global $IMAP, $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL;
  static $sa_attrib;
  
  // keep header table attrib
@@ -851,7 +879,6 @@
  else if (!is_array($attrib) && is_array($sa_attrib))
    $attrib = $sa_attrib;
  
  if (!isset($MESSAGE))
    return FALSE;
@@ -859,58 +886,55 @@
  if (!$headers)
    $headers = is_object($MESSAGE->headers) ? get_object_vars($MESSAGE->headers) : $MESSAGE->headers;
    
  $header_count = 0;
  // allow the following attributes to be added to the <table> tag
  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
  $out = '<table' . $attrib_str . ">\n";
  // show these headers
  $standard_headers = array('subject', 'from', 'to', 'cc', 'bcc', 'replyto', 'date');
  $output_headers = array();
  foreach ($standard_headers as $hkey)
    {
  foreach ($standard_headers as $hkey) {
    if (!$headers[$hkey])
      continue;
    if ($hkey == 'date')
      {
    if ($hkey == 'date') {
      if ($PRINT_MODE)
        $header_value = format_date($headers[$hkey], $CONFIG['date_long'] ? $CONFIG['date_long'] : 'x');
        $header_value = format_date($headers[$hkey], $RCMAIL->config->get('date_long', 'x'));
      else
        $header_value = format_date($headers[$hkey]);
      }
    else if ($hkey == 'replyto')
      {
    }
    else if ($hkey == 'replyto') {
      if ($headers['replyto'] != $headers['from'])
        $header_value = Q(rcmail_address_string($headers['replyto'], null, true, $attrib['addicon']), 'show');
        $header_value = rcmail_address_string($headers['replyto'], null, true, $attrib['addicon']);
      else
        continue;
      }
    else if (in_array($hkey, array('from', 'to', 'cc', 'bcc')))
      $header_value = Q(rcmail_address_string($headers[$hkey], null, true, $attrib['addicon']), 'show');
    else if ($hkey == 'subject' && empty($headers[$hkey]))
      $header_value = Q(rcube_label('nosubject'));
    else
      $header_value = Q(trim($IMAP->decode_header($headers[$hkey])));
    $out .= "\n<tr>\n";
    $out .= '<td class="header-title">'.Q(rcube_label($hkey)).":&nbsp;</td>\n";
    $out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
    $header_count++;
    }
    else if (in_array($hkey, array('from', 'to', 'cc', 'bcc')))
      $header_value = rcmail_address_string($headers[$hkey], null, true, $attrib['addicon']);
    else if ($hkey == 'subject' && empty($headers[$hkey]))
      $header_value = rcube_label('nosubject');
    else
      $header_value = trim($IMAP->decode_header($headers[$hkey]));
    $output_headers[$hkey] = array('title' => rcube_label($hkey), 'value' => $header_value, 'raw' => $headers[$hkey]);
  }
  $plugin = $RCMAIL->plugins->exec_hook('message_headers_output', array('output' => $output_headers, 'headers' => $MESSAGE->headers));
  // compose html table
  $table = new html_table(array('cols' => 2));
  foreach ($plugin['output'] as $hkey => $row) {
    $table->add(array('class' => 'header-title'), Q($row['title']));
    $table->add(array('class' => $hkey, 'width' => "90%"), Q($row['value'], ($hkey == 'subject' ? 'strict' : 'show')));
  }
  // all headers division
  $out .= "\n".'<tr><td colspan="2" class="more-headers show-headers"
   onclick="return '.JS_OBJECT_NAME.'.command(\'load-headers\', \'\', this)"></td></tr>';
  $out .= "\n".'<tr id="all-headers"><td colspan="2" class="all"><div id="headers-source"></div></td></tr>';
  $table->add(array('colspan' => 2, 'class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('load-headers','',this)"), '');
  $table->add_row(array('id' => "all-headers"));
  $table->add(array('colspan' => 2, 'class' => "all"), html::div(array('id' => 'headers-source'), ''));
  $OUTPUT->add_gui_object('all_headers_row', 'all-headers');
  $OUTPUT->add_gui_object('all_headers_box', 'headers-source');
  $out .= "\n</table>\n\n";
  return $header_count ? $out : '';
  return $table->show($attrib);
  }
@@ -1034,7 +1058,9 @@
    }
  // 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);
  $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']);
  // add comments arround html and other tags
  $out = preg_replace(array(
@@ -1062,20 +1088,24 @@
/**
 * parse link attributes and set correct target
 */
function rcmail_alter_html_link($tag, $attrs, $container_id)
function rcmail_alter_html_link($matches)
{
  $attrib = parse_attrib_string($attrs);
  global $EMAIL_ADDRESS_PATTERN;
  $tag = $matches[1];
  $attrib = parse_attrib_string($matches[2]);
  $end = '>';
  if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) {
    $attrib['href'] = "./bin/modcss.php?u=" . urlencode($attrib['href']) . "&amp;c=" . urlencode($container_id);
    $attrib['href'] = "./bin/modcss.php?u=" . urlencode($attrib['href']) . "&amp;c=" . urlencode($GLOBALS['rcmail_html_container_id']);
    $end = ' />';
  }
  else if (stristr((string)$attrib['href'], 'mailto:')) {
  else if (preg_match("/^mailto:$EMAIL_ADDRESS_PATTERN/i", $attrib['href'], $mailto)) {
    $attrib['href'] = $mailto[0];
    $attrib['onclick'] = sprintf(
      "return %s.command('compose','%s',this)",
      JS_OBJECT_NAME,
      JQ(substr($attrib['href'], 7)));
      JQ($mailto[1]));
  }
  else if (!empty($attrib['href']) && $attrib['href'][0] != '#') {
    $attrib['target'] = '_blank';
@@ -1106,7 +1136,7 @@
    if ($PRINT_MODE) {
      $out .= sprintf('%s &lt;%s&gt;', Q($part['name']), $part['mailto']);
    }
    else if (preg_match($EMAIL_ADDRESS_PATTERN, $part['mailto'])) {
    else if (preg_match("/$EMAIL_ADDRESS_PATTERN/i", $part['mailto'])) {
      if ($linked) {
        $out .= html::a(array(
            'href' => 'mailto:'.$part['mailto'],
@@ -1173,7 +1203,7 @@
        $prefix = substr($line, 0, $length);
        // Remove '> ' from the line, then wordwrap() the line
        $line = wordwrap(substr($line, $length), $max - $length);
        $line = rc_wordwrap(substr($line, $length), $max - $length);
        // Rebuild the line with '> ' at the beginning of each 'subline'
        $newline = '';
@@ -1185,7 +1215,7 @@
        $line = rtrim($newline);
      }
      else {
        $line = wordwrap($line, $max);
        $line = rc_wordwrap($line, $max);
      }
    }
@@ -1245,10 +1275,7 @@
  if (!isset($_SESSION['compose']))
    return;
  // remove attachment files from temp dir
  if (is_array($_SESSION['compose']['attachments']))
    foreach ($_SESSION['compose']['attachments'] as $attachment)
      @unlink($attachment['path']);
  rcmail::get_instance()->plugins->exec_hook('cleanup_attachments',array());
  
  unset($_SESSION['compose']);
  }
@@ -1302,7 +1329,14 @@
    // reset stored headers and overwrite
    $message->_headers = array();
    $header_str = $message->txtHeaders($headers_php);
    // #1485779
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
      if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) {
        $headers_enc['To'] = implode(', ', $m[1]);
        }
      }
    if (ini_get('safe_mode'))
      $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
    else
@@ -1384,7 +1418,7 @@
               "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
    
    $compose->headers($headers);
    $compose->setTXTBody(wordwrap($body, 75, "\r\n"));
    $compose->setTXTBody(rc_wordwrap($body, 75, "\r\n"));
    $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
    $sent = rcmail_deliver_message($compose, $identity['email'], $mailto);