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