1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13:
14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
28: class Horde_Kolab_Server_Connection_Mock_Ldap
29: {
30: 31: 32: 33: 34:
35: private $_params;
36:
37: 38: 39: 40: 41:
42: private $_data;
43:
44: 45: 46: 47: 48:
49: private $_bound = false;
50:
51: 52: 53: 54: 55: 56:
57: public function __construct(array $params, array $data)
58: {
59: $this->_params = $params;
60: $this->_data = $data;
61: }
62:
63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75:
76: public function bind($dn = '', $pw = '')
77: {
78: if ($dn == '' && $pw == '') {
79: if (isset($this->_params['binddn'])
80: && isset($this->_params['bindpw'])) {
81: $dn = $this->_params['binddn'];
82: $pw = $this->_params['bindpw'];
83: }
84: }
85:
86: if ($dn != '') {
87: if (!isset($this->_data[$dn])) {
88: throw new Horde_Kolab_Server_Exception('User does not exist!');
89: }
90:
91: if (!isset($this->_data[$dn]['data']['userPassword'][0])) {
92: throw new Horde_Kolab_Server_Exception('User has no password entry!');
93: }
94: if ($this->_data[$dn]['data']['userPassword'][0] != $pw) {
95: throw new Horde_Kolab_Server_Exception_Bindfailed('Incorrect password!');
96: }
97: } else if (!empty($this->_params['no_anonymous_bind'])) {
98: throw new Horde_Kolab_Server_Exception('Anonymous bind is not allowed!');
99: }
100:
101: $this->_bound = true;
102: }
103:
104: 105: 106: 107: 108: 109: 110: 111: 112:
113: public function getEntry($dn, $attr = array())
114: {
115: $this->_checkBound();
116:
117: if (!is_array($attr)) {
118: $attr = array($attr);
119: }
120: $result = $this->search($dn, '(objectClass=*)',
121: array('scope' => 'base', 'attributes' => $attr));
122: if ($result->count() == 0) {
123: throw new Horde_Kolab_Server_Exception('Could not fetch entry '.$dn.': no entry found');
124: }
125: $entry = $result->shiftEntry();
126: if (false == $entry) {
127: throw new Horde_Kolab_Server_Exception('Could not fetch entry (error retrieving entry from search result)');
128: }
129: return $entry;
130: }
131:
132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143:
144: public function search($base = null, $filter = null, $params = array())
145: {
146: $this->_checkBound();
147:
148: if (isset($params['attributes'])) {
149: $attributes = $params['attributes'];
150: if (!is_array($attributes)) {
151: $attributes = array($attributes);
152: }
153: } else {
154: $attributes = array();
155: }
156:
157: $result = array();
158:
159: if (!empty($filter)) {
160: $filter = $this->parse($filter);
161: $result = $this->doSearch($filter, $attributes);
162: } else {
163: if (!isset($params['scope']) || $params['scope'] == 'sub') {
164: foreach (array_keys($this->_data) as $dn) {
165: if (empty($attributes)) {
166: $result[] = $this->_data[$dn];
167: } else {
168: $selection = $this->_data[$dn];
169: foreach ($this->_data[$dn]['data']
170: as $attr => $value) {
171: if (!in_array($attr, $attributes)) {
172: unset($selection['data'][$attr]);
173: }
174: }
175: $result[] = $selection;
176: }
177: }
178: } else if ($params['scope'] == 'base') {
179: if (isset($this->_data[$base])) {
180: $result[] = $this->_data[$base];
181: } else {
182: $result = array();
183: }
184: } else if ($params['scope'] == 'one') {
185: throw new Horde_Kolab_Server_Exception('Not implemented!');
186: }
187: }
188:
189: if (empty($result)) {
190: $search = new Horde_Kolab_Server_Connection_Mock_Search(array());
191: return $search;
192: }
193:
194: if (!empty($base)) {
195: $subtree = array();
196: foreach ($result as $entry) {
197: if (preg_match('/' . $base . '$/', $entry['dn'])) {
198: $subtree[] = $entry;
199: }
200: }
201: $result = $subtree;
202: }
203:
204: $search = new Horde_Kolab_Server_Connection_Mock_Search($this->getEntries($result));
205: return $search;
206: }
207:
208: 209: 210: 211: 212: 213: 214:
215: public function getEntries($result)
216: {
217: if (is_array($result)) {
218: $data = array();
219: foreach ($result as $entry) {
220: $t = $entry['data'];
221: $t['dn'] = $entry['dn'];
222: $data[$entry['dn']] = $t;
223: }
224: return $data;
225: }
226: return false;
227: }
228:
229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239:
240: public function parse($filter)
241: {
242: $result = array();
243: if (preg_match('/^\((.+?)\)$/', $filter, $matches)) {
244: if (in_array(substr($matches[1], 0, 1), array('!', '|', '&'))) {
245: $result['op'] = substr($matches[1], 0, 1);
246: $result['sub'] = $this->parseSub(substr($matches[1], 1));
247: return $result;
248: } else {
249: if (stristr($matches[1], ')(')) {
250: throw new Horde_Kolab_Server_Exception('Filter parsing error: invalid filter syntax - multiple leaf components detected!');
251: } else {
252: $filter_parts = preg_split('/(?<!\\\\)(=|=~|>|<|>=|<=)/',
253: $matches[1], 2,
254: PREG_SPLIT_DELIM_CAPTURE);
255: if (count($filter_parts) != 3) {
256: throw new Horde_Kolab_Server_Exception('Filter parsing error: invalid filter syntax - unknown matching rule used');
257: } else {
258: $result['att'] = $filter_parts[0];
259: $result['log'] = $filter_parts[1];
260: $val = Horde_Ldap_Util::unescapeFilterValue($filter_parts[2]);
261: $result['val'] = $val[0];
262: return $result;
263: }
264: }
265: }
266: } else {
267: throw new Horde_Kolab_Server_Exception(sprintf("Filter parsing error: %s - filter components must be enclosed in round brackets",
268: $filter));
269: }
270: }
271:
272: 273: 274: 275: 276: 277: 278: 279: 280:
281: public function parseSub($filter)
282: {
283: $result = array();
284: $level = 0;
285: $collect = '';
286: while (preg_match('/^(\(.+?\))(.*)/', $filter, $matches)) {
287: if (in_array(substr($matches[1], 0, 2), array('(!', '(|', '(&'))) {
288: $level++;
289: }
290: if ($level) {
291: $collect .= $matches[1];
292: if (substr($matches[2], 0, 1) == ')') {
293: $collect .= ')';
294: $matches[2] = substr($matches[2], 1);
295: $level--;
296: if (!$level) {
297: $result[] = $this->parse($collect);
298: }
299: }
300: } else {
301: $result[] = $this->parse($matches[1]);
302: }
303: $filter = $matches[2];
304: }
305: return $result;
306: }
307:
308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319:
320: public function doSearch($filter, $attributes = null)
321: {
322: if (isset($filter['log'])) {
323: $result = array();
324: foreach ($this->_data as $element) {
325: if (isset($element['data'][$filter['att']])) {
326: switch ($filter['log']) {
327: case '=':
328: $value = $element['data'][$filter['att']];
329: if (!empty($value) && is_array($value)) {
330: $keys = array_keys($value);
331: $first = $value[$keys[0]];
332: } else {
333: $first = $value;
334: }
335: if ((($filter['val'] == '*')
336: && !empty($value))
337: || $value == $filter['val']
338: || (substr($filter['val'], 0, 1) == '*'
339: && substr($filter['val'], strlen($filter['val']) - 1) == '*'
340: && strpos($first, substr($filter['val'], 1, strlen($filter['val']) - 2)) !== false)
341: || (is_array($value)
342: && in_array($filter['val'], $value))) {
343: if (empty($attributes)) {
344: $result[] = $element;
345: } else {
346: $selection = $element;
347: foreach ($element['data'] as $attr => $value) {
348: if (!in_array($attr, $attributes)) {
349: unset($selection['data'][$attr]);
350: }
351: }
352: $result[] = $selection;
353: }
354: }
355: break;
356: default:
357: throw new Horde_Kolab_Server_Exception("Not implemented!");
358: }
359: }
360: }
361: return $result;
362: } else {
363: $subresult = array();
364: $filtercount = count($filter['sub']);
365: foreach ($filter['sub'] as $subfilter) {
366: $subresult = array_merge($subresult,
367: $this->doSearch($subfilter,
368: $attributes));
369: }
370: $result = array();
371: $dns = array();
372: foreach ($subresult as $element) {
373: $dns[] = $element['dn'];
374:
375: $result[$element['dn']] = $element;
376: }
377: switch ($filter['op']) {
378: case '&':
379: $count = array_count_values($dns);
380: $selection = array();
381: foreach ($count as $dn => $value) {
382: if ($value == $filtercount) {
383: $selection[] = $result[$dn];
384: }
385: }
386: return $selection;
387: case '|':
388: return array_values($result);
389: case '!':
390: $dns = array();
391: foreach ($result as $entry) {
392: if (!in_array($entry['dn'], $dns) ) {
393: $dns[] = $entry['dn'];
394: }
395: }
396: $all_dns = array_keys($this->_data);
397: $diff = array_diff($all_dns, $dns);
398:
399: $result = array();
400: foreach ($diff as $dn) {
401: if (empty($attributes)) {
402: $result[] = $this->_data[$dn];
403: } else {
404: $selection = $this->_data[$dn];
405: foreach ($this->_data[$dn]['data']
406: as $attr => $value) {
407: if (!in_array($attr, $attributes)) {
408: unset($selection['data'][$attr]);
409: }
410: }
411: $result[] = $selection;
412: }
413: }
414: return $result;
415: default:
416: throw new Horde_Kolab_Server_Exception("Not implemented!");
417: }
418: }
419: }
420:
421: 422: 423: 424: 425: 426: 427:
428: public function add($entry)
429: {
430: $this->_checkBound();
431:
432: $ldap_data = $this->toStorage($entry->getValues());
433:
434: $guid = $entry->getDn();
435:
436: $this->_data[$guid] = array(
437: 'dn' => $guid,
438: 'data' => array_merge($ldap_data,
439: array('dn' => $guid)),
440: );
441: }
442:
443: 444: 445: 446: 447: 448: 449: 450: 451:
452: public function modify($entry, $data = array())
453: {
454: $this->_checkBound();
455:
456: $guid = $entry->getDn();
457:
458: if (isset($data['delete'])) {
459: foreach ($data['delete'] as $k => $v) {
460: if (is_int($k)) {
461: if (isset($this->_data[$guid]['data'][$w])) {
462:
463: unset($this->_data[$guid]['data'][$w]);
464: }
465: } else {
466: if (isset($this->_data[$guid]['data'][$l])) {
467: if (!is_array($v)) {
468: $v = array($v);
469: }
470: foreach ($v as $w) {
471: $key = array_search($w, $this->_data[$guid]['data'][$l]);
472: if ($key !== false) {
473:
474: unset($this->_data[$guid]['data'][$l][$key]);
475: }
476: }
477: }
478: }
479: }
480: }
481:
482: if (isset($data['replace'])) {
483: $ldap_data = $this->toStorage($data['replace']);
484:
485: $this->_data[$guid] = array(
486: 'dn' => $guid,
487: 'data' => array_merge($this->_data[$guid]['data'],
488: $ldap_data,
489: array('dn' => $guid)),
490: );
491: }
492:
493: if (isset($data['add'])) {
494: $ldap_data = $this->toStorage($data['add']);
495:
496: foreach ($ldap_data as $k => $v) {
497: if (is_array($v)) {
498: foreach ($v as $w) {
499: $this->_data[$guid]['data'][$k][] = $w;
500: }
501: } else {
502: $this->_data[$guid]['data'][$k][] = $v;
503: }
504: $this->_data[$guid]['data'][$k] = array_values($this->_data[$guid]['data'][$k]);
505: }
506: }
507: }
508:
509: 510: 511: 512: 513: 514: 515: 516: 517:
518: public function delete($uid)
519: {
520: $this->_checkBound();
521:
522: if (isset($this->_data[$uid])) {
523: unset($this->_data[$uid]);
524: } else {
525: throw new Horde_Kolab_Server_MissingObjectException(sprintf("No such object: %s",
526: $uid));
527: }
528: }
529:
530: 531: 532: 533: 534: 535: 536: 537: 538: 539:
540: public function move($uid, $new)
541: {
542: $this->_checkBound();
543:
544: if (isset($this->_data[$uid])) {
545: $this->_data[$new] = $this->_data[$uid];
546: unset($this->_data[$uid]);
547: }
548: }
549:
550: public function schema()
551: {
552:
553: }
554:
555: 556: 557: 558: 559: 560: 561:
562: protected function toStorage($data)
563: {
564: $ldap_data = array();
565: foreach ($data as $key => $val) {
566: if (!is_array($val)) {
567: $val = array($val);
568: }
569: $ldap_data[$key] = $val;
570: }
571: return $ldap_data;
572: }
573:
574: 575: 576: 577: 578: 579: 580:
581: private function _checkBound()
582: {
583: if (!$this->_bound) {
584: throw new Horde_Kolab_Server_Exception('Unbound connection!');
585: }
586: }
587: }
588: