1<?php
2/**
3 * Copyright 2011-2017 Horde LLC (http://www.horde.org/)
4 *
5 * See the enclosed file LICENSE for license information (LGPL). If you
6 * did not receive this file, see http://www.horde.org/licenses/lgpl21.
7 *
8 * @category  Horde
9 * @copyright 2011-2017 Horde LLC
10 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
11 * @package   Imap_Client
12 */
13
14/**
15 * Available ACL rights for a mailbox/identifier (see RFC 2086/4314).
16 *
17 * @author    Michael Slusarz <slusarz@horde.org>
18 * @category  Horde
19 * @copyright 2011-2017 Horde LLC
20 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
21 * @package   Imap_Client
22 */
23class Horde_Imap_Client_Data_AclRights extends Horde_Imap_Client_Data_AclCommon implements ArrayAccess, Iterator, Serializable
24{
25    /**
26     * ACL optional rights.
27     *
28     * @var array
29     */
30    protected $_optional = array();
31
32    /**
33     * ACL required rights.
34     *
35     * @var array
36     */
37    protected $_required = array();
38
39    /**
40     * Constructor.
41     *
42     * @param array $required  The required rights (see RFC 4314 [2.1]).
43     * @param array $optional  The optional rights (see RFC 4314 [2.1]).
44     */
45    public function __construct(array $required = array(),
46                                array $optional = array())
47    {
48        $this->_required = $required;
49
50        foreach ($optional as $val) {
51            foreach (str_split($val) as $right) {
52                $this->_optional[$right] = $val;
53            }
54        }
55
56        $this->_normalize();
57    }
58
59    /**
60     * String representation of the ACL.
61     *
62     * @return string  String representation (RFC 4314 compliant).
63     *
64     */
65    public function __toString()
66    {
67        return implode('', array_keys(array_flip(array_merge(array_values($this->_required), array_keys($this->_optional)))));
68    }
69
70    /**
71     * Normalize virtual rights (see RFC 4314 [2.1.1]).
72     */
73    protected function _normalize()
74    {
75        /* Clients conforming to RFC 4314 MUST ignore the virtual ACL_CREATE
76         * and ACL_DELETE rights. See RFC 4314 [2.1]. However, we still need
77         * to handle these rights when dealing with RFC 2086 servers since
78         * we are abstracting out use of ACL_CREATE/ACL_DELETE to their
79         * component RFC 4314 rights. */
80        foreach ($this->_virtual as $key => $val) {
81            if (isset($this->_optional[$key])) {
82                unset($this->_optional[$key]);
83                foreach ($val as $val2) {
84                    $this->_optional[$val2] = implode('', $val);
85                }
86            } elseif (($pos = array_search($key, $this->_required)) !== false) {
87                unset($this->_required[$pos]);
88                $this->_required = array_unique(array_merge($this->_required, $val));
89            }
90        }
91    }
92
93    /* ArrayAccess methods. */
94
95    /**
96     */
97    public function offsetExists($offset)
98    {
99        return (bool)$this[$offset];
100    }
101
102    /**
103     */
104    public function offsetGet($offset)
105    {
106        if (isset($this->_optional[$offset])) {
107            return $this->_optional[$offset];
108        }
109
110        $pos = array_search($offset, $this->_required);
111
112        return ($pos === false)
113            ? null
114            : $this->_required[$pos];
115    }
116
117    /**
118     */
119    public function offsetSet($offset, $value)
120    {
121        $this->_optional[$offset] = $value;
122        $this->_normalize();
123    }
124
125    /**
126     */
127    public function offsetUnset($offset)
128    {
129        unset($this->_optional[$offset]);
130        $this->_required = array_values(array_diff($this->_required, array($offset)));
131
132        if (isset($this->_virtual[$offset])) {
133            foreach ($this->_virtual[$offset] as $val) {
134                unset($this[$val]);
135            }
136        }
137    }
138
139    /* Iterator methods. */
140
141    /**
142     */
143    public function current()
144    {
145        $val = current($this->_required);
146        return is_null($val)
147            ? current($this->_optional)
148            : $val;
149    }
150
151    /**
152     */
153    public function key()
154    {
155        $key = key($this->_required);
156        return is_null($key)
157            ? key($this->_optional)
158            : $key;
159    }
160
161    /**
162     */
163    public function next()
164    {
165        if (key($this->_required) === null) {
166            next($this->_optional);
167        } else {
168            next($this->_required);
169        }
170    }
171
172    /**
173     */
174    public function rewind()
175    {
176        reset($this->_required);
177        reset($this->_optional);
178    }
179
180    /**
181     */
182    public function valid()
183    {
184        return ((key($this->_required) !== null) ||
185                (key($this->_optional) !== null));
186
187    }
188
189    /* Serializable methods. */
190
191    /**
192     */
193    public function serialize()
194    {
195        return json_encode(array(
196            $this->_required,
197            $this->_optional
198        ));
199    }
200
201    /**
202     */
203    public function unserialize($data)
204    {
205        list($this->_required, $this->_optional) = json_decode($data);
206    }
207
208}
209