alecpl
2011-11-16 3fd5e2edfc72cea56ede0512f081a67d547ed61f
program/steps/mail/compose.inc
@@ -5,7 +5,7 @@
 | program/steps/mail/compose.inc                                        |
 |                                                                       |
 | This file is part of the Roundcube Webmail client                     |
 | Copyright (C) 2005-2009, The Roundcube Dev Team                       |
 | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 | PURPOSE:                                                              |
@@ -29,7 +29,15 @@
$MESSAGE = NULL;
$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GET);
$_SESSION['compose'] = $_SESSION['compose_data'][$COMPOSE_ID];
$_SESSION['compose'] = $_SESSION['compose_data_'.$COMPOSE_ID];
// give replicated session storage some time to synchronize
$retries = 0;
while ($COMPOSE_ID && !is_array($_SESSION['compose']) && $RCMAIL->db->is_replicated() && $retries++ < 5) {
  usleep(500000);
  $RCMAIL->session->reload();
  $_SESSION['compose'] = $_SESSION['compose_data_'.$COMPOSE_ID];
}
// Nothing below is called during message composition, only at "new/forward/reply/draft" initialization or
// if a compose-ID is given (i.e. when the compose step is opened in a new window/tab).
@@ -39,14 +47,14 @@
  if ($COMPOSE_ID)
    raise_error(array('code' => 500, 'type' => 'php',
      'file' => __FILE__, 'line' => __LINE__,
      'message' => "Invalid session"), true, true);
      'message' => "Invalid compose ID"), true, true);
  $_SESSION['compose'] = array(
    'id' => uniqid(mt_rand()),
    'param' => request2param(RCUBE_INPUT_GET),
    'mailbox' => $IMAP->get_mailbox_name(),
  );
  // process values like "mailto:foo@bar.com?subject=new+message&cc=another"
  if ($_SESSION['compose']['param']['to']) {
    // #1486037: remove "mailto:" prefix
@@ -59,10 +67,10 @@
        $_SESSION['compose']['param'][$f] = $val;
    }
  }
  // select folder where to save the sent message
  $_SESSION['compose']['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox');
  // pipe compose parameters thru plugins
  $plugin = $RCMAIL->plugins->exec_hook('message_compose', $_SESSION['compose']);
  $_SESSION['compose']['param'] = array_merge($_SESSION['compose']['param'], $plugin['param']);
@@ -84,12 +92,12 @@
          'path' => $attach,
        );
      }
      // save attachment if valid
      if (($attachment['data'] && $attachment['name']) || ($attachment['path'] && file_exists($attachment['path']))) {
        $attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment);
      }
      if ($attachment['status'] && !$attachment['abort']) {
        unset($attachment['data'], $attachment['status'], $attachment['abort']);
        $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
@@ -111,7 +119,7 @@
$OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubjectwarning', 'cancel',
    'nobodywarning', 'notsentwarning', 'notuploadedwarning', 'savingmessage', 'sendingmessage', 
    'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany',
    'fileuploaderror', 'autocompletechars');
    'fileuploaderror');
