class ActiveLdap::Adapter::JndiConnection

def add(dn, records)

def add(dn, records)
  attributes = BasicAttributes.new
  records.each do |record|
    attributes.put(record.to_java_attribute)
  end
  @context.set_request_controls([])
  @context.create_subcontext(escape_dn(dn), attributes)
end

def bind_as_anonymous

def bind_as_anonymous
  setup_context(nil, nil, "none")
  bound?
end

def bound?

def bound?
  not @context.nil?
end

def build_paged_results_control(page_size, page_cookie=nil)

def build_paged_results_control(page_size, page_cookie=nil)
  PagedResultsControl.new(page_size, page_cookie, Control::CRITICAL)
end

def build_raw_search_result(search_result)

def build_raw_search_result(search_result)
  attributes = {}
  search_result.attributes.get_all.each do |attribute|
    attributes[attribute.get_id] = attribute.get_all.collect do |value|
      value.is_a?(String) ? value : String.from_java_bytes(value)
    end
  end
  [search_result.name_in_namespace, attributes]
end

def delete(dn)

def delete(dn)
  escaped_dn = escape_dn(dn)
  @context.set_request_controls([])
  @context.destroy_subcontext(escaped_dn)
end

def escape_dn(dn)

def escape_dn(dn)
  javax.naming.ldap.LdapName.new(dn)
rescue Java::JavaLang::IllegalArgumentException, Java::JavaxNaming::InvalidNameException
  dn
end

def initialize(host, port, method, timeout, follow_referrals)

def initialize(host, port, method, timeout, follow_referrals)
  @host = host
  @port = port
  @method = method
  @timeout = timeout
  @context = nil
  @tls = nil
  @follow_referrals = follow_referrals
end

def ldap_uri

def ldap_uri
  protocol = @method == :ssl ? "ldaps" : "ldap"
  "#{protocol}://#{@host}:#{@port}/"
end

def modify(dn, records)

def modify(dn, records)
  items = records.collect(&:to_java_modification_item)
  @context.set_request_controls([])
  @context.modify_attributes(escape_dn(dn), items.to_java(ModificationItem))
end

def modify_rdn(dn, new_rdn, delete_old_rdn)

def modify_rdn(dn, new_rdn, delete_old_rdn)
  # should use mutex
  delete_rdn_key = "java.naming.ldap.deleteRDN"
  @context.set_request_controls([])
  begin
    @context.add_to_environment(delete_rdn_key, delete_old_rdn.to_s)
    @context.rename(escape_dn(dn), escape_dn(new_rdn))
  ensure
    @context.remove_from_environment(delete_rdn_key)
  end
end

def sasl_bind(bind_dn, mechanism, quiet)

def sasl_bind(bind_dn, mechanism, quiet)
  setup_context(bind_dn, password, mechanism)
  bound?
end

def search(options)

def search(options)
  base = options[:base]
  scope = options[:scope]
  filter = options[:filter]
  attributes = options[:attributes]
  limit = options[:limit]
  use_paged_results = options[:use_paged_results]
  page_size = options[:page_size]
  controls = SearchControls.new
  controls.search_scope = scope
  controls.count_limit = limit if limit
  unless attributes.blank?
    controls.returning_attributes = attributes.to_java(:string)
  end
  page_cookie = nil
  if use_paged_results
    # https://devdocs.io/openjdk~8/javax/naming/ldap/pagedresultscontrol
    @context.set_request_controls([build_paged_results_control(page_size)])
  else
    @context.set_request_controls([])
  end
  escaped_base = escape_dn(base)
  loop do
    @context.search(escaped_base, filter, controls).each do |search_result|
      yield(build_raw_search_result(search_result))
    end
    break unless use_paged_results
    # Find the paged search cookie
    response_controls = @context.get_response_controls
    break unless response_controls
    response_controls.each do |response_control|
      next unless response_control.is_a?(PagedResultsResponseControl)
      page_cookie = response_control.get_cookie
      break
    end
    break unless page_cookie
    # Set paged results control so we can keep getting results.
    paged_results_control =
      build_paged_results_control(page_size, page_cookie)
    @context.set_request_controls([paged_results_control])
  end
end

def setup_context(bind_dn, password, authentication)

def setup_context(bind_dn, password, authentication)
  unbind
  environment = {
    Context::INITIAL_CONTEXT_FACTORY => "com.sun.jndi.ldap.LdapCtxFactory",
    Context::PROVIDER_URL => ldap_uri,
    'com.sun.jndi.ldap.connect.timeout' => (@timeout * 1000).to_i.to_s,
    'com.sun.jndi.ldap.read.timeout' => (@timeout * 1000).to_i.to_s,
    'java.naming.ldap.derefAliases' => 'never',
    'java.naming.referral' => @follow_referrals ? 'follow' : 'ignore',
  }
  context = InitialLdapContext.new(HashTable.new(environment), nil)
  if @method == :start_tls
    @tls = context.extended_operation(StartTlsRequest.new)
    @tls.negotiate
  end
  context.add_to_environment(Context::SECURITY_AUTHENTICATION,
                             authentication)
  if bind_dn
    context.add_to_environment(Context::SECURITY_PRINCIPAL, bind_dn)
  end
  if password
    context.add_to_environment(Context::SECURITY_CREDENTIALS, password)
  end
  context.reconnect(nil)
  @context = context
end

def simple_bind(bind_dn, password)

def simple_bind(bind_dn, password)
  setup_context(bind_dn, password, "simple")
  bound?
end

def unbind

def unbind
  @tls.close if @tls
  @tls = nil
  @context.close if @context
  @context = nil
end