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 * ACL rights for a mailbox (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_Acl extends Horde_Imap_Client_Data_AclCommon implements ArrayAccess, IteratorAggregate, Serializable
24{
25    /**
26     * ACL rights.
27     *
28     * @var array
29     */
30    protected $_rights;
31
32    /**
33     * Constructor.
34     *
35     * @param string $rights  The rights (see RFC 4314 [2.1]).
36     */
37    public function __construct($rights = '')
38    {
39        $this->_rights = str_split($rights);
40        $this->_normalize();
41    }
42
43    /**
44     * String representation of the ACL.
45     *
46     * @return string  String representation (RFC 4314 compliant).
47     */
48    public function __toString()
49    {
50        return implode('', $this->_rights);
51    }
52
53    /**
54     * Computes the difference to another rights string.
55     * Virtual rights are ignored.
56     *
57     * @param string $rights  The rights to compute against.
58     *
59     * @return array  Two element array: added and removed.
60     */
61    public function diff($rights)
62    {
63        $rlist = array_diff(str_split($rights), array_keys($this->_virtual));
64
65        return array(
66            'added' => implode('', array_diff($rlist, $this->_rights)),
67            'removed' => implode('', array_diff($this->_rights, $rlist))
68        );
69    }
70
71    /**
72     * Normalize virtual rights (see RFC 4314 [2.1.1]).
73     */
74    protected function _normalize()
75    {
76        /* Clients conforming to RFC 4314 MUST ignore the virtual ACL_CREATE
77         * and ACL_DELETE rights. See RFC 4314 [2.1]. However, we still need
78         * to handle these rights when dealing with RFC 2086 servers since
79         * we are abstracting out use of ACL_CREATE/ACL_DELETE to their
80         * component RFC 4314 rights. */
81        foreach ($this->_virtual as $key => $val) {
82            foreach ($val as $right) {
83                if ($this[$right]) {
84                    foreach (array_keys($this->_virtual) as $virtual) {
85                        unset($this[$virtual]);
86                    }
87                    return;
88                }
89            }
90        }
91        foreach ($this->_virtual as $key => $val) {
92            if ($this[$key]) {
93                unset($this[$key]);
94                $this->_rights = array_unique(array_merge($this->_rights, $val));
95            }
96        }
97    }
98
99    /* ArrayAccess methods. */
100
101    /**
102     */
103    public function offsetExists($offset)
104    {
105        return $this[$offset];
106    }
107
108    /**
109     */
110    public function offsetGet($offset)
111    {
112        return in_array($offset, $this->_rights);
113    }
114
115    /**
116     */
117    public function offsetSet($offset, $value)
118    {
119        if ($value) {
120            if (!$this[$offset]) {
121                $this->_rights[] = $offset;
122                $this->_normalize();
123            }
124        } elseif ($this[$offset]) {
125            if (isset($this->_virtual[$offset])) {
126                foreach ($this->_virtual[$offset] as $val) {
127                    unset($this[$val]);
128                }
129            }
130            unset($this[$offset]);
131        }
132    }
133
134    /**
135     */
136    public function offsetUnset($offset)
137    {
138        $this->_rights = array_values(array_diff($this->_rights, array($offset)));
139    }
140
141    /* IteratorAggregate method. */
142
143    public function getIterator()
144    {
145        return new ArrayIterator($this->_rights);
146    }
147
148    /* Serializable methods. */
149
150    /**
151     */
152    public function serialize()
153    {
154        return json_encode($this->_rights);
155    }
156
157    /**
158     */
159    public function unserialize($data)
160    {
161        $this->_rights = json_decode($data);
162    }
163
164}
165