Overview

Packages

  • IMP
  • None

Classes

  • IMP
  • IMP_Ajax_Application
  • IMP_Ajax_Imple_ContactAutoCompleter
  • IMP_Ajax_Imple_PassphraseDialog
  • IMP_Ajax_Queue
  • IMP_Api
  • IMP_Auth
  • IMP_Block_Newmail
  • IMP_Block_Summary
  • IMP_Compose
  • IMP_Compose_Exception
  • IMP_Compose_Stationery
  • IMP_Contents
  • IMP_Crypt_Pgp
  • IMP_Crypt_Smime
  • IMP_Dimp
  • IMP_Exception
  • IMP_Factory_AuthImap
  • IMP_Factory_Compose
  • IMP_Factory_Contents
  • IMP_Factory_Flags
  • IMP_Factory_Identity
  • IMP_Factory_Imap
  • IMP_Factory_Imaptree
  • IMP_Factory_Mail
  • IMP_Factory_Mailbox
  • IMP_Factory_MailboxList
  • IMP_Factory_MimeViewer
  • IMP_Factory_Pgp
  • IMP_Factory_Quota
  • IMP_Factory_Search
  • IMP_Factory_Sentmail
  • IMP_Factory_Smime
  • 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_Imap
  • IMP_Imap_Acl
  • IMP_Imap_Exception
  • IMP_Imap_PermanentFlags
  • IMP_Imap_Thread
  • IMP_Imap_Tree
  • IMP_Indices
  • IMP_Indices_Form
  • 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_Track
  • IMP_Maillog
  • IMP_Menu_Dimp
  • IMP_Message
  • IMP_Mime_Status
  • 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_Notification_Event_Status
  • IMP_Notification_Handler_Decorator_ImapAlerts
  • IMP_Notification_Handler_Decorator_NewmailNotify
  • IMP_Notification_Listener_AjaxStatus
  • Imp_Prefs_Identity
  • IMP_Prefs_Ui
  • IMP_Quota
  • IMP_Quota_Base
  • IMP_Quota_Command
  • IMP_Quota_Hook
  • IMP_Quota_Imap
  • IMP_Quota_Maildir
  • IMP_Quota_Mdaemon
  • IMP_Quota_Mercury32
  • IMP_Quota_Null
  • IMP_Quota_Sql
  • IMP_Search
  • IMP_Search_Element
  • IMP_Search_Element_Attachment
  • IMP_Search_Element_Autogenerated
  • IMP_Search_Element_Bulk
  • IMP_Search_Element_Contacts
  • IMP_Search_Element_Date
  • 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_Query
  • IMP_Search_Vfolder
  • IMP_Search_Vfolder_Builtin
  • IMP_Search_Vfolder_Vinbox
  • IMP_Search_Vfolder_Vtrash
  • IMP_Sentmail
  • IMP_Sentmail_Base
  • IMP_Sentmail_Null
  • IMP_Sentmail_Sql
  • IMP_Spam
  • IMP_Test
  • IMP_Tree_Flist
  • IMP_Tree_Jquerymobile
  • IMP_Tree_Simplehtml
  • IMP_Ui_Compose
  • IMP_Ui_Editor
  • IMP_Ui_Folder
  • IMP_Ui_Headers
  • IMP_Ui_Imageview
  • IMP_Ui_Mailbox
  • IMP_Ui_Message
  • IMP_Ui_Mimp
  • IMP_Ui_Search
  • IMP_Views_Compose
  • IMP_Views_ListMessages
  • IMP_Views_ShowMessage
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The IMP_Mime_Viewer_Plain class renders out text/plain MIME parts
  4:  * with URLs made into hyperlinks.
  5:  *
  6:  * Copyright 1999-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file COPYING for license information (GPL). If you
  9:  * did not receive this file, see http://www.horde.org/licenses/gpl.
 10:  *
 11:  * @author   Anil Madhavapeddy <anil@recoil.org>
 12:  * @author   Michael Slusarz <slusarz@horde.org>
 13:  * @category Horde
 14:  * @license  http://www.horde.org/licenses/gpl GPL
 15:  * @package  IMP
 16:  */
 17: class IMP_Mime_Viewer_Plain extends Horde_Mime_Viewer_Plain
 18: {
 19:     /**
 20:      * Cached data.
 21:      *
 22:      * @var array
 23:      */
 24:     static protected $_cache = array();
 25: 
 26:     /**
 27:      * Return the full rendered version of the Horde_Mime_Part object.
 28:      *
 29:      * @return array  See parent::render().
 30:      */
 31:     protected function _render()
 32:     {
 33:         $data = $this->_impRender(false);
 34:         $item = reset($data);
 35:         Horde::startBuffer();
 36:         Horde::includeStylesheetFiles();
 37:         $item['data'] = '<html><head>' . Horde::endBuffer() . '</head><body>' . $item['data'] . '</body></html>';
 38:         $data[key($data)] = $item;
 39:         return $data;
 40:     }
 41: 
 42:     /**
 43:      * Return the rendered inline version of the Horde_Mime_Part object.
 44:      *
 45:      * @return array  See parent::render().
 46:      */
 47:     protected function _renderInline()
 48:     {
 49:         return $this->_impRender(true);
 50:     }
 51: 
 52:     /**
 53:      * Render the object.
 54:      *
 55:      * @param boolean $inline  Viewing inline?
 56:      *
 57:      * @return array  See parent::render().
 58:      */
 59:     protected function _impRender($inline)
 60:     {
 61:         global $conf, $prefs;
 62: 
 63:         $mime_id = $this->_mimepart->getMimeId();
 64: 
 65:         if (isset(self::$_cache[$mime_id])) {
 66:             return array($mime_id => null);
 67:         }
 68: 
 69:         // Trim extra whitespace in the text.
 70:         $charset = $this->_mimepart->getCharset();
 71:         $text = trim($this->_mimepart->getContents());
 72:         if ($text == '') {
 73:             return array(
 74:                 $mime_id => array(
 75:                     'data' => '',
 76:                     'type' => 'text/html; charset=' . $charset
 77:                 )
 78:             );
 79:         }
 80: 
 81:         // Convert to the local charset.
 82:         if ($inline) {
 83:             $text = Horde_String::convertCharset($text, $charset, 'UTF-8');
 84:             $charset = $this->getConfigParam('charset');
 85:         }
 86:         $type = 'text/html; charset=' . $charset;
 87: 
 88:         // Check for 'flowed' text data.
 89:         if ($this->_mimepart->getContentTypeParameter('format') == 'flowed') {
 90:             $text = $this->_formatFlowed($text, $this->_mimepart->getContentTypeParameter('delsp'));
 91:         } else {
 92:             /* A "From" located at the beginning of a line in the body text
 93:              * will be escaped with a '>' by the IMAP server.  Remove this
 94:              * escape character or else the line will display as being
 95:              * quoted. Flowed conversion would have already taken care of this
 96:              * for us. */
 97:             $text = preg_replace('/(\n+)> ?From(\s+)/', "$1From$2", $text);
 98:         }
 99: 
100:         $text = IMP::filterText($text);
101: 
102:         /* Done processing if in mimp mode. */
103:         if (IMP::getViewMode() == 'mimp') {
104:             $filters = array(
105:                 'text2html' => array(
106:                     'charset' => $charset,
107:                     'parselevel' => Horde_Text_Filter_Text2html::NOHTML_NOBREAK
108:                 )
109:             );
110: 
111:             $text = $this->_textFilter($text, array_keys($filters), array_values($filters));
112: 
113:             return array(
114:                 $mime_id => array(
115:                     'data' => $text,
116:                     'type' => $type
117:                 )
118:             );
119:         }
120: 
121:         // Build filter stack. Starts with HTML markup and tab expansion.
122:         $filters = array(
123:             'text2html' => array(
124:                 'charset' => $charset,
125:                 'parselevel' => $inline ? Horde_Text_Filter_Text2html::MICRO : Horde_Text_Filter_Text2html::MICRO_LINKURL
126:             ),
127:             'tabs2spaces' => array(),
128:         );
129: 
130:         // Highlight quoted parts of an email.
131:         if ($prefs->getValue('highlight_text')) {
132:             $show = $prefs->getValue('show_quoteblocks');
133:             $hideBlocks = $inline &&
134:                 (($show == 'hidden') ||
135:                  (($show == 'thread') && (basename(Horde::selfUrl()) == 'thread.php')));
136:             if (!$hideBlocks && in_array($show, array('list', 'listthread'))) {
137:                 $header = $this->getConfigParam('imp_contents')->getHeader();
138:                 $imp_ui = new IMP_Ui_Message();
139:                 $list_info = $imp_ui->getListInformation($header);
140:                 $hideBlocks = $list_info['exists'];
141:             }
142: 
143:             if ($inline) {
144:                 $filters['highlightquotes'] = array(
145:                     'hideBlocks' => $hideBlocks,
146:                     'noJS' => (IMP::getViewMode() == 'dimp')
147:                 );
148:             } else {
149:                 $filters['Horde_Text_Filter_Highlightquotes'] = array(
150:                     'hideBlocks' => $hideBlocks
151:                 );
152:             }
153:         }
154: 
155:         // Highlight simple markup of an email.
156:         if ($prefs->getValue('highlight_simple_markup')) {
157:             $filters['simplemarkup'] = array();
158:         }
159: 
160:         // Dim signatures.
161:         if ($prefs->getValue('dim_signature')) {
162:             $filters['dimsignature'] = array();
163:         }
164: 
165:         if ($prefs->getValue('emoticons')) {
166:             $filters['emoticons'] = array('entities' => true);
167:         }
168: 
169:         // Run filters.
170:         $text = $this->_textFilter($text, array_keys($filters), array_values($filters));
171: 
172:         // Wordwrap.
173:         $text = str_replace(array('  ', "\n "), array(' &nbsp;', "\n&nbsp;"), $text);
174:         if (!strncmp($text, ' ', 1)) {
175:             $text = '&nbsp;' . substr($text, 1);
176:         }
177: 
178:         return array(
179:             $mime_id => array(
180:                 'data' => "<div class=\"fixed leftAlign\">\n" . $text . '</div>',
181:                 'type' => $type
182:             )
183:         );
184:     }
185: 
186:     /**
187:      * Does this MIME part possibly contain embedded MIME parts?
188:      *
189:      * @return boolean  True if this driver supports parsing embedded MIME
190:      *                  parts.
191:      */
192:     public function embeddedMimeParts()
193:     {
194:         return (!empty($GLOBALS['conf']['gnupg']['path']) && $GLOBALS['prefs']->getValue('pgp_scan_body')) || $this->getConfigParam('uudecode');
195:     }
196: 
197:     /**
198:      * If this MIME part can contain embedded MIME part(s), and those part(s)
199:      * exist, return a representation of that data.
200:      *
201:      * @return mixed  A Horde_Mime_Part object representing the embedded data.
202:      *                Returns null if no embedded MIME part(s) exist.
203:      */
204:     protected function _getEmbeddedMimeParts()
205:     {
206:         $ret = null;
207: 
208:         if (!empty($GLOBALS['conf']['gnupg']['path']) &&
209:             $GLOBALS['prefs']->getValue('pgp_scan_body')) {
210:             $ret = $this->_parsePGP();
211:         }
212: 
213:         if (is_null($ret) && $this->getConfigParam('uudecode')) {
214:             $ret = $this->_parseUUencode();
215:         }
216: 
217:         return $ret;
218:     }
219: 
220:     /**
221:      * Scan text for armored PGP blocks and, if they exist, convert the part
222:      * to the embedded MIME representation.
223:      *
224:      * @return mixed  See self::_getEmbeddedMimeParts().
225:      */
226:     protected function _parsePGP()
227:     {
228:         /* Avoid infinite loop. */
229:         $parts = $GLOBALS['injector']->getInstance('IMP_Crypt_Pgp')->parsePGPData($this->_mimepart->getContents());
230:         if (empty($parts) ||
231:             ((count($parts) == 1) &&
232:              ($parts[0]['type'] == Horde_Crypt_Pgp::ARMOR_TEXT))) {
233:             return null;
234:         }
235: 
236:         $new_part = new Horde_Mime_Part();
237:         $new_part->setType('multipart/mixed');
238:         $charset = $this->_mimepart->getCharset();
239:         $mime_id = $this->_mimepart->getMimeId();
240: 
241:         while (list(,$val) = each($parts)) {
242:             switch ($val['type']) {
243:             case Horde_Crypt_Pgp::ARMOR_TEXT:
244:                 $part = new Horde_Mime_Part();
245:                 $part->setType('text/plain');
246:                 $part->setCharset($charset);
247:                 $part->setContents(implode("\n", $val['data']));
248:                 $new_part->addPart($part);
249:                 break;
250: 
251:             case Horde_Crypt_Pgp::ARMOR_PUBLIC_KEY:
252:                 $part = new Horde_Mime_Part();
253:                 $part->setType('application/pgp-keys');
254:                 $part->setContents(implode("\n", $val['data']));
255:                 $new_part->addPart($part);
256:                 break;
257: 
258:             case Horde_Crypt_Pgp::ARMOR_MESSAGE:
259:                 $part = new Horde_Mime_Part();
260:                 $part->setType('multipart/encrypted');
261:                 $part->setMetadata(IMP_Mime_Viewer_Pgp::PGP_ARMOR, true);
262:                 // TODO: add micalg parameter
263:                 $part->setContentTypeParameter('protocol', 'application/pgp-encrypted');
264: 
265:                 $part1 = new Horde_Mime_Part();
266:                 $part1->setType('application/pgp-encrypted');
267:                 $part1->setContents("Version: 1\n");
268: 
269:                 $part2 = new Horde_Mime_Part();
270:                 $part2->setType('application/octet-stream');
271:                 $part2->setContents(implode("\n", $val['data']));
272:                 $part2->setDisposition('inline');
273: 
274:                 $part->addPart($part1);
275:                 $part->addPart($part2);
276: 
277:                 $new_part->addPart($part);
278:                 break;
279: 
280:             case Horde_Crypt_Pgp::ARMOR_SIGNED_MESSAGE:
281:                 if (($sig = current($parts)) &&
282:                     ($sig['type'] == Horde_Crypt_Pgp::ARMOR_SIGNATURE)) {
283:                     $part = new Horde_Mime_Part();
284:                     $part->setType('multipart/signed');
285:                     // TODO: add micalg parameter
286:                     $part->setContentTypeParameter('protocol', 'application/pgp-signature');
287: 
288:                     $part1 = new Horde_Mime_Part();
289:                     $part1->setType('text/plain');
290:                     $part1->setCharset($charset);
291: 
292:                     $part1_data = implode("\n", $val['data']);
293:                     $part1->setContents(substr($part1_data, strpos($part1_data, "\n\n") + 2));
294: 
295:                     $part2 = new Horde_Mime_Part();
296:                     $part2->setType('application/pgp-signature');
297:                     $part2->setContents(implode("\n", $val['data']) . "\n" . implode("\n", $sig['data']));
298:                     // A true pgp-signature part would only contain the
299:                     // detached signature. However, we need to carry around
300:                     // the entire armored text to verify correctly. Use a
301:                     // IMP-specific content-type parameter to clue the PGP
302:                     // driver into this fact.
303:                     $part2->setMetadata(IMP_Mime_Viewer_Pgp::PGP_SIG, true);
304:                     $part2->setMetadata(IMP_Mime_Viewer_Pgp::PGP_CHARSET, $charset);
305: 
306:                     $part->addPart($part1);
307:                     $part->addPart($part2);
308:                     $new_part->addPart($part);
309: 
310:                     next($parts);
311:                 }
312:             }
313:         }
314: 
315:         self::$_cache[$mime_id] = true;
316: 
317:         return $new_part;
318:     }
319: 
320:     /**
321:      * Scan text for UUencode data an, if it exists, convert the part to the
322:      * embedded MIME representation.
323:      *
324:      * @return mixed  See self::_getEmbeddedMimeParts().
325:      */
326:     protected function _parseUUencode()
327:     {
328:         $text = Horde_String::convertCharset($this->_mimepart->getContents(), $this->_mimepart->getCharset(), 'UTF-8');
329: 
330:         $files = Horde_Mime::uudecode($text);
331:         if (empty($files)) {
332:             return null;
333:         }
334: 
335:         $new_part = new Horde_Mime_Part();
336:         $new_part->setType('multipart/mixed');
337:         $mime_id = $this->_mimepart->getMimeId();
338: 
339:         $text_part = new Horde_Mime_Part();
340:         $text_part->setType('text/plain');
341:         $text_part->setCharset($this->getConfigParam('charset'));
342:         $text_part->setContents(preg_replace("/begin [0-7]{3} .+\r?\n.+\r?\nend/Us", "\n", $text));
343:         $new_part->addPart($text_part);
344: 
345:         reset($files);
346:         while (list(,$file) = each($files)) {
347:             $uupart = new Horde_Mime_Part();
348:             $uupart->setType('application/octet-stream');
349:             $uupart->setContents($file['data']);
350:             $uupart->setName(strip_tags($file['name']));
351:             $new_part->addPart($uupart);
352:         }
353: 
354:         return $new_part;
355:     }
356: 
357:     /**
358:      * Output to use if text size is over the limit.
359:      * See IMP_Contents::renderMIMEPart().
360:      *
361:      * @return string  The text to display inline.
362:      */
363:     public function overLimitText()
364:     {
365:         $stream = $this->_mimepart->getContents(array('stream' => true));
366:         rewind($stream);
367: 
368:         // Escape text
369:         $filters = array(
370:             'text2html' => array(
371:                 'parselevel' => Horde_Text_Filter_Text2html::MICRO
372:             ),
373:             'tabs2spaces' => array(),
374:         );
375: 
376:         return '<div class="fixed">' .
377:             $this->_textFilter(Horde_String::convertCharset(fread($stream, 1024), $this->_mimepart->getCharset(), 'UTF-8'), array_keys($filters), array_values($filters)) .
378:             ' [...]</div>';
379:     }
380: 
381: }
382: 
API documentation generated by ApiGen