thomascube
2011-03-10 4591de7018414267311b421ef42ef1b4a2f6aa89
Prepare for multiple concurrent compose windows

10 files modified
87 ■■■■■ changed files
CHANGELOG 1 ●●●● patch | view | raw | blame | history
program/include/rcmail.php 4 ●●● patch | view | raw | blame | history
program/include/rcube_session.php 13 ●●●●● patch | view | raw | blame | history
program/js/app.js 6 ●●●● patch | view | raw | blame | history
program/js/editor_images.js 2 ●●● patch | view | raw | blame | history
program/steps/addressbook/save.inc 3 ●●●● patch | view | raw | blame | history
program/steps/mail/attachments.inc 6 ●●●● patch | view | raw | blame | history
program/steps/mail/compose.inc 37 ●●●● patch | view | raw | blame | history
program/steps/mail/func.inc 8 ●●●● patch | view | raw | blame | history
program/steps/mail/sendmail.inc 7 ●●●● patch | view | raw | blame | history
CHANGELOG
@@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
- Allow multiple concurrent compose sessions
- Force names of unique constraints in PostgreSQL DDL
- Add code for prevention from IMAP connection hangs when server closes socket unexpectedly
- Remove redundant DELETE query (for old session deletion) on login
program/include/rcmail.php
@@ -1078,8 +1078,10 @@
      $this->imap->close();
    // before closing the database connection, write session data
    if ($_SERVER['REMOTE_ADDR'])
    if ($_SERVER['REMOTE_ADDR']) {
      $this->session->cleanup();
      session_write_close();
    }
    // write performance stats to logs/console
    if ($this->config->get('devel_mode')) {
program/include/rcube_session.php
@@ -192,6 +192,19 @@
  /**
   * Cleanup session data before saving
   */
  public function cleanup()
  {
    // current compose information is stored in $_SESSION['compose'], move it to $_SESSION['compose_data']
    if ($_SESSION['compose']) {
      $_SESSION['compose_data'][$_SESSION['compose']['id']] = $_SESSION['compose'];
      $this->remove('compose');
    }
  }
  /**
   * Register additional garbage collector functions
   *
   * @param mixed Callback function
program/js/app.js
@@ -227,7 +227,7 @@
          }
        }
        else if (this.env.action == 'compose') {
          this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'toggle-editor'];
          this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel', 'toggle-editor'];
          if (this.env.drafts_mailbox)
            this.env.compose_commands.push('savedraft')
@@ -3270,7 +3270,7 @@
  this.remove_attachment = function(name)
  {
    if (name && this.env.attachments[name])
      this.http_post('remove-attachment', '_file='+urlencode(name));
      this.http_post('remove-attachment', { _id:this.env.compose_id, _file:name });
    return true;
  };
@@ -5408,7 +5408,7 @@
    $(frame_name).bind('load', {ts:ts}, onload);
    form.target = frame_name;
    form.action = this.url(action, { _uploadid:ts });
    form.action = this.url(action, { _id:this.env.compose_id||'', _uploadid:ts });
    form.setAttribute('enctype', 'multipart/form-data');
    form.submit();
program/js/editor_images.js
@@ -7,6 +7,6 @@
   {
      var att = rc_client.env.attachments[id];
      if (att.complete && att.mimetype.indexOf('image/') == 0)
        tinyMCEImageList.push([att.name, rc_client.env.comm_path+'&_action=display-attachment&_file='+id]);
        tinyMCEImageList.push([att.name, rc_client.env.comm_path+'&_action=display-attachment&_file='+id+'&_id='+rc_client.env.compose_id]);
   }
};
program/steps/addressbook/save.inc
@@ -57,6 +57,7 @@
                'size' => $_FILES['_photo']['size'],
                'name' => $_FILES['_photo']['name'],
                'mimetype' => 'image/' . $imageprop['type'],
                'group' => 'contact',
            ));
        }
        else
@@ -156,7 +157,7 @@
        unset($a_record['photo']);
    
    // cleanup session data
    $RCMAIL->plugins->exec_hook('attachments_cleanup', array());
    $RCMAIL->plugins->exec_hook('attachments_cleanup', array('group' => 'contact'));
    $RCMAIL->session->remove('contacts');
}
program/steps/mail/attachments.inc
@@ -20,6 +20,9 @@
*/
$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC);
$_SESSION['compose'] = $_SESSION['compose_data'][$COMPOSE_ID];
if (!$_SESSION['compose']) {
  die("Invalid session var!");
}
@@ -84,7 +87,8 @@
      'path' => $filepath,
      'size' => $_FILES['_attachments']['size'][$i],
      'name' => $_FILES['_attachments']['name'][$i],
      'mimetype' => rc_mime_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i])
      'mimetype' => rc_mime_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i]),
      'group' => $COMPOSE_ID,
    );
    $attachment = $RCMAIL->plugins->exec_hook('attachment_upload', $attachment);
program/steps/mail/compose.inc
@@ -28,18 +28,15 @@
$MESSAGE_FORM = NULL;
$MESSAGE = NULL;
$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GET);
$_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).
// Since there are many ways to leave the compose page improperly, it seems necessary to clean-up an old
// compose when a "new/forward/reply/draft" is called - otherwise the old session attachments will appear
$MESSAGE_ID = get_input_value('_id', RCUBE_INPUT_GET);
if (!is_array($_SESSION['compose']) || $_SESSION['compose']['id'] != $MESSAGE_ID)
if (!is_array($_SESSION['compose']))
{
  rcmail_compose_cleanup();
  // Infinite redirect prevention in case of broken session (#1487028)
  if ($MESSAGE_ID)
  if ($COMPOSE_ID)
    raise_error(array('code' => 500, 'type' => 'php',
      'file' => __FILE__, 'line' => __LINE__,
      'message' => "Invalid session"), true, true);
@@ -81,9 +78,10 @@
      else {
        $filename = basename($attach);
        $attachment = array(
          'group' => $COMPOSE_ID,
          'name' => $filename,
          'mimetype' => rc_mime_content_type($attach, $filename),
          'path' => $attach
          'path' => $attach,
        );
      }
      
@@ -115,6 +113,8 @@
    'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'fileuploaderror',
    'autocompletechars');
