1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14:
15: class Shout_Driver_Ldap extends Shout_Driver
16: {
17: var $_ldapKey;
18: var $_appKey;
19:
20: 21: 22: 23:
24: private $_LDAP;
25:
26: 27: 28: 29: 30:
31: private $_connected = false;
32:
33:
34: 35: 36: 37: 38:
39: function __construct($params = array())
40: {
41: parent::__construct($params);
42: $this->_connect();
43: }
44:
45: 46: 47: 48: 49: 50: 51:
52: public function getExtensions($account)
53: {
54: if (empty($account)) {
55: throw new Shout_Exception('Must specify an account code');
56: }
57: static $entries = array();
58: if (isset($entries[$account])) {
59: return $entries[$account];
60: }
61:
62: $this->_params['basedn'];
63:
64: $filter = '(&';
65: $filter .= '(objectClass=AsteriskVoiceMail)';
66: $filter .= '(objectClass=AsteriskUser)';
67: $filter .= '(AstContext='.$account.')';
68: $filter .= ')';
69:
70: $attributes = array(
71: 'cn',
72: 'AstVoicemailEmail',
73: 'AstVoicemailMailbox',
74: 'AstVoicemailPassword',
75: 'AstVoicemailOptions',
76: 'AstVoicemailPager',
77: 'telephoneNumber',
78: 'AstUserChannel'
79: );
80:
81: $search = ldap_search($this->_LDAP, $this->_params['basedn'], $filter, $attributes);
82: if ($search === false) {
83: throw new Shout_Exception("Unable to search directory: " .
84: ldap_error($this->_LDAP), ldap_errno($this->_LDAP));
85: }
86:
87: $res = ldap_get_entries($this->_LDAP, $search);
88: if ($res === false) {
89: throw new Shout_Exception("Unable to fetch results from directory: " .
90: ldap_error($this->_LDAP), ldap_errno($this->_LDAP));
91: }
92:
93:
94:
95: $entries[$account] = array();
96: $i = 0;
97: while ($i < $res['count']) {
98: list($extension) = explode('@', $res[$i]['astvoicemailmailbox'][0]);
99: $entries[$account][$extension] = array('extension' => $extension);
100:
101: $j = 0;
102: $entries[$account][$extension]['mailboxopts'] = array();
103: if (empty($res[$i]['astvoicemailoptions']['count'])) {
104: $res[$i]['astvoicemailoptions']['count'] = -1;
105: }
106: while ($j < $res[$i]['astvoicemailoptions']['count']) {
107: $entries[$account][$extension]['mailboxopts'][] =
108: $res[$i]['astvoicemailoptions'][$j];
109: $j++;
110: }
111:
112: $entries[$account][$extension]['mailboxpin'] =
113: $res[$i]['astvoicemailpassword'][0];
114:
115: $entries[$account][$extension]['name'] =
116: $res[$i]['cn'][0];
117:
118: $entries[$account][$extension]['email'] =
119: $res[$i]['astvoicemailemail'][0];
120:
121: $entries[$account][$extension]['pageremail'] =
122: $res[$i]['astvoicemailpager'][0];
123:
124: $j = 0;
125: $entries[$account][$extension]['numbers'] = array();
126: if (empty($res[$i]['telephonenumber']['count'])) {
127: $res[$i]['telephonenumber']['count'] = -1;
128: }
129: while ($j < $res[$i]['telephonenumber']['count']) {
130: $entries[$account][$extension]['numbers'][] =
131: $res[$i]['telephonenumber'][$j];
132: $j++;
133: }
134:
135: $j = 0;
136: $entries[$account][$extension]['devices'] = array();
137: if (empty($res[$i]['astuserchannel']['count'])) {
138: $res[$i]['astuserchannel']['count'] = -1;
139: }
140: while ($j < $res[$i]['astuserchannel']['count']) {
141:
142: $device = explode('/', $res[$i]['astuserchannel'][$j], 2);
143: $entries[$account][$extension]['devices'][] = $device[1];
144: $j++;
145: }
146:
147:
148: $i++;
149:
150: }
151:
152: ksort($entries[$account]);
153:
154: return($entries[$account]);
155: }
156:
157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167:
168: function addDestination($account, $extension, $type, $destination)
169: {
170:
171: $dn = $this->_getExtensionDn($account, $extension);
172: $attr = $this->_getDestAttr($type, $destination);
173:
174: $res = ldap_mod_add($this->_LDAP, $dn, $attr);
175: if ($res === false) {
176: $msg = sprintf('Error while modifying the LDAP entry. Code %s; Message "%s"',
177: ldap_errno($this->_LDAP), ldap_error($this->_LDAP));
178: Horde::logMessage($msg, 'ERR');
179: throw new Shout_Exception(_("Internal error modifing the directory. Details have been logged for the administrator."));
180: }
181:
182: return true;
183: }
184:
185: 186: 187: 188: 189: 190: 191:
192: function getDestinations($account, $extension)
193: {
194:
195: $filter = '(&(AstContext=%s)(AstVoicemailMailbox=%s))';
196: $filter = sprintf($filter, $account, $extension);
197:
198: $attrs = array('telephoneNumber', 'AstUserChannel');
199:
200: $res = ldap_search($this->_LDAP, $this->_params['basedn'],
201: $filter, $attrs);
202:
203: if ($res === false) {
204: $msg = sprintf('Error while searching LDAP. Code %s; Message "%s"',
205: ldap_errno($this->_LDAP), ldap_error($this->_LDAP));
206: Horde::logMessage($msg, 'ERR');
207: throw new Shout_Exception(_("Internal error searching the directory."));
208: }
209:
210: $res = ldap_get_entries($this->_LDAP, $res);
211:
212: if ($res === false) {
213: $msg = sprintf('Error while searching LDAP. Code %s; Message "%s"',
214: ldap_errno($this->_LDAP), ldap_error($this->_LDAP));
215: Horde::logMessage($msg, 'ERR');
216: throw new Shout_Exception(_("Internal error searching the directory."));
217: }
218:
219: if ($res['count'] != 1) {
220: $msg = sprintf('Error while searching LDAP. Code %s; Message "%s"',
221: ldap_errno($this->_LDAP), ldap_error($this->_LDAP));
222: Horde::logMessage($msg, 'ERR');
223: throw new Shout_Exception(_("Wrong number of entries found for this search."));
224: }
225:
226: return array('numbers' => $res['telephonenumbers'],
227: 'devices' => $res['astuserchannel']);
228: }
229:
230: function deleteDestination($account, $extension, $type, $destination)
231: {
232: $dn = $this->_getExtensionDn($account, $extension);
233: $attr = $this->_getDestAttr($type, $destination);
234:
235: $res = ldap_mod_del($this->_LDAP, $dn, $attr);
236: if ($res === false) {
237: $msg = sprintf('Error while modifying the LDAP entry. Code %s; Message "%s"',
238: ldap_errno($this->_LDAP), ldap_error($this->_LDAP));
239: Horde::logMessage($msg, 'ERR');
240: throw new Shout_Exception(_("Internal error modifing the directory. Details have been logged for the administrator."));
241: }
242:
243: return true;
244: }
245:
246: protected function _getDestAttr($type, $destination)
247: {
248: switch ($type) {
249: case 'number':
250:
251:
252: $attr = array('telephoneNumber' => $destination);
253: break;
254:
255: case 'device':
256:
257:
258:
259: $attr = array('AstUserChannel' => "SIP/" . $destination);
260: break;
261:
262: default:
263: throw new Shout_Exception(_("Invalid destination type specified."));
264: break;
265: }
266:
267: return $attr;
268: }
269:
270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281:
282: public function saveExtension($account, $extension, $details)
283: {
284:
285: parent::saveExtension($account, $extension, $details);
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305: $uid = $extension . '@' . $account;
306: $entry = array(
307: 'objectClass' => array('top', 'account',
308: 'AsteriskVoicemail', 'AsteriskUser'),
309: 'uid' => $uid,
310: 'cn' => $details['name'],
311: 'AstVoicemailEmail' => $details['email'],
312: 'AstVoicemailMailbox' => $extension,
313: 'AstVoicemailPassword' => $details['mailboxpin'],
314: 'AstContext' => $account,
315: );
316: $rdn = 'uid=' . $uid;
317: $dn = $rdn . ',' . $this->_params['basedn'];
318:
319: if (!empty($details['oldextension'])) {
320:
321:
322:
323: $olddn = $this->_getExtensionDn($account, $extension);
324:
325:
326: if ($extension != $details['oldextension']) {
327: $res = ldap_rename($this->_LDAP, $olddn, $rdn,
328: $this->_params['basedn'], true);
329:
330: if ($res === false) {
331: $msg = sprintf('LDAP Error (%s): %s', ldap_errno($this->_LDAP),
332: ldap_error($this->_LDAP));
333: Horde::logMessage($msg, 'ERR');
334: throw new Shout_Exception(_("Error while modifying the directory. Details have been logged for the administrator."));
335: }
336: }
337:
338:
339:
340: unset($entry['objectClass']);
341: $res = ldap_modify($this->_LDAP, $dn, $entry);
342: if ($res === false) {
343: $msg = sprintf('LDAP Error (%s): %s', ldap_errno($this->_LDAP),
344: ldap_error($this->_LDAP));
345: Horde::logMessage($msg, 'ERR');
346: throw new Shout_Exception(_("Error while modifying the directory. Details have been logged for the administrator."));
347: }
348:
349: return true;
350: } else {
351:
352: $res = ldap_add($this->_LDAP, $dn, $entry);
353: if ($res === false) {
354: $msg = sprintf('LDAP Error (%s): %s', ldap_errno($this->_LDAP),
355: ldap_error($this->_LDAP));
356: Horde::logMessage($msg, 'ERR');
357: throw new Shout_Exception(_("Error while modifying the directory. Details have been logged for the administrator."));
358: }
359: return true;
360: }
361:
362:
363: throw new Shout_Exception(_("Unspecified error."));
364: }
365:
366: 367: 368: 369: 370: 371: 372: 373:
374: public function deleteExtension($account, $extension)
375: {
376:
377: parent::deleteExtension($account, $extension);
378:
379: $dn = $this->_getExtensionDn($account, $extension);
380:
381: $res = @ldap_delete($this->_LDAP, $dn);
382: if ($res === false) {
383: $msg = sprintf('LDAP Error (%s): %s', ldap_errno($this->_LDAP),
384: ldap_error($this->_LDAP));
385: Horde::logMessage($msg, 'ERR');
386: throw new Horde_Exception(_("Error while deleting from the directory. Details have been logged for the administrator."));
387: }
388:
389: return true;
390: }
391:
392: 393: 394: 395: 396:
397: protected function _getExtensionDn($account, $extension)
398: {
399:
400: $filter = '(&(AstVoicemailMailbox=%s)(AstContext=%s))';
401: $filter = sprintf($filter, $extension, $account);
402: $attributes = array('dn');
403:
404: $res = ldap_search($this->_LDAP, $this->_params['basedn'],
405: $filter, $attributes);
406: if ($res === false) {
407: $msg = sprintf('LDAP Error (%s): %s', ldap_errno($this->_LDAP),
408: ldap_error($this->_LDAP));
409: Horde::logMessage($msg, 'ERR');
410: throw new Shout_Exception(_("Error while searching the directory. Details have been logged for the administrator."));
411: }
412:
413: if (ldap_count_entries($this->_LDAP, $res) < 1) {
414: throw new Shout_Exception(_("No such extension found."));
415: }
416:
417: $res = ldap_first_entry($this->_LDAP, $res);
418: if ($res === false) {
419: $msg = sprintf('LDAP Error (%s): %s', ldap_errno($this->_LDAP),
420: ldap_error($this->_LDAP));
421: Horde::logMessage($msg, 'ERR');
422: throw new Shout_Exception(_("Error while searching the directory. Details have been logged for the administrator."));
423: }
424:
425: $dn = ldap_get_dn($this->_LDAP, $res);
426: if ($dn === false) {
427: $msg = sprintf('LDAP Error (%s): %s', ldap_errno($this->_LDAP),
428: ldap_error($this->_LDAP));
429: Horde::logMessage($msg, 'ERR');
430: throw new Shout_Exception(_("Internal LDAP error. Details have been logged for the administrator."));
431: }
432:
433: return $dn;
434: }
435:
436: 437: 438: 439: 440: 441: 442: 443:
444: protected function _connect()
445: {
446: if ($this->_connected) {
447: return;
448: }
449:
450: if (!Horde_Util::extensionExists('ldap')) {
451: throw new Shout_Exception('Required LDAP extension not found.');
452: }
453:
454: Horde::assertDriverConfig($this->_params, $this->_params['class'],
455: array('hostspec', 'basedn', 'writedn'));
456:
457:
458: $conn = ldap_connect($this->_params['hostspec'], $this->_params['port']);
459: if (!$conn) {
460: Horde::logMessage(
461: sprintf('Failed to open an LDAP connection to %s.',
462: $this->_params['hostspec']), 'ERR');
463: throw new Shout_Exception('Internal LDAP error. Details have been logged for the administrator.');
464: }
465:
466:
467: if (isset($this->_params['version'])) {
468: $result = ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION,
469: $this->_params['version']);
470: if ($result === false) {
471: Horde::logMessage(
472: sprintf('Set LDAP protocol version to %d failed: [%d] %s',
473: $this->_params['version'],
474: ldap_errno($conn),
475: ldap_error($conn)), 'WARN');
476: throw new Shout_Exception('Internal LDAP error. Details have been logged for the administrator.', ldap_errno($conn));
477: }
478: }
479:
480:
481: if (!empty($this->_params['tls'])) {
482: if (!@ldap_start_tls($conn)) {
483: Horde::logMessage(
484: sprintf('STARTTLS failed: [%d] %s',
485: @ldap_errno($this->_ds),
486: @ldap_error($this->_ds)), 'ERR');
487: }
488: }
489:
490: 491:
492: if (!empty($this->_params['searchdn'])) {
493: $bind = ldap_bind($conn, $this->_params['searchdn'],
494: $this->_params['searchpw']);
495: if ($bind === false) {
496: Horde::logMessage(
497: sprintf('Bind to server %s:%d with DN %s failed: [%d] %s',
498: $this->_params['hostspec'],
499: $this->_params['port'],
500: $this->_params['searchdn'],
501: @ldap_errno($conn),
502: @ldap_error($conn)), 'ERR');
503: throw new Shout_Exception('Internal LDAP error. Details have been logged for the administrator.', ldap_errno($conn));
504: }
505: }
506:
507:
508: $this->_LDAP = $conn;
509: }
510:
511: }
512: