1: <?php
2:
3: /**
4: * LDAP contact modules
5: * @package modules
6: * @subpackage ldap_contacts
7: */
8:
9: if (!defined('DEBUG_MODE')) { die(); }
10:
11: require APP_PATH.'modules/ldap_contacts/hm-ldap-contacts.php';
12:
13: /**
14: * @subpackage ldap_contacts/handler
15: */
16: class Hm_Handler_process_add_ldap_contact_from_message extends Hm_Handler_Module {
17: public function process() {
18: $ldap_config = $this->get('ldap_config');
19: list($success, $form) = $this->process_form(array('contact_source', 'contact_value'));
20: if (!$success) {
21: return;
22: }
23: list($type, $source) = explode(':', $form['contact_source']);
24: if ($type == 'ldap' && array_key_exists($source, $ldap_config)) {
25: $addresses = Hm_Address_Field::parse($form['contact_value']);
26: $config = $ldap_config[$source];
27: if (count($config) == 0) {
28: Hm_Msgs::add('Unable to add contact', 'danger');
29: return;
30: }
31: $ldap = new Hm_LDAP_Contacts($config);
32: if (!empty($addresses)) {
33: $contacts = $this->get('contact_store');
34: if ($ldap->connect()) {
35: foreach ($addresses as $vals) {
36: $atts = array('mail' => $vals['email'], 'objectclass' => $config['objectclass']);
37: if (array_key_exists('name', $vals) && trim($vals['name'])) {
38: $dn = sprintf('cn=%s,%s', $vals['name'], $config['base_dn']);
39: $atts['sn'] = $vals['name'];
40: $atts['cn'] = $vals['name'];
41: $atts['displayname'] = $vals['name'];
42: }
43: else {
44: $dn = sprintf('cn=%s,%s', str_replace(array('<', '>'), '', $vals['email']), $config['base_dn']);
45: $atts['cn'] = str_replace(array('<', '>'), '', $vals['email']);
46: $atts['sn'] = $atts['cn'];
47: }
48: if ($ldap->add($atts, $dn)) {
49: Hm_Msgs::add('Contact Added');
50: }
51: else {
52: Hm_Msgs::add('Unable to add contact', 'danger');
53: }
54: }
55: }
56: }
57: }
58: }
59: }
60:
61: /**
62: * @subpackage ldap_contacts/handler
63: */
64: class Hm_Handler_process_delete_ldap_contact extends Hm_Handler_Module {
65: public function process() {
66: $contacts = $this->get('contact_store');
67: $ldap_config = ldap_config($this->config);
68: $sources = array_keys($ldap_config);
69: list($success, $form) = $this->process_form(array('contact_type', 'contact_source', 'contact_id'));
70: if ($success && $form['contact_type'] == 'ldap' && in_array($form['contact_source'], $sources, true)) {
71: $config = $ldap_config[$form['contact_source']];
72:
73: // For LDAP contacts, skip contact ID lookup and use DN-based lookup directly
74: $contacts = $this->get('contact_store');
75: $dn = null;
76: $contact = null;
77:
78: // Get DN from request data
79: $target_dn = null;
80: if (isset($this->request->post['ldap_dn']) && !empty($this->request->post['ldap_dn'])) {
81: $target_dn = $this->request->post['ldap_dn'];
82: } elseif (isset($this->request->get['dn'])) {
83: $target_dn = Hm_LDAP_Contact::decodeDN($this->request->get['dn']);
84: } elseif (isset($this->request->post['dn'])) {
85: $target_dn = Hm_LDAP_Contact::decodeDN($this->request->post['dn']);
86: }
87:
88: if ($target_dn) {
89: $contact = Hm_LDAP_Contact::findByDN($contacts, $target_dn, $form['contact_source']);
90: $dn = $target_dn;
91: }
92:
93: if (!$contact || !$dn) {
94: Hm_Msgs::add('Unable to find contact to delete', 'danger');
95: return;
96: }
97:
98: $ldap = new Hm_LDAP_Contacts($config);
99: if ($ldap->connect()) {
100: if ($ldap->delete($dn)) {
101: Hm_Msgs::add('Contact Deleted');
102: $this->out('contact_deleted', 1);
103: }
104: else {
105: if (is_resource($ldap->fh) || (is_object($ldap->fh) && get_class($ldap->fh) === 'LDAP\Connection')) {
106: $ldap_error = @ldap_error($ldap->fh);
107: $ldap_errno = @ldap_errno($ldap->fh);
108: } else {
109: $ldap_error = '';
110: $ldap_errno = 0;
111: }
112:
113: switch ($ldap_errno) {
114: case 50: // LDAP_INSUFFICIENT_ACCESS
115: Hm_Msgs::add('Permission denied: You do not have sufficient privileges to delete contacts from this LDAP directory', 'danger');
116: break;
117: case 66: // LDAP_NOT_ALLOWED_ON_NONLEAF
118: Hm_Msgs::add('Cannot delete: This contact has dependent entries that must be deleted first', 'danger');
119: break;
120: default:
121: Hm_Msgs::add('Could not delete contact', 'danger');
122: break;
123: }
124: }
125: }
126: else {
127: Hm_Msgs::add('Could not connect to LDAP server', 'danger');
128: }
129: }
130: }
131: }
132:
133: /**
134: * @subpackage ldap_contacts/handler
135: */
136: class Hm_Handler_process_ldap_fields extends Hm_Handler_Module {
137: public function process() {
138: $form = $this->get('ldap_entry_data', array());
139: if (!is_array($form) || count($form) == 0) {
140: return;
141: }
142: $config = ldap_config($this->config, $form['ldap_source']);
143: $uidattr = isset($form['ldap_uidattr']) ? $form['ldap_uidattr'] : 'cn';
144: $uid_val = '';
145:
146: if ($uidattr === 'uid') {
147: if (!empty($form['ldap_uid'])) {
148: $uid_val = $form['ldap_uid'];
149: } else {
150: Hm_Msgs::add('Username is required for UID-based contacts', 'danger');
151: return;
152: }
153: } else {
154: $uid_val = trim($form['ldap_first_name'].' '.$form['ldap_last_name']);
155: }
156:
157: $dn = sprintf('%s=%s,%s', $uidattr, $uid_val, $config['base_dn']);
158:
159: $result = array('objectclass' => $config['objectclass']);
160: // Always include cn as it's required by most objectclasses
161: $cn = trim($form['ldap_first_name'].' '.$form['ldap_last_name']);
162: $result['cn'] = $cn;
163:
164: if ($uidattr === 'uid' && !empty($form['ldap_uid'])) {
165: $result['uid'] = $form['ldap_uid'];
166: }
167:
168: $ldap_map = array(
169: 'ldap_first_name' => 'givenname',
170: 'ldap_last_name' => 'sn',
171: 'ldap_displayname' => 'displayname',
172: 'ldap_uid' => 'uid',
173: 'ldap_mail' => 'mail',
174: 'ldap_locality' => 'l',
175: 'ldap_state' => 'st',
176: 'ldap_street' => 'street',
177: 'ldap_postalcode' => 'postalcode',
178: 'ldap_title' => 'title',
179: 'ldap_phone' => 'telephonenumber',
180: 'ldap_fax' => 'facsimiletelephonenumber',
181: 'ldap_mobile' => 'mobile',
182: 'ldap_room' => 'roomnumber',
183: 'ldap_car' => 'carlicense',
184: 'ldap_org' => 'o',
185: 'ldap_org_unit' => 'ou',
186: 'ldap_org_dpt' => 'departmentnumber',
187: 'ldap_emp_num' => 'employeenumber',
188: 'ldap_emp_type' => 'employeetype',
189: 'ldap_lang' => 'preferredlanguage',
190: 'ldap_uri' => 'labeleduri'
191: );
192: foreach ($ldap_map as $name => $val) {
193: // Skip uid attribute when uidattr is 'cn' to avoid empty uid values
194: if ($name === 'ldap_uid' && $uidattr === 'cn') {
195: continue;
196: }
197: if (array_key_exists($name, $form)) {
198: $result[$val] = $form[$name];
199: }
200: elseif (array_key_exists($name, $this->request->post) && trim($this->request->post[$name])) {
201: $result[$val] = $this->request->post[$name];
202: }
203: }
204: $this->out('entry_dn', $dn);
205: $this->out('ldap_entry_data', $result, false);
206: $this->out('ldap_config', $config, false);
207: }
208: }
209:
210: /**
211: * @subpackage ldap_contacts/handler
212: */
213: class Hm_Handler_process_update_ldap_server extends Hm_Handler_Module {
214: public function process() {
215: if ($this->get('ldap_action') != 'update') {
216: return;
217: }
218: $entry = $this->get('ldap_entry_data', array());
219: if (!is_array($entry) || count($entry) == 0) {
220: return;
221: }
222: $dn = get_ldap_value('dn', $this);
223: $config = $this->get('ldap_config');
224: $ldap = new Hm_LDAP_Contacts($config);
225: if ($ldap->connect()) {
226: if ($ldap->modify($entry, $dn)) {
227: Hm_Msgs::add('Contact Updated');
228: $this->save_hm_msgs();
229: Hm_Dispatch::page_redirect('?page=contacts');
230: }
231: else {
232: Hm_Msgs::add('Unable to update contact', 'danger');
233: }
234: } else {
235: Hm_Msgs::add('Unable to connect to LDAP server', 'danger');
236: }
237: }
238: }
239:
240: /**
241: * @subpackage ldap_contacts/handler
242: */
243: class Hm_Handler_process_add_to_ldap_server extends Hm_Handler_Module {
244: public function process() {
245: if ($this->get('ldap_action') != 'add') {
246: return;
247: }
248: $entry = $this->get('ldap_entry_data', array());
249: if (!is_array($entry) || count($entry) == 0) {
250: return;
251: }
252:
253: $config = $this->get('ldap_config');
254: $dn = $this->get('entry_dn');
255: $ldap = new Hm_LDAP_Contacts($config);
256: if ($ldap->connect()) {
257: if ($ldap->add($entry, $dn)) {
258: Hm_Msgs::add('Contact Added');
259: }
260: else {
261: Hm_Msgs::add('Could not add contact', 'danger');
262: }
263: } else {
264: Hm_Msgs::add('Could not add contact: failed to connect to LDAP server', 'danger');
265: }
266: }
267: }
268:
269: /**
270: * @subpackage ldap_contacts/handler
271: */
272: class Hm_Handler_process_update_ldap_contact extends Hm_Handler_Module {
273: public function process() {
274: list($success, $form) = $this->process_form(array('ldap_source', 'contact_source',
275: 'ldap_first_name', 'update_ldap_contact', 'ldap_last_name', 'ldap_mail', 'ldap_uidattr'));
276: if ($success && $form['contact_source'] == 'ldap') {
277: if (isset($form['ldap_uidattr']) && $form['ldap_uidattr'] === 'uid') {
278: if (empty($this->request->post['ldap_uid'])) {
279: Hm_Msgs::add('Username is required when using UID attribute', 'danger');
280: return;
281: }
282: $form['ldap_uid'] = $this->request->post['ldap_uid'];
283: } else {
284: if (isset($this->request->post['ldap_uid'])) {
285: $form['ldap_uid'] = $this->request->post['ldap_uid'];
286: }
287: }
288:
289: $this->out('ldap_entry_data', $form, false);
290: $this->out('ldap_action', 'update');
291: }
292: }
293: }
294:
295: /**
296: * @subpackage ldap_contacts/handler
297: */
298: class Hm_Handler_process_add_ldap_contact extends Hm_Handler_Module {
299: public function process() {
300: list($success, $form) = $this->process_form(array('contact_source', 'ldap_first_name',
301: 'add_ldap_contact', 'ldap_last_name', 'ldap_mail', 'ldap_source', 'ldap_uidattr'));
302:
303: if ($success && $form['contact_source'] == 'ldap') {
304: if (isset($form['ldap_uidattr']) && $form['ldap_uidattr'] === 'uid') {
305: if (empty($this->request->post['ldap_uid'])) {
306: return;
307: }
308: $form['ldap_uid'] = $this->request->post['ldap_uid'];
309: } else {
310: if (isset($this->request->post['ldap_uid'])) {
311: $form['ldap_uid'] = $this->request->post['ldap_uid'];
312: }
313: }
314:
315: $this->out('ldap_entry_data', $form, false);
316: $this->out('ldap_action', 'add');
317: }
318: }
319: }
320:
321: /**
322: * @subpackage ldap_contacts/handler
323: */
324: class Hm_Handler_load_ldap_contacts extends Hm_Handler_Module {
325: public function process() {
326: $contacts = $this->get('contact_store');
327: list($contacts, $ldap_config) = fetch_ldap_contacts($this->config, $this->user_config, $contacts);
328: $this->append('contact_sources', 'ldap');
329: $edit = false;
330: $sources = array();
331: foreach ($ldap_config as $name => $vals) {
332: if (is_array($vals) && array_key_exists('read_write', $vals) && $vals['read_write']) {
333: $this->append('contact_edit', sprintf('ldap:%s', $name));
334: $sources[] = $name;
335: $edit = true;
336: }
337: }
338: $this->out('ldap_edit', $edit);
339: $this->out('ldap_sources', $sources);
340: $this->out('contact_store', $contacts, false);
341: $this->out('ldap_config', $ldap_config, false);
342: }
343: }
344: /**
345: * @subpackage ldap_contacts/handler
346: */
347: class Hm_Handler_load_edit_ldap_contact extends Hm_Handler_Module {
348: public function process() {
349: $ldap_config = $this->get('ldap_config');
350: if (array_key_exists('contact_source', $this->request->get) &&
351: array_key_exists('contact_type', $this->request->get) &&
352: $this->request->get['contact_type'] == 'ldap' &&
353: array_key_exists($this->request->get['contact_source'], $ldap_config) &&
354: array_key_exists('contact_id', $this->request->get)) {
355:
356: $contacts = $this->get('contact_store');
357: $contact_id = $this->request->get['contact_id'];
358: $contact_source = $this->request->get['contact_source'];
359:
360: $target_dn = isset($this->request->get['dn']) ? Hm_LDAP_Contact::decodeDN($this->request->get['dn']) : null;
361:
362: if ($target_dn) {
363: $contact = Hm_LDAP_Contact::findByDN($contacts, $target_dn, $contact_source);
364:
365: if ($contact) {
366: $current = $contact->export();
367: $current['id'] = $contact_id;
368: $this->out('current_ldap_contact', $current);
369: $this->handler_response['ldap_edit'] = true;
370: }
371: }
372:
373: } else {
374: $missing = array();
375: if (!array_key_exists('contact_source', $this->request->get)) $missing[] = 'contact_source';
376: if (!array_key_exists('contact_type', $this->request->get)) $missing[] = 'contact_type';
377: if (!array_key_exists('contact_id', $this->request->get)) $missing[] = 'contact_id';
378: if (array_key_exists('contact_source', $this->request->get) && !array_key_exists($this->request->get['contact_source'], $ldap_config)) $missing[] = 'ldap_config_for_source';
379:
380: error_log("LDAP Edit: Handler skipped due to missing: " . implode(', ', $missing));
381: }
382: }
383: }
384:
385: /**
386: * @subpackage ldap_contacts/handler
387: */
388: class Hm_Handler_process_ldap_auth_settings extends Hm_Handler_Module {
389: public function process() {
390: if (!array_key_exists('save_settings', $this->request->post)) {
391: return;
392: }
393: $connections = $this->get('ldap_contact_connections');
394: $users = array();
395: $passwords = array();
396: $results = $connections;
397: if (array_key_exists('ldap_usernames', $this->request->post)) {
398: $users = $this->request->post['ldap_usernames'];
399: }
400: if (array_key_exists('ldap_passwords', $this->request->post)) {
401: $passwords = $this->request->post['ldap_passwords'];
402: }
403: foreach ($connections as $name => $vals) {
404: if (array_key_exists($name, $users)) {
405: $results[$name]['user'] = $users[$name];
406: }
407: if (array_key_exists($name, $passwords)) {
408: $results[$name]['pass'] = $passwords[$name];
409: }
410: }
411: if (count($results) > 0) {
412: $new_settings = $this->get('new_user_settings');
413: $new_settings['ldap_contacts_auth_setting'] = $results;
414: $this->out('new_user_settings', $new_settings, false);
415: }
416: }
417: }
418:
419: /**
420: * @subpackage ldap_contacts/handler
421: */
422: class Hm_Handler_load_ldap_settings extends Hm_Handler_Module {
423: public function process() {
424: $connections = array();
425: foreach (ldap_config($this->config) as $name => $vals) {
426: if (is_array($vals) && array_key_exists('auth', $vals) && $vals['auth']) {
427: if ((!array_key_exists('user', $vals) || !$vals['user']) &&
428: (!array_key_exists('pass', $vals) || !$vals['pass'])) {
429: $connections[$name] = $vals;
430: }
431: }
432: }
433: $this->out('ldap_contacts_auth', $this->user_config->get('ldap_contacts_auth_setting'));
434: $this->out('ldap_contact_connections', $connections);
435: }
436: }
437:
438: /**
439: * @subpackage ldap_contacts/handler
440: */
441: class Hm_Handler_ldap_send_to_contact extends Hm_Handler_Module {
442: public function process() {
443: if (!$this->get('compose_draft') &&
444: array_key_exists('contact_id', $this->request->get) &&
445: array_key_exists('contact_type', $this->request->get) &&
446: array_key_exists('contact_source', $this->request->get) &&
447: array_key_exists('dn', $this->request->get) &&
448: $this->request->get['contact_type'] == 'ldap') {
449:
450: $contacts = $this->get('contact_store');
451: $contact_id = $this->request->get['contact_id'];
452: $contact_source = $this->request->get['contact_source'];
453:
454: // For LDAP contacts, use DN-based lookup directly (contact_id is unreliable for LDAP)
455: $target_dn = Hm_LDAP_Contact::decodeDN($this->request->get['dn']);
456: $contact = Hm_LDAP_Contact::findByDN($contacts, $target_dn, $contact_source);
457:
458: if ($contact) {
459: $to = sprintf('%s <%s>', $contact->value('display_name'), $contact->value('email_address'));
460: $this->out('compose_draft', array('draft_to' => $to, 'draft_subject' => '', 'draft_body' => ''));
461: }
462: }
463: }
464: }
465:
466: /**
467: * @subpackage ldap_contacts/output
468: */
469: class Hm_Output_ldap_auth_settings extends Hm_Output_Module {
470: protected function output() {
471: $connections = $this->get('ldap_contact_connections', array());
472: $auths = $this->get('ldap_contacts_auth', array());
473: if (count($connections) > 0) {
474: $res = '<tr><td data-target=".ldap_settings" colspan="2" class="settings_subtitle cursor-pointer border-bottom p-2">'.
475: '<i class="bi bi-people-fill fs-5 me-2"></i>'.
476: $this->trans('LDAP Addressbooks').'</td></tr>';
477: foreach ($connections as $name => $con) {
478: $user = '';
479: $pass = false;
480: if (array_key_exists($name, $auths)) {
481: $user = $auths[$name]['user'];
482: if (array_key_exists('pass', $auths[$name]) && $auths[$name]['pass']) {
483: $pass = true;
484: }
485: }
486: $res .= '<tr class="ldap_settings"><td>'.$this->html_safe($name).'</td><td>';
487: $res .= '<input autocomplete="username" type="text" value="'.$user.'" name="ldap_usernames['.$this->html_safe($name).']" ';
488: $res .= 'placeholder="'.$this->trans('Username').'" class="form-control mb-2" /> <input type="password" class="form-control"';
489: if ($pass) {
490: $res .= 'disabled="disabled" placeholder="'.$this->trans('Password saved').'" ';
491: $res .= 'name="ldap_passwords['.$this->html_safe($name).']" /> <input type="button" ';
492: $res .= 'value="'.$this->trans('Unlock').'" class="ldap_password_change btn btn-primary" /></td></tr>';
493: }
494: else {
495: $res .= 'autocomplete="new-password" placeholder="'.$this->trans('Password').'" ';
496: $res .= 'name="ldap_passwords['.$this->html_safe($name).']" /></td></tr>';
497: }
498: }
499: return $res;
500: }
501: }
502: }
503:
504: /**
505: * @subpackage ldap_contacts/output
506: */
507: class Hm_Output_ldap_contact_form_end extends Hm_Output_Module {
508: protected function output() {
509: if (!$this->get('ldap_edit')) {
510: return;
511: }
512: return '</div></form></div>';
513: }
514: }
515:
516: /**
517: * @subpackage ldap_contacts/output
518: */
519: class Hm_Output_ldap_form_first_name extends Hm_Output_Module {
520: protected function output() {
521: if (!$this->get('ldap_edit')) {
522: return;
523: }
524: $name = get_ldap_value('givenname', $this);
525: $current = $this->get('current_ldap_contact');
526: $is_edit = !empty($current);
527:
528: if ($is_edit) {
529: $uidattr = get_ldap_value('uidattr', $this);
530: if ($uidattr === 'cn') {
531: // For CN-based contacts, make first name read-only to prevent DN conflicts
532: return '<div class="form-floating mb-2">'.
533: '<input placeholder="'.$this->trans('First Name').'" id="ldap_first_name" '.
534: 'type="text" name="ldap_first_name" value="'.$this->html_safe($name).'" class="form-control" readonly />'.
535: '<label for="ldap_first_name">'.$this->trans('First Name').' ('.$this->trans('Read Only').')</label></div>';
536: }
537: }
538:
539: return '<div class="form-floating mb-2">'.
540: '<input required placeholder="'.$this->trans('First Name').'" id="ldap_first_name" '.
541: 'type="text" name="ldap_first_name" value="'.$this->html_safe($name).'" class="form-control" />'.
542: '<label for="ldap_first_name">'.$this->trans('First Name').' *</label></div>';
543: }
544: }
545:
546: /**
547: * @subpackage ldap_contacts/output
548: */
549: class Hm_Output_ldap_form_submit extends Hm_Output_Module {
550: protected function output() {
551: if (!$this->get('ldap_edit')) {
552: return;
553: }
554: $label = 'Add';
555: $name = 'add_ldap_contact';
556: if ($this->get('current_ldap_contact')) {
557: $label = 'Update';
558: $name = 'update_ldap_contact';
559: }
560: return '<br /><input name="'.$name.'" type="submit" value="'.$this->trans($label).'" class="btn btn-primary me-1" />'.
561: '<input type="button" class="reset_contact btn btn-secondary" value="'.$this->trans('Cancel').'" />';
562: }
563: }
564:
565: /**
566: * @subpackage ldap_contacts/output
567: */
568: class Hm_Output_ldap_form_last_name extends Hm_Output_Module {
569: protected function output() {
570: if (!$this->get('ldap_edit')) {
571: return;
572: }
573: $name = get_ldap_value('sn', $this);
574: $current = $this->get('current_ldap_contact');
575: $is_edit = !empty($current);
576:
577: if ($is_edit) {
578: $uidattr = get_ldap_value('uidattr', $this);
579: if ($uidattr === 'cn') {
580: // For CN-based contacts, make last name read-only to prevent DN conflicts
581: return '<div class="form-floating mb-2">'.
582: '<input placeholder="'.$this->trans('Last Name').'" id="ldap_last_name" type="text" '.
583: 'name="ldap_last_name" value="'.$this->html_safe($name).'" class="form-control" readonly />'.
584: '<label for="ldap_last_name">'.$this->trans('Last Name').' ('.$this->trans('Read Only').')</label></div>';
585: }
586: }
587:
588: return '<div class="form-floating mb-2">'.
589: '<input required placeholder="'.$this->trans('Last Name').'" id="ldap_last_name" type="text" '.
590: 'name="ldap_last_name" value="'.$this->html_safe($name).'" class="form-control" />'.
591: '<label for="ldap_last_name">'.$this->trans('Last Name').' *</label></div>';
592: }
593: }
594:
595: /**
596: * @subpackage ldap_contacts/output
597: */
598: class Hm_Output_ldap_form_title extends Hm_Output_Module {
599: protected function output() {
600: if (!$this->get('ldap_edit')) {
601: return;
602: }
603: $title = get_ldap_value('title', $this);
604: return '<div class="form-floating mb-2">'.
605: '<input placeholder="'.$this->trans('Title').'" id="ldap_title" type="text" name="ldap_title" '.
606: 'value="'.$this->html_safe($title).'" class="form-control" />'.
607: '<label for="ldap_title">'.$this->trans('Title').'</label></div>';
608: }
609: }
610:
611: /**
612: * @subpackage ldap_contacts/output
613: */
614: class Hm_Output_ldap_contact_form_start extends Hm_Output_Module {
615: protected function output() {
616: if (!$this->get('ldap_edit')) {
617: return;
618: }
619: $sources = $this->get('ldap_sources');
620: $title = $this->trans('Add LDAP');
621: $form_class='contact_form';
622: $current = $this->get('current_ldap_contact');
623: $current_source = false;
624: if ($current) {
625: $form_class = 'contact_update_form mt-3';
626: $current_source = $current['source'];
627: $title = sprintf($this->trans('Update LDAP - %s'), $this->html_safe($current_source));
628: }
629: $source_html = '';
630: if ($current_source) {
631: $source_html = '<input type="hidden" name="ldap_source" value="'.$this->html_safe($current_source).'" />';
632: } else {
633: $source_select = '<select id="ldap_source" name="ldap_source" class="form-select">';
634: foreach ($sources as $name) {
635: $source_select .= '<option value="'.$this->html_safe($name).'">'.$this->html_safe($name).'</option>';
636: }
637: $source_select .= '</select>';
638: $source_html = '<div class="form-floating mb-2">'.$source_select.'<label for="ldap_source">'.$this->trans('Source').'</label></div>';
639: }
640: return '<div class="add_contact_responsive"><form class="add_contact_form" method="POST">'.
641: '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />'.
642: '<button class="server_title mt-2 btn btn-light"><i class="bi bi-person-add me-2"></i>'.$title.'</button>'.
643: '<div class="'.$form_class.'"><input type="hidden" name="contact_source" value="ldap" />'.$source_html;
644: }
645: }
646:
647: /**
648: * @subpackage ldap_contacts/output
649: */
650: class Hm_Output_ldap_form_displayname extends Hm_Output_Module {
651: protected function output() {
652: if (!$this->get('ldap_edit')) {
653: return;
654: }
655: $val = get_ldap_value('displayname', $this);
656: return '<div class="form-floating mb-2">'.
657: '<input placeholder="'.$this->trans('Display Name').'" id="ldap_displayname" type="text" name="ldap_displayname" '.
658: 'value="'.$this->html_safe($val).'" class="form-control" />'.
659: '<label for="ldap_displayname">'.$this->trans('Display Name').'</label></div>';
660: }
661: }
662:
663: /**
664: * @subpackage ldap_contacts/output
665: */
666:
667: class Hm_Output_ldap_form_uidattr extends Hm_Output_Module {
668: protected function output() {
669: if (!$this->get('ldap_edit')) {
670: return;
671: }
672: $val = get_ldap_value('uidattr', $this);
673: $current = $this->get('current_ldap_contact');
674: $is_edit = !empty($current);
675: $options = array('cn', 'uid');
676:
677: if ($is_edit) {
678: $select = '<div class="form-floating mb-2">'.
679: '<input placeholder="'.$this->trans('UID Attribute').'" id="ldap_uidattr_display" type="text" '.
680: 'value="'.$this->html_safe($this->trans($val)).'" class="form-control" readonly />'.
681: '<label for="ldap_uidattr_display">'.$this->trans('UID Attribute').' ('.$this->trans('Read Only').')</label></div>';
682:
683: $select .= '<input type="hidden" name="ldap_uidattr" value="'.$this->html_safe($val).'" />';
684:
685: $uid_val = get_ldap_value('uid', $this);
686:
687: // Username field - also read-only in edit mode
688: if ($val === 'uid' && !empty($uid_val)) {
689: $select .= '<div class="form-floating mb-2">'.
690: '<input placeholder="'.$this->trans('Username').'" id="ldap_uid_display" type="text" '.
691: 'value="'.$this->html_safe($uid_val).'" class="form-control" readonly />'.
692: '<label for="ldap_uid_display">'.$this->trans('Username').' ('.$this->trans('Read Only').')</label></div>';
693:
694: $select .= '<input type="hidden" name="ldap_uid" value="'.$this->html_safe($uid_val).'" />';
695: }
696: } else {
697: $select = '<div class="form-floating mb-2">';
698:
699: $select .= '<select id="ldap_uidattr" name="ldap_uidattr" class="form-select">';
700: foreach ($options as $opt) {
701: $selected = ($val == $opt) ? ' selected' : '';
702: $select .= '<option value="'.$this->html_safe($opt).'"'.$selected.'>'.$this->trans($opt).'</option>';
703: }
704: $select .= '</select>';
705: $select .= '<label for="ldap_uidattr">'.$this->trans('UID Attribute').'</label></div>';
706:
707: $uid_val = get_ldap_value('uid', $this);
708:
709: $required = ($val === 'uid') ? ' required' : '';
710: $hidden_class = ($val !== 'uid') ? ' d-none' : '';
711: $select .= '<div class="form-floating mb-2'.$hidden_class.'" id="ldap_uid_field_wrapper">'.
712: '<input placeholder="'.$this->trans('Username').'" id="ldap_uid" type="text" name="ldap_uid" '.
713: 'value="'.$this->html_safe($uid_val).'" class="form-control" autocomplete="username"'.$required.' />'.
714: '<label for="ldap_uid">'.$this->trans('Username').'</label></div>';
715: }
716:
717: return $select;
718: }
719: }
720:
721: /**
722: * @subpackage ldap_contacts/output
723: */
724: class Hm_Output_ldap_form_dn_display extends Hm_Output_Module {
725: protected function output() {
726: if (!$this->get('ldap_edit')) {
727: return;
728: }
729: $current = $this->get('current_ldap_contact');
730: if (empty($current)) {
731: return; // Only show DN in edit mode
732: }
733:
734: $dn = get_ldap_value('dn', $this);
735: if (empty($dn)) {
736: return;
737: }
738:
739: return '<div class="form-floating mb-2">'.
740: '<input type="text" id="ldap_dn_display" class="form-control" value="'.$this->html_safe($dn).'" readonly />'.
741: '<label for="ldap_dn_display">'.$this->trans('Distinguished Name').' ('.$this->trans('Read Only').')</label></div>';
742: }
743: }
744:
745: /**
746: * @subpackage ldap_contacts/output
747: */
748: class Hm_Output_ldap_form_mail extends Hm_Output_Module {
749: protected function output() {
750: if (!$this->get('ldap_edit')) {
751: return;
752: }
753: $val = get_ldap_value('email_address', $this);
754: return '<div class="form-floating mb-2">'.
755: '<input required placeholder="'.$this->trans('E-mail Address').'" id="ldap_mail" type="email" name="ldap_mail" '.
756: 'value="'.$this->html_safe($val).'" class="form-control" />'.
757: '<label for="ldap_mail">'.$this->trans('E-mail Address').' *</label></div>';
758: }
759: }
760:
761: /**
762: * @subpackage ldap_contacts/output
763: */
764: class Hm_Output_ldap_form_phone extends Hm_Output_Module {
765: protected function output() {
766: if (!$this->get('ldap_edit')) {
767: return;
768: }
769: $val = get_ldap_value('phone_number', $this);
770: return '<div class="form-floating mb-2">'.
771: '<input placeholder="'.$this->trans('Telephone Number').'" id="ldap_phone" type="text" name="ldap_phone" '.
772: 'value="'.$this->html_safe($val).'" class="form-control" />'.
773: '<label for="ldap_phone">'.$this->trans('Telephone Number').'</label></div>';
774: }
775: }
776:
777: /**
778: * @subpackage ldap_contacts/output
779: */
780: class Hm_Output_ldap_form_fax extends Hm_Output_Module {
781: protected function output() {
782: if (!$this->get('ldap_edit')) {
783: return;
784: }
785: $val = get_ldap_value('facsimiletelephonenumber', $this);
786: return '<div class="form-floating mb-2">'.
787: '<input placeholder="'.$this->trans('Fax Number').'" id="ldap_fax" type="text" name="ldap_fax" '.
788: 'value="'.$this->html_safe($val).'" class="form-control" />'.
789: '<label for="ldap_fax">'.$this->trans('Fax Number').'</label></div>';
790: }
791: }
792:
793: /**
794: * @subpackage ldap_contacts/output
795: */
796: class Hm_Output_ldap_form_mobile extends Hm_Output_Module {
797: protected function output() {
798: if (!$this->get('ldap_edit')) {
799: return;
800: }
801: $val = get_ldap_value('mobile', $this);
802: return '<div class="form-floating mb-2">'.
803: '<input placeholder="'.$this->trans('Mobile Number').'" id="ldap_mobile" type="text" name="ldap_mobile" '.
804: 'value="'.$this->html_safe($val).'" class="form-control" />'.
805: '<label for="ldap_mobile">'.$this->trans('Mobile Number').'</label></div>';
806: }
807: }
808:
809: /**
810: * @subpackage ldap_contacts/output
811: */
812: class Hm_Output_ldap_form_room extends Hm_Output_Module {
813: protected function output() {
814: if (!$this->get('ldap_edit')) {
815: return;
816: }
817: $val = get_ldap_value('roomnumber', $this);
818: return '<div class="form-floating mb-2">'.
819: '<input placeholder="'.$this->trans('Room Number').'" id="ldap_room" type="text" name="ldap_room" '.
820: 'value="'.$this->html_safe($val).'" class="form-control" />'.
821: '<label for="ldap_room">'.$this->trans('Room Number').'</label></div>';
822: }
823: }
824:
825: /**
826: * @subpackage ldap_contacts/output
827: */
828: class Hm_Output_ldap_form_car extends Hm_Output_Module {
829: protected function output() {
830: if (!$this->get('ldap_edit')) {
831: return;
832: }
833: $val = get_ldap_value('carlicense', $this);
834: return '<div class="form-floating mb-2">'.
835: '<input placeholder="'.$this->trans('License Plate Number').'" id="ldap_car" type="text" name="ldap_car" '.
836: 'value="'.$this->html_safe($val).'" class="form-control" />'.
837: '<label for="ldap_car">'.$this->trans('License Plate Number').'</label></div>';
838: }
839: }
840:
841: /**
842: * @subpackage ldap_contacts/output
843: */
844: class Hm_Output_ldap_form_org extends Hm_Output_Module {
845: protected function output() {
846: if (!$this->get('ldap_edit')) {
847: return;
848: }
849: $val = get_ldap_value('o', $this);
850: return '<div class="form-floating mb-2">'.
851: '<input placeholder="'.$this->trans('Organization').'" id="ldap_org" type="text" name="ldap_org" '.
852: 'value="'.$this->html_safe($val).'" class="form-control" />'.
853: '<label for="ldap_org">'.$this->trans('Organization').'</label></div>';
854: }
855: }
856:
857: /**
858: * @subpackage ldap_contacts/output
859: */
860: class Hm_Output_ldap_form_org_unit extends Hm_Output_Module {
861: protected function output() {
862: if (!$this->get('ldap_edit')) {
863: return;
864: }
865: $val = get_ldap_value('ou', $this);
866: return '<div class="form-floating mb-2">'.
867: '<input placeholder="'.$this->trans('Organization Unit').'" id="ldap_org_unit" type="text" name="ldap_org_unit" '.
868: 'value="'.$this->html_safe($val).'" class="form-control" />'.
869: '<label for="ldap_org_unit">'.$this->trans('Organization Unit').'</label></div>';
870: }
871: }
872:
873: /**
874: * @subpackage ldap_contacts/output
875: */
876: class Hm_Output_ldap_form_org_dpt extends Hm_Output_Module {
877: protected function output() {
878: if (!$this->get('ldap_edit')) {
879: return;
880: }
881: $val = get_ldap_value('departmentnumber', $this);
882: return '<div class="form-floating mb-2">'.
883: '<input placeholder="'.$this->trans('Department Number').'" id="ldap_org_dpt" type="text" name="ldap_org_dpt" '.
884: 'value="'.$this->html_safe($val).'" class="form-control" />'.
885: '<label for="ldap_org_dpt">'.$this->trans('Department Number').'</label></div>';
886: }
887: }
888:
889: /**
890: * @subpackage ldap_contacts/output
891: */
892: class Hm_Output_ldap_form_emp_num extends Hm_Output_Module {
893: protected function output() {
894: if (!$this->get('ldap_edit')) {
895: return;
896: }
897: $val = get_ldap_value('employeenumber', $this);
898: return '<div class="form-floating mb-2">'.
899: '<input placeholder="'.$this->trans('Employee Number').'" id="ldap_emp_num" type="text" name="ldap_emp_num" '.
900: 'value="'.$this->html_safe($val).'" class="form-control" />'.
901: '<label for="ldap_emp_num">'.$this->trans('Employee Number').'</label></div>';
902: }
903: }
904:
905: /**
906: * @subpackage ldap_contacts/output
907: */
908: class Hm_Output_ldap_form_emp_type extends Hm_Output_Module {
909: protected function output() {
910: if (!$this->get('ldap_edit')) {
911: return;
912: }
913: $val = get_ldap_value('employeetype', $this);
914: return '<div class="form-floating mb-2">'.
915: '<input placeholder="'.$this->trans('Employment Type').'" id="ldap_emp_type" type="text" name="ldap_emp_type" '.
916: 'value="'.$this->html_safe($val).'" class="form-control" />'.
917: '<label for="ldap_emp_type">'.$this->trans('Employment Type').'</label></div>';
918: }
919: }
920:
921: /**
922: * @subpackage ldap_contacts/output
923: */
924: class Hm_Output_ldap_form_lang extends Hm_Output_Module {
925: protected function output() {
926: if (!$this->get('ldap_edit')) {
927: return;
928: }
929: $val = get_ldap_value('preferredlanguage', $this);
930: return '<div class="form-floating mb-2">'.
931: '<input placeholder="'.$this->trans('Language').'" id="ldap_lang" type="text" name="ldap_lang" '.
932: 'value="'.$this->html_safe($val).'" class="form-control" />'.
933: '<label for="ldap_lang">'.$this->trans('Language').'</label></div>';
934: }
935: }
936:
937: /**
938: * @subpackage ldap_contacts/output
939: */
940: class Hm_Output_ldap_form_uri extends Hm_Output_Module {
941: protected function output() {
942: if (!$this->get('ldap_edit')) {
943: return;
944: }
945: $val = get_ldap_value('labeleduri', $this);
946: return '<div class="form-floating mb-2">'.
947: '<input placeholder="'.$this->trans('Website').'" id="ldap_uri" type="text" name="ldap_uri" '.
948: 'value="'.$this->html_safe($val).'" class="form-control" />'.
949: '<label for="ldap_uri">'.$this->trans('Website').'</label></div>';
950: }
951: }
952:
953: /**
954: * @subpackage ldap_contacts/output
955: */
956: class Hm_Output_ldap_form_locality extends Hm_Output_Module {
957: protected function output() {
958: if (!$this->get('ldap_edit')) {
959: return;
960: }
961: $val = get_ldap_value('l', $this);
962: return '<div class="form-floating mb-2">'.
963: '<input placeholder="'.$this->trans('Locality').'" id="ldap_locality" type="text" name="ldap_locality" '.
964: 'value="'.$this->html_safe($val).'" class="form-control" />'.
965: '<label for="ldap_locality">'.$this->trans('Locality').'</label></div>';
966: }
967: }
968:
969: /**
970: * @subpackage ldap_contacts/output
971: */
972: class Hm_Output_ldap_form_street extends Hm_Output_Module {
973: protected function output() {
974: if (!$this->get('ldap_edit')) {
975: return;
976: }
977: $val = get_ldap_value('street', $this);
978: return '<div class="form-floating mb-2">'.
979: '<input placeholder="'.$this->trans('Street').'" id="ldap_street" type="text" name="ldap_street" '.
980: 'value="'.$this->html_safe($val).'" class="form-control" />'.
981: '<label for="ldap_street">'.$this->trans('Street').'</label></div>';
982: }
983: }
984:
985: /**
986: * @subpackage ldap_contacts/output
987: */
988: class Hm_Output_ldap_form_state extends Hm_Output_Module {
989: protected function output() {
990: if (!$this->get('ldap_edit')) {
991: return;
992: }
993: $val = get_ldap_value('st', $this);
994: return '<div class="form-floating mb-2">'.
995: '<input placeholder="'.$this->trans('State').'" id="ldap_state" type="text" name="ldap_state" '.
996: 'value="'.$this->html_safe($val).'" class="form-control" />'.
997: '<label for="ldap_state">'.$this->trans('State').'</label></div>';
998: }
999: }
1000:
1001: /**
1002: * @subpackage ldap_contacts/output
1003: */
1004: class Hm_Output_ldap_form_postalcode extends Hm_Output_Module {
1005: protected function output() {
1006: if (!$this->get('ldap_edit')) {
1007: return;
1008: }
1009: $val = get_ldap_value('postalcode', $this);
1010: return '<div class="form-floating mb-2">'.
1011: '<input placeholder="'.$this->trans('Postal Code').'" id="ldap_postalcode" type="text" name="ldap_postalcode" '.
1012: 'value="'.$this->html_safe($val).'" class="form-control" />'.
1013: '<label for="ldap_postalcode">'.$this->trans('Postal Code').'</label></div>';
1014: }
1015: }
1016:
1017: /**
1018: * @subpackage ldap_contacts/functions
1019: */
1020: if (!hm_exists('get_ldap_value')) {
1021: function get_ldap_value($fld, $mod) {
1022: $current = $mod->get('current_ldap_contact');
1023: if (!is_array($current) || !array_key_exists('all_fields', $current)) {
1024: return '';
1025: }
1026:
1027: if ($fld === 'uidattr') {
1028: $all_fields = $current['all_fields'];
1029: if (isset($all_fields['dn'])) {
1030: $dn = $all_fields['dn'];
1031: if (strpos($dn, 'uid=') === 0) {
1032: return 'uid';
1033: } elseif (strpos($dn, 'cn=') === 0) {
1034: return 'cn';
1035: }
1036: }
1037: if (isset($all_fields['uid']) && !empty($all_fields['uid'])) {
1038: return 'uid';
1039: }
1040: return 'uid';
1041: }
1042:
1043: if (array_key_exists($fld, $current['all_fields'])) {
1044: return $current['all_fields'][$fld];
1045: }
1046: if (array_key_exists($fld, $current)) {
1047: return $current[$fld];
1048: }
1049: return '';
1050: }}
1051:
1052: /**
1053: * @subpackage ldap_contacts/functions
1054: */
1055: if (!hm_exists('fetch_ldap_contacts')) {
1056: function fetch_ldap_contacts($config, $user_config, $contact_store, $session=false) {
1057: $ldap_config = ldap_config($config);
1058: $ldap_config = ldap_add_user_auth($ldap_config, $user_config->get('ldap_contacts_auth_setting', array()));
1059: if (count($ldap_config) > 0) {
1060: foreach ($ldap_config as $name => $vals) {
1061: if (is_array($vals)) {
1062: $vals['name'] = $name;
1063: }
1064: $ldap = new Hm_LDAP_Contacts($vals);
1065: if ($ldap->connect()) {
1066: $contacts = $ldap->fetch();
1067: if (count($contacts) > 0) {
1068: $contact_store->import($contacts);
1069: }
1070: }
1071: }
1072: }
1073: return array($contact_store, $ldap_config);
1074: }}
1075:
1076: /**
1077: * @subpackage ldap_contacts/functions
1078: */
1079: if (!hm_exists('ldap_add_user_auth')) {
1080: function ldap_add_user_auth($ldap_config, $auths) {
1081: if (!is_array($ldap_config) || !is_array($auths)) {
1082: return $ldap_config;
1083: }
1084: foreach ($auths as $name => $vals) {
1085: if (array_key_exists($name, $ldap_config)) {
1086: if (array_key_exists('user', $vals)) {
1087: if (!$vals['user']) {
1088: continue;
1089: }
1090: $uid_attr = $ldap_config[$name]['ldap_uid_attr'] ?? 'uid';
1091: $user = sprintf('%s=%s,%s', $uid_attr, $vals['user'], $ldap_config[$name]['base_dn']);
1092: $ldap_config[$name]['user'] = $user;
1093: }
1094: if (array_key_exists('pass', $vals)) {
1095: $ldap_config[$name]['pass'] = $vals['pass'];
1096: }
1097: }
1098: }
1099: return $ldap_config;
1100: }}
1101:
1102: /**
1103: * @subpackage ldap_contacts/functions
1104: */
1105: if (!hm_exists('ldap_config')) {
1106: function ldap_config($config, $key=false) {
1107: $details = $config->dump()['ldap'];
1108: if ($key && array_key_exists($key, $details)) {
1109: return $details[$key];
1110: }
1111: return $details;
1112: }}
1113: