///|
/// Encode a filter into its BER wire form (RFC 4511 4.5.1.7).
pub fn encode_filter(f : LdapFilter) -> Bytes {
match f {
And(items) => {
let mut content = Bytes::new(0)
for item in items {
content = content + encode_filter(item)
}
encode_tlv(context_tag(0, true), content)
}
Or(items) => {
let mut content = Bytes::new(0)
for item in items {
content = content + encode_filter(item)
}
encode_tlv(context_tag(1, true), content)
}
Not(inner) => encode_tlv(context_tag(2, true), encode_filter(inner))
Equality(attr, value) =>
encode_tlv(
context_tag(3, true),
encode_octet_string(@utf8.encode(attr)) + encode_octet_string(value),
)
Substrings(attr, sf) => {
let mut sub = Bytes::new(0)
match sf.initial {
Some(i) => sub = sub + encode_tlv(context_tag(0, false), i)
None => ()
}
for seg in sf.any {
sub = sub + encode_tlv(context_tag(1, false), seg)
}
match sf.final_ {
Some(f) => sub = sub + encode_tlv(context_tag(2, false), f)
None => ()
}
let content = encode_octet_string(@utf8.encode(attr)) +
encode_sequence([sub])
encode_tlv(context_tag(4, true), content)
}
GreaterOrEqual(attr, value) =>
encode_tlv(
context_tag(5, true),
encode_octet_string(@utf8.encode(attr)) + encode_octet_string(value),
)
LessOrEqual(attr, value) =>
encode_tlv(
context_tag(6, true),
encode_octet_string(@utf8.encode(attr)) + encode_octet_string(value),
)
Present(attr) => encode_tlv(context_tag(7, false), @utf8.encode(attr))
Approx(attr, value) =>
encode_tlv(
context_tag(8, true),
encode_octet_string(@utf8.encode(attr)) + encode_octet_string(value),
)
Extensible(em) => {
let mut content = Bytes::new(0)
match em.matching_rule {
Some(r) =>
content = content + encode_tlv(context_tag(1, false), @utf8.encode(r))
None => ()
}
match em.attr_type {
Some(t) =>
content = content + encode_tlv(context_tag(2, false), @utf8.encode(t))
None => ()
}
content = content + encode_tlv(context_tag(3, false), em.match_value)
if em.dn_attributes {
content = content +
encode_tlv(context_tag(4, false), Bytes::from_array([0xFF]))
}
encode_tlv(context_tag(9, true), content)
}
}
}
///|
pub fn LdapFilter::to_bytes(self : LdapFilter) -> Bytes {
encode_filter(self)
}
///|
/// Decode a filter from its BER wire form.
pub fn decode_filter(
bytes : Bytes,
limits : BerLimits?,
) -> Result[LdapFilter, LdapError] {
result_of_ldap(fn() raise LdapError {
let value = match decode_ber(bytes, limits) {
Ok(v) => v
Err(e) => raise LdapError::Ber(e)
}
decode_filter_value(value)
})
}
///|
fn decode_filter_value(value : BerValue) -> LdapFilter raise LdapError {
if value.tag.class != ContextSpecific {
raise LdapError::Decode("filter tag must be context-specific")
}
match value.tag.number {
0 => {
let items : Array[LdapFilter] = []
for child in value.children_or_empty() {
items.push(decode_filter_value(child))
}
And(items)
}
1 => {
let items : Array[LdapFilter] = []
for child in value.children_or_empty() {
items.push(decode_filter_value(child))
}
Or(items)
}
2 => {
let kids = value.children_or_empty()
if kids.length() != 1 {
raise LdapError::Decode("NOT filter must have exactly one child")
}
Not(decode_filter_value(kids[0]))
}
3 => {
let (attr, val) = decode_ava(value)
Equality(attr, val)
}
4 => {
let kids = value.children_or_empty()
if kids.length() != 2 {
raise LdapError::Decode(
"SUBSTRING filter must have type and substrings",
)
}
let attr = as_ldap_string(kids[0])
let sub_parts = kids[1].children_or_empty()
let mut initial : Bytes? = None
let any : Array[Bytes] = []
let mut final_ : Bytes? = None
for part in sub_parts {
if part.tag.class == ContextSpecific {
match part.tag.number {
0 => initial = Some(part.content)
1 => any.push(part.content)
2 => final_ = Some(part.content)
_ => raise LdapError::Decode("invalid substring part tag")
}
} else {
raise LdapError::Decode("invalid substring part")
}
}
Substrings(attr, SubstringFilter::new(initial, any, final_))
}
5 => {
let (attr, val) = decode_ava(value)
GreaterOrEqual(attr, val)
}
6 => {
let (attr, val) = decode_ava(value)
LessOrEqual(attr, val)
}
7 => Present(@utf8.decode_lossy(value.content[:]))
8 => {
let (attr, val) = decode_ava(value)
Approx(attr, val)
}
9 => {
let kids = value.children_or_empty()
let mut rule : String? = None
let mut attr_type : String? = None
let mut match_value = Bytes::new(0)
let mut dn_attrs = false
for kid in kids {
if kid.tag.class == ContextSpecific {
match kid.tag.number {
1 => rule = Some(@utf8.decode_lossy(kid.content[:]))
2 => attr_type = Some(@utf8.decode_lossy(kid.content[:]))
3 => match_value = kid.content
4 =>
dn_attrs = !kid.content.is_empty() &&
kid.content.get(0) != Some(0)
_ => raise LdapError::Decode("invalid extensible match part")
}
} else {
raise LdapError::Decode("invalid extensible match part")
}
}
Extensible(ExtensibleMatch::new(rule, attr_type, match_value, dn_attrs))
}
_ => raise LdapError::Decode("unknown filter tag \{value.tag.number}")
}
}
///|
fn decode_ava(value : BerValue) -> (String, Bytes) raise LdapError {
let kids = value.children_or_empty()
if kids.length() != 2 {
raise LdapError::Decode("attribute value assertion must have two elements")
}
(as_ldap_string(kids[0]), as_octets(kids[1]))
}