{
  $Project$
  $Workfile$
  $Revision$
  $DateUTC$
  $Id$

  This file is part of the Indy (Internet Direct) project, and is offered
  under the dual-licensing agreement described on the Indy website.
  (http://www.indyproject.org/)

  Copyright:
   (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
  $Log$
}
{
  Rev 1.0    15/04/2005 7:25:06 AM  GGrieve
  first ported to INdy
}

unit IdLDAPV3Coder;

interface

uses
  IdASN1Coder,
  IdLdapV3;

type
  TIdLDAPV3Encoder = Class (TIdASN1Encoder)
  private
    procedure WriteAuthentication(oAuthentication: TIdLDAPv3AuthenticationChoice);
    procedure WriteFilterList(oFilterList : TIdLDAPV3FilterList; iTag : integer);
    procedure WriteAttributeValueAssertion(oAttributeValueAssertion : TIdLDAPV3AttributeValueAssertion; iTag : integer);
    procedure WriteSubstring(oSubstring : TIdLDAPV3Substring; iTag : integer);
    procedure WriteSubstringFilter(oSubstringFilter : TIdLDAPV3SubstringFilter; iTag : integer);
    procedure WriteMatchingRuleAssertion(oMatchingRuleAssertion : TIdLDAPV3MatchingRuleAssertion; iTag : integer);
    procedure WriteFilter(oFilter : TIdLDAPV3Filter; iTag : integer);
    procedure WriteAttributes(oAttributes : TIdLDAPV3AttributeDescriptionList; iTag : integer);
    procedure WriteReferral(oReferral : TIdLDAPV3Referral; iTag : integer);
    procedure WritePartialAttributeList(oAttributes : TIdLDAPV3PartialAttributeList; iTag : integer);
    procedure WritePartialAttribute(oAttribute : TIdLDAPV3PartialAttribute; iTag : integer);

    procedure WriteResultInner(oResult : TIdLDAPV3LDAPResult);

    procedure WritebindRequest(oBindRequest : TIdLDAPV3BindRequest);
    procedure WritebindResponse(oBindResponse : TIdLDAPV3BindResponse);
    procedure WriteunbindRequest(oUnbindRequest : TIdLDAPV3UnbindRequest);
    procedure WritesearchRequest(oSearchRequest : TIdLDAPV3SearchRequest);
    procedure WritesearchResEntry(oSearchResEntry : TIdLDAPV3SearchResultEntry);
    procedure WritesearchResDone(oSearchResDone : TIdLDAPV3SearchResultDone);
    procedure WritesearchResRef(oSearchResRef : TIdLDAPV3SearchResultReference);
    procedure WritemodifyRequest(oModifyRequest : TIdLDAPV3ModifyRequest);
    procedure WritemodifyResponse(oModifyResponse : TIdLDAPV3ModifyResponse);
    procedure WriteaddRequest(oAddRequest : TIdLDAPV3AddRequest);
    procedure WriteaddResponse(oAddResponse : TIdLDAPV3AddResponse);
    procedure WritedelRequest(oDelRequest : TIdLDAPV3DelRequest);
    procedure WritedelResponse(oDelResponse : TIdLDAPV3DelResponse);
    procedure WritemodDNRequest(oModDNRequest : TIdLDAPV3ModifyDNRequest);
    procedure WritemodDNResponse(oModDNResponse : TIdLDAPV3ModifyDNResponse);
    procedure WritecompareRequest(oCompareRequest : TIdLDAPV3CompareRequest);
    procedure WritecompareResponse(oCompareResponse : TIdLDAPV3CompareResponse);
    procedure WriteabandonRequest(oAbandonRequest : TIdLDAPV3AbandonRequest);
    procedure WriteextendedReq(oExtendedReq : TIdLDAPV3ExtendedRequest);
    procedure WriteextendedResp(oExtendedResp : TIdLDAPV3ExtendedResponse);
  public
    procedure Produce(oMessage : TIdLDAPV3Message); Overload; Virtual;
  end;

type
  TIdLDAPV3Decoder = Class (TIdASN1Decoder)
  private
    procedure ReadFilter(oFilter : TIdLDAPV3Filter);
    procedure ReadFilterList(oFilterList : TIdLDAPV3FilterList);
    procedure ReadAttributes(oAttributes : TIdLDAPV3AttributeDescriptionList);
    procedure ReadAttributeValueAssertion(oEqualityMatch : TIdLDAPV3AttributeValueAssertion);
    procedure ReadSubstringFilter(oSubstrings : TIdLDAPV3SubstringFilter);
    procedure ReadMatchingRuleAssertion(oExtensibleMatch : TIdLDAPV3MatchingRuleAssertion);
    procedure ReadSubString(oString : TIdLDAPV3Substring);
    procedure ReadPartialAttribute(oAttribute : TIdLDAPV3PartialAttribute);
    procedure ReadPartialAttributeList(oAttributes : TIdLDAPV3PartialAttributeList);

    procedure ReadReferral(oReferral : TIdLDAPV3Referral);
    procedure ReadResultInner(oResult : TIdLDAPV3LDAPResult);

    procedure ReadBindRequest(oMessage : TIdLDAPV3Message);
    procedure ReadBindResponse(oMessage : TIdLDAPV3Message);
    procedure ReadSearchRequest(oMessage : TIdLDAPV3Message);
    procedure ReadSearchResultEntry(oMessage : TIdLDAPV3Message);
    procedure ReadSearchResultDone(oMessage : TIdLDAPV3Message);
  public
    procedure ReadMessage(oMessage : TIdLDAPV3Message); Overload; Virtual;
  end;


implementation

uses
  SysUtils,
  IdException;

{ TIdLDAPV3Encoder }

procedure TIdLDAPV3Encoder.Produce(oMessage: TIdLDAPV3Message);
begin
  StartWriting;
  StartSequence;
  WriteInteger(oMessage.messageID);
  if assigned(oMessage.bindRequest) then
    WritebindRequest(oMessage.bindRequest) // TIdLDAPV3BindRequest
  else if assigned(oMessage.bindResponse) then
    WritebindResponse(oMessage.bindResponse) // TIdLDAPV3BindResponse
  else if assigned(oMessage.unbindRequest) then
    WriteunbindRequest(oMessage.unbindRequest) // TIdLDAPV3UnbindRequest
  else if assigned(oMessage.searchRequest) then
    WritesearchRequest(oMessage.searchRequest) // TIdLDAPV3SearchRequest
  else if assigned(oMessage.searchResEntry) then
    WritesearchResEntry(oMessage.searchResEntry) // TIdLDAPV3SearchResultEntry
  else if assigned(oMessage.searchResDone) then
    WritesearchResDone(oMessage.searchResDone) // TIdLDAPV3SearchResultDone
  else if assigned(oMessage.searchResRef) then
    WritesearchResRef(oMessage.searchResRef) // TIdLDAPV3SearchResultReference
  else if assigned(oMessage.modifyRequest) then
    WritemodifyRequest(oMessage.modifyRequest) // TIdLDAPV3ModifyRequest
  else if assigned(oMessage.modifyResponse) then
    WritemodifyResponse(oMessage.modifyResponse) // TIdLDAPV3ModifyResponse
  else if assigned(oMessage.addRequest) then
    WriteaddRequest(oMessage.addRequest) // TIdLDAPV3AddRequest
  else if assigned(oMessage.addResponse) then
    WriteaddResponse(oMessage.addResponse) // TIdLDAPV3AddResponse
  else if assigned(oMessage.delRequest) then
    WritedelRequest(oMessage.delRequest) // TIdLDAPV3DelRequest
  else if assigned(oMessage.delResponse) then
    WritedelResponse(oMessage.delResponse) // TIdLDAPV3DelResponse
  else if assigned(oMessage.modDNRequest) then
    WritemodDNRequest(oMessage.modDNRequest) // TIdLDAPV3ModifyDNRequest
  else if assigned(oMessage.modDNResponse) then
    WritemodDNResponse(oMessage.modDNResponse) // TIdLDAPV3ModifyDNResponse
  else if assigned(oMessage.compareRequest) then
    WritecompareRequest(oMessage.compareRequest) // TIdLDAPV3CompareRequest
  else if assigned(oMessage.compareResponse) then
    WritecompareResponse(oMessage.compareResponse) // TIdLDAPV3CompareResponse
  else if assigned(oMessage.abandonRequest) then
    WriteabandonRequest(oMessage.abandonRequest) // TIdLDAPV3AbandonRequest
  else if assigned(oMessage.extendedReq) then
    WriteextendedReq(oMessage.extendedReq) // TIdLDAPV3ExtendedRequest
  else if assigned(oMessage.extendedResp) then
    WriteextendedResp(oMessage.extendedResp) // TIdLDAPV3ExtendedResponse
  else
    raise EIdException.create('No Protocol Choice operation on message');
  StopSequence;
  StopWriting;
end;

procedure TIdLDAPV3Encoder.WritebindRequest(oBindRequest : TIdLDAPV3BindRequest);
begin
  StartSequence(0);
  WriteInteger(oBindRequest.version);
  WriteString(oBindRequest.name);
  WriteAuthentication(oBindRequest.authentication);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WritebindResponse(oBindResponse : TIdLDAPV3BindResponse);
begin
  StartSequence(1);
  WriteResultInner(oBindResponse);
  if oBindResponse.serverSaslCreds <> '' then
    WriteString(oBindResponse.serverSaslCreds);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WriteunbindRequest(oUnbindRequest : TIdLDAPV3UnbindRequest);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WritesearchRequest(oSearchRequest : TIdLDAPV3SearchRequest);
begin
  StartSequence(3);
  WriteString(oSearchRequest.baseObject);
  WriteEnum(ord(oSearchRequest.scope));
  WriteEnum(ord(oSearchRequest.derefAliases));
  WriteInteger(oSearchRequest.sizeLimit);
  WriteInteger(oSearchRequest.timeLimit);
  WriteBoolean(oSearchRequest.typesOnly);
  WriteFilter(osearchRequest.filter, -1);
  WriteAttributes(osearchRequest.attributes, -1);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WritesearchResEntry(oSearchResEntry : TIdLDAPV3SearchResultEntry);
begin
  StartSequence(4);
  WriteString(oSearchResEntry.objectName);
  WritePartialAttributeList(oSearchResEntry.attributes, -1);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WritesearchResDone(oSearchResDone : TIdLDAPV3SearchResultDone);
begin
  StartSequence(5);
  WriteResultInner(oSearchResDone);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WritesearchResRef(oSearchResRef : TIdLDAPV3SearchResultReference);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WritemodifyRequest(oModifyRequest : TIdLDAPV3ModifyRequest);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WritemodifyResponse(oModifyResponse : TIdLDAPV3ModifyResponse);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WriteaddRequest(oAddRequest : TIdLDAPV3AddRequest);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WriteaddResponse(oAddResponse : TIdLDAPV3AddResponse);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WritedelRequest(oDelRequest : TIdLDAPV3DelRequest);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WritedelResponse(oDelResponse : TIdLDAPV3DelResponse);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WritemodDNRequest(oModDNRequest : TIdLDAPV3ModifyDNRequest);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WritemodDNResponse(oModDNResponse : TIdLDAPV3ModifyDNResponse);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WritecompareRequest(oCompareRequest : TIdLDAPV3CompareRequest);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WritecompareResponse(oCompareResponse : TIdLDAPV3CompareResponse);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WriteabandonRequest(oAbandonRequest : TIdLDAPV3AbandonRequest);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WriteextendedReq(oExtendedReq : TIdLDAPV3ExtendedRequest);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WriteextendedResp(oExtendedResp : TIdLDAPV3ExtendedResponse);
begin
  raise EIdException.create('Not yet implemented');
end;

procedure TIdLDAPV3Encoder.WriteAuthentication(oAuthentication: TIdLDAPv3AuthenticationChoice);
begin
  if assigned(oAuthentication.sasl) then
    raise EIdException.create('sasl not handled yet')
  else
    WriteString(0, oAuthentication.simple);
end;

procedure TIdLDAPV3Encoder.WriteFilter(oFilter : TIdLDAPV3Filter; iTag : integer);
begin
  if assigned(oFilter._and) then
    WriteFilterList(oFilter._And, 0)
  else if assigned(oFilter._or) then
    WriteFilterList(oFilter._or, 1)
  else if assigned(oFilter._not) then
    WriteFilter(oFilter._not, 2)
  else if assigned(oFilter.equalityMatch ) then
    WriteAttributeValueAssertion(oFilter.equalityMatch, 3)
  else if assigned(oFilter.substrings) then
    WriteSubstringFilter(oFilter.substrings, 4)
  else if assigned(oFilter.greaterOrEqual) then
    WriteAttributeValueAssertion(oFilter.greaterOrEqual, 5)
  else if assigned(oFilter.lessOrEqual) then
    WriteAttributeValueAssertion(oFilter.lessOrEqual, 6)
  else if oFilter.present <> '' then
    WriteString(7, oFilter.present)
  else if assigned(oFilter.approxMatch) then
    WriteAttributeValueAssertion(oFilter.approxMatch, 8)
  else if assigned(oFilter.extensibleMatch) then
    WriteMatchingRuleAssertion(oFilter.extensibleMatch, 9)
  else
    raise EIdException.create('No operation Choice on Filter');
end;

procedure TIdLDAPV3Encoder.WriteFilterList(oFilterList : TIdLDAPV3FilterList; iTag : integer);
var
  iLoop : integer;
begin
  for iLoop := 0 to oFilterList.count - 1 do
    writeFilter(oFilterList[iLoop], iTag);
end;

procedure TIdLDAPV3Encoder.WriteAttributeValueAssertion(oAttributeValueAssertion : TIdLDAPV3AttributeValueAssertion; iTag : integer);
begin
  StartSequence(aicContextSpecific, iTag);
  WriteString(oAttributeValueAssertion.attributeDesc);
  WriteString(oAttributeValueAssertion.assertionValue);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WriteSubstring(oSubstring : TIdLDAPV3Substring; iTag : integer);
begin
  StartSequence(aicContextSpecific, iTag);
  if oSubstring.initial <> '' then
    WriteString(0, oSubstring.initial)
  else if oSubstring.any <> '' then
    WriteString(1, oSubstring.any)
  else if oSubstring.final <> '' then
    WriteString(2, oSubstring.final);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WriteSubstringFilter(oSubstringFilter : TIdLDAPV3SubstringFilter; iTag : integer);
var
  iLoop : integer;
begin
  StartSequence(aicContextSpecific, iTag);
  WriteString(oSubstringFilter._type);
  for iLoop := 0 to oSubstringFilter.substrings.count - 1 do
    WriteSubString(oSubstringFilter.substrings[iLoop], -1);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WriteMatchingRuleAssertion(oMatchingRuleAssertion : TIdLDAPV3MatchingRuleAssertion; iTag : integer);
begin
  StartSequence(aicContextSpecific, iTag);
  if oMatchingRuleAssertion.matchingRule <> '' then
    writeString(1, oMatchingRuleAssertion.matchingRule)
  else if oMatchingRuleAssertion._type <> '' then
    writeString(2, oMatchingRuleAssertion._type);
  WriteString(oMatchingRuleAssertion.matchValue);
  if oMatchingRuleAssertion.dnAttributes then
    WriteBoolean(true);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WriteAttributes(oAttributes : TIdLDAPV3AttributeDescriptionList; iTag : integer);
var
  iLoop : integer;
begin
  StartSequence(iTag);
  for iLoop := 0 to oAttributes.Count - 1 do
    WriteString(oAttributes[iLoop]);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WriteResultInner(oResult: TIdLDAPV3LDAPResult);
begin
  WriteEnum(ord(oResult.resultCode));
  WriteString(oResult.matchedDN);
  WriteString(oResult.errorMessage);
  if assigned(oResult.referral) then
    writeReferral(oResult.referral, 3);
end;

procedure TIdLDAPV3Encoder.WriteReferral(oReferral: TIdLDAPV3Referral; iTag : integer);
var
  iLoop : integer;
begin
  StartSequence(aicContextSpecific, iTag);
  for iLoop := 0 to oReferral.Count - 1 do
    WriteString(oReferral[iLoop]);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WritePartialAttributeList(oAttributes: TIdLDAPV3PartialAttributeList; iTag: integer);
var
  iLoop : integer;
begin
  StartSequence(iTag);
  for iLoop := 0 to oAttributes.Count - 1 do
    WritePartialAttribute(oAttributes[iLoop], -1);
  StopSequence;
end;

procedure TIdLDAPV3Encoder.WritePartialAttribute(oAttribute: TIdLDAPV3PartialAttribute; iTag: integer);
var
  iLoop : integer;
begin
  StartSequence(iTag);
  WriteString(oAttribute._type);
  StartSequence(aicUniversal, 17);
  for iLoop := 0 to oAttribute.vals.count - 1 do
    WriteString(oAttribute.vals[iLoop]);
  StopSequence;
  StopSequence;
end;


{ TIdLDAPV3Decoder }

procedure TIdLDAPV3Decoder.ReadBindRequest(oMessage: TIdLDAPV3Message);
begin
  oMessage.bindRequest := TIdLDAPV3BindRequest.Create;
  ReadSequencebegin;
  oMessage.bindRequest.version := ReadInteger;
  oMessage.bindRequest.name := ReadString;
  case NextTag Of
    0: oMessage.bindRequest.authentication.simple := ReadString;
    3: raise EIdException.create('SASL not handled yet');
  else
    raise EIdException.create('Unknown Tag '+IntToStr(NextTag));
  end;
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadBindResponse(oMessage: TIdLDAPV3Message);
begin
  oMessage.bindResponse := TIdLDAPV3BindResponse.Create;
  ReadSequencebegin;
  ReadResultInner(oMessage.BindResponse);
  if not SequenceEnded then
    oMessage.bindResponse.serverSaslCreds := ReadString;
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadFilter(oFilter: TIdLDAPV3Filter);
begin
  case NextTag Of
    0: begin
        oFilter._and := TIdLDAPV3FilterList.Create;
        ReadFilterList(oFilter._and);
       end;
    1: begin
        oFilter._or := TIdLDAPV3FilterList.Create;
        ReadFilterList(oFilter._or);
       end;
    2: begin
        oFilter._not := TIdLDAPV3Filter.Create;
        ReadFilter(oFilter._not);
       end;
    3: begin
        oFilter.equalityMatch  := TIdLDAPV3AttributeValueAssertion.Create;
        ReadAttributeValueAssertion(oFilter.equalityMatch );
       end;
    4: begin
        oFilter.substrings     := TIdLDAPV3SubstringFilter.Create;
        ReadSubstringFilter(oFilter.substrings);
       end;
    5: begin
        oFilter.greaterOrEqual := TIdLDAPV3AttributeValueAssertion.Create;
        ReadAttributeValueAssertion(oFilter.greaterOrEqual);
       end;
    6: begin
        oFilter.lessOrEqual    := TIdLDAPV3AttributeValueAssertion.Create;
        ReadAttributeValueAssertion(oFilter.lessOrEqual);
       end;
    7: oFilter.present := ReadString;
    8: begin
        oFilter.approxMatch    := TIdLDAPV3AttributeValueAssertion.Create;
        ReadAttributeValueAssertion(oFilter.approxMatch);
       end;
    9: begin
        oFilter.extensibleMatch := TIdLDAPV3MatchingRuleAssertion.Create;
        ReadMatchingRuleAssertion(oFilter.extensibleMatch);
       end;
  else
    raise EIdException.create('Unknown Tag '+IntToStr(NextTag));
  end;
end;

procedure TIdLDAPV3Decoder.ReadFilterList(oFilterList : TIdLDAPV3FilterList);
Var
  oFilter : TIdLDAPV3Filter;
begin
  while NextTagType = aitSequence do
  begin
    ReadFilter(oFilter);
    oFilterList.Add(oFilter);
  end;
end;

procedure TIdLDAPV3Decoder.ReadAttributeValueAssertion(oEqualityMatch : TIdLDAPV3AttributeValueAssertion);
begin
  ReadSequencebegin;
  oEqualityMatch.attributeDesc := ReadString;
  oEqualityMatch.assertionValue := ReadString;
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadSubString(oString: TIdLDAPV3Substring);
begin
  ReadSequencebegin;
  case NextTag Of
    0 : oString.initial := ReadString;
    1 : oString.any     := ReadString;
    2 : oString.final   := ReadString;
  else
    raise EIdException.create('Unknown Tag '+IntToStr(NextTag));
  end;
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadSubstringFilter(oSubstrings : TIdLDAPV3SubstringFilter);
Var
  oString : TIdLDAPV3Substring;
begin
  ReadSequencebegin;
  oSubstrings._type := ReadString;
  while not SequenceEnded do
  begin
    ReadSubString(oString);
    oSubstrings.substrings.Add(oString);
  end;
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadMatchingRuleAssertion(oExtensibleMatch : TIdLDAPV3MatchingRuleAssertion);
begin
  ReadSequencebegin;
  if NextTag = 1 then
    oExtensibleMatch.matchingRule := ReadString;
  if NextTag = 2 then
    oExtensibleMatch._type := ReadString;
  oExtensibleMatch.matchValue := ReadString;
  if not SequenceEnded then
    oExtensibleMatch.dnAttributes := ReadBoolean
  else
    oExtensibleMatch.dnAttributes := False;
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadAttributes(oAttributes: TIdLDAPV3AttributeDescriptionList);
begin
  ReadSequencebegin;
  while not SequenceEnded do
    oAttributes.Add(ReadString);
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadSearchRequest(oMessage : TIdLDAPV3Message);
begin
  oMessage.searchRequest := TIdLDAPV3SearchRequest.Create;
  ReadSequencebegin;
  oMessage.searchRequest.baseObject := ReadString;
  oMessage.searchRequest.scope := TIdLDAPV3SearchScope(ReadEnum);
  oMessage.searchRequest.derefAliases := TIdLDAPV3SearchDerefAliases(ReadEnum);
  oMessage.searchRequest.sizeLimit := ReadInteger;
  oMessage.searchRequest.timeLimit := ReadInteger;
  oMessage.searchRequest.typesOnly := ReadBoolean;
  ReadFilter(oMessage.searchRequest.filter);
  ReadAttributes(oMessage.searchRequest.attributes);
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadSearchResultEntry(oMessage: TIdLDAPV3Message);
begin
  oMessage.searchResEntry := TIdLDAPV3SearchResultEntry.Create;
  ReadSequencebegin;
  oMessage.searchResEntry.objectName := ReadString;
  ReadPartialAttributeList(oMessage.searchResEntry.attributes);
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadSearchResultDone(oMessage: TIdLDAPV3Message);
begin
  oMessage.searchResDone := TIdLDAPV3SearchResultDone.Create;
  ReadSequencebegin;
  ReadResultInner(oMessage.searchResDone);
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadMessage(oMessage: TIdLDAPV3Message);
begin
  StartReading;
  oMessage.Clear;
  ReadSequencebegin;
  oMessage.messageID := ReadInteger;
  case NextTag Of
    0  : ReadBindRequest(oMessage);
    1  : ReadBindResponse(oMessage);
//    2  : ReadUnbindRequest(oMessage);
    3  : ReadSearchRequest(oMessage);
    4  : ReadSearchResultEntry(oMessage);
    5  : ReadSearchResultDone(oMessage);
//    6  : ReadModifyRequest(oMessage);
//    7  : ReadModifyResponse(oMessage);
//    8  : ReadAddRequest(oMessage);
//    9  : ReadAddResponse(oMessage);
//    10  : ReadDelRequest(oMessage);
//    11  : ReadDelResponse(oMessage);
//    12  : ReadModifyDNRequest(oMessage);
//    13  : ReadModifyDNResponse(oMessage);
//    14  : ReadCompareRequest(oMessage);
//    15  : ReadCompareResponse(oMessage);
//    16  : ReadAbandonRequest(oMessage);
//    19  : ReadSearchResultReference(oMessage);
//    23  : ReadExtendedRequest(oMessage);
//    24  : ReadExtendedResponse(oMessage);
  else
    raise EIdException.create('Unknown Tag '+IntToStr(NextTag));
  end;
  // while not SequenceEnded do
  //   readControl....
  ReadSequenceend;
  StopReading;
end;

procedure TIdLDAPV3Decoder.ReadResultInner(oResult: TIdLDAPV3LDAPResult);
begin
  oResult.resultCode := TIdLDAPV3ResultCode(ReadEnum);
  oResult.matchedDN := ReadString;
  oResult.errorMessage := ReadString;
  if not SequenceEnded and (NextTag = 3) then
  begin
    oResult.referral := TIdLDAPV3Referral.Create;
    ReadReferral(oResult.Referral);
  end;
end;

procedure TIdLDAPV3Decoder.ReadReferral(oReferral: TIdLDAPV3Referral);
begin
  ReadSequencebegin;
  while not SequenceEnded do
    oReferral.Add(ReadString);
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadPartialAttributeList(oAttributes: TIdLDAPV3PartialAttributeList);
Var
  oAttr : TIdLDAPV3PartialAttribute;
begin
  ReadSequencebegin;
  while not SequenceEnded do
    begin
    ReadPartialAttribute(oAttr);
    oAttributes.Add(oAttr);
    end;
  ReadSequenceend;
end;

procedure TIdLDAPV3Decoder.ReadPartialAttribute(oAttribute: TIdLDAPV3PartialAttribute);
begin
  ReadSequencebegin;
  oAttribute._type := ReadString;
  ReadSequencebegin;
  while not SequenceEnded do
    oAttribute.vals.Add(ReadString);
  ReadSequenceend;
  ReadSequenceend;
end;

End.
