1<?php
2/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
3
4namespace Icinga\Protocol\Ldap;
5
6use Icinga\Exception\IcingaException;
7
8/**
9 * This class is a special node object, representing your connections root node
10 *
11 * @copyright  Copyright (c) 2013 Icinga-Web Team <info@icinga.com>
12 * @author     Icinga-Web Team <info@icinga.com>
13 * @package    Icinga\Protocol\Ldap
14 * @license    http://www.gnu.org/copyleft/gpl.html GNU General Public License
15 * @package Icinga\Protocol\Ldap
16 */
17class Root
18{
19    /**
20     * @var string
21     */
22    protected $rdn;
23
24    /**
25     * @var LdapConnection
26     */
27    protected $connection;
28
29    /**
30     * @var array
31     */
32    protected $children = array();
33
34    /**
35     * @var array
36     */
37    protected $props = array();
38
39    /**
40     * @param LdapConnection $connection
41     */
42    protected function __construct(LdapConnection $connection)
43    {
44        $this->connection = $connection;
45    }
46
47    /**
48     * @return bool
49     */
50    public function hasParent()
51    {
52        return false;
53    }
54
55    /**
56     * @param LdapConnection $connection
57     * @return Root
58     */
59    public static function forConnection(LdapConnection $connection)
60    {
61        $root = new Root($connection);
62        return $root;
63    }
64
65    /**
66     * @param $dn
67     * @param array $props
68     * @return Node
69     */
70    public function createChildByDN($dn, $props = array())
71    {
72        $dn = $this->stripMyDN($dn);
73        $parts = array_reverse(LdapUtils::explodeDN($dn));
74        $parent = $this;
75        while ($rdn = array_shift($parts)) {
76            if ($parent->hasChildRDN($rdn)) {
77                $child = $parent->getChildByRDN($rdn);
78            } else {
79                $child = Node::createWithRDN($parent, $rdn, (array)$props);
80                $parent->addChild($child);
81            }
82            $parent = $child;
83        }
84        return $child;
85    }
86
87    /**
88     * @param $rdn
89     * @return bool
90     */
91    public function hasChildRDN($rdn)
92    {
93        return array_key_exists(strtolower($rdn), $this->children);
94    }
95
96    /**
97     * @param $rdn
98     * @return mixed
99     * @throws IcingaException
100     */
101    public function getChildByRDN($rdn)
102    {
103        if (!$this->hasChildRDN($rdn)) {
104            throw new IcingaException(
105                'The child RDN "%s" is not available',
106                $rdn
107            );
108        }
109        return $this->children[strtolower($rdn)];
110    }
111
112    /**
113     * @return array
114     */
115    public function children()
116    {
117        return $this->children;
118    }
119
120    public function countChildren()
121    {
122        return count($this->children);
123    }
124
125    /**
126     * @return bool
127     */
128    public function hasChildren()
129    {
130        return !empty($this->children);
131    }
132
133    /**
134     * @param Node $child
135     * @return $this
136     */
137    public function addChild(Node $child)
138    {
139        $this->children[strtolower($child->getRDN())] = $child;
140        return $this;
141    }
142
143    /**
144     * @param $dn
145     * @return string
146     */
147    protected function stripMyDN($dn)
148    {
149        $this->assertSubDN($dn);
150        return substr($dn, 0, strlen($dn) - strlen($this->getDN()) - 1);
151    }
152
153    /**
154     * @param $dn
155     * @return $this
156     * @throws IcingaException
157     */
158    protected function assertSubDN($dn)
159    {
160        $mydn = $this->getDN();
161        $end = substr($dn, -1 * strlen($mydn));
162        if (strtolower($end) !== strtolower($mydn)) {
163            throw new IcingaException(
164                '"%s" is not a child of "%s"',
165                $dn,
166                $mydn
167            );
168        }
169        if (strlen($dn) === strlen($mydn)) {
170            throw new IcingaException(
171                '"%s" is not a child of "%s", they are equal',
172                $dn,
173                $mydn
174            );
175        }
176        return $this;
177    }
178
179    /**
180     * @param LdapConnection $connection
181     * @return $this
182     */
183    public function setConnection(LdapConnection $connection)
184    {
185        $this->connection = $connection;
186        return $this;
187    }
188
189    /**
190     * @return LdapConnection
191     */
192    public function getConnection()
193    {
194        return $this->connection;
195    }
196
197    /**
198     * @return bool
199     */
200    public function hasBeenChanged()
201    {
202        return false;
203    }
204
205    /**
206     * @return mixed
207     */
208    public function getRDN()
209    {
210        return $this->getDN();
211    }
212
213    /**
214     * @return mixed
215     */
216    public function getDN()
217    {
218        return $this->connection->getDn();
219    }
220
221    /**
222     * @param $key
223     * @return null
224     */
225    public function __get($key)
226    {
227        if (!array_key_exists($key, $this->props)) {
228            return null;
229        }
230        return $this->props[$key];
231    }
232
233    /**
234     * @param $key
235     * @return bool
236     */
237    public function __isset($key)
238    {
239        return array_key_exists($key, $this->props);
240    }
241}
242