]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.util/src/org/argeo/util/directory/ldap/LdifParser.java
Use runtime namespace context as default.
[lgpl/argeo-commons.git] / org.argeo.util / src / org / argeo / util / directory / ldap / LdifParser.java
1 package org.argeo.util.directory.ldap;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.InputStreamReader;
7 import java.io.Reader;
8 import java.nio.charset.Charset;
9 import java.nio.charset.StandardCharsets;
10 import java.util.ArrayList;
11 import java.util.Base64;
12 import java.util.List;
13 import java.util.SortedMap;
14 import java.util.TreeMap;
15
16 import javax.naming.InvalidNameException;
17 import javax.naming.NamingException;
18 import javax.naming.directory.Attribute;
19 import javax.naming.directory.Attributes;
20 import javax.naming.directory.BasicAttribute;
21 import javax.naming.directory.BasicAttributes;
22 import javax.naming.ldap.LdapName;
23 import javax.naming.ldap.Rdn;
24
25 import org.argeo.util.naming.LdapAttrs;
26
27 /** Basic LDIF parser. */
28 public class LdifParser {
29 private final static Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
30
31 protected Attributes addAttributes(SortedMap<LdapName, Attributes> res, int lineNumber, LdapName currentDn,
32 Attributes currentAttributes) {
33 try {
34 Rdn nameRdn = currentDn.getRdn(currentDn.size() - 1);
35 Attribute nameAttr = currentAttributes.get(nameRdn.getType());
36 if (nameAttr == null)
37 currentAttributes.put(nameRdn.getType(), nameRdn.getValue());
38 else if (!nameAttr.get().equals(nameRdn.getValue()))
39 throw new IllegalStateException(
40 "Attribute " + nameAttr.getID() + "=" + nameAttr.get() + " not consistent with DN " + currentDn
41 + " (shortly before line " + lineNumber + " in LDIF file)");
42 Attributes previous = res.put(currentDn, currentAttributes);
43 return previous;
44 } catch (NamingException e) {
45 throw new IllegalStateException("Cannot add " + currentDn, e);
46 }
47 }
48
49 /** With UTF-8 charset */
50 public SortedMap<LdapName, Attributes> read(InputStream in) throws IOException {
51 try (Reader reader = new InputStreamReader(in, DEFAULT_CHARSET)) {
52 return read(reader);
53 } finally {
54 try {
55 in.close();
56 } catch (IOException e) {
57 // silent
58 }
59 }
60 }
61
62 /** Will close the reader. */
63 public SortedMap<LdapName, Attributes> read(Reader reader) throws IOException {
64 SortedMap<LdapName, Attributes> res = new TreeMap<LdapName, Attributes>();
65 try {
66 List<String> lines = new ArrayList<>();
67 try (BufferedReader br = new BufferedReader(reader)) {
68 String line;
69 while ((line = br.readLine()) != null) {
70 lines.add(line);
71 }
72 }
73 if (lines.size() == 0)
74 return res;
75 // add an empty new line since the last line is not checked
76 if (!lines.get(lines.size() - 1).equals(""))
77 lines.add("");
78
79 LdapName currentDn = null;
80 Attributes currentAttributes = null;
81 StringBuilder currentEntry = new StringBuilder();
82
83 readLines: for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
84 String line = lines.get(lineNumber);
85 boolean isLastLine = false;
86 if (lineNumber == lines.size() - 1)
87 isLastLine = true;
88 if (line.startsWith(" ")) {
89 currentEntry.append(line.substring(1));
90 if (!isLastLine)
91 continue readLines;
92 }
93
94 if (currentEntry.length() != 0 || isLastLine) {
95 // read previous attribute
96 StringBuilder attrId = new StringBuilder(8);
97 boolean isBase64 = false;
98 readAttrId: for (int i = 0; i < currentEntry.length(); i++) {
99 char c = currentEntry.charAt(i);
100 if (c == ':') {
101 if (i + 1 < currentEntry.length() && currentEntry.charAt(i + 1) == ':')
102 isBase64 = true;
103 currentEntry.delete(0, i + (isBase64 ? 2 : 1));
104 break readAttrId;
105 } else {
106 attrId.append(c);
107 }
108 }
109
110 String attributeId = attrId.toString();
111 // TODO should we really trim the end of the string as well?
112 String cleanValueStr = currentEntry.toString().trim();
113 Object attributeValue = isBase64 ? Base64.getDecoder().decode(cleanValueStr) : cleanValueStr;
114
115 // manage DN attributes
116 if (attributeId.equals(LdapAttrs.DN) || isLastLine) {
117 if (currentDn != null) {
118 //
119 // ADD
120 //
121 Attributes previous = addAttributes(res, lineNumber, currentDn, currentAttributes);
122 if (previous != null) {
123 // log.warn("There was already an entry with DN " + currentDn
124 // + ", which has been discarded by a subsequent one.");
125 }
126 }
127
128 if (attributeId.equals(LdapAttrs.DN))
129 try {
130 currentDn = new LdapName(attributeValue.toString());
131 currentAttributes = new BasicAttributes(true);
132 } catch (InvalidNameException e) {
133 // log.error(attributeValue + " not a valid DN, skipping the entry.");
134 currentDn = null;
135 currentAttributes = null;
136 }
137 }
138
139 // store attribute
140 if (currentAttributes != null) {
141 Attribute attribute = currentAttributes.get(attributeId);
142 if (attribute == null) {
143 attribute = new BasicAttribute(attributeId);
144 currentAttributes.put(attribute);
145 }
146 attribute.add(attributeValue);
147 }
148 currentEntry = new StringBuilder();
149 }
150 currentEntry.append(line);
151 }
152 } finally {
153 try {
154 reader.close();
155 } catch (IOException e) {
156 // silent
157 }
158 }
159 return res;
160 }
161 }