alecpl
2010-05-28 d311d809d650e4cab6a5bf0aeb92b97631672c64
program/include/rcube_message.php
@@ -67,14 +67,16 @@
    $this->uid = $uid;
    $this->headers = $this->imap->get_headers($uid, NULL, true, true);
    $this->subject = rcube_imap::decode_mime_string($this->headers->subject, $this->headers->charset);
        $this->subject = rcube_imap::decode_mime_string(
            $this->headers->subject, $this->headers->charset);
    list(, $this->sender) = each($this->imap->decode_address_list($this->headers->from));
    
    $this->set_safe((intval($_GET['_safe']) || $_SESSION['safe_messages'][$uid]));
    $this->opt = array(
      'safe' => $this->is_safe,
      'prefer_html' => $this->app->config->get('prefer_html'),
      'get_url' => rcmail_url('get', array('_mbox' => $this->imap->get_mailbox_name(), '_uid' => $uid))
            'get_url' => rcmail_url('get', array(
                '_mbox' => $this->imap->get_mailbox_name(), '_uid' => $uid))
    );
    if ($this->structure = $this->imap->get_structure($uid, $this->headers->body_structure)) {
@@ -103,6 +105,7 @@
    return $raw ? $value : $this->imap->decode_header($value);
  }
  
  /**
   * Set is_safe var and session data
   *
@@ -114,6 +117,7 @@
    $_SESSION['safe_messages'][$this->uid] = $this->is_safe;
  }
  
  /**
   * Compose a valid URL for getting a message part
   *
@@ -123,7 +127,7 @@
  public function get_part_url($mime_id)
  {
    if ($this->mime_parts[$mime_id])
      return $this->opt['get_url'] . "&_part=" . $mime_id;
            return $this->opt['get_url'] . '&_part=' . $mime_id;
    else
      return false;
  }
@@ -138,9 +142,17 @@
   */
  public function get_part_content($mime_id, $fp=NULL)
  {
    if ($part = $this->mime_parts[$mime_id])
        if ($part = $this->mime_parts[$mime_id]) {
            // stored in message structure (winmail/inline-uuencode)
            if ($part->encoding == 'stream') {
                if ($fp) {
                    fwrite($fp, $part->body);
                }
                return $fp ? true : $part->body;
            }
            // get from IMAP
      return $this->imap->get_message_part($this->uid, $mime_id, $part, NULL, $fp);
    else
        } else
      return null;
  }
  
