RaspberryIO/Unosquare.Swan/Networking/Ldap/Message.cs
2019-12-03 18:44:25 +01:00

292 lines
9.5 KiB
C#

using System;
using System.Collections.Generic;
namespace Unosquare.Swan.Networking.Ldap {
/// <summary>
/// The class performs token processing from strings.
/// </summary>
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<String> _elements;
private String _source;
/// <summary>
/// Initializes a new instance of the <see cref="Tokenizer" /> class.
/// Initializes a new class instance with a specified string to process
/// and the specified token delimiters to use.
/// </summary>
/// <param name="source">String to tokenize.</param>
/// <param name="delimiters">String containing the delimiters.</param>
/// <param name="retDel">if set to <c>true</c> [ret delete].</param>
public Tokenizer(String source, String delimiters, Boolean retDel = false) {
this._elements = new List<String>();
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<String>();
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);
}
}
}
/// <summary>
/// Represents an Ldap Matching Rule Assertion.
/// <pre>
/// MatchingRuleAssertion ::= SEQUENCE {
/// matchingRule [1] MatchingRuleId OPTIONAL,
/// type [2] AttributeDescription OPTIONAL,
/// matchValue [3] AssertionValue,
/// dnAttributes [4] BOOLEAN DEFAULT FALSE }
/// </pre></summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1Sequence" />
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));
}
}
}
/// <summary>
/// The AttributeDescriptionList is used to list attributes to be returned in
/// a search request.
/// <pre>
/// AttributeDescriptionList ::= SEQUENCE OF
/// AttributeDescription
/// </pre></summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1SequenceOf" />
internal class RfcAttributeDescriptionList : Asn1SequenceOf {
public RfcAttributeDescriptionList(String[] attrs)
: base(attrs?.Length ?? 0) {
if(attrs == null) {
return;
}
foreach(String attr in attrs) {
this.Add(attr);
}
}
}
/// <summary>
/// Represents an Ldap Search Request.
/// <pre>
/// 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 }
/// </pre>
/// </summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1Sequence" />
/// <seealso cref="Unosquare.Swan.Networking.Ldap.IRfcRequest" />
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();
}
/// <summary>
/// Represents an Ldap Substring Filter.
/// <pre>
/// SubstringFilter ::= SEQUENCE {
/// type AttributeDescription,
/// -- at least one must be present
/// substrings SEQUENCE OF CHOICE {
/// initial [0] LdapString,
/// any [1] LdapString,
/// final [2] LdapString } }
/// </pre>
/// </summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1Sequence" />
internal class RfcSubstringFilter : Asn1Sequence {
public RfcSubstringFilter(String type, Asn1Object substrings)
: base(2) {
this.Add(type);
this.Add(substrings);
}
}
/// <summary>
/// Represents an Ldap Attribute Value Assertion.
/// <pre>
/// AttributeValueAssertion ::= SEQUENCE {
/// attributeDesc AttributeDescription,
/// assertionValue AssertionValue }
/// </pre>
/// </summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1Sequence" />
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();
}
/// <summary> Encapsulates an Ldap Bind properties.</summary>
internal class BindProperties {
/// <summary>
/// Initializes a new instance of the <see cref="BindProperties" /> class.
/// </summary>
/// <param name="version">The version.</param>
/// <param name="dn">The dn.</param>
/// <param name="method">The method.</param>
/// <param name="anonymous">if set to <c>true</c> [anonymous].</param>
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;
}
}
}