$OUTPUT->set_env('compose_id', $COMPOSE_ID);
// add config parameters to client script
if (!empty($CONFIG['drafts_mbox'])) {
  $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
@@ -127,16 +127,16 @@
$OUTPUT->set_env('autocomplete_min_length', $CONFIG['autocomplete_min_length']);
// get reference message and set compose mode
if ($msg_uid = $_SESSION['compose']['param']['reply_uid'])
if ($msg_uid = $_SESSION['compose']['param']['draft_uid']) {
  $RCMAIL->imap->set_mailbox($CONFIG['drafts_mbox']);
  $compose_mode = RCUBE_COMPOSE_DRAFT;
}
else if ($msg_uid = $_SESSION['compose']['param']['reply_uid'])
  $compose_mode = RCUBE_COMPOSE_REPLY;
else if ($msg_uid = $_SESSION['compose']['param']['forward_uid'])
  $compose_mode = RCUBE_COMPOSE_FORWARD;
else if ($msg_uid = $_SESSION['compose']['param']['uid'])
  $compose_mode = RCUBE_COMPOSE_EDIT;
else if ($msg_uid = $_SESSION['compose']['param']['draft_uid']) {
  $RCMAIL->imap->set_mailbox($CONFIG['drafts_mbox']);
  $compose_mode = RCUBE_COMPOSE_DRAFT;
}
$config_show_sig = $RCMAIL->config->get('show_sig', 1);
if ($config_show_sig == 1)
@@ -620,7 +620,7 @@
    if ($attachment = rcmail_save_image('program/blocked.gif', 'image/gif')) {
      $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
      $body = preg_replace('#\./program/blocked\.gif#',
        $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'],
        $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$_SESSION['compose']['id'],
        $body);
    }
  }
@@ -921,7 +921,7 @@
      if (!$skip && ($attachment = rcmail_save_attachment($message, $pid))) {
        $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
        if ($bodyIsHtml && ($part->content_id || $part->content_location)) {
          $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'];
          $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$_SESSION['compose']['id'];
          if ($part->content_id)
            $cid_map['cid:'.$part->content_id] = $url;
          else
@@ -946,7 +946,7 @@
    if (($part->content_id || $part->content_location) && $part->filename) {
      if ($attachment = rcmail_save_attachment($message, $pid)) {
        $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
        $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'];
        $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$_SESSION['compose']['id'];
        if ($part->content_id)
          $cid_map['cid:'.$part->content_id] = $url;
        else
@@ -980,6 +980,7 @@
  }
  $attachment = array(
    'group' => $_SESSION['compose']['id'],
    'name' => $part->filename ? $part->filename : 'Part_'.$pid.'.'.$part->ctype_secondary,
    'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
    'content_id' => $part->content_id,
@@ -1006,6 +1007,7 @@
  $data = file_get_contents($path);
  $attachment = array(
    'group' => $_SESSION['compose']['id'],
    'name' => rcmail_basename($path),
    'mimetype' => $mimetype ? $mimetype : rc_mime_content_type($path, $name),
    'data' => $data,
@@ -1323,6 +1325,7 @@
  {
    $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task));
    $hiddenfields->add(array('name' => '_action', 'value' => 'send'));
    $hiddenfields->add(array('name' => '_id', 'value' => $_SESSION['compose']['id']));
    $form_start = empty($attrib['form']) ? $RCMAIL->output->form_tag(array('name' => "form", 'method' => "post")) : '';
    $form_start .= $hiddenfields->show();
program/steps/mail/func.inc
@@ -1419,14 +1419,14 @@
/**
 * clear message composing settings
 */
function rcmail_compose_cleanup()
function rcmail_compose_cleanup($id)
{
  if (!isset($_SESSION['compose']))
  if (!isset($_SESSION['compose_data'][$id]))
    return;
  $rcmail = rcmail::get_instance();
  $rcmail->plugins->exec_hook('attachments_cleanup', array());
  $rcmail->session->remove('compose');
  $rcmail->plugins->exec_hook('attachments_cleanup', array('group' => $id));
  unset($_SESSION['compose_data'][$id]);
}
program/steps/mail/sendmail.inc
@@ -26,6 +26,9 @@
$savedraft = !empty($_POST['_draft']) ? true : false;
$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC);
$_SESSION['compose'] = $_SESSION['compose_data'][$COMPOSE_ID];
/****** checks ********/
if (!isset($_SESSION['compose']['id'])) {
@@ -702,7 +705,7 @@
  
  // remember new draft-uid
  $draftuids = $IMAP->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid, true);
  $_SESSION['compose']['param']['_draft_uid'] = $draftuids[0];
  $_SESSION['compose']['param']['draft_uid'] = $draftuids[0];
  // display success
  $OUTPUT->show_message('messagesaved', 'confirmation');
@@ -718,7 +721,7 @@
  }
else
  {
  rcmail_compose_cleanup();
  rcmail_compose_cleanup($COMPOSE_ID);
  if ($store_folder && !$saved)
    $OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent'));