1: <?php
2: /**
3: * The driver for accessing objects stored in LDAP.
4: *
5: * PHP version 5
6: *
7: * @category Kolab
8: * @package Kolab_Server
9: * @author Gunnar Wrobel <wrobel@pardus.de>
10: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
11: * @link http://pear.horde.org/index.php?package=Kolab_Server
12: */
13:
14: /**
15: * This class provides methods to deal with objects stored in
16: * a LDAP db.
17: *
18: * Copyright 2008-2012 Horde LLC (http://www.horde.org/)
19: *
20: * See the enclosed file COPYING for license information (LGPL). If you
21: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
22: *
23: * @category Kolab
24: * @package Kolab_Server
25: * @author Gunnar Wrobel <wrobel@pardus.de>
26: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
27: * @link http://pear.horde.org/index.php?package=Kolab_Server
28: */
29: abstract class Horde_Kolab_Server_Ldap
30: implements Horde_Kolab_Server_Interface
31: {
32: /**
33: * The GUID of the current user.
34: *
35: * @var string|boolean
36: */
37: private $_guid = false;
38:
39: /**
40: * LDAP connection handle.
41: *
42: * @var Horde_Kolab_Server_Connection
43: */
44: private $_conn;
45:
46: /**
47: * Base DN of the LDAP server.
48: *
49: * @var string
50: */
51: private $_base_dn;
52:
53: /**
54: * Constructor.
55: *
56: * @param Horde_Kolab_Server_Connection $connection The LDAP connection.
57: * @param string $base_dn The LDAP server base DN.
58: * @param string $filter A global filter to add
59: * to all queries.
60: */
61: public function __construct(
62: Horde_Kolab_Server_Connection_Interface $connection,
63: $base_dn
64: ) {
65: $this->_conn = $connection;
66: $this->_base_dn = $base_dn;
67:
68: try {
69: Horde_Ldap::checkLDAPExtension();
70: } catch (Horde_Ldap_Exception $e) {
71: throw new Horde_Kolab_Server_Exception(
72: $e->getMessage(),
73: Horde_Kolab_Server_Exception::MISSING_LDAP_EXTENSION,
74: $e
75: );
76: }
77: }
78:
79: /**
80: * Connect to the server.
81: *
82: * @param string $guid The global unique id of the user.
83: * @param string $pass The password.
84: *
85: * @return NULL.
86: *
87: * @throws Horde_Kolab_Server_Exception If the connection failed.
88: */
89: public function connectGuid($guid = '', $pass = '')
90: {
91: /** Do we need to switch the user? */
92: if ($guid !== $this->_guid) {
93: try {
94: $this->_conn->getRead()->bind($guid, $pass);
95: } catch (Horde_Ldap_Exception $e) {
96: if ($e->getCode() == 49) {
97: throw new Horde_Kolab_Server_Exception_Bindfailed(
98: 'Invalid username/password!',
99: Horde_Kolab_Server_Exception::BIND_FAILED,
100: $e
101: );
102: } else {
103: throw new Horde_Kolab_Server_Exception(
104: 'Bind failed!',
105: Horde_Kolab_Server_Exception::SYSTEM,
106: $e
107: );
108: }
109: }
110: $this->_guid = $guid;
111: }
112: }
113:
114: /**
115: * Get the current GUID
116: *
117: * @return string The GUID of the connected user.
118: */
119: public function getGuid()
120: {
121: return $this->_guid;
122: }
123:
124: /**
125: * Get the base GUID of this server
126: *
127: * @return string The base GUID of this server.
128: */
129: public function getBaseGuid()
130: {
131: return $this->_base_dn;
132: }
133:
134: /**
135: * Low level access to reading object data.
136: *
137: * @param string $guid The object to retrieve.
138: *
139: * @return array An array of attributes.
140: *
141: * @throws Horde_Kolab_Server_Exception If the search operation hit an error
142: * or returned no result.
143: */
144: public function read($guid)
145: {
146: $params = array('scope' => 'base');
147: $data = $this->_search(null, $params, $guid);
148: if ($data->count() == 0) {
149: throw new Horde_Kolab_Server_Exception(
150: 'Empty result!',
151: Horde_Kolab_Server_Exception::EMPTY_RESULT
152: );
153: }
154: $result = $data->asArray();
155: return array_pop($result);
156: }
157:
158: /**
159: * Low level access to reading some object attributes.
160: *
161: * @param string $guid The object to retrieve.
162: * @param string $attrs Restrict to these attributes.
163: *
164: * @return array An array of attributes.
165: *
166: * @throws Horde_Kolab_Server_Exception If the search operation hit an error
167: * or returned no result.
168: */
169: public function readAttributes($guid, array $attrs)
170: {
171: $params = array(
172: 'scope' => 'base',
173: 'attributes' => $attrs
174: );
175: $data = $this->_search(null, $params, $guid);
176: if ($data->count() == 0) {
177: throw new Horde_Kolab_Server_Exception(
178: 'Empty result!',
179: Horde_Kolab_Server_Exception::EMPTY_RESULT
180: );
181: }
182: $result = $data->asArray();
183: return array_pop($result);
184: }
185:
186: /**
187: * Finds object data matching a given set of criteria.
188: *
189: * @param string $query The LDAP search query
190: * @param array $params Additional search parameters.
191: *
192: * @return Horde_Kolab_Server_Result The result object.
193: *
194: * @throws Horde_Kolab_Server_Exception
195: */
196: public function find($query, array $params = array())
197: {
198: return $this->findBelow($query, $this->_base_dn, $params);
199: }
200:
201: /**
202: * Modify existing object data.
203: *
204: * @param Horde_Kolab_Server_Object $object The object to be modified.
205: * @param array $data The attributes of the object
206: * to be stored.
207: *
208: * @return NULL
209: *
210: * @throws Horde_Kolab_Server_Exception
211: */
212: public function save(
213: Horde_Kolab_Server_Object_Interface $object,
214: array $data
215: ) {
216: $changes = new Horde_Kolab_Server_Ldap_Changes($object, $data);
217: try {
218: $entry = $this->_conn->getWrite()->getEntry(
219: $object->getGuid(), array_keys($data)
220: );
221: $this->_conn->getWrite()->modify($entry, $changes->getChangeset());
222: } catch (Horde_Ldap_Exception $e) {
223: throw new Horde_Kolab_Server_Exception(
224: 'Saving failed!',
225: Horde_Kolab_Server_Exception::SYSTEM,
226: $e
227: );
228: }
229: }
230:
231: /**
232: * Add new object data.
233: *
234: * @param Horde_Kolab_Server_Object $object The object to be added.
235: * @param array $data The attributes of the object
236: * to be added.
237: *
238: * @return NULL
239: *
240: * @throws Horde_Kolab_Server_Exception
241: */
242: public function add(
243: Horde_Kolab_Server_Object_Interface $object,
244: array $data
245: ) {
246: try {
247: $entry = Horde_Ldap_Entry::createFresh($object->getGuid(), $data);
248: $this->_conn->getWrite()->add($entry);
249: } catch (Horde_Ldap_Exception $e) {
250: throw new Horde_Kolab_Server_Exception(
251: 'Adding object failed!',
252: Horde_Kolab_Server_Exception::SYSTEM,
253: $e
254: );
255: }
256: }
257:
258: /**
259: * Delete an object.
260: *
261: * @param string $guid The UID of the object to be deleted.
262: *
263: * @return NULL
264: *
265: * @throws Horde_Kolab_Server_Exception
266: */
267: public function delete($guid)
268: {
269: try {
270: $this->_conn->getWrite()->delete($guid);
271: } catch (Horde_Ldap_Exception $e) {
272: throw new Horde_Kolab_Server_Exception(
273: 'Deleting object failed!',
274: Horde_Kolab_Server_Exception::SYSTEM,
275: $e
276: );
277: }
278: }
279:
280: /**
281: * Rename an object.
282: *
283: * @param string $guid The UID of the object to be renamed.
284: * @param string $new The new UID of the object.
285: *
286: * @return NULL
287: *
288: * @throws Horde_Kolab_Server_Exception
289: */
290: public function rename($guid, $new)
291: {
292: try {
293: $this->_conn->getWrite()->move($guid, $new);
294: } catch (Horde_Ldap_Exception $e) {
295: throw new Horde_Kolab_Server_Exception(
296: 'Renaming object failed!',
297: Horde_Kolab_Server_Exception::SYSTEM,
298: $e
299: );
300: }
301: }
302:
303: /**
304: * Return the ldap schema.
305: *
306: * @return Horde_Ldap_Schema The LDAP schema.
307: *
308: * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
309: */
310: public function getSchema()
311: {
312: try {
313: return $this->_conn->getRead()->schema();
314: } catch (Horde_Ldap_Exception $e) {
315: throw new Horde_Kolab_Server_Exception(
316: 'Retrieving the schema failed!',
317: Horde_Kolab_Server_Exception::SYSTEM,
318: $e
319: );
320: }
321: }
322:
323: /**
324: * Get the parent GUID of this object.
325: *
326: * @param string $guid The GUID of the child.
327: *
328: * @return string the parent GUID of this object.
329: */
330: public function getParentGuid($guid)
331: {
332: try {
333: $base = Horde_Ldap_Util::explodeDN(
334: $guid,
335: array(
336: 'casefold' => 'none',
337: 'reverse' => false,
338: 'onlyvalues' => false
339: )
340: );
341: $id = array_shift($base);
342: $parent = Horde_Ldap_Util::canonicalDN(
343: $base, array('casefold' => 'none')
344: );
345: } catch (Horde_Ldap_Exception $e) {
346: throw new Horde_Kolab_Server_Exception(
347: 'Retrieving the parent object failed!',
348: Horde_Kolab_Server_Exception::SYSTEM,
349: $e
350: );
351: }
352: return $parent;
353: }
354:
355: /**
356: * Search for object data.
357: *
358: * @param string $filter The LDAP search filter.
359: * @param array $params Additional search parameters.
360: * @param string $base The search base
361: *
362: * @return array The result array.
363: *
364: * @throws Horde_Kolab_Server_Exception If the search operation encountered
365: * a problem.
366: */
367: protected function _search($filter, array $params, $base)
368: {
369: try {
370: $search = $this->_conn->getRead()->search($base, $filter, $params);
371: } catch (Horde_Ldap_Exception $e) {
372: throw new Horde_Kolab_Server_Exception(
373: 'Search failed!',
374: Horde_Kolab_Server_Exception::SYSTEM,
375: $e
376: );
377: }
378: return new Horde_Kolab_Server_Result_Ldap($search);
379: }
380: }
381: