1<?php
2/**
3 * Copyright 2012-2017 Horde LLC (http://www.horde.org/)
4 *
5 * See the enclosed file LICENSE for license information (BSD). If you
6 * did not receive this file, see http://www.horde.org/licenses/bsd.
7 *
8 * @category  Horde
9 * @copyright 2012-2017 Horde LLC
10 * @license   http://www.horde.org/licenses/bsd New BSD License
11 * @package   Mail
12 */
13
14/**
15 * Class to parse identification headers (RFC 5322 [3.6.4]): Message-ID,
16 * References, and In-Reply-To.
17 *
18 * @author    Michael Slusarz <slusarz@horde.org>
19 * @category  Horde
20 * @copyright 2012-2017 Horde LLC
21 * @license   http://www.horde.org/licenses/bsd New BSD License
22 * @package   Mail
23 * @since     2.2.0
24 */
25class Horde_Mail_Rfc822_Identification extends Horde_Mail_Rfc822
26{
27    /**
28     * List of message IDs parsed.
29     *
30     * @var array
31     */
32    public $ids = array();
33
34    /**
35     * Constructor.
36     *
37     * @param string $value  Identification field value to parse.
38     */
39    public function __construct($value = null)
40    {
41        $this->parse($value);
42    }
43
44    /**
45     * Parse an identification header.
46     *
47     * @param string $value  Identification field value to parse.
48     */
49    public function parse($value)
50    {
51        if (!strlen($value)) {
52            return;
53        }
54
55        $this->_data = $value;
56        $this->_datalen = strlen($value);
57        $this->_params['validate'] = true;
58        $this->_ptr = 0;
59
60        $this->_rfc822SkipLwsp();
61
62        while ($this->_curr() !== false) {
63            try {
64                $this->ids[] = $this->_parseMessageId();
65            } catch (Horde_Mail_Exception $e) {
66                break;
67            }
68
69            // Some mailers incorrectly insert commas between reference items
70            if ($this->_curr() == ',') {
71                $this->_rfc822SkipLwsp(true);
72            }
73        }
74    }
75
76    /**
77     * Message IDs are defined in RFC 5322 [3.6.4]. In short, they can only
78     * contain one '@' character. However, Outlook can produce invalid
79     * Message-IDs containing multiple '@' characters, which will fail the
80     * strict RFC checks.
81     *
82     * Since we don't care about the structure/details of the Message-ID,
83     * just do a basic parse that considers all characters inside of angled
84     * brackets to be valid.
85     *
86     * @return string  A full Message-ID (enclosed in angled brackets).
87     *
88     * @throws Horde_Mail_Exception
89     */
90    private function _parseMessageId()
91    {
92        $bracket = ($this->_curr(true) === '<');
93        $str = '<';
94
95        while (($chr = $this->_curr(true)) !== false) {
96            if ($bracket) {
97                $str .= $chr;
98                if ($chr == '>') {
99                    $this->_rfc822SkipLwsp();
100                    return $str;
101                }
102            } else {
103                if (!strcspn($chr, " \n\r\t,")) {
104                    $this->_rfc822SkipLwsp();
105                    return $str;
106                }
107                $str .= $chr;
108            }
109        }
110
111        if (!$bracket) {
112            return $str;
113        }
114
115        throw new Horde_Mail_Exception('Invalid Message-ID.');
116    }
117
118}
119