PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/imp/lib/Mime/Viewer/Plain.php

https://github.com/imr/horde
PHP | 316 lines | 194 code | 37 blank | 85 comment | 30 complexity | 01e06db5b41eb337247be218c62313a1 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright 1999-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 1999-2014 Horde LLC
  10. * @license http://www.horde.org/licenses/gpl GPL
  11. * @package IMP
  12. */
  13. /**
  14. * Renderer for text/plain MIME parts with URLs made into hyperlinks.
  15. *
  16. * @author Anil Madhavapeddy <anil@recoil.org>
  17. * @author Michael Slusarz <slusarz@horde.org>
  18. * @category Horde
  19. * @copyright 1999-2014 Horde LLC
  20. * @license http://www.horde.org/licenses/gpl GPL
  21. * @package IMP
  22. */
  23. class IMP_Mime_Viewer_Plain extends Horde_Mime_Viewer_Plain
  24. {
  25. /**
  26. * Return the full rendered version of the Horde_Mime_Part object.
  27. *
  28. * @return array See parent::render().
  29. */
  30. protected function _render()
  31. {
  32. $data = $this->_impRender(false);
  33. $item = reset($data);
  34. Horde::startBuffer();
  35. $GLOBALS['page_output']->includeStylesheetFiles();
  36. $item['data'] = '<html><head>' . Horde::endBuffer() . '</head><body>' . $item['data'] . '</body></html>';
  37. $data[key($data)] = $item;
  38. return $data;
  39. }
  40. /**
  41. * Return the rendered inline version of the Horde_Mime_Part object.
  42. *
  43. * @return array See parent::render().
  44. */
  45. protected function _renderInline()
  46. {
  47. return $this->_impRender(true);
  48. }
  49. /**
  50. * Render the object.
  51. *
  52. * @param boolean $inline Viewing inline?
  53. *
  54. * @return array See parent::render().
  55. */
  56. protected function _impRender($inline)
  57. {
  58. global $injector, $prefs, $registry;
  59. $cache = $this->getConfigParam('imp_contents')->getViewCache();
  60. $mime_id = $this->_mimepart->getMimeId();
  61. if (isset($cache->plain[$mime_id])) {
  62. return array($mime_id => null);
  63. }
  64. // Trim extra whitespace in the text.
  65. $charset = $this->_mimepart->getCharset();
  66. $text = trim($this->_mimepart->getContents());
  67. if ($text == '') {
  68. return array(
  69. $mime_id => array(
  70. 'data' => '',
  71. 'type' => 'text/html; charset=' . $charset
  72. )
  73. );
  74. }
  75. // Convert to the local charset.
  76. if ($inline) {
  77. $text = Horde_String::convertCharset($text, $charset, 'UTF-8');
  78. $charset = $this->getConfigParam('charset');
  79. }
  80. $type = 'text/html; charset=' . $charset;
  81. // Check for 'flowed' text data.
  82. if ($this->_mimepart->getContentTypeParameter('format') == 'flowed') {
  83. $text = $this->_formatFlowed($text, $this->_mimepart->getContentTypeParameter('delsp'));
  84. } else {
  85. /* A "From" located at the beginning of a line in the body text
  86. * will be escaped with a '>' by the IMAP server. Remove this
  87. * escape character or else the line will display as being
  88. * quoted. Flowed conversion would have already taken care of this
  89. * for us. */
  90. $text = preg_replace('/(\n+)> ?From(\s+)/', "$1From$2", $text);
  91. }
  92. $text = IMP::filterText($text);
  93. /* Done processing if in minimal mode. */
  94. if ($registry->getView() == Horde_Registry::VIEW_MINIMAL) {
  95. $filters = array(
  96. 'text2html' => array(
  97. 'charset' => $charset,
  98. 'parselevel' => Horde_Text_Filter_Text2html::NOHTML_NOBREAK
  99. )
  100. );
  101. $text = $this->_textFilter($text, array_keys($filters), array_values($filters));
  102. return array(
  103. $mime_id => array(
  104. 'data' => $text,
  105. 'type' => $type
  106. )
  107. );
  108. }
  109. // Build filter stack. Starts with HTML markup and tab expansion.
  110. $filters = array(
  111. 'text2html' => array(
  112. 'charset' => $charset,
  113. 'parselevel' => $inline ? Horde_Text_Filter_Text2html::MICRO : Horde_Text_Filter_Text2html::MICRO_LINKURL
  114. ),
  115. 'tabs2spaces' => array(),
  116. );
  117. // Highlight quoted parts of an email.
  118. if ($prefs->getValue('highlight_text')) {
  119. $hideBlocks = $js_blocks = false;
  120. if ($registry->getView() !== $registry::VIEW_SMARTMOBILE) {
  121. $js_blocks = $inline;
  122. if ($inline) {
  123. $show = $prefs->getValue('show_quoteblocks');
  124. $hideBlocks = (($show == 'hidden') ||
  125. (($show == 'thread') && ($injector->getInstance('Horde_Variables')->page == 'thread')));
  126. if (!$hideBlocks &&
  127. in_array($show, array('list', 'listthread'))) {
  128. $header = $this->getConfigParam('imp_contents')->getHeader();
  129. $list_info = $injector->getInstance('IMP_Message_Ui')->getListInformation($header);
  130. $hideBlocks = $list_info['exists'];
  131. }
  132. }
  133. }
  134. if ($js_blocks) {
  135. $filters['highlightquotes'] = array(
  136. 'hideBlocks' => $hideBlocks,
  137. 'noJS' => ($registry->getView() == Horde_Registry::VIEW_DYNAMIC)
  138. );
  139. } else {
  140. $filters['Horde_Text_Filter_Highlightquotes'] = array(
  141. 'hideBlocks' => $hideBlocks
  142. );
  143. }
  144. }
  145. // Highlight simple markup of an email.
  146. if ($prefs->getValue('highlight_simple_markup')) {
  147. $filters['simplemarkup'] = array();
  148. }
  149. // Dim signatures.
  150. if ($prefs->getValue('dim_signature')) {
  151. $filters['dimsignature'] = array();
  152. }
  153. if ($prefs->getValue('emoticons')) {
  154. $filters['emoticons'] = array('entities' => true);
  155. }
  156. // Run filters.
  157. $status = array();
  158. $text = $this->_textFilter($text, array_keys($filters), array_values($filters));
  159. if (strlen($text)) {
  160. // Wordwrap.
  161. $text = str_replace(array(' ', "\n "), array(' &nbsp;', "\n&nbsp;"), $text);
  162. if (!strncmp($text, ' ', 1)) {
  163. $text = '&nbsp;' . substr($text, 1);
  164. }
  165. } else {
  166. $error = new IMP_Mime_Status(array(
  167. _("Cannot display message text."),
  168. _("The message part may contain incorrect character set information preventing correct display.")
  169. ));
  170. $error->action(IMP_Mime_Status::ERROR);
  171. $status[] = $error;
  172. }
  173. return array(
  174. $mime_id => array(
  175. 'data' => "<div class=\"fixed leftAlign\">\n" . $text . '</div>',
  176. 'status' => $status,
  177. 'type' => $type
  178. )
  179. );
  180. }
  181. /**
  182. * Does this MIME part possibly contain embedded MIME parts?
  183. *
  184. * @return boolean True if this driver supports parsing embedded MIME
  185. * parts.
  186. */
  187. public function embeddedMimeParts()
  188. {
  189. return ($this->getConfigParam('pgp_inline') ||
  190. $this->getConfigParam('uudecode'));
  191. }
  192. /**
  193. * If this MIME part can contain embedded MIME part(s), and those part(s)
  194. * exist, return a representation of that data.
  195. *
  196. * @return mixed A Horde_Mime_Part object representing the embedded data.
  197. * Returns null if no embedded MIME part(s) exist.
  198. */
  199. protected function _getEmbeddedMimeParts()
  200. {
  201. $ret = $this->getConfigParam('pgp_inline')
  202. ? $this->_parsePGP()
  203. : null;
  204. return (is_null($ret) && $this->getConfigParam('uudecode'))
  205. ? $this->_parseUUencode()
  206. : $ret;
  207. }
  208. /**
  209. * Scan text for inline, armored PGP blocks and, if they exist, convert
  210. * the part to the embedded MIME representation.
  211. *
  212. * @return mixed See self::_getEmbeddedMimeParts().
  213. */
  214. protected function _parsePGP()
  215. {
  216. $part = $GLOBALS['injector']->getInstance('Horde_Crypt_Pgp_Parse')->parseToPart(
  217. new Horde_Stream_Existing(array(
  218. 'stream' => $this->_mimepart->getContents(array('stream' => true))
  219. )),
  220. $this->_mimepart->getCharset()
  221. );
  222. if (!is_null($part)) {
  223. $cache = $this->getConfigParam('imp_contents')->getViewCache();
  224. $cache->plain[$this->_mimepart->getMimeId()] = true;
  225. }
  226. return $part;
  227. }
  228. /**
  229. * Scan text for UUencode data an, if it exists, convert the part to the
  230. * embedded MIME representation.
  231. *
  232. * @return mixed See self::_getEmbeddedMimeParts().
  233. */
  234. protected function _parseUUencode()
  235. {
  236. $text = Horde_String::convertCharset($this->_mimepart->getContents(), $this->_mimepart->getCharset(), 'UTF-8');
  237. $files = Horde_Mime::uudecode($text);
  238. if (empty($files)) {
  239. return null;
  240. }
  241. $new_part = new Horde_Mime_Part();
  242. $new_part->setType('multipart/mixed');
  243. $text_part = new Horde_Mime_Part();
  244. $text_part->setType('text/plain');
  245. $text_part->setCharset($this->getConfigParam('charset'));
  246. $text_part->setContents(preg_replace("/begin [0-7]{3} .+\r?\n.+\r?\nend/Us", "\n", $text));
  247. $new_part->addPart($text_part);
  248. reset($files);
  249. while (list(,$file) = each($files)) {
  250. $uupart = new Horde_Mime_Part();
  251. $uupart->setType('application/octet-stream');
  252. $uupart->setContents($file['data']);
  253. $uupart->setName(strip_tags($file['name']));
  254. $new_part->addPart($uupart);
  255. }
  256. return $new_part;
  257. }
  258. /**
  259. * Output to use if text size is over the limit.
  260. * See IMP_Contents::renderMIMEPart().
  261. *
  262. * @return string The text to display inline.
  263. */
  264. public function overLimitText()
  265. {
  266. $stream = $this->_mimepart->getContents(array('stream' => true));
  267. rewind($stream);
  268. // Escape text
  269. $filters = array(
  270. 'text2html' => array(
  271. 'parselevel' => Horde_Text_Filter_Text2html::MICRO
  272. ),
  273. 'tabs2spaces' => array(),
  274. );
  275. return '<div class="fixed">' .
  276. $this->_textFilter(Horde_String::convertCharset(fread($stream, 1024), $this->_mimepart->getCharset(), 'UTF-8'), array_keys($filters), array_values($filters)) .
  277. ' [...]</div>';
  278. }
  279. }