PageRenderTime 51ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/imp-h3-4.3.10/lib/MIME/Headers.php

#
PHP | 457 lines | 247 code | 59 blank | 151 comment | 56 complexity | 456e6fed93c5ae05bd00cda3e7e19f1e MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /**
  3. * @package Horde_MIME
  4. */
  5. require_once 'Horde/MIME/Headers.php';
  6. require_once IMP_BASE . '/lib/version.php';
  7. /**
  8. * The description of the IMP program to use in the 'User-Agent:' header.
  9. */
  10. define('IMP_AGENT_HEADER', 'Internet Messaging Program (IMP) ' . IMP_VERSION);
  11. /**
  12. * The IMP_Headers:: class contains all functions related to handling the
  13. * headers of mail messages in IMP.
  14. *
  15. * $Horde: imp/lib/MIME/Headers.php,v 1.92.2.41 2009/11/19 19:04:35 slusarz Exp $
  16. *
  17. * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
  18. *
  19. * See the enclosed file COPYING for license information (GPL). If you
  20. * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
  21. *
  22. * @author Michael Slusarz <slusarz@horde.org>
  23. * @package Horde_MIME
  24. */
  25. class IMP_Headers extends MIME_Headers {
  26. /**
  27. * The User-Agent string to use.
  28. *
  29. * @var string
  30. */
  31. var $_agent = IMP_AGENT_HEADER;
  32. /**
  33. * The header object cache.
  34. *
  35. * @var array
  36. */
  37. var $_obCache = array();
  38. /**
  39. * Returns a reference to a currently open IMAP stream.
  40. *
  41. * @see MIME_Headers::_getStream()
  42. */
  43. function _getStream()
  44. {
  45. $imp_imap = &IMP_IMAP::singleton();
  46. return $imp_imap->stream();
  47. }
  48. /**
  49. * Parses all of the available mailing list headers.
  50. */
  51. function parseAllListHeaders()
  52. {
  53. foreach ($this->listHeaders() as $val => $str) {
  54. $this->parseListHeaders($val);
  55. }
  56. }
  57. /**
  58. * Parses the information in the mailing list headers.
  59. *
  60. * @param string $header The header to process.
  61. * @param boolean $raw Should the raw email be returned instead of
  62. * setting the header value?
  63. *
  64. * @return string The header value (if $raw == true).
  65. */
  66. function parseListHeaders($header, $raw = false)
  67. {
  68. if (!($data = $this->getValue($header))) {
  69. return;
  70. }
  71. $output = '';
  72. require_once 'Horde/Text.php';
  73. /* Split the incoming data by the ',' character. */
  74. foreach (preg_split("/,/", $data) as $entry) {
  75. /* Get the data inside of the brackets. If there is no brackets,
  76. * then return the raw text. */
  77. if (!preg_match("/\<([^\>]+)\>/", $entry, $matches)) {
  78. return trim($entry);
  79. }
  80. /* Remove all whitespace from between brackets (RFC 2369 [2]). */
  81. $match = preg_replace("/\s+/", '', $matches[1]);
  82. /* Determine if there are any comments. */
  83. preg_match("/(\(.+\))/", $entry, $comments);
  84. /* RFC 2369 [2] states that we should only show the *FIRST* URL
  85. * that appears in a header that we can adequately handle. */
  86. if (stristr($match, 'mailto:') !== false) {
  87. $match = substr($match, strpos($match, ':') + 1);
  88. if ($raw) {
  89. return $match;
  90. }
  91. $output = Horde::link(IMP::composeLink($match)) . $match . '</a>';
  92. if (!empty($comments[1])) {
  93. $output .= '&nbsp;' . $comments[1];
  94. }
  95. break;
  96. } elseif (!$raw) {
  97. require_once 'Horde/Text/Filter.php';
  98. if ($url = Text_Filter::filter($match, 'linkurls', array('callback' => 'Horde::externalUrl'))) {
  99. $output = $url;
  100. if (!empty($comments[1])) {
  101. $output .= '&nbsp;' . $comments[1];
  102. }
  103. break;
  104. } else {
  105. /* Use this entry unless we can find a better one. */
  106. $output = $match;
  107. }
  108. }
  109. }
  110. $this->setValue($header, $output);
  111. }
  112. /**
  113. * Adds any site-specific headers defined in config/header.php to the
  114. * internal header array.
  115. */
  116. function addSiteHeaders()
  117. {
  118. static $_header;
  119. /* Add the 'User-Agent' header. */
  120. $this->addAgentHeader();
  121. /* Tack on any site-specific headers. */
  122. if (is_callable(array('Horde', 'loadConfiguration'))) {
  123. $result = Horde::loadConfiguration('header.php', array('_header'));
  124. if (!is_a($result, 'PEAR_Error')) {
  125. extract($result);
  126. }
  127. } else {
  128. require IMP_BASE . '/config/header.php';
  129. $result = true;
  130. }
  131. if (!is_a($result, 'PEAR_Error')) {
  132. foreach ($_header as $key => $val) {
  133. $this->addHeader(trim($key), trim($val));
  134. }
  135. }
  136. }
  137. /**
  138. * Builds a string containing a list of addresses.
  139. *
  140. * @param string $field The address field to parse.
  141. * @param integer $addURL The self URL.
  142. * @param boolean $set Set the associated header with the return
  143. * string?
  144. * @param boolean $link Link each address to the compose screen?
  145. *
  146. * @return string String containing the formatted address list.
  147. */
  148. function buildAddressLinks($field, $addURL, $set = false, $link = true)
  149. {
  150. global $prefs, $registry;
  151. $add_link = null;
  152. /* Make sure this is a valid object address field. */
  153. $array = $this->getOb($field);
  154. if (empty($array) || !is_array($array)) {
  155. return null;
  156. }
  157. /* Set up the add address icon link if contact manager is
  158. * available. */
  159. if ($link && $prefs->getValue('add_source')) {
  160. $add_link = $registry->link('contacts/add', array('source' => $prefs->getValue('add_source')));
  161. if (is_a($add_link, 'PEAR_Error')) {
  162. if ($registry->hasMethod('contacts/import')) {
  163. $add_link = Util::addParameter($addURL, 'actionID', 'add_address');
  164. } else {
  165. $add_link = null;
  166. }
  167. }
  168. }
  169. $addr_array = array();
  170. foreach ($this->getAddressesFromObject($array) as $ob) {
  171. if (isset($ob->groupname)) {
  172. $group_array = array();
  173. foreach ($ob->addresses as $ad) {
  174. if (empty($ad->address) || empty($ad->inner)) {
  175. continue;
  176. }
  177. $ret = htmlspecialchars($ad->display);
  178. /* If this is an incomplete e-mail address, don't link to
  179. * anything. */
  180. if (stristr($ad->host, 'UNKNOWN') === false) {
  181. if ($link) {
  182. $ret = Horde::link(IMP::composeLink(array('to' => $ad->address)), sprintf(_("New Message to %s"), $ad->inner)) . htmlspecialchars($ad->display) . '</a>';
  183. }
  184. /* Append the add address icon to every address if contact
  185. * manager is available. */
  186. if ($add_link) {
  187. $curr_link = Util::addParameter($add_link, array('name' => $ad->personal, 'address' => $ad->inner));
  188. $ret .= Horde::link($curr_link, sprintf(_("Add %s to my Address Book"), $ad->inner)) .
  189. Horde::img('addressbook_add.png', sprintf(_("Add %s to my Address Book"), $ad->inner)) . '</a>';
  190. }
  191. }
  192. $group_array[] = $ret;
  193. }
  194. $addr_array[] = htmlspecialchars($ob->groupname) . ':' . (count($group_array) ? ' ' . implode(', ', $group_array) : '');
  195. } elseif (!empty($ob->address) && !empty($ob->inner)) {
  196. $ret = htmlspecialchars($ob->display);
  197. /* If this is an incomplete e-mail address, don't link to
  198. * anything. */
  199. if (stristr($ob->host, 'UNKNOWN') === false) {
  200. if ($link) {
  201. $ret = Horde::link(IMP::composeLink(array('to' => $ob->address)), sprintf(_("New Message to %s"), $ob->inner)) . htmlspecialchars($ob->display) . '</a>';
  202. }
  203. /* Append the add address icon to every address if contact
  204. * manager is available. */
  205. if ($add_link) {
  206. $curr_link = Util::addParameter($add_link, array('name' => $ob->personal, 'address' => $ob->inner));
  207. $ret .= Horde::link($curr_link, sprintf(_("Add %s to my Address Book"), $ob->inner)) .
  208. Horde::img('addressbook_add.png', sprintf(_("Add %s to my Address Book"), $ob->inner)) . '</a>';
  209. }
  210. }
  211. $addr_array[] = $ret;
  212. }
  213. }
  214. /* If left with an empty address list ($ret), inform the user that the
  215. * recipient list is purposely "undisclosed". */
  216. if (empty($addr_array)) {
  217. $ret = _("Undisclosed Recipients");
  218. } else {
  219. /* Build the address line. */
  220. $addr_count = count($addr_array);
  221. $ret = '<span class="nowrap">' . implode(',</span> <span class="nowrap">', $addr_array) . '</span>';
  222. if ($link && $addr_count > 15) {
  223. Horde::addScriptFile('prototype.js', 'imp', true);
  224. $ret = '<span>' .
  225. '<span onclick="[ this, this.next(), this.next(1) ].invoke(\'toggle\')" class="widget largeaddrlist">' . sprintf(_("[Show Addresses - %d recipients]"), $addr_count) . '</span>' .
  226. '<span onclick="[ this, this.previous(), this.next() ].invoke(\'toggle\')" class="widget largeaddrlist" style="display:none">' . _("[Hide Addresses]") . '</span>' .
  227. '<span style="display:none">' .
  228. $ret . '</span></span>';
  229. }
  230. }
  231. /* Set the header value, if requested. */
  232. if (!empty($set)) {
  233. $this->setValue($field, $ret);
  234. }
  235. return $ret;
  236. }
  237. /**
  238. * Return the list of addresses for a header object.
  239. *
  240. * @TODO Merge back to Horde_Mime_Headers with the changes to support
  241. * groups.
  242. *
  243. * @param array $obs An array of header objects (See imap_headerinfo()
  244. * for the object structure).
  245. *
  246. * @return array An array of objects.
  247. * <pre>
  248. * Object elements:
  249. * 'address' - Full address
  250. * 'display' - A displayable version of the address
  251. * 'host' - Host name
  252. * 'inner' - Trimmed, bare address
  253. * 'personal' - Personal string
  254. * </pre>
  255. */
  256. function getAddressesFromObject($obs)
  257. {
  258. $retArray = array();
  259. if (!is_array($obs) || empty($obs)) {
  260. return $retArray;
  261. }
  262. foreach ($obs as $ob) {
  263. if (isset($ob->groupname)) {
  264. $newOb = new stdClass;
  265. $newOb->addresses = $this->getAddressesFromObject($ob->addresses);
  266. $newOb->groupname = $ob->groupname;
  267. $retArray[] = $newOb;
  268. continue;
  269. }
  270. /* Ensure we're working with initialized values. */
  271. $ob->personal = (isset($ob->personal)) ? stripslashes(trim(MIME::decode($ob->personal), '"')) : '';
  272. if (isset($ob->mailbox)) {
  273. /* Don't process invalid addresses. */
  274. if (strpos($ob->mailbox, 'UNEXPECTED_DATA_AFTER_ADDRESS') !== false ||
  275. strpos($ob->mailbox, 'INVALID_ADDRESS') !== false) {
  276. continue;
  277. }
  278. } else {
  279. $ob->mailbox = '';
  280. }
  281. if (!isset($ob->host)) {
  282. $ob->host = '';
  283. }
  284. $inner = MIME::trimEmailAddress(MIME::rfc822WriteAddress($ob->mailbox, $ob->host, ''));
  285. /* Generate the new object. */
  286. $newOb = new stdClass;
  287. $newOb->address = MIME::addrObject2String($ob, array('undisclosed-recipients@', 'Undisclosed recipients@'));
  288. $newOb->display = (empty($ob->personal) ? '' : $ob->personal . ' <') . $inner . (empty($ob->personal) ? '' : '>');
  289. $newOb->host = $ob->host;
  290. $newOb->inner = $inner;
  291. $newOb->personal = $ob->personal;
  292. $retArray[] = $newOb;
  293. }
  294. return $retArray;
  295. }
  296. /**
  297. * Adds the local time string to the date header.
  298. *
  299. * @param string $date The date string.
  300. *
  301. * @return string The date string with the local time added on.
  302. */
  303. function addLocalTime($date)
  304. {
  305. if (empty($date)) {
  306. $ltime = false;
  307. } else {
  308. $date = preg_replace('/\s+\(\w+\)$/', '', $date);
  309. $ltime = strtotime($date);
  310. }
  311. if ($ltime !== false && $ltime !== -1) {
  312. $date_str = strftime($GLOBALS['prefs']->getValue('date_format'), $ltime);
  313. $time_str = strftime($GLOBALS['prefs']->getValue('time_format'), $ltime);
  314. $tz = strftime('%Z');
  315. if ((date('Y') != @date('Y', $ltime)) ||
  316. (date('M') != @date('M', $ltime)) ||
  317. (date('d') != @date('d', $ltime))) {
  318. /* Not today, use the date. */
  319. $date .= sprintf(' <small>[%s %s %s]</small>', $date_str, $time_str, $tz);
  320. } else {
  321. /* Else, it's today, use the time only. */
  322. $date .= sprintf(' <small>[%s %s]</small>', $time_str, $tz);
  323. }
  324. }
  325. return $date;
  326. }
  327. /**
  328. * Returns a header from the header object.
  329. *
  330. * @todo Move to framework for Horde 4.0.
  331. *
  332. * @param string $field The header to return as an object.
  333. *
  334. * @return mixed The field requested.
  335. */
  336. function getOb($field)
  337. {
  338. if (!isset($this->_obCache[$field])) {
  339. $ob = IMP::parseAddressList($this->getValue($field));
  340. if (is_a($ob, 'PEAR_Error')) {
  341. $ob = array();
  342. }
  343. $this->_obCache[$field] = $ob;
  344. }
  345. return $this->_obCache[$field];
  346. }
  347. /**
  348. * Explicitly sets the User-Agent string.
  349. *
  350. * @todo Move to framework for Horde 4.0.
  351. * @since IMP 4.2
  352. *
  353. * @param string $useragent The User-Agent string to use.
  354. */
  355. function setUserAgent($useragent)
  356. {
  357. $this->_agent = $useragent;
  358. }
  359. /**
  360. * Determines the X-Priority of the message based on the headers.
  361. *
  362. * @since IMP 4.2
  363. *
  364. * @return string 'high', 'low', or 'normal'.
  365. */
  366. function getXpriority()
  367. {
  368. if (($priority = $this->getValue('x-priority')) &&
  369. preg_match('/\s*(\d+)\s*/', $priority, $matches)) {
  370. if (in_array($matches[1], array(1, 2))) {
  371. return 'high';
  372. } elseif (in_array($matches[1], array(4, 5))) {
  373. return 'low';
  374. }
  375. }
  376. return 'normal';
  377. }
  378. /**
  379. * Returns e-mail information for a mailing list.
  380. *
  381. * @since IMP 4.2
  382. *
  383. * @return array An array with 2 elements: 'exists' and 'reply_list'.
  384. */
  385. function getListInformation()
  386. {
  387. $ret = array('exists' => false, 'reply_list' => null);
  388. if ($this->listHeadersExist()) {
  389. $ret['exists'] = true;
  390. /* See if the List-Post header provides an e-mail address for the
  391. * list. */
  392. if ($this->getValue('list-post')) {
  393. $ret['reply_list'] = $this->parseListHeaders('list-post', true);
  394. }
  395. }
  396. return $ret;
  397. }
  398. }