$OUTPUT->set_env('compose_id', $COMPOSE_ID);
@@ -122,9 +130,9 @@
}
// set current mailbox in client environment
$OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
$OUTPUT->set_env('sig_above', $CONFIG['sig_above']);
$OUTPUT->set_env('top_posting', $CONFIG['top_posting']);
$OUTPUT->set_env('autocomplete_min_length', $CONFIG['autocomplete_min_length']);
$OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false));
$OUTPUT->set_env('top_posting', $RCMAIL->config->get('top_posting', false));
$OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ',')));
// get reference message and set compose mode
if ($msg_uid = $_SESSION['compose']['param']['draft_uid']) {
@@ -157,14 +165,14 @@
  // re-set 'prefer_html' to have possibility to use html part for compose
  $CONFIG['prefer_html'] = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT;
  $MESSAGE = new rcube_message($msg_uid);
  // make sure message is marked as read
  if ($MESSAGE && $MESSAGE->headers && !$MESSAGE->headers->seen)
  if ($MESSAGE && $MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN']))
    $IMAP->set_flag($msg_uid, 'SEEN');
  if (!empty($MESSAGE->headers->charset))
    $IMAP->set_charset($MESSAGE->headers->charset);
  if ($compose_mode == RCUBE_COMPOSE_REPLY)
  {
    $_SESSION['compose']['reply_uid'] = $msg_uid;
@@ -226,9 +234,11 @@
$MESSAGE->identities = $USER->list_identities();
if (count($MESSAGE->identities))
{
  foreach ($MESSAGE->identities as $idx => $sql_arr) {
    $email = mb_strtolower(rcube_idn_to_utf8($sql_arr['email']));
    $MESSAGE->identities[$idx]['email_ascii'] = $sql_arr['email'];
  foreach ($MESSAGE->identities as $idx => $ident) {
    $email = mb_strtolower(rcube_idn_to_utf8($ident['email']));
    $MESSAGE->identities[$idx]['email_ascii'] = $ident['email'];
    $MESSAGE->identities[$idx]['ident']       = format_email_recipient($ident['email'], $ident['name']);
    $MESSAGE->identities[$idx]['email']       = $email;
  }
}
@@ -241,65 +251,76 @@
  $MESSAGE->compose['from'] = $_SESSION['compose']['param']['from'];
}
else if (count($MESSAGE->identities)) {
  // extract all recipients of the reply-message
  $a_recipients = array();
  if ($compose_mode == RCUBE_COMPOSE_REPLY && is_object($MESSAGE->headers))
  $a_names      = array();
  // extract all recipients of the reply-message
  if (is_object($MESSAGE->headers) && in_array($compose_mode, array(RCUBE_COMPOSE_REPLY, RCUBE_COMPOSE_FORWARD)))
  {
    $a_to = $IMAP->decode_address_list($MESSAGE->headers->to);
    foreach ($a_to as $addr) {
      if (!empty($addr['mailto']))
      if (!empty($addr['mailto'])) {
        $a_recipients[] = strtolower($addr['mailto']);
        $a_names[]      = $addr['name'];
      }
    }
    if (!empty($MESSAGE->headers->cc)) {
      $a_cc = $IMAP->decode_address_list($MESSAGE->headers->cc);
      foreach ($a_cc as $addr) {
        if (!empty($addr['mailto']))
        if (!empty($addr['mailto'])) {
          $a_recipients[] = strtolower($addr['mailto']);
          $a_names[]      = $addr['name'];
        }
      }
    }
  }
  $from_idx         = null;
  $default_identity = 0;
  $default_identity = null;
  $return_path      = $MESSAGE->headers->others['return-path'];
  // Select identity
  foreach ($MESSAGE->identities as $idx => $sql_arr) {
  foreach ($MESSAGE->identities as $idx => $ident) {
    // save default identity ID
    if ($sql_arr['standard']) {
    if ($ident['standard']) {
      $default_identity = $idx;
    }
    // we need ascii here
    $email = $sql_arr['email_ascii'];
    $ident = format_email_recipient($email, $sql_arr['name']);
    // select identity
    // use From header
    if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
      if ($MESSAGE->headers->from == $ident) {
      if ($MESSAGE->headers->from == $ident['ident']) {
        $from_idx = $idx;
        break;
      }
    }
    // reply to self, force To header value
    else if ($compose_mode == RCUBE_COMPOSE_REPLY && $MESSAGE->headers->from == $ident) {
    // reply to yourself
    else if ($compose_mode == RCUBE_COMPOSE_REPLY && $MESSAGE->headers->from == $ident['ident']) {
      $from_idx = $idx;
      $MESSAGE->compose['to'] = $MESSAGE->headers->to;
      break;
    }
    // set identity if it's one of the reply-message recipients
    else if (in_array($email, $a_recipients) && ($from_idx === null || $sql_arr['standard'])) {
      $from_idx = $idx;
    }
    // set identity when replying to mailing list
    else if (strpos($return_path, str_replace('@', '=', $email).'@') !== false) {
      $from_idx = $idx;
    // use replied message recipients
    else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) {
      // match identity name, prefer default identity
      if ($from_idx === null || ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name'])) {
        $from_idx = $idx;
      }
    }
  }
  // Still no ID, use first identity
  // Fallback using Return-Path
  if ($from_idx === null && $return_path) {
    foreach ($MESSAGE->identities as $idx => $ident) {
      if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) {
        $from_idx = $idx;
        break;
      }
    }
  }
  // Still no ID, use default/first identity
  if ($from_idx === null) {
    $from_idx = $default_identity;
    $from_idx = $default_identity !== null ? $default_identity : key(reset($MESSAGE->identities));
  }
  $ident   = $MESSAGE->identities[$from_idx];
@@ -312,6 +333,7 @@
// Set other headers
$a_recipients = array();
$parts        = array('to', 'cc', 'bcc', 'replyto', 'followupto');
$separator    = trim($RCMAIL->config->get('recipients_separator', ',')) . ' ';
foreach ($parts as $header) {
  $fvalue = '';
@@ -336,26 +358,33 @@
      $mailfollowup = $MESSAGE->headers->others['mail-followup-to'];
      $mailreplyto  = $MESSAGE->headers->others['mail-reply-to'];
      if ($MESSAGE->compose['to'])
        $fvalue = $MESSAGE->compose['to'];
      else if ($MESSAGE->reply_all == 'list' && $mailfollowup)
      // Reply to mailing list...
      if ($MESSAGE->reply_all == 'list' && $mailfollowup)
        $fvalue = $mailfollowup;
      else if ($MESSAGE->reply_all == 'list'
        && preg_match('/<mailto:([^>]+)>/i', $MESSAGE->headers->others['list-post'], $m))
        $fvalue = $m[1];
      // Reply to...
      else if ($MESSAGE->reply_all && $mailfollowup)
        $fvalue = $mailfollowup;
      else if ($mailreplyto)
        $fvalue = $mailreplyto;
      else if (!empty($MESSAGE->headers->replyto))
        $fvalue = $MESSAGE->headers->replyto;
      else if (!empty($MESSAGE->headers->from))
        $fvalue = $MESSAGE->headers->from;
      // Reply to message sent by yourself (#1487074)
      if (!empty($ident) && $fvalue == $ident['ident']) {
        $fvalue = $MESSAGE->headers->to;
      }
    }
    // add recipient of original message if reply to all
    else if ($header == 'cc' && !empty($MESSAGE->reply_all) && $MESSAGE->reply_all != 'list') {
      if ($v = $MESSAGE->headers->to)
        $fvalue .= $v;
      if ($v = $MESSAGE->headers->cc)
        $fvalue .= (!empty($fvalue) ? ', ' : '') . $v;
        $fvalue .= (!empty($fvalue) ? $separator : '') . $v;
    }
  }
  else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
@@ -386,7 +415,7 @@
      $mailto = mb_strtolower(rcube_idn_to_utf8($addr_part['mailto']));
      if (!in_array($mailto, $a_recipients)
        && (empty($MESSAGE->compose['from_email']) || $mailto != $MESSAGE->compose['from_email'])
        && ($header == 'to' || empty($MESSAGE->compose['from_email']) || $mailto != $MESSAGE->compose['from_email'])
      ) {
        if ($addr_part['name'] && $addr_part['mailto'] != $addr_part['name'])
          $string = format_email_recipient($mailto, $addr_part['name']);
@@ -397,8 +426,8 @@
        $a_recipients[] = $addr_part['mailto'];
      }
    }
    $fvalue = implode(', ', $fvalue);
    $fvalue = implode($separator, $fvalue);
  }
  $MESSAGE->compose[$header] = $fvalue;
@@ -466,9 +495,12 @@
    $input = new $field_type($field_attrib);
    $out = $input->show($MESSAGE->compose[$param]);
  }
  if ($form_start)
    $out = $form_start.$out;
  // configure autocompletion
  rcube_autocomplete_init();
  return $out;
}
@@ -691,8 +723,8 @@
  // include GoogieSpell
  if (!empty($CONFIG['enable_spellcheck'])) {
    $engine = $RCMAIL->config->get('spellcheck_engine','googie');
    $engine           = $RCMAIL->config->get('spellcheck_engine','googie');
    $dictionary       = (bool) $RCMAIL->config->get('spellcheck_dictionary');
    $spellcheck_langs = (array) $RCMAIL->config->get('spellcheck_languages',
      array('da'=>'Dansk', 'de'=>'Deutsch', 'en' => 'English', 'es'=>'Español',
            'fr'=>'Français', 'it'=>'Italiano', 'nl'=>'Nederlands', 'pl'=>'Polski',
@@ -722,25 +754,28 @@
    foreach ($spellcheck_langs as $key => $name) {
      $editor_lang_set[] = ($key == $lang ? '+' : '') . JQ($name).'='.JQ($key);
    }
    $OUTPUT->include_script('googiespell.js');
    $OUTPUT->add_script(sprintf(
      "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=');\n".
      "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=', %s);\n".
      "googie.lang_chck_spell = \"%s\";\n".
      "googie.lang_rsm_edt = \"%s\";\n".
      "googie.lang_close = \"%s\";\n".
      "googie.lang_revert = \"%s\";\n".
      "googie.lang_no_error_found = \"%s\";\n".
      "googie.lang_learn_word = \"%s\";\n".
      "googie.setLanguages(%s);\n".
      "googie.setCurrentLanguage('%s');\n".
      "googie.setSpellContainer('spellcheck-control');\n".
      "googie.decorateTextarea('%s');\n".
      "%s.set_env('spellcheck', googie);",
      !empty($dictionary) ? 'true' : 'false',
      JQ(Q(rcube_label('checkspelling'))),
      JQ(Q(rcube_label('resumeediting'))),
      JQ(Q(rcube_label('close'))),
      JQ(Q(rcube_label('revertto'))),
      JQ(Q(rcube_label('nospellerrors'))),
      JQ(Q(rcube_label('addtodict'))),
      json_serialize($spellcheck_langs),
      $lang,
      $attrib['id'],
@@ -762,8 +797,13 @@
  // build reply prefix
  $from = array_pop($RCMAIL->imap->decode_address_list($MESSAGE->get_header('from'), 1, false));
  $prefix = sprintf("On %s, %s wrote:",
    $MESSAGE->headers->date, $from['name'] ? $from['name'] : rcube_idn_to_utf8($from['mailto']));
  $prefix = rcube_label(array(
    'name' => 'mailreplyintro',
    'vars' => array(
      'date' => format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long')),
      'sender' => $from['name'] ? $from['name'] : rcube_idn_to_utf8($from['mailto']),
    )
  ));
  if (!$bodyIsHtml) {
    $body = preg_replace('/\r?\n/', "\n", $body);
@@ -808,24 +848,27 @@
function rcmail_create_forward_body($body, $bodyIsHtml)
{
  global $IMAP, $MESSAGE, $OUTPUT;
  global $RCMAIL, $MESSAGE;
  // add attachments
  if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE->mime_parts))
    $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
  $date    = format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long'));
  $charset = $RCMAIL->output->get_charset();
  if (!$bodyIsHtml)
  {
    $prefix = "\n\n\n-------- Original Message --------\n";
    $prefix .= 'Subject: ' . $MESSAGE->subject . "\n";
    $prefix .= 'Date: ' . $MESSAGE->headers->date . "\n";
    $prefix .= 'From: ' . $MESSAGE->get_header('from') . "\n";
    $prefix .= 'To: ' . $MESSAGE->get_header('to') . "\n";
    $prefix = "\n\n\n-------- " . rcube_label('originalmessage') . " --------\n";
    $prefix .= rcube_label('subject') . ': ' . $MESSAGE->subject . "\n";
    $prefix .= rcube_label('date')    . ': ' . $date . "\n";
    $prefix .= rcube_label('from')    . ': ' . $MESSAGE->get_header('from') . "\n";
    $prefix .= rcube_label('to')      . ': ' . $MESSAGE->get_header('to') . "\n";
    if ($MESSAGE->headers->cc)
      $prefix .= 'Cc: ' . $MESSAGE->get_header('cc') . "\n";
      $prefix .= rcube_label('cc') . ': ' . $MESSAGE->get_header('cc') . "\n";
    if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
      $prefix .= 'Reply-To: ' . $MESSAGE->get_header('replyto') . "\n";
      $prefix .= rcube_label('replyto') . ': ' . $MESSAGE->get_header('replyto') . "\n";
    $prefix .= "\n";
  }
@@ -837,24 +880,26 @@
    $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
    $prefix = sprintf(
      "<br /><p>-------- Original Message --------</p>" .
      "<br /><p>-------- " . rcube_label('originalmessage') . " --------</p>" .
        "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Subject: </th><td>%s</td></tr>" .
        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Date: </th><td>%s</td></tr>" .
        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">From: </th><td>%s</td></tr>" .
        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">To: </th><td>%s</td></tr>",
      Q($MESSAGE->subject),
      Q($MESSAGE->headers->date),
      htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()),
      htmlspecialchars(Q($MESSAGE->get_header('to'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()));
        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
      rcube_label('subject'), Q($MESSAGE->subject),
      rcube_label('date'), Q($date),
      rcube_label('from'), htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $charset),
      rcube_label('to'), htmlspecialchars(Q($MESSAGE->get_header('to'), 'replace'), ENT_COMPAT, $charset));
    if ($MESSAGE->headers->cc)
      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Cc: </th><td>%s</td></tr>",
        htmlspecialchars(Q($MESSAGE->get_header('cc'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()));
      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
        rcube_label('cc'),
        htmlspecialchars(Q($MESSAGE->get_header('cc'), 'replace'), ENT_COMPAT, $charset));
    if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Reply-To: </th><td>%s</td></tr>",
        htmlspecialchars(Q($MESSAGE->get_header('replyto'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()));
      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
        rcube_label('replyto'),
        htmlspecialchars(Q($MESSAGE->get_header('replyto'), 'replace'), ENT_COMPAT, $charset));
    $prefix .= "</tbody></table><br>";
  }
@@ -1205,20 +1250,11 @@
  if (!$attrib['id'])
    $attrib['id'] = 'rcmUploadbox';
  // Enable upload progress bar
  rcube_upload_progress_init();
  // Get filesize, enable upload progress bar
  $max_filesize = rcube_upload_init();
  // find max filesize value
  $max_filesize = parse_bytes(ini_get('upload_max_filesize'));
  $max_postsize = parse_bytes(ini_get('post_max_size'));
  if ($max_postsize && $max_postsize < $max_filesize)
    $max_filesize = $max_postsize;
  $OUTPUT->set_env('max_filesize', $max_filesize);
  $max_filesize = show_bytes($max_filesize);
  $button = new html_inputfield(array('type' => 'button'));
  $out = html::div($attrib,
    $OUTPUT->form_tag(array('name' => 'uploadform', 'method' => 'post', 'enctype' => 'multipart/form-data'),
      html::div(null, rcmail_compose_attachment_field(array('size' => $attrib['attachmentfieldsize']))) .
@@ -1229,7 +1265,7 @@
      )
    )
  );
  $OUTPUT->add_gui_object('uploadbox', $attrib['id']);
  return $out;
}
@@ -1364,7 +1400,8 @@
  $attrib['name'] = '_store_target';
  $select = rcmail_mailbox_select(array_merge($attrib, array(
    'noselection' => '- '.rcube_label('dontsave').' -',
    'folder_filter' => 'mail'
    'folder_filter' => 'mail',
    'folder_rights' => 'w',
  )));
  return $select->show($_SESSION['compose']['param']['sent_mbox'], $attrib);
}