using System; using System.Collections.Generic; namespace Unosquare.Swan.Networking.Ldap { /// /// The class performs token processing from strings. /// internal class Tokenizer { // The tokenizer uses the default delimiter set: the space character, the tab character, the newline character, and the carriage-return character private readonly String _delimiters = " \t\n\r"; private readonly Boolean _returnDelims; private List _elements; private String _source; /// /// Initializes a new instance of the class. /// Initializes a new class instance with a specified string to process /// and the specified token delimiters to use. /// /// String to tokenize. /// String containing the delimiters. /// if set to true [ret delete]. public Tokenizer(String source, String delimiters, Boolean retDel = false) { this._elements = new List(); this._delimiters = delimiters ?? this._delimiters; this._source = source; this._returnDelims = retDel; if(this._returnDelims) { this.Tokenize(); } else { this._elements.AddRange(source.Split(this._delimiters.ToCharArray())); } this.RemoveEmptyStrings(); } public Int32 Count => this._elements.Count; public Boolean HasMoreTokens() => this._elements.Count > 0; public String NextToken() { if(this._source == String.Empty) { throw new InvalidOperationException(); } String result; if(this._returnDelims) { this.RemoveEmptyStrings(); result = this._elements[0]; this._elements.RemoveAt(0); return result; } this._elements = new List(); this._elements.AddRange(this._source.Split(this._delimiters.ToCharArray())); this.RemoveEmptyStrings(); result = this._elements[0]; this._elements.RemoveAt(0); this._source = this._source.Remove(this._source.IndexOf(result, StringComparison.Ordinal), result.Length); this._source = this._source.TrimStart(this._delimiters.ToCharArray()); return result; } private void RemoveEmptyStrings() { for(Int32 index = 0; index < this._elements.Count; index++) { if(this._elements[index] != String.Empty) { continue; } this._elements.RemoveAt(index); index--; } } private void Tokenize() { String tempstr = this._source; if(tempstr.IndexOfAny(this._delimiters.ToCharArray()) < 0 && tempstr.Length > 0) { this._elements.Add(tempstr); } else if(tempstr.IndexOfAny(this._delimiters.ToCharArray()) < 0 && tempstr.Length <= 0) { return; } while(tempstr.IndexOfAny(this._delimiters.ToCharArray()) >= 0) { if(tempstr.IndexOfAny(this._delimiters.ToCharArray()) == 0) { if(tempstr.Length > 1) { this._elements.Add(tempstr.Substring(0, 1)); tempstr = tempstr.Substring(1); } else { tempstr = String.Empty; } } else { String toks = tempstr.Substring(0, tempstr.IndexOfAny(this._delimiters.ToCharArray())); this._elements.Add(toks); this._elements.Add(tempstr.Substring(toks.Length, 1)); tempstr = tempstr.Length > toks.Length + 1 ? tempstr.Substring(toks.Length + 1) : String.Empty; } } if(tempstr.Length > 0) { this._elements.Add(tempstr); } } } /// /// Represents an Ldap Matching Rule Assertion. ///
  /// MatchingRuleAssertion ::= SEQUENCE {
  /// matchingRule    [1] MatchingRuleId OPTIONAL,
  /// type            [2] AttributeDescription OPTIONAL,
  /// matchValue      [3] AssertionValue,
  /// dnAttributes    [4] BOOLEAN DEFAULT FALSE }
  /// 
/// internal class RfcMatchingRuleAssertion : Asn1Sequence { public RfcMatchingRuleAssertion( String matchingRule, String type, SByte[] matchValue, Asn1Boolean dnAttributes = null) : base(4) { if(matchingRule != null) { this.Add(new Asn1Tagged(new Asn1Identifier(1), new Asn1OctetString(matchingRule), false)); } if(type != null) { this.Add(new Asn1Tagged(new Asn1Identifier(2), new Asn1OctetString(type), false)); } this.Add(new Asn1Tagged(new Asn1Identifier(3), new Asn1OctetString(matchValue), false)); // if dnAttributes if false, that is the default value and we must not // encode it. (See RFC 2251 5.1 number 4) if(dnAttributes != null && dnAttributes.BooleanValue()) { this.Add(new Asn1Tagged(new Asn1Identifier(4), dnAttributes, false)); } } } /// /// The AttributeDescriptionList is used to list attributes to be returned in /// a search request. ///
  /// AttributeDescriptionList ::= SEQUENCE OF
  /// AttributeDescription
  /// 
/// internal class RfcAttributeDescriptionList : Asn1SequenceOf { public RfcAttributeDescriptionList(String[] attrs) : base(attrs?.Length ?? 0) { if(attrs == null) { return; } foreach(String attr in attrs) { this.Add(attr); } } } /// /// Represents an Ldap Search Request. ///
  /// SearchRequest ::= [APPLICATION 3] SEQUENCE {
  /// baseObject      LdapDN,
  /// scope           ENUMERATED {
  /// baseObject              (0),
  /// singleLevel             (1),
  /// wholeSubtree            (2) },
  /// derefAliases    ENUMERATED {
  /// neverDerefAliases       (0),
  /// derefInSearching        (1),
  /// derefFindingBaseObj     (2),
  /// derefAlways             (3) },
  /// sizeLimit       INTEGER (0 .. maxInt),
  /// timeLimit       INTEGER (0 .. maxInt),
  /// typesOnly       BOOLEAN,
  /// filter          Filter,
  /// attributes      AttributeDescriptionList }
  /// 
///
/// /// internal class RfcSearchRequest : Asn1Sequence, IRfcRequest { public RfcSearchRequest( String basePath, LdapScope scope, Int32 derefAliases, Int32 sizeLimit, Int32 timeLimit, Boolean typesOnly, String filter, String[] attributes) : base(8) { this.Add(basePath); this.Add(new Asn1Enumerated(scope)); this.Add(new Asn1Enumerated(derefAliases)); this.Add(new Asn1Integer(sizeLimit)); this.Add(new Asn1Integer(timeLimit)); this.Add(new Asn1Boolean(typesOnly)); this.Add(new RfcFilter(filter)); this.Add(new RfcAttributeDescriptionList(attributes)); } public override Asn1Identifier GetIdentifier() => new Asn1Identifier(LdapOperation.SearchRequest); public String GetRequestDN() => ((Asn1OctetString)this.Get(0)).StringValue(); } /// /// Represents an Ldap Substring Filter. ///
  /// SubstringFilter ::= SEQUENCE {
  /// type            AttributeDescription,
  /// -- at least one must be present
  /// substrings      SEQUENCE OF CHOICE {
  /// initial [0] LdapString,
  /// any     [1] LdapString,
  /// final   [2] LdapString } }
  /// 
///
/// internal class RfcSubstringFilter : Asn1Sequence { public RfcSubstringFilter(String type, Asn1Object substrings) : base(2) { this.Add(type); this.Add(substrings); } } /// /// Represents an Ldap Attribute Value Assertion. ///
  /// AttributeValueAssertion ::= SEQUENCE {
  /// attributeDesc   AttributeDescription,
  /// assertionValue  AssertionValue }
  /// 
///
/// internal class RfcAttributeValueAssertion : Asn1Sequence { public RfcAttributeValueAssertion(String ad, SByte[] av) : base(2) { this.Add(ad); this.Add(new Asn1OctetString(av)); } public String AttributeDescription => ((Asn1OctetString)this.Get(0)).StringValue(); public SByte[] AssertionValue => ((Asn1OctetString)this.Get(1)).ByteValue(); } /// Encapsulates an Ldap Bind properties. internal class BindProperties { /// /// Initializes a new instance of the class. /// /// The version. /// The dn. /// The method. /// if set to true [anonymous]. public BindProperties( Int32 version, String dn, String method, Boolean anonymous) { this.ProtocolVersion = version; this.AuthenticationDN = dn; this.AuthenticationMethod = method; this.Anonymous = anonymous; } public Int32 ProtocolVersion { get; } public String AuthenticationDN { get; } public String AuthenticationMethod { get; } public Boolean Anonymous { get; } } }