Overview

Packages

  • IMP

Classes

  • IMP
  • IMP_Ajax_Addresses
  • IMP_Ajax_Application
  • IMP_Ajax_Application_Compose
  • IMP_Ajax_Application_Handler_Common
  • IMP_Ajax_Application_Handler_ComposeAttach
  • IMP_Ajax_Application_Handler_Draft
  • IMP_Ajax_Application_Handler_Dynamic
  • IMP_Ajax_Application_Handler_ImageUnblock
  • IMP_Ajax_Application_Handler_Mboxtoggle
  • IMP_Ajax_Application_Handler_Passphrase
  • IMP_Ajax_Application_Handler_Remote
  • IMP_Ajax_Application_Handler_RemotePrefs
  • IMP_Ajax_Application_Handler_Search
  • IMP_Ajax_Application_Handler_Smartmobile
  • IMP_Ajax_Application_ListMessages
  • IMP_Ajax_Application_ShowMessage
  • IMP_Ajax_Application_Viewport
  • IMP_Ajax_Application_Viewport_Error
  • IMP_Ajax_Imple_ImportEncryptKey
  • IMP_Ajax_Imple_ItipRequest
  • IMP_Ajax_Imple_PassphraseDialog
  • IMP_Ajax_Imple_VcardImport
  • IMP_Ajax_Queue
  • IMP_Api
  • IMP_Application
  • IMP_Auth
  • IMP_Basic_Base
  • IMP_Basic_Compose
  • IMP_Basic_Contacts
  • IMP_Basic_Error
  • IMP_Basic_Folders
  • IMP_Basic_Listinfo
  • IMP_Basic_Mailbox
  • IMP_Basic_Message
  • IMP_Basic_Pgp
  • IMP_Basic_Saveimage
  • IMP_Basic_Search
  • IMP_Basic_Searchbasic
  • IMP_Basic_Smime
  • IMP_Basic_Thread
  • IMP_Block_Newmail
  • IMP_Block_Summary
  • IMP_Compose
  • IMP_Compose_Attachment
  • IMP_Compose_Attachment_Linked_Metadata
  • IMP_Compose_Attachment_Metadata
  • IMP_Compose_Attachment_Storage
  • IMP_Compose_Attachment_Storage_AutoDetermine
  • IMP_Compose_Attachment_Storage_Temp
  • IMP_Compose_Attachment_Storage_VfsLinked
  • IMP_Compose_Exception
  • IMP_Compose_Exception_Address
  • IMP_Compose_HtmlSignature
  • IMP_Compose_Link
  • IMP_Compose_LinkedAttachment
  • IMP_Compose_Ui
  • IMP_Compose_View
  • IMP_Contacts
  • IMP_Contacts_Avatar_Addressbook
  • IMP_Contacts_Avatar_Gravatar
  • IMP_Contacts_Avatar_Unknown
  • IMP_Contacts_Flag_Host
  • IMP_Contacts_Image
  • IMP_Contents
  • IMP_Contents_InlineOutput
  • IMP_Contents_View
  • IMP_Crypt_Pgp
  • IMP_Crypt_Smime
  • IMP_Dynamic_AddressList
  • IMP_Dynamic_Base
  • IMP_Dynamic_Compose
  • IMP_Dynamic_Compose_Common
  • IMP_Dynamic_Helper_Base
  • IMP_Dynamic_Mailbox
  • IMP_Dynamic_Message
  • IMP_Exception
  • IMP_Factory_AuthImap
  • IMP_Factory_Compose
  • IMP_Factory_ComposeAtc
  • IMP_Factory_Contacts
  • IMP_Factory_Contents
  • IMP_Factory_Flags
  • IMP_Factory_Ftree
  • IMP_Factory_Identity
  • IMP_Factory_Imap
  • IMP_Factory_Mail
  • IMP_Factory_MailAutoconfig
  • IMP_Factory_Mailbox
  • IMP_Factory_MailboxCache
  • IMP_Factory_MailboxList
  • IMP_Factory_Maillog
  • IMP_Factory_MimeViewer
  • IMP_Factory_Pgp
  • IMP_Factory_PrefsSort
  • IMP_Factory_Quota
  • IMP_Factory_Search
  • IMP_Factory_Sentmail
  • IMP_Factory_Smime
  • IMP_Factory_Spam
  • IMP_Filter
  • IMP_Flag_Base
  • IMP_Flag_Imap
  • IMP_Flag_Imap_Answered
  • IMP_Flag_Imap_Deleted
  • IMP_Flag_Imap_Draft
  • IMP_Flag_Imap_Flagged
  • IMP_Flag_Imap_Forwarded
  • IMP_Flag_Imap_Junk
  • IMP_Flag_Imap_NotJunk
  • IMP_Flag_Imap_Seen
  • IMP_Flag_System_Attachment
  • IMP_Flag_System_Encrypted
  • IMP_Flag_System_HighPriority
  • IMP_Flag_System_List
  • IMP_Flag_System_LowPriority
  • IMP_Flag_System_Match_Address
  • IMP_Flag_System_Match_Flag
  • IMP_Flag_System_Match_Header
  • IMP_Flag_System_Personal
  • IMP_Flag_System_Signed
  • IMP_Flag_System_Unseen
  • IMP_Flag_User
  • IMP_Flags
  • IMP_Ftree
  • IMP_Ftree_Account
  • IMP_Ftree_Account_Imap
  • IMP_Ftree_Account_Inboxonly
  • IMP_Ftree_Account_Remote
  • IMP_Ftree_Account_Vfolder
  • IMP_Ftree_Element
  • IMP_Ftree_Eltdiff
  • IMP_Ftree_Iterator
  • IMP_Ftree_Iterator_Ancestors
  • IMP_Ftree_IteratorFilter
  • IMP_Ftree_IteratorFilter_Children
  • IMP_Ftree_IteratorFilter_Containers
  • IMP_Ftree_IteratorFilter_Expanded
  • IMP_Ftree_IteratorFilter_Invisible
  • IMP_Ftree_IteratorFilter_Mailboxes
  • IMP_Ftree_IteratorFilter_Nonimap
  • IMP_Ftree_IteratorFilter_Polled
  • IMP_Ftree_IteratorFilter_Remote
  • IMP_Ftree_IteratorFilter_Special
  • IMP_Ftree_IteratorFilter_Subscribed
  • IMP_Ftree_IteratorFilter_Vfolder
  • IMP_Ftree_Prefs
  • IMP_Ftree_Prefs_Expanded
  • IMP_Ftree_Prefs_Poll
  • IMP_Ftree_Select
  • IMP_Images
  • IMP_Imap
  • IMP_Imap_Acl
  • IMP_Imap_Cache_Wrapper
  • IMP_Imap_Config
  • IMP_Imap_Exception
  • IMP_Imap_Password
  • IMP_Imap_PermanentFlags
  • IMP_Imap_Remote
  • IMP_Indices
  • IMP_Indices_Mailbox
  • IMP_LoginTasks_SystemTask_GarbageCollection
  • IMP_LoginTasks_SystemTask_Upgrade
  • IMP_LoginTasks_SystemTask_UpgradeAuth
  • IMP_LoginTasks_Task_Autocreate
  • IMP_LoginTasks_Task_DeleteAttachmentsMonthly
  • IMP_LoginTasks_Task_DeleteSentmailMonthly
  • IMP_LoginTasks_Task_FilterOnLogin
  • IMP_LoginTasks_Task_PurgeSentmail
  • IMP_LoginTasks_Task_PurgeSpam
  • IMP_LoginTasks_Task_PurgeTrash
  • IMP_LoginTasks_Task_RecoverDraft
  • IMP_LoginTasks_Task_RenameSentmailMonthly
  • IMP_Mailbox
  • IMP_Mailbox_List
  • IMP_Mailbox_List_Pop3
  • IMP_Mailbox_List_Thread
  • IMP_Mailbox_List_Virtual
  • IMP_Mailbox_SessionCache
  • IMP_Mailbox_Ui
  • IMP_Maillog
  • IMP_Maillog_Log_Base
  • IMP_Maillog_Log_Forward
  • IMP_Maillog_Log_Mdn
  • IMP_Maillog_Log_Redirect
  • IMP_Maillog_Log_Reply
  • IMP_Maillog_Log_Replyall
  • IMP_Maillog_Log_Replylist
  • IMP_Maillog_Message
  • IMP_Maillog_Storage_Base
  • IMP_Maillog_Storage_Composite
  • IMP_Maillog_Storage_History
  • IMP_Maillog_Storage_Mdnsent
  • IMP_Maillog_Storage_Null
  • IMP_Mbox_Generate
  • IMP_Mbox_Import
  • IMP_Mbox_Size
  • IMP_Message
  • IMP_Message_Date
  • IMP_Message_Ui
  • IMP_Mime_Headers
  • IMP_Mime_Status
  • IMP_Mime_Status_RenderIssue
  • IMP_Mime_Status_RenderIssue_Display
  • IMP_Mime_Viewer_Alternative
  • IMP_Mime_Viewer_Appledouble
  • IMP_Mime_Viewer_Audio
  • IMP_Mime_Viewer_Enriched
  • IMP_Mime_Viewer_Externalbody
  • IMP_Mime_Viewer_Html
  • IMP_Mime_Viewer_Images
  • IMP_Mime_Viewer_Itip
  • IMP_Mime_Viewer_Mdn
  • IMP_Mime_Viewer_Partial
  • IMP_Mime_Viewer_Pdf
  • IMP_Mime_Viewer_Pgp
  • IMP_Mime_Viewer_Plain
  • IMP_Mime_Viewer_Related
  • IMP_Mime_Viewer_Rfc822
  • IMP_Mime_Viewer_Smil
  • IMP_Mime_Viewer_Smime
  • IMP_Mime_Viewer_Status
  • IMP_Mime_Viewer_Vcard
  • IMP_Mime_Viewer_Video
  • IMP_Mime_Viewer_Zip
  • IMP_Minimal_Base
  • IMP_Minimal_Compose
  • IMP_Minimal_Error
  • IMP_Minimal_Folders
  • IMP_Minimal_Mailbox
  • IMP_Minimal_Message
  • IMP_Minimal_Messagepart
  • IMP_Minimal_Search
  • IMP_Notification_Event_Status
  • IMP_Notification_Handler_Decorator_ImapAlerts
  • IMP_Notification_Handler_Decorator_NewmailNotify
  • IMP_Perms
  • IMP_Prefs_AttribText
  • IMP_Prefs_Identity
  • IMP_Prefs_Sort
  • IMP_Prefs_Sort_FixedDate
  • IMP_Prefs_Sort_None
  • IMP_Prefs_Sort_Sortpref
  • IMP_Prefs_Sort_Sortpref_Locked
  • IMP_Prefs_Special_Acl
  • IMP_Prefs_Special_ComposeTemplates
  • IMP_Prefs_Special_Drafts
  • IMP_Prefs_Special_Encrypt
  • IMP_Prefs_Special_Flag
  • IMP_Prefs_Special_HtmlSignature
  • IMP_Prefs_Special_ImageReplacement
  • IMP_Prefs_Special_InitialPage
  • IMP_Prefs_Special_Mailto
  • IMP_Prefs_Special_NewmailSound
  • IMP_Prefs_Special_PgpPrivateKey
  • IMP_Prefs_Special_PgpPublicKey
  • IMP_Prefs_Special_Remote
  • IMP_Prefs_Special_Searches
  • IMP_Prefs_Special_Sentmail
  • IMP_Prefs_Special_SmimePrivateKey
  • IMP_Prefs_Special_SmimePublicKey
  • IMP_Prefs_Special_Sourceselect
  • IMP_Prefs_Special_Spam
  • IMP_Prefs_Special_SpecialMboxes
  • IMP_Prefs_Special_Trash
  • IMP_Quota
  • IMP_Quota_Hook
  • IMP_Quota_Imap
  • IMP_Quota_Null
  • IMP_Quota_Ui
  • IMP_Remote
  • IMP_Remote_Account
  • IMP_Script_Package_Autocomplete
  • IMP_Script_Package_ComposeBase
  • IMP_Script_Package_DynamicBase
  • IMP_Script_Package_Editor
  • IMP_Script_Package_Imp
  • IMP_Search
  • IMP_Search_Element
  • IMP_Search_Element_Attachment
  • IMP_Search_Element_Autogenerated
  • IMP_Search_Element_Bulk
  • IMP_Search_Element_Contacts
  • IMP_Search_Element_Daterange
  • IMP_Search_Element_Flag
  • IMP_Search_Element_Header
  • IMP_Search_Element_Mailinglist
  • IMP_Search_Element_Or
  • IMP_Search_Element_Personal
  • IMP_Search_Element_Recipient
  • IMP_Search_Element_Size
  • IMP_Search_Element_Text
  • IMP_Search_Element_Within
  • IMP_Search_Filter
  • IMP_Search_Filter_Attachment
  • IMP_Search_Filter_Autogenerated
  • IMP_Search_Filter_Builtin
  • IMP_Search_Filter_Bulk
  • IMP_Search_Filter_Contacts
  • IMP_Search_Filter_Mailinglist
  • IMP_Search_Filter_Personal
  • IMP_Search_IteratorFilter
  • IMP_Search_Query
  • IMP_Search_Ui
  • IMP_Search_Vfolder
  • IMP_Search_Vfolder_Builtin
  • IMP_Search_Vfolder_Vinbox
  • IMP_Search_Vfolder_Vtrash
  • IMP_Sentmail
  • IMP_Sentmail_Mongo
  • IMP_Sentmail_Null
  • IMP_Sentmail_Sql
  • IMP_Smartmobile
  • IMP_Spam
  • IMP_Spam_Email
  • IMP_Spam_Null
  • IMP_Spam_Program
  • IMP_Test
  • IMP_Tree_Flist
  • IMP_Tree_Jquerymobile
  • IMP_Tree_Simplehtml
  • IMP_View_Subinfo