@@ -161,6 +173,7 @@
     return false;
  }
  /**
   * Return the first HTML part of this message
@@ -194,13 +207,13 @@
    // check all message parts
    foreach ($this->mime_parts as $mime_id => $part) {
      $mimetype = strtolower($part->ctype_primary . '/' . $part->ctype_secondary);
            $mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
      if ($mimetype == 'text/plain') {
        $out = $this->imap->get_message_part($this->uid, $mime_id, $part);
        
        // re-format format=flowed content
        if ($part->ctype_secondary == "plain" && $part->ctype_parameters['format'] == "flowed")
                if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed')
          $out = self::unfold_flowed($out);
        break;
      }
@@ -255,6 +268,15 @@
    if ($message_ctype_primary == 'text' && !$recursive) {
      $structure->type = 'content';
      $this->parts[] = &$structure;
            // Parse simple (plain text) message body
            if ($message_ctype_secondary == 'plain')
                foreach ((array)$this->uu_decode($structure) as $uupart) {
                    $this->mime_parts[$uupart->mime_id] = $uupart;
                    $this->attachments[] = $uupart;
                }
            // @TODO: plugin hook?
    }
    // the same for pgp signed messages
    else if ($mimetype == 'application/pgp' && !$recursive) {
@@ -311,9 +333,9 @@
      else if ($html_part !== null && empty($this->parts)) {
        $c = new stdClass;
        $c->type = 'content';
        $c->body = rcube_label('htmlmessage');
        $c->ctype_primary = 'text';
        $c->ctype_secondary = 'plain';
                $c->body            = rcube_label('htmlmessage');
        $this->parts[] = $c;
      }
@@ -337,7 +359,9 @@
      $p->size = strlen($p->body);
      
      // maybe some plugins are able to decode this encrypted message part
      $data = $this->app->plugins->exec_hook('message_part_encrypted', array('object' => $this, 'struct' => $structure, 'part' => $p));
            $data = $this->app->plugins->exec_hook('message_part_encrypted',
                array('object' => $this, 'struct' => $structure, 'part' => $p));
      if (is_array($data['parts'])) {
        $this->parts = array_merge($this->parts, $data['parts']);
      }
@@ -372,12 +396,13 @@
        }
        // part text/[plain|html] OR message/delivery-status
        else if ((($part_mimetype == 'text/plain' || $part_mimetype == 'text/html') && $mail_part->disposition != 'attachment') ||
            $part_mimetype == 'message/delivery-status' || $part_mimetype == 'message/disposition-notification') {
                    $part_mimetype == 'message/delivery-status' || $part_mimetype == 'message/disposition-notification'
                ) {
          // add text part if it matches the prefs
          if (!$this->parse_alternative ||
              ($secondary_type == 'html' && $this->opt['prefer_html']) ||
              ($secondary_type == 'plain' && !$this->opt['prefer_html'])) {
                        ($secondary_type == 'plain' && !$this->opt['prefer_html'])
                    ) {
            $mail_part->type = 'content';
            $this->parts[] = $mail_part;
          }
@@ -395,20 +420,20 @@
            $this->attachments[] = $mail_part;
        }
        // ignore "virtual" protocol parts
        else if ($primary_type == 'protocol')
                else if ($primary_type == 'protocol') {
          continue;
                }
        // part is Microsoft Outlook TNEF (winmail.dat)
        else if ($part_mimetype == 'application/ms-tnef') {
          foreach ((array)$this->imap->tnef_decode($mail_part, $structure->headers['uid']) as $tnef_part) {
            $this->mime_parts[$tnef_part->mime_id] = $tnef_part;
            $this->attachments[] = $tnef_part;
                    foreach ((array)$this->tnef_decode($mail_part) as $tpart) {
                        $this->mime_parts[$tpart->mime_id] = $tpart;
                        $this->attachments[] = $tpart;
          }
        }
        // part is a file/attachment
        else if (preg_match('/^(inline|attach)/', $mail_part->disposition) ||
                 $mail_part->headers['content-id'] || (empty($mail_part->disposition) && $mail_part->filename)) {
                    $mail_part->headers['content-id'] || (empty($mail_part->disposition) && $mail_part->filename)
                ) {
          // skip apple resource forks
          if ($message_ctype_secondary == 'appledouble' && $secondary_type == 'applefile')
            continue;
@@ -457,7 +482,6 @@
        }
      }
    }
    // message is a single part non-text
    else if ($structure->filename) {
      $this->attachments[] = $structure;
@@ -482,6 +506,88 @@
  /**
     * Decode a Microsoft Outlook TNEF part (winmail.dat)
     *
     * @param object rcube_message_part Message part to decode
     */
    function tnef_decode(&$part)
    {
        // @TODO: attachment may be huge, hadle it via file
        if (!isset($part->body))
            $part->body = $this->imap->get_message_part($this->uid, $part->mime_id, $part);
        require_once('lib/tnef_decoder.inc');
        $parts = array();
        $tnef_arr = tnef_decode($part->body);
        foreach ($tnef_arr as $pid => $winatt) {
            $tpart = new rcube_message_part;
            $tpart->filename        = trim($winatt['name']);
            $tpart->encoding        = 'stream';
            $tpart->ctype_primary   = trim(strtolower($winatt['type0']));
            $tpart->ctype_secondary = trim(strtolower($winatt['type1']));
            $tpart->mimetype        = $tpart->ctype_primary . '/' . $tpart->ctype_secondary;
            $tpart->mime_id         = 'winmail.' . $part->mime_id . '.' . $pid;
            $tpart->size            = $winatt['size'];
            $tpart->body            = $winatt['stream'];
            $parts[] = $tpart;
            unset($tnef_arr[$pid]);
        }
        return $parts;
    }
    /**
     * Parse message body for UUencoded attachments bodies
     *
     * @param object rcube_message_part Message part to decode
     */
    function uu_decode(&$part)
    {
        // @TODO: messages may be huge, hadle body via file
        if (!isset($part->body))
            $part->body = $this->imap->get_message_part($this->uid, $part->mime_id, $part);
        $parts = array();
        // FIXME: line length is max.65?
        $uu_regexp = '/begin [0-7]{3,4} ([^\n]+)\n(([\x21-\x7E]{0,65}\n)+)`\nend/s';
        if (preg_match_all($uu_regexp, $part->body, $matches, PREG_SET_ORDER)) {
            // remove attachments bodies from the message body
            $part->body = preg_replace($uu_regexp, '', $part->body);
            // update message content-type
            $part->ctype_primary   = 'multipart';
            $part->ctype_secondary = 'mixed';
            $part->mimetype        = $part->ctype_primary . '/' . $part->ctype_secondary;
            // add attachments to the structure
            foreach ($matches as $pid => $att) {
                $uupart = new rcube_message_part;
                $uupart->filename = trim($att[1]);
                $uupart->encoding = 'stream';
                $uupart->body     = convert_uudecode($att[2]);
                $uupart->size     = strlen($uupart->body);
                $uupart->mime_id  = 'uu.' . $part->mime_id . '.' . $pid;
                $ctype = rc_mime_content_type($uupart->body, $uupart->filename, 'application/octet-stream', true);
                $uupart->mimetype = $ctype;
                list($uupart->ctype_primary, $uupart->ctype_secondary) = explode('/', $ctype);
                $parts[] = $uupart;
                unset($matches[$pid]);
            }
        }
        return $parts;
    }
    /**
   * Interpret a format=flowed message body according to RFC 2646
   *
   * @param string  Raw body formatted as flowed text
@@ -494,6 +600,7 @@
      array('--%SIGEND%\\1',  '',       '\\1 ',         '-- \\1'),
      $text);
  }
  
  /**
   * Wrap the given text to comply with RFC 2646
@@ -514,4 +621,3 @@
  }
}