def LDIF.parse_entry( lines )
header = true
comment = false
change_type = nil
sep = nil
attr = nil
bvalues = []
controls = nil
hash = {}
mods = {}
mod_type = nil
lines.each do |line|
if line =~ /^#/ || ( comment && line[0..0] == ' ' )
comment = true
next
end
next if line =~ /^$/
if line =~ /^-$/ && change_type == LDAP_MOD_REPLACE
next
end
line.chomp!
if md = line.match( /^[^ ].*?((:[:<]?) ?)/ )
if sep == '::'
if mod_type
mods[mod_type][attr][-1] =
base64_decode( mods[mod_type][attr][-1] )
bvalues << attr if unsafe_char?( mods[mod_type][attr][-1] )
else
hash[attr][-1] = base64_decode( hash[attr][-1] )
bvalues << attr if unsafe_char?( hash[attr][-1] )
end
end
attr, val = line.split( md[1], 2 )
attr.downcase!
if attr !~ /^(?:(?:\d+\.)*\d+|[[:alnum:]-]+)(?:;[[:alnum:]-]+)*$/
raise LDIFError, "Invalid attribute: #{attr}"
end
if attr == 'dn'
header = false
change_type = nil
controls = []
end
sep = md[2]
val = read_file( val ) if sep == ':<'
case attr
when 'version'
if header
if val != '1'
raise LDIFError, "Unsupported LDIF version: #{val}"
else
header = false
next
end
end
when 'changetype'
change_type = case val
when 'add' then LDAP_MOD_ADD
when 'delete' then LDAP_MOD_DELETE
when 'modify' then LDAP_MOD_REPLACE
when /^modr?dn$/ then :MODRDN
end
raise LDIFError, "Invalid change type: #{attr}" unless change_type
when 'add', 'delete', 'replace'
unless change_type == LDAP_MOD_REPLACE
raise LDIFError, "Cannot #{attr} here."
end
mod_type = case attr
when 'add' then LDAP_MOD_ADD
when 'delete' then LDAP_MOD_DELETE
when 'replace' then LDAP_MOD_REPLACE
end
mods[mod_type] ||= {}
mods[mod_type][val] ||= []
when 'control'
oid, criticality = val.split( / /, 2 )
unless oid =~ /(?:\d+\.)*\d+/
raise LDIFError, "Bad control OID: #{oid}"
end
if criticality
md = criticality.match( /(:[:<]?) ?/ )
ctl_sep = md[1] if md
criticality, value = criticality.split( /:[:<]? ?/, 2 )
if criticality !~ /^(?:true|false)$/
raise LDIFError, "Bad control criticality: #{criticality}"
end
criticality = eval( criticality )
end
if value
value = base64_decode( value ) if ctl_sep == '::'
value = read_file( value ) if ctl_sep == ':<'
value = Control.encode( value )
end
controls << Control.new( oid, value, criticality )
else
if change_type == :MODRDN && attr == 'deleteoldrdn'
val = val == '1' ? true : false
end
if change_type == LDAP_MOD_REPLACE
mods[mod_type][attr] << val
else
hash[attr] ||= []
hash[attr] << val
end
comment = false
bvalues << attr if unsafe_char?( val )
end
else
if sep == ':' && line[0..0] != ' ' || comment
raise LDIFError, "Improperly continued line: #{line}"
end
line[0] = '' if line[0..0] == ' '
if change_type == LDAP_MOD_REPLACE
mods[mod_type][attr][-1] << line
else
hash[attr][-1] << line
end
end
end
if sep == '::'
if mod_type
mods[mod_type][attr][-1] =
base64_decode( mods[mod_type][attr][-1] )
bvalues << attr if unsafe_char?( mods[mod_type][attr][-1] )
else
hash[attr][-1] = base64_decode( hash[attr][-1] )
bvalues << attr if unsafe_char?( hash[attr][-1] )
end
end
dn = hash.delete( 'dn' )[0]
bvalues.delete( 'dn' )
change_type ||= LDAP_MOD_ADD
case change_type
when LDAP_MOD_ADD
mods[LDAP_MOD_ADD] = []
hash.each do |attr,val|
if bvalues.include?( attr )
ct = LDAP_MOD_ADD | LDAP_MOD_BVALUES
else
ct = LDAP_MOD_ADD
end
mods[LDAP_MOD_ADD] << LDAP.mod( ct, attr, val )
end
when LDAP_MOD_DELETE
when LDAP_MOD_REPLACE
raise LDIFError, "mods should not be empty" if mods == {}
new_mods = {}
mods.each do |mod_type,attrs|
attrs.each_key do |attr|
if bvalues.include?( attr )
mt = mod_type | LDAP_MOD_BVALUES
else
mt = mod_type
end
new_mods[mt] ||= {}
new_mods[mt][attr] = mods[mod_type][attr]
end
end
mods = new_mods
when :MODRDN
end
Record.new( dn, change_type, hash, mods, controls )
end