Interfaces

  • IMP_Compose_Attachment_Linked
  • IMP_Contacts_Avatar_Backend
  • IMP_Contacts_Flag_Backend
  • IMP_Spam_Base
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * Copyright 2002-2014 Horde LLC (http://www.horde.org/)
   4:  *
   5:  * See the enclosed file COPYING for license information (GPL). If you
   6:  * did not receive this file, see http://www.horde.org/licenses/gpl.
   7:  *
   8:  * @category  Horde
   9:  * @copyright 2002-2014 Horde LLC
  10:  * @license   http://www.horde.org/licenses/gpl GPL
  11:  * @package   IMP
  12:  */
  13: 
  14: /**
  15:  * The IMP_Contents:: class contains all functions related to handling the
  16:  * content and output of mail messages in IMP.
  17:  *
  18:  * @author    Michael Slusarz <slusarz@horde.org>
  19:  * @category  Horde
  20:  * @copyright 2002-2014 Horde LLC
  21:  * @license   http://www.horde.org/licenses/gpl GPL
  22:  * @package   IMP
  23:  */
  24: class IMP_Contents
  25: {
  26:     /* Mask entries for getSummary(). */
  27:     const SUMMARY_BYTES = 1;
  28:     const SUMMARY_SIZE = 2;
  29:     const SUMMARY_ICON = 4;
  30:     const SUMMARY_ICON_RAW = 16384;
  31:     const SUMMARY_DESCRIP = 8;
  32:     const SUMMARY_DESCRIP_LINK = 16;
  33:     const SUMMARY_DOWNLOAD = 32;
  34:     const SUMMARY_DOWNLOAD_ZIP = 64;
  35:     const SUMMARY_IMAGE_SAVE = 128;
  36:     const SUMMARY_PRINT = 256;
  37:     const SUMMARY_PRINT_STUB = 512;
  38:     const SUMMARY_STRIP = 1024;
  39: 
  40:     /* Rendering mask entries. */
  41:     const RENDER_FULL = 1;
  42:     const RENDER_INLINE = 2;
  43:     const RENDER_INLINE_DISP_NO = 4;
  44:     const RENDER_INFO = 8;
  45:     const RENDER_INLINE_AUTO = 16;
  46:     const RENDER_RAW = 32;
  47:     const RENDER_RAW_FALLBACK = 64;
  48: 
  49:     /* Header return type for getHeader(). */
  50:     const HEADER_OB = 1;
  51:     const HEADER_TEXT = 2;
  52:     const HEADER_STREAM = 3;
  53: 
  54:     /**
  55:      * Have we scanned for embedded parts?
  56:      *
  57:      * @var boolean
  58:      */
  59:     protected $_build = false;
  60: 
  61:     /**
  62:      * The list of MIME IDs that consist of embedded data.
  63:      *
  64:      * @var array
  65:      */
  66:     protected $_embedded = array();
  67: 
  68:     /**
  69:      * Message header.
  70:      *
  71:      * @var mixed
  72:      */
  73:     protected $_header;
  74: 
  75:     /**
  76:      * The index of the current message.
  77:      *
  78:      * @var IMP_Indices_Mailbox
  79:      */
  80:     protected $_indices;
  81: 
  82:     /**
  83:      * The Horde_Mime_Part object for the message.
  84:      *
  85:      * @var Horde_Mime_Part
  86:      */
  87:     protected $_message;
  88: 
  89:     /**
  90:      * Cached data for the MIME Viewer objects.
  91:      *
  92:      * @var object
  93:      */
  94:     protected $_viewcache;
  95: 
  96:     /**
  97:      * Constructor.
  98:      *
  99:      * @param mixed $in  An IMP_Indices_Mailbox or Horde_Mime_Part object.
 100:      *
 101:      * @throws IMP_Exception
 102:      */
 103:     public function __construct($in)
 104:     {
 105:         if ($in instanceof Horde_Mime_Part) {
 106:             $this->_message = $in;
 107:         } else {
 108:             $this->_indices = $in;
 109: 
 110:             /* Get the Horde_Mime_Part object for the given UID. */
 111:             $query = new Horde_Imap_Client_Fetch_Query();
 112:             $query->structure();
 113: 
 114:             if (!($ret = $this->_fetchData($query))) {
 115:                 $e = new IMP_Exception(_("Error displaying message: message does not exist on server."));
 116:                 $e->setLogLevel('NOTICE');
 117:                 throw $e;
 118:             }
 119: 
 120:             $this->_message = $ret->getStructure();
 121:         }
 122:     }
 123: 
 124:     /**
 125:      * String representation of object.
 126:      *
 127:      * @return string  The indices string.
 128:      */
 129:     public function __toString()
 130:     {
 131:         return strval($this->getIndicesOb());
 132:     }
 133: 
 134:     /**
 135:      * Returns the IMAP UID for the current message.
 136:      *
 137:      * @return integer  The message UID.
 138:      */
 139:     public function getUid()
 140:     {
 141:         list(,$uid) = $this->_indices->getSingle();
 142:         return $uid;
 143:     }
 144: 
 145:     /**
 146:      * Returns the IMAP mailbox for the current message.
 147:      *
 148:      * @return IMP_Mailbox  The message mailbox.
 149:      */
 150:     public function getMailbox()
 151:     {
 152:         list($mbox,) = $this->_indices->getSingle();
 153:         return $mbox;
 154:     }
 155: 
 156:     /**
 157:      * Return an IMP_Indices object for the current message.
 158:      *
 159:      * @return IMP_Indices  An indices object.
 160:      */
 161:     public function getIndicesOb()
 162:     {
 163:         return $this->_indices;
 164:     }
 165: 
 166:     /**
 167:      * Returns the entire body of the message.
 168:      *
 169:      * @param array $options  Additional options:
 170:      *   - stream: (boolean) If true, return a stream.
 171:      *             DEFAULT: No
 172:      *
 173:      * @return mixed  The text of the part, or a stream resource if 'stream'
 174:      *                is true.
 175:      */
 176:     public function getBody($options = array())
 177:     {
 178:         if (!$this->_indices) {
 179:             return $this->_message->toString(array(
 180:                 'headers' => true,
 181:                 'stream' => !empty($options['stream'])
 182:             ));
 183:         }
 184: 
 185:         $query = new Horde_Imap_Client_Fetch_Query();
 186:         $query->bodytext(array(
 187:             'peek' => true
 188:         ));
 189: 
 190:         return ($res = $this->_fetchData($query))
 191:             ? $res->getBodyText(0, !empty($options['stream']))
 192:             : '';
 193:     }
 194: 
 195:     /**
 196:      * Gets the raw text for one section of the message.
 197:      *
 198:      * @param integer $id     The ID of the MIME part.
 199:      * @param array $options  Additional options:
 200:      *   - decode: (boolean) Attempt to decode the bodypart on the remote
 201:      *             server.
 202:      *             DEFAULT: No
 203:      *   - length: (integer) If set, only download this many bytes of the
 204:      *             bodypart from the server.
 205:      *             DEFAULT: All data is retrieved.
 206:      *   - mimeheaders: (boolean) Include the MIME headers also?
 207:      *                  DEFAULT: No
 208:      *   - stream: (boolean) If true, return a stream.
 209:      *             DEFAULT: No
 210:      *
 211:      * @return object  Object with the following properties:
 212:      *   - data: (mixed) The text of the part or a stream resource if 'stream'
 213:      *           option is true.
 214:      *   - decode: (string) If 'decode' option is true, and bodypart decoded
 215:      *             on server, the content-type of the decoded data.
 216:      */
 217:     public function getBodyPart($id, $options = array())
 218:     {
 219:         $ret = new stdClass;
 220:         $ret->data = '';
 221:         $ret->decode = null;
 222: 
 223:         if (empty($id)) {
 224:             return $ret;
 225:         }
 226: 
 227:         if (!$this->_indices || $this->isEmbedded($id)) {
 228:             if (empty($options['mimeheaders']) ||
 229:                 in_array($id, $this->_embedded)) {
 230:                 $ob = $this->getMIMEPart($id, array('nocontents' => true));
 231: 
 232:                 if (empty($options['stream'])) {
 233:                     if (!is_null($ob)) {
 234:                         $ret->data = $ob->getContents();
 235:                     }
 236:                 } else {
 237:                     $ret->data = is_null($ob)
 238:                         ? fopen('php://temp', 'r+')
 239:                         : $ob->getContents(array('stream' => true));
 240:                 }
 241: 
 242:                 return $ret;
 243:             }
 244: 
 245:             $base_id = new Horde_Mime_Id($id);
 246:             while (!in_array($base_id->id, $this->_embedded, true)) {
 247:                 $base_id->id = $base_id->idArithmetic($base_id::ID_UP);
 248:                 if (is_null($base_id->id)) {
 249:                     return $ret;
 250:                 }
 251:             }
 252: 
 253:             $part = $this->getMIMEPart($base_id->id, array('nocontents' => true));
 254:             $txt = $part->addMimeHeaders()->toString() .
 255:                 "\n" .
 256:                 $part->getContents();
 257: 
 258:             try {
 259:                 $body = Horde_Mime_Part::getRawPartText($txt, 'header', '1') .
 260:                     "\n\n" .
 261:                     Horde_Mime_Part::getRawPartText($txt, 'body', '1');
 262:             } catch (Horde_Mime_Exception $e) {
 263:                 $body = '';
 264:             }
 265: 
 266:             if (empty($options['stream'])) {
 267:                 $ret->data = $body;
 268:                 return $ret;
 269:             }
 270: 
 271:             $ret->data = fopen('php://temp', 'r+');
 272:             if (strlen($body)) {
 273:                 fwrite($ret->data, $body);
 274:                 fseek($ret->data, 0);
 275:             }
 276:             return $ret;
 277:         }
 278: 
 279:         $query = new Horde_Imap_Client_Fetch_Query();
 280:         if (substr($id, -2) === '.0') {
 281:             $rfc822 = true;
 282:             $id = substr($id, 0, -2);
 283:         } else {
 284:             $rfc822 = false;
 285:         }
 286: 
 287:         if (!isset($options['length']) || !empty($options['length'])) {
 288:             $bodypart_params = array(
 289:                 'decode' => !empty($options['decode']),
 290:                 'peek' => true
 291:             );
 292: 
 293:             if (isset($options['length'])) {
 294:                 $bodypart_params['start'] = 0;
 295:                 $bodypart_params['length'] = $options['length'];
 296:             }
 297: 
 298:             if ($rfc822) {
 299:                 $bodypart_params['id'] = $id;
 300:                 $query->bodyText($bodypart_params);
 301:             } else {
 302:                 $query->bodyPart($id, $bodypart_params);
 303:             }
 304:         }
 305: 
 306:         if (!empty($options['mimeheaders'])) {
 307:             if ($rfc822) {
 308:                 $query->headerText(array(
 309:                     'id' => $id,
 310:                     'peek' => true
 311:                 ));
 312:             } else {
 313:                 $query->mimeHeader($id, array(
 314:                     'peek' => true
 315:                 ));
 316:             }
 317:         }
 318: 
 319:         if ($res = $this->_fetchData($query)) {
 320:             try {
 321:                 if (empty($options['mimeheaders'])) {
 322:                     $ret->decode = $res->getBodyPartDecode($id);
 323:                     $ret->data = $rfc822
 324:                         ? $res->getBodyText($id, !empty($options['stream']))
 325:                         : $res->getBodyPart($id, !empty($options['stream']));
 326:                     return $ret;
 327:                 } elseif (empty($options['stream'])) {
 328:                     $ret->data = $rfc822
 329:                         ? ($res->getHeaderText($id) . $res->getBodyText($id))
 330:                         : ($res->getMimeHeader($id) . $res->getBodyPart($id));
 331:                     return $ret;
 332:                 }
 333: 
 334:                 if ($rfc822) {
 335:                     $data = array(
 336:                         $res->getHeaderText($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM),
 337:                         $res->getBodyText($id, true)
 338:                     );
 339:                 } else {
 340:                     $data = array(
 341:                         $res->getMimeHeader($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM),
 342:                         $res->getBodyPart($id, true)
 343:                     );
 344:                 }
 345: 
 346:                 $ret->data = Horde_Stream_Wrapper_Combine::getStream($data);
 347:                 return $ret;
 348:             } catch (Horde_Exception $e) {}
 349:         }
 350: 
 351:         if (!empty($options['stream'])) {
 352:             $ret->data = fopen('php://temp', 'r+');
 353:         }
 354: 
 355:         return $ret;
 356:     }
 357: 
 358:     /**
 359:      * Returns the full message text.
 360:      *
 361:      * @param array $options  Additional options:
 362:      *   - stream: (boolean) If true, return a stream for bodytext.
 363:      *             DEFAULT: No
 364:      *
 365:      * @return mixed  The full message text or a stream resource if 'stream'
 366:      *                is true.
 367:      */
 368:     public function fullMessageText($options = array())
 369:     {
 370:         if (!$this->_indices) {
 371:             return $this->_message->toString();
 372:         }
 373: 
 374:         $query = new Horde_Imap_Client_Fetch_Query();
 375:         $query->bodyText(array(
 376:             'peek' => true
 377:         ));
 378: 
 379:         if ($res = $this->_fetchData($query)) {
 380:             try {
 381:                 if (empty($options['stream'])) {
 382:                     return $this->getHeader(self::HEADER_TEXT) . $res->getBodyText(0);
 383:                 }
 384: 
 385:                 return Horde_Stream_Wrapper_Combine::getStream(array(
 386:                     $this->getHeader(self::HEADER_STREAM),
 387:                     $res->getBodyText(0, true)
 388:                 ));
 389:             } catch (Horde_Exception $e) {}
 390:         }
 391: 
 392:         return empty($options['stream'])
 393:             ? ''
 394:             : fopen('php://temp', 'r+');
 395:     }
 396: 
 397:     /**
 398:      * Returns base header information.
 399:      *
 400:      * @param integer $type  Return type (HEADER_* constant).
 401:      *
 402:      * @return mixed  Either a Horde_Mime_Headers object (HEADER_OB), header
 403:      *                text (HEADER_TEXT), or a stream resource (HEADER_STREAM).
 404:      */
 405:     public function getHeader($type = self::HEADER_OB)
 406:     {
 407:         return $this->_getHeader($type, false);
 408:     }
 409: 
 410:     /**
 411:      * Returns base header information and marks the message as seen.
 412:      *
 413:      * @param integer $type  See getHeader().
 414:      *
 415:      * @return mixed  See getHeader().
 416:      */
 417:     public function getHeaderAndMarkAsSeen($type = self::HEADER_OB)
 418:     {
 419:         $mbox = $this->getMailbox();
 420: 
 421:         if ($mbox->readonly) {
 422:             $seen = false;
 423:         } else {
 424:             $seen = true;
 425: 
 426:             if (isset($this->_header)) {
 427:                 try {
 428:                     $imp_imap = $mbox->imp_imap;
 429:                     $imp_imap->store($mbox, array(
 430:                         'add' => array(
 431:                             Horde_Imap_Client::FLAG_SEEN
 432:                         ),
 433:                         'ids' => $imp_imap->getIdsOb($this->getUid())
 434:                     ));
 435:                 } catch (Exception $e) {}
 436:             }
 437:         }
 438: 
 439:         return $this->_getHeader($type, $seen);
 440:     }
 441: 
 442:     /**
 443:      * Returns base header information.
 444:      *
 445:      * @param integer $type  See getHeader().
 446:      * @param boolean $seen  Mark message as seen?
 447:      *
 448:      * @return mixed  See getHeader().
 449:      */
 450:     protected function _getHeader($type, $seen)
 451:     {
 452:         if (!isset($this->_header)) {
 453:             if (!$this->_indices) {
 454:                 $this->_header = $this->_message->addMimeHeaders();
 455:             } else {
 456:                 $query = new Horde_Imap_Client_Fetch_Query();
 457:                 $query->headerText(array(
 458:                     'peek' => !$seen
 459:                 ));
 460: 
 461:                 $this->_header = ($res = $this->_fetchData($query))
 462:                     ? $res
 463:                     : new Horde_Imap_Client_Data_Fetch();
 464:             }
 465:         }
 466: 
 467:         switch ($type) {
 468:         case self::HEADER_OB:
 469:             return $this->_indices
 470:                 ? $this->_header->getHeaderText(0, Horde_Imap_Client_Data_Fetch::HEADER_PARSE)
 471:                 : $this->_header;
 472: 
 473:         case self::HEADER_TEXT:
 474:             return $this->_indices
 475:                 ? $this->_header->getHeaderText()
 476:                 : $this->_header->toString();
 477: 
 478:         case self::HEADER_STREAM:
 479:             if ($this->_indices) {
 480:                 return $this->_header->getHeaderText(0, Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
 481:             }
 482: 
 483:             $stream = new Horde_Support_StringStream($this->_header->toString());
 484:             $stream->fopen();
 485:             return $stream;
 486:         }
 487:     }
 488: 
 489:     /**
 490:      * Returns the Horde_Mime_Part object.
 491:      *
 492:      * @return Horde_Mime_Part  A Horde_Mime_Part object.
 493:      */
 494:     public function getMIMEMessage()
 495:     {
 496:         return $this->_message;
 497:     }
 498: 
 499:     /**
 500:      * Fetch a part of a MIME message.
 501:      *
 502:      * @param integer $id     The MIME index of the part requested.
 503:      * @param array $options  Additional options:
 504:      *   - length: (integer) If set, only download this many bytes of the
 505:      *             bodypart from the server.
 506:      *             DEFAULT: All data is retrieved.
 507:      *   - nocontents: (boolean) If true, don't add the contents to the part
 508:      *                 DEFAULT: Contents are added to the part
 509:      *
 510:      * @return Horde_Mime_Part  The raw MIME part asked for (reference).
 511:      */
 512:     public function getMIMEPart($id, $options = array())
 513:     {
 514:         $this->_buildMessage();
 515: 
 516:         $part = $this->_message->getPart($id);
 517: 
 518:         /* Ticket #9201: Treat 'ISO-8859-1' as 'windows-1252'. 1252 has some
 519:          * characters (e.g. euro sign, back quote) not in 8859-1. There
 520:          * shouldn't be any issue doing this since the additional code points
 521:          * in 1252 don't map to anything in 8859-1. */
 522:         if ($part &&
 523:             (strcasecmp($part->getCharset(), 'ISO-8859-1') === 0)) {
 524:             $part->setCharset('windows-1252');
 525:         }
 526: 
 527:         /* Don't download contents of entire body if ID == 0 (indicating the
 528:          * body of the main multipart message).  I'm pretty sure we never
 529:          * want to download the body of that part here. */
 530:         if (!empty($id) &&
 531:             !is_null($part) &&
 532:             (substr($id, -2) != '.0') &&
 533:             empty($options['nocontents']) &&
 534:             $this->_indices &&
 535:             !$part->getContents(array('stream' => true))) {
 536:             $body = $this->getBodyPart($id, array(
 537:                 'decode' => true,
 538:                 'length' => empty($options['length']) ? null : $options['length'],
 539:                 'stream' => true
 540:             ));
 541:             $part->setContents($body->data, array(
 542:                 'encoding' => $body->decode,
 543:                 'usestream' => true
 544:             ));
 545:         }
 546: 
 547:         return $part;
 548:     }
 549: 
 550:     /**
 551:      * Render a MIME Part.
 552:      *
 553:      * @param string $mime_id  The MIME ID to render.
 554:      * @param integer $mode    One of the RENDER_ constants.
 555:      * @param array $options   Additional options:
 556:      *   - autodetect: (boolean) Attempt to auto-detect MIME type?
 557:      *   - mime_part: (Horde_Mime_Part) The MIME part to render.
 558:      *   - type: (string) Use this MIME type instead of the MIME type
 559:      *           identified in the MIME part.
 560:      *
 561:      * @return array  See Horde_Mime_Viewer_Base::render(). The following
 562:      *                fields may also be present in addition to the fields
 563:      *                defined in Horde_Mime_Viewer_Base:
 564:      *   - attach: (boolean) Force display of this part as an attachment.
 565:      *   - js: (array) A list of javascript commands to run after the content
 566:      *         is displayed on screen.
 567:      *   - name: (string) Contains the MIME name information.
 568:      *   - wrap: (string) If present, indicates that this part, and all child
 569:      *           parts, will be wrapped in a DIV with the given class name.
 570:      */
 571:     public function renderMIMEPart($mime_id, $mode, array $options = array())
 572:     {
 573:         $this->_buildMessage();
 574: 
 575:         $mime_part = empty($options['mime_part'])
 576:             ? $this->getMIMEPart($mime_id)
 577:             : $options['mime_part'];
 578:         if (!$mime_part) {
 579:             return array($mime_id => null);
 580:         }
 581: 
 582:         if (!empty($options['autodetect']) &&
 583:             ($tempfile = Horde::getTempFile()) &&
 584:             ($fp = fopen($tempfile, 'w')) &&
 585:             !is_null($contents = $mime_part->getContents(array('stream' => true)))) {
 586:             rewind($contents);
 587:             while (!feof($contents)) {
 588:                 fwrite($fp, fread($contents, 8192));
 589:             }
 590:             fclose($fp);
 591: 
 592:             $options['type'] = Horde_Mime_Magic::analyzeFile($tempfile, empty($GLOBALS['conf']['mime']['magic_db']) ? null : $GLOBALS['conf']['mime']['magic_db']);
 593:         }
 594: 
 595:         $type = empty($options['type'])
 596:             ? null
 597:             : $options['type'];
 598: 
 599:         $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($mime_part, array('contents' => $this, 'type' => $type));
 600: 
 601:         switch ($mode) {
 602:         case self::RENDER_INLINE:
 603:         case self::RENDER_INLINE_AUTO:
 604:         case self::RENDER_INLINE_DISP_NO:
 605:             $textmode = 'inline';
 606:             $limit = $viewer->getConfigParam('limit_inline_size');
 607: 
 608:             if ($limit && ($mime_part->getBytes() > $limit)) {
 609:                 $data = '';
 610:                 $status = new IMP_Mime_Status(array(
 611:                     _("This message part cannot be viewed because it is too large."),
 612:                     sprintf(_("Click %s to download the data."), $this->linkView($mime_part, 'download_attach', _("HERE")))
 613:                 ));
 614:                 $status->icon('alerts/warning.png', _("Warning"));
 615: 
 616:                 if (method_exists($viewer, 'overLimitText')) {
 617:                     $data = $viewer->overLimitText();
 618:                     $status->addText(_("The initial portion of this text part is displayed below."));
 619:                 }
 620: 
 621:                 return array(
 622:                     $mime_id => array(
 623:                         'data' => $data,
 624:                         'name' => '',
 625:                         'status' => $status,
 626:                         'type' => 'text/html; charset=' . 'UTF-8'
 627:                     )
 628:                 );
 629:             }
 630:             break;
 631: 
 632:         case self::RENDER_INFO:
 633:             $textmode = 'info';
 634:             break;
 635: 
 636:         case self::RENDER_RAW:
 637:             $textmode = 'raw';
 638:             break;
 639: 
 640:         case self::RENDER_RAW_FALLBACK:
 641:             $textmode = $viewer->canRender('raw')
 642:                 ? 'raw'
 643:                 : 'full';
 644:             break;
 645: 
 646:         case self::RENDER_FULL:
 647:         default:
 648:             $textmode = 'full';
 649:             break;
 650:         }
 651: 
 652:         $ret = $viewer->render($textmode);
 653: 
 654:         if (empty($ret)) {
 655:             return ($mode == self::RENDER_INLINE_AUTO)
 656:                 ? $this->renderMIMEPart($mime_id, self::RENDER_INFO, $options)
 657:                 : array();
 658:         }
 659: 
 660:         if (!empty($ret[$mime_id]) && !isset($ret[$mime_id]['name'])) {
 661:             $ret[$mime_id]['name'] = $mime_part->getName(true);
 662:         }
 663: 
 664:         /* Don't show empty parts. */
 665:         if (($textmode == 'inline') &&
 666:             !is_null($ret[$mime_id]['data']) &&
 667:             !strlen($ret[$mime_id]['data']) &&
 668:             !isset($ret[$mime_id]['status'])) {
 669:             $ret[$mime_id] = null;
 670:         }
 671: 
 672:         return $ret;
 673:     }
 674: 
 675:     /**
 676:      * Finds the main "body" text part (if any) in a message.
 677:      * "Body" data is the first text part in the base MIME part.
 678:      *
 679:      * @param string $subtype  Specifically search for this subtype.
 680:      *
 681:      * @return string  The MIME ID of the main body part.
 682:      */
 683:     public function findBody($subtype = null)
 684:     {
 685:         $this->_buildMessage();
 686:         return $this->_message->findBody($subtype);
 687:     }
 688: 
 689:     /**
 690:      * Generate the preview text.
 691:      *
 692:      * @return array  Array with the following keys:
 693:      *   - cut: (boolean) Was the preview text cut?
 694:      *   - text: (string) The preview text.
 695:      */
 696:     public function generatePreview()
 697:     {
 698:         // For preview generation, don't go through overhead of scanning for
 699:         // embedded parts. Necessary evil, or else very large parts (e.g
 700:         // 5 MB+ text parts) will take ages to scan.
 701:         $oldbuild = $this->_build;
 702:         $this->_build = true;
 703:         $mimeid = $this->findBody();
 704: 
 705:         if (is_null($mimeid)) {
 706:             $this->_build = $oldbuild;
 707:             return array('cut' => false, 'text' => '');
 708:         }
 709: 
 710:         $maxlen = empty($GLOBALS['conf']['msgcache']['preview_size'])
 711:             ? $GLOBALS['prefs']->getValue('preview_maxlen')
 712:             : $GLOBALS['conf']['msgcache']['preview_size'];
 713: 
 714:         // Retrieve 3x the size of $maxlen of bodytext data. This should
 715:         // account for any content-encoding & HTML tags.
 716:         $pmime = $this->getMIMEPart($mimeid, array('length' => $maxlen * 3));
 717: 
 718:         $ptext = Horde_String::convertCharset($pmime->getContents(), $pmime->getCharset(), 'UTF-8');
 719: 
 720:         if ($pmime->getType() == 'text/html') {
 721:             $ptext = $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($ptext, 'Html2text');
 722:         }
 723: 
 724:         $this->_build = $oldbuild;
 725: 
 726:         if (Horde_String::length($ptext) > $maxlen) {
 727:             return array(
 728:                 'cut' => true,
 729:                 'text' => Horde_String::truncate($ptext, $maxlen)
 730:             );
 731:         }
 732: 
 733:         return array(
 734:             'cut' => false,
 735:             'text' => $ptext
 736:         );
 737:     }
 738: 
 739:     /**
 740:      * Get summary info for a MIME ID.
 741:      *
 742:      * @param string $id     The MIME ID.
 743:      * @param integer $mask  A bitmask indicating what information to return:
 744:      * <pre>
 745:      * Always output:
 746:      *   'type' = MIME type
 747:      *
 748:      * IMP_Contents::SUMMARY_BYTES
 749:      *   Output: parts = 'bytes'
 750:      *
 751:      * IMP_Contents::SUMMARY_SIZE
 752:      *   Output: parts = 'size'
 753:      *
 754:      * IMP_Contents::SUMMARY_ICON
 755:      * IMP_Contents::SUMMARY_ICON_RAW
 756:      *   Output: parts = 'icon'
 757:      *
 758:      * IMP_Contents::SUMMARY_DESCRIP
 759:      *   Output: parts = 'description_raw'
 760:      *
 761:      * IMP_Contents::SUMMARY_DESCRIP_LINK
 762:      *   Output: parts = 'description'
 763:      *
 764:      * IMP_Contents::SUMMARY_DOWNLOAD
 765:      *   Output: parts = 'download', 'download_url'
 766:      *
 767:      * IMP_Contents::SUMMARY_DOWNLOAD_ZIP
 768:      *   Output: parts = 'download_zip'
 769:      *
 770:      * IMP_Contents::SUMMARY_IMAGE_SAVE
 771:      *   Output: parts = 'img_save'
 772:      *
 773:      * IMP_Contents::SUMMARY_PRINT
 774:      * IMP_Contents::SUMMARY_PRINT_STUB
 775:      *   Output: parts = 'print'
 776:      *
 777:      * IMP_Contents::SUMMARY_STRIP
 778:      *   Output: parts = 'strip'
 779:      * </pre>
 780:      *
 781:      * @return array  An array with the requested information.
 782:      */
 783:     public function getSummary($id, $mask = 0)
 784:     {
 785:         $autodetect_link = false;
 786:         $download_zip = (($mask & self::SUMMARY_DOWNLOAD_ZIP) && Horde_Util::extensionExists('zlib'));
 787:         $param_array = array();
 788: 
 789:         $this->_buildMessage();
 790: 
 791:         $part = array(
 792:             'bytes' => null,
 793:             'download' => null,
 794:             'download_url' => null,
 795:             'download_zip' => null,
 796:             'id' => $id,
 797:             'img_save' => null,
 798:             'size' => null,
 799:             'strip' => null
 800:         );
 801: 
 802:         $mime_part = $this->getMIMEPart($id, array('nocontents' => true));
 803:         $mime_type = $mime_part->getType();
 804: 
 805:         /* If this is an attachment that has no specific MIME type info, see
 806:          * if we can guess a rendering type. */
 807:         if (in_array($mime_type, array('application/octet-stream', 'application/base64'))) {
 808:             $mime_type = Horde_Mime_Magic::filenameToMIME($mime_part->getName());
 809:             if ($mime_type == $mime_part->getType()) {
 810:                 $autodetect_link = true;
 811:             } else {
 812:                 $mime_part = clone $mime_part;
 813:                 $mime_part->setType($mime_type);
 814:                 $param_array['ctype'] = $mime_type;
 815:             }
 816:         }
 817:         $part['type'] = $mime_type;
 818: 
 819:         /* Is this part an attachment? */
 820:         $is_atc = $this->isAttachment($mime_type);
 821: 
 822:         /* Get bytes/size information. */
 823:         if (($mask & self::SUMMARY_BYTES) ||
 824:             $download_zip ||
 825:             ($mask & self::SUMMARY_SIZE)) {
 826:             $part['bytes'] = $size = $mime_part->getBytes();
 827:             $part['size'] = ($size > 1048576)
 828:                 ? sprintf(_("%s MB"), IMP::numberFormat($size / 1048576, 1))
 829:                 : sprintf(_("%s KB"), max(round($size / 1024), 1));
 830:         }
 831: 
 832:         /* Get part's icon. */
 833:         if (($mask & self::SUMMARY_ICON) ||
 834:             ($mask & self::SUMMARY_ICON_RAW)) {
 835:             $part['icon'] = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->getIcon($mime_type);
 836:             if ($mask & self::SUMMARY_ICON) {
 837:                 $part['icon'] = Horde_Themes_Image::tag($part['icon'], array(
 838:                     'attr' => array(
 839:                         'title' => $mime_type
 840:                     )
 841:                 ));
 842:             }
 843:         } else {
 844:             $part['icon'] = null;
 845:         }
 846: 
 847:         /* Get part's description. */
 848:         $description = $this->getPartName($mime_part, true);
 849: 
 850:         if ($mask & self::SUMMARY_DESCRIP_LINK) {
 851:             if (($can_d = $this->canDisplay($mime_part, self::RENDER_FULL)) ||
 852:                 $autodetect_link) {
 853:                 $part['description'] = $this->linkViewJS($mime_part, 'view_attach', htmlspecialchars($description), array('jstext' => sprintf(_("View %s"), $description), 'params' => array_filter(array_merge($param_array, array(
 854:                     'autodetect' => !$can_d
 855:                 )))));
 856:             } else {
 857:                 $part['description'] = htmlspecialchars($description);
 858:             }
 859:         }
 860:         if ($mask & self::SUMMARY_DESCRIP) {
 861:             $part['description_raw'] = $description;
 862:         }
 863: 
 864:         /* Download column. */
 865:         if (($mask & self::SUMMARY_DOWNLOAD) &&
 866:             $is_atc &&
 867:             (is_null($part['bytes']) || $part['bytes'])) {
 868:             $part['download'] = $this->linkView($mime_part, 'download_attach', '', array('class' => 'iconImg downloadAtc', 'jstext' => _("Download")));
 869:             $part['download_url'] = $this->urlView($mime_part, 'download_attach');
 870:         }
 871: 
 872:         /* Display the compressed download link only if size is greater
 873:          * than 200 KB. */
 874:         if ($is_atc &&
 875:             $download_zip &&
 876:             ($part['bytes'] > 204800)) {
 877:             $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($mime_part, array('contents' => $this, 'type' => $mime_type));
 878:             if (!$viewer->getMetadata('compressed')) {
 879:                 $part['download_zip'] = $this->linkView($mime_part, 'download_attach', null, array('class' => 'iconImg downloadZipAtc', 'jstext' => sprintf(_("Download %s in .zip Format"), $description), 'params' => array('zip' => 1)));
 880:             }
 881:         }
 882: 
 883:         /* Display the image save link if the required registry calls are
 884:          * present. */
 885:         if (($mask & self::SUMMARY_IMAGE_SAVE) &&
 886:             $GLOBALS['registry']->hasMethod('images/selectGalleries') &&
 887:             ($mime_part->getPrimaryType() == 'image')) {
 888:             $part['img_save'] = Horde::link('#', _("Save Image in Gallery"), 'iconImg saveImgAtc', null, Horde::popupJs(IMP_Basic_Saveimage::url(), array('params' => array('muid' => strval($this->getIndicesOb()), 'id' => $id), 'height' => 200, 'width' => 450, 'urlencode' => true)) . 'return false;') . '</a>';
 889:         }
 890: 
 891:         /* Add print link? */
 892:         if ((($mask & self::SUMMARY_PRINT) ||
 893:              ($mask & self::SUMMARY_PRINT_STUB)) &&
 894:             $this->canDisplay($id, self::RENDER_FULL)) {
 895:             $part['print'] = ($mask & self::SUMMARY_PRINT)
 896:                 ? $this->linkViewJS($mime_part, 'print_attach', '', array('css' => 'iconImg printAtc', 'jstext' => _("Print"), 'onload' => 'IMP_JS.printWindow', 'params' => $param_array))
 897:                 : Horde::link('#', _("Print"), 'iconImg printAtc', null, null, null, null, array('mimeid' => $id)) . '</a>';
 898:         }
 899: 
 900:         /* Strip Attachment? Allow stripping of base parts other than the
 901:          * base multipart and the base text (body) part. */
 902:         if (($mask & self::SUMMARY_STRIP) &&
 903:             ($id != 0) &&
 904:             (intval($id) != 1) &&
 905:             (strpos($id, '.') === false)) {
 906:             $part['strip'] = Horde::link(
 907:                 Horde::selfUrlParams()->add(array(
 908:                     'actionID' => 'strip_attachment',
 909:                     'imapid' => $id,
 910:                     'muid' => strval($this->getIndicesOb()),
 911:                     'token' => $GLOBALS['session']->getToken()
 912:                 )),
 913:                 _("Strip Attachment"),
 914:                 'iconImg deleteImg stripAtc',
 915:                 null,
 916:                 null,
 917:                 null,
 918:                 null,
 919:                 array('mimeid' => $id)
 920:             ) . '</a>';
 921:         }
 922: 
 923:         return $part;
 924:     }
 925: 
 926:     /**
 927:      * Return the URL to the download/view page.
 928:      *
 929:      * @param Horde_Mime_Part $mime_part  The MIME part to view.
 930:      * @param integer $actionID           The actionID to perform.
 931:      * @param array $options              Additional options:
 932:      *   - params: (array) A list of any additional parameters that need to be
 933:      *             passed to the download/view page (key => name).
 934:      *
 935:      * @return Horde_Url  The URL to the download/view page.
 936:      */
 937:     public function urlView($mime_part = null, $actionID = 'view_attach',
 938:                             array $options = array())
 939:     {
 940:         $params = $this->_urlViewParams($mime_part, $actionID, isset($options['params']) ? $options['params'] : array());
 941: 
 942:         return (strpos($actionID, 'download_') === 0)
 943:             ? IMP_Contents_View::downloadUrl($mime_part->getName(true), $params)
 944:             : Horde::url('view.php', true)->add($params);
 945:     }
 946: 
 947:     /**
 948:      * Generates the necessary URL parameters for the download/view page.
 949:      *
 950:      * @param Horde_Mime_Part $mime_part  The MIME part to view.
 951:      * @param integer $actionID           The actionID to perform.
 952:      * @param array $params               Additional parameters to pass.
 953:      *
 954:      * @return array  The array of parameters.
 955:      */
 956:     protected function _urlViewParams($mime_part, $actionID, $params)
 957:     {
 958:         /* Add the necessary local parameters. */
 959:         $params = array_merge($params, array(
 960:             'actionID' => $actionID,
 961:             'id' => isset($params['id']) ? $params['id'] : $mime_part->getMIMEId()
 962:         ));
 963: 
 964:         if ($this->_indices) {
 965:             $params['muid'] = strval($this->getIndicesOb());
 966:         }
 967: 
 968:         return IMP_Contents_View::addToken($params);
 969:     }
 970: 
 971:     /**
 972:      * Generate a link to the download/view page.
 973:      *
 974:      * @param Horde_Mime_Part $mime_part  The MIME part to view.
 975:      * @param integer $actionID           The actionID value.
 976:      * @param string $text                The ESCAPED (!) link text.
 977:      * @param array $options              Additional parameters:
 978:      *   - class: (string) The CSS class to use.
 979:      *   - jstext: (string) The JS text to use.
 980:      *   - params: (array) A list of any additional parameters that need to be
 981:      *             passed to the download/view page.
 982:      *
 983:      * @return string  A HTML href link to the download/view page.
 984:      */
 985:     public function linkView($mime_part, $actionID, $text, $options = array())
 986:     {
 987:         $options = array_merge(array(
 988:             'class' => null,
 989:             'jstext' => $text,
 990:             'params' => array()
 991:         ), $options);
 992: 
 993:         return Horde::link(
 994:             $this->urlView($mime_part, $actionID, $options),
 995:             $options['jstext'],
 996:             $options['class'],
 997:             ($actionID == 'download_attach') ? null : strval(new Horde_Support_Randomid())
 998:         ) . $text . '</a>';
 999:     }
1000: 
1001:     /**
1002:      * Generate a javascript link to the download/view page.
1003:      *
1004:      * @param Horde_Mime_Part $mime_part  The MIME part to view.
1005:      * @param string $actionID            The actionID to perform.
1006:      * @param string $text                The ESCAPED (!) link text.
1007:      * @param array $options              Additional options:
1008:      *   - css: (string) The CSS class to use.
1009:      *   - jstext: (string) The javascript link text.
1010:      *   - onload: (string) A JS function to run when popup window is
1011:      *             fully loaded.
1012:      *   - params: (array) A list of any additional parameters that need to be
1013:      *             passed to download/view page. (key = name)
1014:      *   - widget: (boolean) If true use Horde::widget() to generate,
1015:      *             Horde::link() otherwise.
1016:      *
1017:      * @return string  A HTML href link to the download/view page.
1018:      */
1019:     public function linkViewJS($mime_part, $actionID, $text,
1020:                                $options = array())
1021:     {
1022:         if (empty($options['params'])) {
1023:             $options['params'] = array();
1024:         }
1025: 
1026:         if (empty($options['jstext'])) {
1027:             $options['jstext'] = sprintf(_("View %s"), $mime_part->getDescription(true));
1028:         }
1029: 
1030:         $url = Horde::popupJs(Horde::url('view.php'), array(
1031:             'menu' => true,
1032:             'onload' => empty($options['onload']) ? 'IMP_JS.resizePopup' : $options['onload'],
1033:             'params' => $this->_urlViewParams($mime_part, $actionID, isset($options['params']) ? $options['params'] : array()),
1034:             'urlencode' => true
1035:         ));
1036: 
1037:         return empty($options['widget'])
1038:             ? Horde::link('#', $options['jstext'], empty($options['css']) ? null : $options['css'], null, $url) . $text . '</a>'
1039:             : Horde::widget(array('url' => '#', 'class' => empty($options['css']) ? null : $options['css'], 'onclick' => $url, 'title' => $text));
1040:     }
1041: 
1042:     /**
1043:      * Determines if a MIME type is an attachment.
1044:      * For IMP's purposes, an attachment is any MIME part that can be
1045:      * downloaded by itself (i.e. all the data needed to view the part is
1046:      * contained within the download data).
1047:      *
1048:      * @param string $mime_type  The MIME type.
1049:      *
1050:      * @return boolean  True if an attachment.
1051:      */
1052:     public function isAttachment($mime_type)
1053:     {
1054:         switch ($mime_type) {
1055:         case 'application/ms-tnef':
1056:             return false;
1057:         }
1058: 
1059:         list($ptype,) = explode('/', $mime_type, 2);
1060: 
1061:         switch ($ptype) {
1062:         case 'message':
1063:             return in_array($mime_type, array('message/rfc822', 'message/disposition-notification'));
1064: 
1065:         case 'multipart':
1066:             return false;
1067: 
1068:         default:
1069:             return true;
1070:         }
1071:     }
1072: 
1073:     /**
1074:      * Builds the "virtual" Horde_Mime_Part object by checking for embedded
1075:      * parts.
1076:      *
1077:      * @param array $parts  The parts list to process.
1078:      */
1079:     protected function _buildMessage($parts = null)
1080:     {
1081:         global $injector;
1082: 
1083:         if (is_null($parts)) {
1084:             if ($this->_build) {
1085:                 return;
1086:             }
1087:             $this->_build = true;
1088:             $parts = array_keys($this->_message->contentTypeMap());
1089:             $first_id = reset($parts);
1090:         } else {
1091:             $first_id = null;
1092:         }
1093: 
1094:         $last_id = null;
1095:         $to_process = array();
1096: 
1097:         $mv_factory = $injector->getInstance('IMP_Factory_MimeViewer');
1098: 
1099:         foreach ($parts as $id) {
1100:             if (!is_null($last_id) &&
1101:                 (strpos($id, $last_id) === 0)) {
1102:                 continue;
1103:             }
1104: 
1105:             $last_id = null;
1106: 
1107:             $mime_part = $this->getMIMEPart($id, array('nocontents' => true));
1108:             $viewer = $mv_factory->create($mime_part, array('contents' => $this));
1109:             if ($viewer->embeddedMimeParts()) {
1110:                 $mime_part = $this->getMIMEPart($id);
1111:                 $viewer->setMIMEPart($mime_part);
1112:                 $new_part = $viewer->getEmbeddedMimeParts();
1113:                 if (!is_null($new_part)) {
1114:                     $mime_part->addPart($new_part);
1115:                     $mime_part->buildMimeIds($id);
1116:                     $this->_embedded[] = $new_part->getMimeId();
1117:                     $to_process = array_merge($to_process, array_keys($new_part->contentTypeMap()));
1118:                     $last_id = $id;
1119:                 }
1120:             }
1121:         }
1122: 
1123:         if (!empty($to_process)) {
1124:             $this->_buildMessage($to_process);
1125:         }
1126:     }
1127: 
1128:     /**
1129:      * Can this MIME part be displayed in the given mode?
1130:      *
1131:      * @param mixed $part    The MIME part or a MIME ID string.
1132:      * @param integer $mask  One of the RENDER_ constants.
1133:      * @param string $type   The type to use (overrides the MIME ID if $id is
1134:      *                       a MIME part).
1135:      *
1136:      * @return integer  The RENDER_ constant of the allowable display.
1137:      */
1138:     public function canDisplay($part, $mask, $type = null)
1139:     {
1140:         if (!is_object($part)) {
1141:             $part = $this->getMIMEPart($part, array('nocontents' => true));
1142:         }
1143:         $viewer = $GLOBALS['injector']->getInstance('IMP_Factory_MimeViewer')->create($part, array('contents' => $this, 'type' => $type));
1144: 
1145:         if ($mask & self::RENDER_INLINE_AUTO) {
1146:             $mask |= self::RENDER_INLINE | self::RENDER_INFO;
1147:         }
1148: 
1149:         if (($mask & self::RENDER_RAW) && $viewer->canRender('raw')) {
1150:             return self::RENDER_RAW;
1151:         }
1152: 
1153:         if (($mask & self::RENDER_FULL) && $viewer->canRender('full')) {
1154:             return self::RENDER_FULL;
1155:         }
1156: 
1157:         if ($mask & self::RENDER_INLINE) {
1158:             if ($viewer->canRender('inline')) {
1159:                 return self::RENDER_INLINE;
1160:             }
1161:         } elseif (($mask & self::RENDER_INLINE_DISP_NO) &&
1162:                   $viewer->canRender('inline')) {
1163:             return self::RENDER_INLINE_DISP_NO;
1164:         }
1165: 
1166:         if (($mask & self::RENDER_INFO) && $viewer->canRender('info')) {
1167:             return self::RENDER_INFO;
1168:         }
1169: 
1170:         return 0;
1171:     }
1172: 
1173:     /**
1174:      * Returns the Content-Type map for the entire message, regenerating
1175:      * embedded parts if needed.
1176:      *
1177:      * @return array  See Horde_Mime_Part::contentTypeMap().
1178:      */
1179:     public function getContentTypeMap()
1180:     {
1181:         $this->_buildMessage();
1182:         return $this->_message->contentTypeMap();
1183:     }
1184: 
1185:     /**
1186:      * Returns the MIME part tree of the message.
1187:      *
1188:      * @param string $renderer  Either the tree renderer driver or a full
1189:      *                          class name to use.
1190:      *
1191:      * @return Horde_Tree_Renderer_Base  A tree instance representing the MIME parts.
1192:      * @throws Horde_Tree_Exception
1193:      */
1194:     public function getTree($renderer = 'Horde_Core_Tree_Renderer_Html')
1195:     {
1196:         $tree = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Tree')->create('mime-' . $this->getUid(), $renderer, array(
1197:             'nosession' => true
1198:         ));
1199:         $this->_addTreeNodes($tree, $this->_message);
1200:         return $tree;
1201:     }
1202: 
1203:     /**
1204:      * Adds MIME parts to the tree instance.
1205:      *
1206:      * @param Horde_Tree_Renderer_Base tree   A tree instance.
1207:      * @param Horde_Mime_Part $part           The MIME part to add to the
1208:      *                                        tree, including its sub-parts.
1209:      * @param string $parent                  The parent part's MIME id.
1210:      */
1211:     protected function _addTreeNodes($tree, $part, $parent = null)
1212:     {
1213:         $mimeid = $part->getMimeId();
1214: 
1215:         $summary_mask = self::SUMMARY_ICON_RAW | self::SUMMARY_DESCRIP_LINK | self::SUMMARY_SIZE | self::SUMMARY_DOWNLOAD;
1216:         if ($GLOBALS['prefs']->getValue('strip_attachments')) {
1217:             $summary_mask += self::SUMMARY_STRIP;
1218:         }
1219: 
1220:         $summary = $this->getSummary($mimeid, $summary_mask);
1221: 
1222:         $tree->addNode(array(
1223:             'id' => $mimeid,
1224:             'parent' => $parent,
1225:             'label' => sprintf(
1226:                 '%s (%s) %s %s',
1227:                 $summary['description'],
1228:                 $summary['size'],
1229:                 $summary['download'],
1230:                 $summary['strip']
1231:             ),
1232:             'params' => array(
1233:                 'class' => 'partsTreeDiv',
1234:                 'icon' => $summary['icon']
1235:             )
1236:         ));
1237: 
1238:         foreach ($part->getParts() as $part) {
1239:             $this->_addTreeNodes($tree, $part, $mimeid);
1240:         }
1241:     }
1242: 
1243:     /**
1244:      * Get download all list.
1245:      *
1246:      * @return array  An array of downloadable parts.
1247:      */
1248:     public function downloadAllList()
1249:     {
1250:         $ret = array();
1251: 
1252:         foreach ($this->getContentTypeMap() as $key => $val) {
1253:             if ($this->isAttachment($val)) {
1254:                 $ret[] = $key;
1255:             }
1256:         }
1257: 
1258:         return $ret;
1259:     }
1260: 
1261:     /**
1262:      * Injects body contents into the base Horde_Mime_part object.
1263:      *
1264:      * @param array $ignore  A list of MIME IDs to ignore.
1265:      *
1266:      * @return Horde_Mime_Part  The part with body contents set.
1267:      */
1268:     public function buildMessageContents($ignore = array())
1269:     {
1270:         $message = $this->_message;
1271:         $curr_ignore = null;
1272: 
1273:         foreach ($message->contentTypeMap() as $key => $val) {
1274:             if (is_null($curr_ignore) && in_array($key, $ignore)) {
1275:                 $curr_ignore = $key . '.';
1276:             } elseif (is_null($curr_ignore) ||
1277:                       (strpos($key, $curr_ignore) === false)) {
1278:                 $curr_ignore = null;
1279:                 if (($key != 0) &&
1280:                     ($val != 'message/rfc822') &&
1281:                     (strpos($val, 'multipart/') === false)) {
1282:                     $part = $this->getMIMEPart($key);
1283:                     $message->alterPart($key, $part);
1284:                 }
1285:             }
1286:         }
1287: 
1288:         return $message;
1289:     }
1290: 
1291:     /**
1292:      * Determines if a given MIME part ID is a part of embedded data.
1293:      *
1294:      * @param string $mime_id  The MIME ID.
1295:      *
1296:      * @return boolean  True if the MIME ID is part of embedded data.
1297:      */
1298:     public function isEmbedded($mime_id)
1299:     {
1300:         foreach ($this->_embedded as $val) {
1301:             if (($mime_id == $val) ||
1302:                 (($id_ob = new Horde_Mime_Id($val)) &&
1303:                  $id_ob->isChild($mime_id))) {
1304:                 return true;
1305:             }
1306:         }
1307: 
1308:         return false;
1309:     }
1310: 
1311:     /**
1312:      * Find a MIME type in parent parts.
1313:      *
1314:      * @param string $id    The MIME ID to begin the search at.
1315:      * @param string $type  The MIME type to search for.
1316:      *
1317:      * @return mixed  Either the requested MIME part, or null if not found.
1318:      */
1319:     public function findMimeType($id, $type)
1320:     {
1321:         $id_ob = new Horde_Mime_Id($id);
1322: 
1323:         while (($id_ob->id = $id_ob->idArithmetic($id_ob::ID_UP)) !== null) {
1324:             if (($part = $this->getMIMEPart($id_ob->id, array('nocontents' => true))) &&
1325:                 ($part->getType() == $type)) {
1326:                 return $part;
1327:             }
1328:         }
1329: 
1330:         return null;
1331:     }
1332: 
1333:     /**
1334:      * Return the descriptive part label, making sure it is not empty.
1335:      *
1336:      * @param Horde_Mime_Part $part  The MIME Part object.
1337:      * @param boolean $use_descrip   Use description? If false, uses name.
1338:      *
1339:      * @return string  The part label (non-empty).
1340:      */
1341:     public function getPartName(Horde_Mime_Part $part, $use_descrip = false)
1342:     {
1343:         $name = $use_descrip
1344:             ? $part->getDescription(true)
1345:             : $part->getName(true);
1346: 
1347:         if ($name) {
1348:             return $name;
1349:         }
1350: 
1351:         switch ($ptype = $part->getPrimaryType()) {
1352:         case 'multipart':
1353:             if (($part->getSubType() == 'related') &&
1354:                 ($view_id = $part->getMetaData('viewable_part')) &&
1355:                 ($viewable = $this->getMIMEPart($view_id, array('nocontents' => true)))) {
1356:                 return $this->getPartName($viewable, $use_descrip);
1357:             }
1358:             /* Fall-through. */
1359: 
1360:         case 'application':
1361:         case 'model':
1362:             $ptype = $part->getSubType();
1363:             break;
1364:         }
1365: 
1366:         switch ($ptype) {
1367:         case 'audio':
1368:             return _("Audio");
1369: 
1370:         case 'image':
1371:             return _("Image");
1372: 
1373:         case 'message':
1374:         case '':
1375:         case Horde_Mime_Part::UNKNOWN:
1376:             return _("Message");
1377: 
1378:         case 'multipart':
1379:             return _("Multipart");
1380: 
1381:         case 'text':
1382:             return _("Text");
1383: 
1384:         case 'video':
1385:             return _("Video");
1386: 
1387:         default:
1388:             // Attempt to translate this type, if possible. Odds are that
1389:             // it won't appear in the dictionary though.
1390:             return _(Horde_String::ucfirst($ptype));
1391:         }
1392:     }
1393: 
1394:     /**
1395:      * Get FETCH data from IMAP server for this message.
1396:      *
1397:      * @param Horde_Imap_Client_Fetch_Query $query  Search query.
1398:      *
1399:      * @return Horde_Imap_Client_Data_Fetch  Fetch data for the message.
1400:      */
1401:     protected function _fetchData(Horde_Imap_Client_Fetch_Query $query)
1402:     {
1403:         try {
1404:             $mbox = $this->getMailbox();
1405:             $imp_imap = $mbox->imp_imap;
1406:             return $imp_imap->fetch($mbox, $query, array(
1407:                 'ids' => $imp_imap->getIdsOb($this->getUid())
1408:             ))->first();
1409:         } catch (Horde_Imap_Client_Exception $e) {
1410:             return new Horde_Imap_Client_Data_Fetch();
1411:         }
1412:     }
1413: 
1414:     /**
1415:      * Return the view cache object for this message.
1416:      *
1417:      * @return object  View object.
1418:      */
1419:     public function getViewCache()
1420:     {
1421:         if (!isset($this->_viewcache)) {
1422:             $this->_viewcache = new stdClass;
1423:         }
1424: 
1425:         return $this->_viewcache;
1426:     }
1427: 
1428: }
1429: 
API documentation generated by ApiGen