<?php

/**
 * LDAP contact modules
 * @package modules
 * @subpackage ldap_contacts
 */

if (!defined('DEBUG_MODE')) { die(); }

require APP_PATH.'modules/ldap_contacts/hm-ldap-contacts.php';

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_process_add_ldap_contact_from_message extends Hm_Handler_Module {
    public function process() {
        $ldap_config = $this->get('ldap_config');
        list($success, $form) = $this->process_form(array('contact_source', 'contact_value'));
        if (!$success) {
            return;
        }
        list($type, $source) = explode(':', $form['contact_source']);
        if ($type == 'ldap' && array_key_exists($source, $ldap_config)) {
            $addresses = Hm_Address_Field::parse($form['contact_value']);
            $config = $ldap_config[$source];
            if (count($config) == 0) {
                Hm_Msgs::add('Unable to add contact', 'danger');
                return;
            }
            $ldap = new Hm_LDAP_Contacts($config);
            if (!empty($addresses)) {
                $contacts = $this->get('contact_store');
                if ($ldap->connect()) {
                    foreach ($addresses as $vals) {
                        $atts = array('mail' => $vals['email'], 'objectclass' => $config['objectclass']);
                        if (array_key_exists('name', $vals) && trim($vals['name'])) {
                            $dn = sprintf('cn=%s,%s', $vals['name'], $config['base_dn']);
                            $atts['sn'] = $vals['name'];
                            $atts['cn'] = $vals['name'];
                            $atts['displayname'] = $vals['name'];
                        }
                        else {
                            $dn = sprintf('cn=%s,%s', str_replace(array('<', '>'), '', $vals['email']), $config['base_dn']);
                            $atts['cn'] = str_replace(array('<', '>'), '', $vals['email']);
                            $atts['sn'] = $atts['cn'];
                        }
                        if ($ldap->add($atts, $dn)) {
                            Hm_Msgs::add('Contact Added');
                        }
                        else {
                            Hm_Msgs::add('Unable to add contact', 'danger');
                        }
                    }
                }
            }
        }
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_process_delete_ldap_contact extends Hm_Handler_Module {
    public function process() {
        $contacts = $this->get('contact_store');
        $ldap_config = ldap_config($this->config);
        $sources = array_keys($ldap_config);
        list($success, $form) = $this->process_form(array('contact_type', 'contact_source', 'contact_id'));
        if ($success && $form['contact_type'] == 'ldap' && in_array($form['contact_source'], $sources, true)) {
            $config = $ldap_config[$form['contact_source']];
            
            // For LDAP contacts, skip contact ID lookup and use DN-based lookup directly
            $contacts = $this->get('contact_store');
            $dn = null;
            $contact = null;
            
            // Get DN from request data
            $target_dn = null;
            if (isset($this->request->post['ldap_dn']) && !empty($this->request->post['ldap_dn'])) {
                $target_dn = $this->request->post['ldap_dn'];
            } elseif (isset($this->request->get['dn'])) {
                $target_dn = Hm_LDAP_Contact::decodeDN($this->request->get['dn']);
            } elseif (isset($this->request->post['dn'])) {
                $target_dn = Hm_LDAP_Contact::decodeDN($this->request->post['dn']);
            }
            
            if ($target_dn) {
                $contact = Hm_LDAP_Contact::findByDN($contacts, $target_dn, $form['contact_source']);
                $dn = $target_dn;
            }
            
            if (!$contact || !$dn) {
                Hm_Msgs::add('Unable to find contact to delete', 'danger');
                return;
            }
            
            $ldap = new Hm_LDAP_Contacts($config);
            if ($ldap->connect()) {
                if ($ldap->delete($dn)) {
                    Hm_Msgs::add('Contact Deleted');
                    $this->out('contact_deleted', 1);
                }
                else {
                    if (is_resource($ldap->fh) || (is_object($ldap->fh) && get_class($ldap->fh) === 'LDAP\Connection')) {
                        $ldap_error = @ldap_error($ldap->fh);
                        $ldap_errno = @ldap_errno($ldap->fh);
                    } else {
                        $ldap_error = '';
                        $ldap_errno = 0;
                    }
                    
                    switch ($ldap_errno) {
                        case 50: // LDAP_INSUFFICIENT_ACCESS
                            Hm_Msgs::add('Permission denied: You do not have sufficient privileges to delete contacts from this LDAP directory', 'danger');
                            break;
                        case 66: // LDAP_NOT_ALLOWED_ON_NONLEAF
                            Hm_Msgs::add('Cannot delete: This contact has dependent entries that must be deleted first', 'danger');
                            break;
                        default:
                            Hm_Msgs::add('Could not delete contact', 'danger');
                            break;
                    }
                }
            }
            else {
                Hm_Msgs::add('Could not connect to LDAP server', 'danger');
            }
        }
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_process_ldap_fields extends Hm_Handler_Module {
    public function process() {
        $form = $this->get('ldap_entry_data', array());
        if (!is_array($form) || count($form) == 0) {
            return;
        }
        $config = ldap_config($this->config, $form['ldap_source']);
        $uidattr = isset($form['ldap_uidattr']) ? $form['ldap_uidattr'] : 'cn';
        $uid_val = '';

        if ($uidattr === 'uid') {
            if (!empty($form['ldap_uid'])) {
                $uid_val = $form['ldap_uid'];
            } else {
                Hm_Msgs::add('Username is required for UID-based contacts', 'danger');
                return;
            }
        } else {
            $uid_val = trim($form['ldap_first_name'].' '.$form['ldap_last_name']);
        }

        $dn = sprintf('%s=%s,%s', $uidattr, $uid_val, $config['base_dn']);

        $result = array('objectclass' => $config['objectclass']);
        // Always include cn as it's required by most objectclasses
        $cn = trim($form['ldap_first_name'].' '.$form['ldap_last_name']);
        $result['cn'] = $cn;
        
        if ($uidattr === 'uid' && !empty($form['ldap_uid'])) {
            $result['uid'] = $form['ldap_uid'];
        }
        
        $ldap_map = array(
            'ldap_first_name' => 'givenname',
            'ldap_last_name' => 'sn',
            'ldap_displayname' => 'displayname',
            'ldap_uid' => 'uid',
            'ldap_mail' => 'mail',
            'ldap_locality' => 'l',
            'ldap_state' => 'st',
            'ldap_street' => 'street',
            'ldap_postalcode' => 'postalcode',
            'ldap_title' => 'title',
            'ldap_phone' => 'telephonenumber',
            'ldap_fax' => 'facsimiletelephonenumber',
            'ldap_mobile' => 'mobile',
            'ldap_room' => 'roomnumber',
            'ldap_car' => 'carlicense',
            'ldap_org' => 'o',
            'ldap_org_unit' => 'ou',
            'ldap_org_dpt' => 'departmentnumber',
            'ldap_emp_num' => 'employeenumber',
            'ldap_emp_type' => 'employeetype',
            'ldap_lang' => 'preferredlanguage',
            'ldap_uri' => 'labeleduri'
        );
        foreach ($ldap_map as $name => $val) {
            // Skip uid attribute when uidattr is 'cn' to avoid empty uid values
            if ($name === 'ldap_uid' && $uidattr === 'cn') {
                continue;
            }      
            if (array_key_exists($name, $form)) {
                $result[$val] = $form[$name];
            }
            elseif (array_key_exists($name, $this->request->post) && trim($this->request->post[$name])) {
                $result[$val] = $this->request->post[$name];
            }
        }
        $this->out('entry_dn', $dn);
        $this->out('ldap_entry_data', $result, false);
        $this->out('ldap_config', $config, false);
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_process_update_ldap_server extends Hm_Handler_Module {
    public function process() {
        if ($this->get('ldap_action') != 'update') {
            return;
        }
        $entry = $this->get('ldap_entry_data', array());
        if (!is_array($entry) || count($entry) == 0) {
            return;
        }
        $dn = get_ldap_value('dn', $this);
        $config = $this->get('ldap_config');
        $ldap = new Hm_LDAP_Contacts($config);
        if ($ldap->connect()) {
            if ($ldap->modify($entry, $dn)) {
                Hm_Msgs::add('Contact Updated');
                $this->save_hm_msgs();
                Hm_Dispatch::page_redirect('?page=contacts');
            }
            else {
                Hm_Msgs::add('Unable to update contact', 'danger');
            }
        } else {
            Hm_Msgs::add('Unable to connect to LDAP server', 'danger');
        }
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_process_add_to_ldap_server extends Hm_Handler_Module {
    public function process() {
        if ($this->get('ldap_action') != 'add') {
            return;
        }
        $entry = $this->get('ldap_entry_data', array());
        if (!is_array($entry) || count($entry) == 0) {
            return;
        }

        $config = $this->get('ldap_config');
        $dn = $this->get('entry_dn');
        $ldap = new Hm_LDAP_Contacts($config);
        if ($ldap->connect()) {
            if ($ldap->add($entry, $dn)) {
                Hm_Msgs::add('Contact Added');
            }
            else {
                Hm_Msgs::add('Could not add contact', 'danger');
            }
        } else {
            Hm_Msgs::add('Could not add contact: failed to connect to LDAP server', 'danger');
        }
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_process_update_ldap_contact extends Hm_Handler_Module {
    public function process() {
        list($success, $form) = $this->process_form(array('ldap_source', 'contact_source',
            'ldap_first_name', 'update_ldap_contact', 'ldap_last_name', 'ldap_mail', 'ldap_uidattr'));
        if ($success && $form['contact_source'] == 'ldap') {
            if (isset($form['ldap_uidattr']) && $form['ldap_uidattr'] === 'uid') {
                if (empty($this->request->post['ldap_uid'])) {
                    Hm_Msgs::add('Username is required when using UID attribute', 'danger');
                    return;
                }
                $form['ldap_uid'] = $this->request->post['ldap_uid'];
            } else {
                if (isset($this->request->post['ldap_uid'])) {
                    $form['ldap_uid'] = $this->request->post['ldap_uid'];
                }
            }
            
            $this->out('ldap_entry_data', $form, false);
            $this->out('ldap_action', 'update');
        }
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_process_add_ldap_contact extends Hm_Handler_Module {
    public function process() {
        list($success, $form) = $this->process_form(array('contact_source', 'ldap_first_name',
            'add_ldap_contact', 'ldap_last_name', 'ldap_mail', 'ldap_source', 'ldap_uidattr'));
        
        if ($success && $form['contact_source'] == 'ldap') {
            if (isset($form['ldap_uidattr']) && $form['ldap_uidattr'] === 'uid') {
                if (empty($this->request->post['ldap_uid'])) {
                    return;
                }
                $form['ldap_uid'] = $this->request->post['ldap_uid'];
            } else {
                if (isset($this->request->post['ldap_uid'])) {
                    $form['ldap_uid'] = $this->request->post['ldap_uid'];
                }
            }
            
            $this->out('ldap_entry_data', $form, false);
            $this->out('ldap_action', 'add');
        }
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_load_ldap_contacts extends Hm_Handler_Module {
    public function process() {
        $contacts = $this->get('contact_store');
        list($contacts, $ldap_config) = fetch_ldap_contacts($this->config, $this->user_config, $contacts);
        $this->append('contact_sources', 'ldap');
        $edit = false;
        $sources = array();
        foreach ($ldap_config as $name => $vals) {
            if (is_array($vals) && array_key_exists('read_write', $vals) && $vals['read_write']) {
                $this->append('contact_edit', sprintf('ldap:%s', $name));
                $sources[] = $name;
                $edit = true;
            }
        }
        $this->out('ldap_edit', $edit);
        $this->out('ldap_sources', $sources);
        $this->out('contact_store', $contacts, false);
        $this->out('ldap_config', $ldap_config, false);
    }
}
/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_load_edit_ldap_contact extends Hm_Handler_Module {
    public function process() {
        $ldap_config = $this->get('ldap_config');
        if (array_key_exists('contact_source', $this->request->get) &&
            array_key_exists('contact_type', $this->request->get) &&
            $this->request->get['contact_type'] == 'ldap' &&
            array_key_exists($this->request->get['contact_source'], $ldap_config) &&
            array_key_exists('contact_id', $this->request->get)) {

            $contacts = $this->get('contact_store');
            $contact_id = $this->request->get['contact_id'];
            $contact_source = $this->request->get['contact_source'];
            
            $target_dn = isset($this->request->get['dn']) ? Hm_LDAP_Contact::decodeDN($this->request->get['dn']) : null;
            
            if ($target_dn) {
                $contact = Hm_LDAP_Contact::findByDN($contacts, $target_dn, $contact_source);
                
                if ($contact) {
                    $current = $contact->export();
                    $current['id'] = $contact_id;
                    $this->out('current_ldap_contact', $current);
                    $this->handler_response['ldap_edit'] = true;
                }
            }
            
        } else {
            $missing = array();
            if (!array_key_exists('contact_source', $this->request->get)) $missing[] = 'contact_source';
            if (!array_key_exists('contact_type', $this->request->get)) $missing[] = 'contact_type';
            if (!array_key_exists('contact_id', $this->request->get)) $missing[] = 'contact_id';
            if (array_key_exists('contact_source', $this->request->get) && !array_key_exists($this->request->get['contact_source'], $ldap_config)) $missing[] = 'ldap_config_for_source';
            
            error_log("LDAP Edit: Handler skipped due to missing: " . implode(', ', $missing));
        }
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_process_ldap_auth_settings extends Hm_Handler_Module {
    public function process() {
        if (!array_key_exists('save_settings', $this->request->post)) {
            return;
        }
        $connections = $this->get('ldap_contact_connections');
        $users = array();
        $passwords = array();
        $results = $connections;
        if (array_key_exists('ldap_usernames', $this->request->post)) {
            $users = $this->request->post['ldap_usernames'];
        }
        if (array_key_exists('ldap_passwords', $this->request->post)) {
            $passwords = $this->request->post['ldap_passwords'];
        }
        foreach ($connections as $name => $vals) {
            if (array_key_exists($name, $users)) {
                $results[$name]['user'] = $users[$name];
            }
            if (array_key_exists($name, $passwords)) {
                $results[$name]['pass'] = $passwords[$name];
            }
        }
        if (count($results) > 0) {
            $new_settings = $this->get('new_user_settings');
            $new_settings['ldap_contacts_auth_setting'] = $results;
            $this->out('new_user_settings', $new_settings, false);
        }
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_load_ldap_settings extends Hm_Handler_Module {
    public function process() {
        $connections = array();
        foreach (ldap_config($this->config) as $name => $vals) {
            if (is_array($vals) && array_key_exists('auth', $vals) && $vals['auth']) {
                if ((!array_key_exists('user', $vals) || !$vals['user']) &&
                    (!array_key_exists('pass', $vals) || !$vals['pass'])) {
                    $connections[$name] = $vals;
                }
            }
        }
        $this->out('ldap_contacts_auth', $this->user_config->get('ldap_contacts_auth_setting'));
        $this->out('ldap_contact_connections', $connections);
    }
}

/**
 * @subpackage ldap_contacts/handler
 */
class Hm_Handler_ldap_send_to_contact extends Hm_Handler_Module {
    public function process() {
        if (!$this->get('compose_draft') && 
            array_key_exists('contact_id', $this->request->get) &&
            array_key_exists('contact_type', $this->request->get) &&
            array_key_exists('contact_source', $this->request->get) &&
            array_key_exists('dn', $this->request->get) &&
            $this->request->get['contact_type'] == 'ldap') {
            
            $contacts = $this->get('contact_store');
            $contact_id = $this->request->get['contact_id'];
            $contact_source = $this->request->get['contact_source'];
            
            // For LDAP contacts, use DN-based lookup directly (contact_id is unreliable for LDAP)
            $target_dn = Hm_LDAP_Contact::decodeDN($this->request->get['dn']);
            $contact = Hm_LDAP_Contact::findByDN($contacts, $target_dn, $contact_source);
            
            if ($contact) {
                $to = sprintf('%s <%s>', $contact->value('display_name'), $contact->value('email_address'));
                $this->out('compose_draft', array('draft_to' => $to, 'draft_subject' => '', 'draft_body' => ''));
            }
        }
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_auth_settings extends Hm_Output_Module {
    protected function output() {
        $connections = $this->get('ldap_contact_connections', array());
        $auths = $this->get('ldap_contacts_auth', array());
        if (count($connections) > 0) {
            $res = '<tr><td data-target=".ldap_settings" colspan="2" class="settings_subtitle cursor-pointer border-bottom p-2">'.
                '<i class="bi bi-people-fill fs-5 me-2"></i>'.
                $this->trans('LDAP Addressbooks').'</td></tr>';
            foreach ($connections as $name => $con) {
                $user = '';
                $pass = false;
                if (array_key_exists($name, $auths)) {
                    $user = $auths[$name]['user'];
                    if (array_key_exists('pass', $auths[$name]) && $auths[$name]['pass']) {
                        $pass = true;
                    }
                }
                $res .= '<tr class="ldap_settings"><td>'.$this->html_safe($name).'</td><td>';
                $res .= '<input autocomplete="username" type="text" value="'.$user.'" name="ldap_usernames['.$this->html_safe($name).']" ';
                $res .= 'placeholder="'.$this->trans('Username').'" class="form-control mb-2" /> <input type="password"  class="form-control"';
                if ($pass) {
                    $res .= 'disabled="disabled" placeholder="'.$this->trans('Password saved').'" ';
                    $res .= 'name="ldap_passwords['.$this->html_safe($name).']" /> <input type="button" ';
                    $res .= 'value="'.$this->trans('Unlock').'" class="ldap_password_change btn btn-primary" /></td></tr>';
                }
                else {
                    $res .= 'autocomplete="new-password" placeholder="'.$this->trans('Password').'" ';
                    $res .= 'name="ldap_passwords['.$this->html_safe($name).']" /></td></tr>';
                }
            }
            return $res;
        }
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_contact_form_end extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        return '</div></form></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_first_name extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $name = get_ldap_value('givenname', $this);
        $current = $this->get('current_ldap_contact');
        $is_edit = !empty($current);
        
        if ($is_edit) {
            $uidattr = get_ldap_value('uidattr', $this);
            if ($uidattr === 'cn') {
                // For CN-based contacts, make first name read-only to prevent DN conflicts
                return '<div class="form-floating mb-2">'.
                    '<input placeholder="'.$this->trans('First Name').'" id="ldap_first_name" '.
                    'type="text" name="ldap_first_name" value="'.$this->html_safe($name).'" class="form-control" readonly />'.
                    '<label for="ldap_first_name">'.$this->trans('First Name').' ('.$this->trans('Read Only').')</label></div>';
            }
        }

        return '<div class="form-floating mb-2">'.
            '<input required placeholder="'.$this->trans('First Name').'" id="ldap_first_name" '.
            'type="text" name="ldap_first_name" value="'.$this->html_safe($name).'" class="form-control" />'.
            '<label for="ldap_first_name">'.$this->trans('First Name').' *</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_submit extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $label = 'Add';
        $name = 'add_ldap_contact';
        if ($this->get('current_ldap_contact')) {
            $label = 'Update';
            $name = 'update_ldap_contact';
        }
        return '<br /><input name="'.$name.'" type="submit" value="'.$this->trans($label).'" class="btn btn-primary me-1" />'.
            '<input type="button" class="reset_contact btn btn-secondary" value="'.$this->trans('Cancel').'" />';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_last_name extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $name = get_ldap_value('sn', $this);
        $current = $this->get('current_ldap_contact');
        $is_edit = !empty($current);
        
        if ($is_edit) {
            $uidattr = get_ldap_value('uidattr', $this);
            if ($uidattr === 'cn') {
                // For CN-based contacts, make last name read-only to prevent DN conflicts
                return '<div class="form-floating mb-2">'.
                    '<input placeholder="'.$this->trans('Last Name').'" id="ldap_last_name" type="text" '.
                    'name="ldap_last_name" value="'.$this->html_safe($name).'" class="form-control" readonly />'.
                    '<label for="ldap_last_name">'.$this->trans('Last Name').' ('.$this->trans('Read Only').')</label></div>';
            }
        }

        return '<div class="form-floating mb-2">'.
            '<input required placeholder="'.$this->trans('Last Name').'" id="ldap_last_name" type="text" '.
            'name="ldap_last_name" value="'.$this->html_safe($name).'" class="form-control" />'.
            '<label for="ldap_last_name">'.$this->trans('Last Name').' *</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_title extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $title = get_ldap_value('title', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Title').'" id="ldap_title" type="text" name="ldap_title" '.
            'value="'.$this->html_safe($title).'" class="form-control"  />'.
            '<label for="ldap_title">'.$this->trans('Title').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_contact_form_start extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $sources = $this->get('ldap_sources');
        $title = $this->trans('Add LDAP');
        $form_class='contact_form';
        $current = $this->get('current_ldap_contact');
        $current_source = false;
        if ($current) {
            $form_class = 'contact_update_form mt-3';
            $current_source = $current['source'];
            $title = sprintf($this->trans('Update LDAP - %s'), $this->html_safe($current_source));
        }
        $source_html = '';
        if ($current_source) {
            $source_html = '<input type="hidden" name="ldap_source" value="'.$this->html_safe($current_source).'" />';
        } else {
            $source_select = '<select id="ldap_source" name="ldap_source" class="form-select">';
            foreach ($sources as $name) {
                $source_select .= '<option value="'.$this->html_safe($name).'">'.$this->html_safe($name).'</option>';
            }
            $source_select .= '</select>';
            $source_html = '<div class="form-floating mb-2">'.$source_select.'<label for="ldap_source">'.$this->trans('Source').'</label></div>';
        }
        return '<div class="add_contact_responsive"><form class="add_contact_form" method="POST">'.
            '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />'.
            '<button class="server_title mt-2 btn btn-light"><i class="bi bi-person-add me-2"></i>'.$title.'</button>'.
            '<div class="'.$form_class.'"><input type="hidden" name="contact_source" value="ldap" />'.$source_html;
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_displayname extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('displayname', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Display Name').'" id="ldap_displayname" type="text" name="ldap_displayname" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_displayname">'.$this->trans('Display Name').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */

class Hm_Output_ldap_form_uidattr extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('uidattr', $this);
        $current = $this->get('current_ldap_contact');
        $is_edit = !empty($current);
        $options = array('cn', 'uid');
        
        if ($is_edit) {
            $select = '<div class="form-floating mb-2">'.
                '<input placeholder="'.$this->trans('UID Attribute').'" id="ldap_uidattr_display" type="text" '.
                'value="'.$this->html_safe($this->trans($val)).'" class="form-control" readonly />'.
                '<label for="ldap_uidattr_display">'.$this->trans('UID Attribute').' ('.$this->trans('Read Only').')</label></div>';
            
            $select .= '<input type="hidden" name="ldap_uidattr" value="'.$this->html_safe($val).'" />';
            
            $uid_val = get_ldap_value('uid', $this);
            
            // Username field - also read-only in edit mode
            if ($val === 'uid' && !empty($uid_val)) {
                $select .= '<div class="form-floating mb-2">'.
                    '<input placeholder="'.$this->trans('Username').'" id="ldap_uid_display" type="text" '.
                    'value="'.$this->html_safe($uid_val).'" class="form-control" readonly />'.
                    '<label for="ldap_uid_display">'.$this->trans('Username').' ('.$this->trans('Read Only').')</label></div>';
                
                $select .= '<input type="hidden" name="ldap_uid" value="'.$this->html_safe($uid_val).'" />';
            }
        } else {
            $select = '<div class="form-floating mb-2">';
            
            $select .= '<select id="ldap_uidattr" name="ldap_uidattr" class="form-select">';
            foreach ($options as $opt) {
                $selected = ($val == $opt) ? ' selected' : '';
                $select .= '<option value="'.$this->html_safe($opt).'"'.$selected.'>'.$this->trans($opt).'</option>';
            }
            $select .= '</select>';
            $select .= '<label for="ldap_uidattr">'.$this->trans('UID Attribute').'</label></div>';
            
            $uid_val = get_ldap_value('uid', $this);
            
            $required = ($val === 'uid') ? ' required' : '';
            $hidden_class = ($val !== 'uid') ? ' d-none' : '';
            $select .= '<div class="form-floating mb-2'.$hidden_class.'" id="ldap_uid_field_wrapper">'.
                '<input placeholder="'.$this->trans('Username').'" id="ldap_uid" type="text" name="ldap_uid" '.
                'value="'.$this->html_safe($uid_val).'" class="form-control" autocomplete="username"'.$required.' />'.
                '<label for="ldap_uid">'.$this->trans('Username').'</label></div>';
        }
        
        return $select;
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_dn_display extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $current = $this->get('current_ldap_contact');
        if (empty($current)) {
            return; // Only show DN in edit mode
        }
        
        $dn = get_ldap_value('dn', $this);
        if (empty($dn)) {
            return;
        }
        
        return '<div class="form-floating mb-2">'.
            '<input type="text" id="ldap_dn_display" class="form-control" value="'.$this->html_safe($dn).'" readonly />'.
            '<label for="ldap_dn_display">'.$this->trans('Distinguished Name').' ('.$this->trans('Read Only').')</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_mail extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('email_address', $this);
        return '<div class="form-floating mb-2">'.
            '<input required placeholder="'.$this->trans('E-mail Address').'" id="ldap_mail" type="email" name="ldap_mail" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_mail">'.$this->trans('E-mail Address').' *</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_phone extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('phone_number', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Telephone Number').'" id="ldap_phone" type="text" name="ldap_phone" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_phone">'.$this->trans('Telephone Number').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_fax extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('facsimiletelephonenumber', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Fax Number').'" id="ldap_fax" type="text" name="ldap_fax" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_fax">'.$this->trans('Fax Number').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_mobile extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('mobile', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Mobile Number').'" id="ldap_mobile" type="text" name="ldap_mobile" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_mobile">'.$this->trans('Mobile Number').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_room extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('roomnumber', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Room Number').'" id="ldap_room" type="text" name="ldap_room" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_room">'.$this->trans('Room Number').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_car extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('carlicense', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('License Plate Number').'" id="ldap_car" type="text" name="ldap_car" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_car">'.$this->trans('License Plate Number').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_org extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('o', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Organization').'" id="ldap_org" type="text" name="ldap_org" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_org">'.$this->trans('Organization').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_org_unit extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('ou', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Organization Unit').'" id="ldap_org_unit" type="text" name="ldap_org_unit" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_org_unit">'.$this->trans('Organization Unit').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_org_dpt extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('departmentnumber', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Department Number').'" id="ldap_org_dpt" type="text" name="ldap_org_dpt" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_org_dpt">'.$this->trans('Department Number').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_emp_num extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('employeenumber', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Employee Number').'" id="ldap_emp_num" type="text" name="ldap_emp_num" '.
            'value="'.$this->html_safe($val).'" class="form-control"  />'.
            '<label for="ldap_emp_num">'.$this->trans('Employee Number').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_emp_type extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('employeetype', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Employment Type').'" id="ldap_emp_type" type="text" name="ldap_emp_type" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_emp_type">'.$this->trans('Employment Type').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_lang extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('preferredlanguage', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Language').'" id="ldap_lang" type="text" name="ldap_lang" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_lang">'.$this->trans('Language').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_uri extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('labeleduri', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Website').'" id="ldap_uri" type="text" name="ldap_uri" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_uri">'.$this->trans('Website').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_locality extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('l', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Locality').'" id="ldap_locality" type="text" name="ldap_locality" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_locality">'.$this->trans('Locality').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_street extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('street', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Street').'" id="ldap_street" type="text" name="ldap_street" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_street">'.$this->trans('Street').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_state extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('st', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('State').'" id="ldap_state" type="text" name="ldap_state" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_state">'.$this->trans('State').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/output
 */
class Hm_Output_ldap_form_postalcode extends Hm_Output_Module {
    protected function output() {
        if (!$this->get('ldap_edit')) {
            return;
        }
        $val = get_ldap_value('postalcode', $this);
        return '<div class="form-floating mb-2">'.
            '<input placeholder="'.$this->trans('Postal Code').'" id="ldap_postalcode" type="text" name="ldap_postalcode" '.
            'value="'.$this->html_safe($val).'" class="form-control" />'.
            '<label for="ldap_postalcode">'.$this->trans('Postal Code').'</label></div>';
    }
}

/**
 * @subpackage ldap_contacts/functions
 */
if (!hm_exists('get_ldap_value')) {
function get_ldap_value($fld, $mod) {
    $current = $mod->get('current_ldap_contact');
    if (!is_array($current) || !array_key_exists('all_fields', $current)) {
        return '';
    }
    
    if ($fld === 'uidattr') {
        $all_fields = $current['all_fields'];
        if (isset($all_fields['dn'])) {
            $dn = $all_fields['dn'];
            if (strpos($dn, 'uid=') === 0) {
                return 'uid';
            } elseif (strpos($dn, 'cn=') === 0) {
                return 'cn';
            }
        }
        if (isset($all_fields['uid']) && !empty($all_fields['uid'])) {
            return 'uid';
        }
        return 'uid';
    }
    
    if (array_key_exists($fld, $current['all_fields'])) {
        return $current['all_fields'][$fld];
    }
    if (array_key_exists($fld, $current)) {
        return $current[$fld];
    }
    return '';
}}

/**
 * @subpackage ldap_contacts/functions
 */
if (!hm_exists('fetch_ldap_contacts')) {
function fetch_ldap_contacts($config, $user_config, $contact_store, $session=false) {
    $ldap_config = ldap_config($config);
    $ldap_config = ldap_add_user_auth($ldap_config, $user_config->get('ldap_contacts_auth_setting', array()));
    if (count($ldap_config) > 0) {
        foreach ($ldap_config as $name => $vals) {
            if (is_array($vals)) {
                $vals['name'] = $name;
            }
            $ldap = new Hm_LDAP_Contacts($vals);
            if ($ldap->connect()) {
                $contacts = $ldap->fetch();
                if (count($contacts) > 0) {
                    $contact_store->import($contacts);
                }
            }
        }
    }
    return array($contact_store, $ldap_config);
}}

/**
 * @subpackage ldap_contacts/functions
 */
if (!hm_exists('ldap_add_user_auth')) {
function ldap_add_user_auth($ldap_config, $auths) {
    if (!is_array($ldap_config) || !is_array($auths)) {
        return $ldap_config;
    }
    foreach ($auths as $name => $vals) {
        if (array_key_exists($name, $ldap_config)) {
            if (array_key_exists('user', $vals)) {
                if (!$vals['user']) {
                    continue;
                }
                $uid_attr = $ldap_config[$name]['ldap_uid_attr'] ?? 'uid';
                $user = sprintf('%s=%s,%s', $uid_attr, $vals['user'], $ldap_config[$name]['base_dn']);
                $ldap_config[$name]['user'] = $user;
            }
            if (array_key_exists('pass', $vals)) {
                $ldap_config[$name]['pass'] = $vals['pass'];
            }
        }
    }
    return $ldap_config;
}}

/**
 * @subpackage ldap_contacts/functions
 */
if (!hm_exists('ldap_config')) {
function ldap_config($config, $key=false) {
    $details = $config->dump()['ldap'];
    if ($key && array_key_exists($key, $details)) {
        return $details[$key];
    }
    return $details;
}}
