/vt-ldap/branches/vt-ldap-3/src/main/java/edu/vt/middleware/ldap/ldif/Ldif.java
Java | 398 lines | 241 code | 39 blank | 118 comment | 81 complexity | 7a7c086d093c3a777e10477c2d054352 MD5 | raw file
Possible License(s): GPL-3.0, Apache-2.0, LGPL-3.0, LGPL-2.1
1/*
2 $Id: Ldif.java 1330 2010-05-23 22:10:53Z dfisher $
3
4 Copyright (C) 2003-2010 Virginia Tech.
5 All rights reserved.
6
7 SEE LICENSE FOR MORE INFORMATION
8
9 Author: Middleware Services
10 Email: middleware@vt.edu
11 Version: $Revision: 1330 $
12 Updated: $Date: 2010-05-24 00:10:53 +0200 (Mon, 24 May 2010) $
13*/
14package edu.vt.middleware.ldap.ldif;
15
16import java.io.BufferedReader;
17import java.io.IOException;
18import java.io.Reader;
19import java.io.Serializable;
20import java.io.Writer;
21import java.net.URL;
22import java.util.Iterator;
23import javax.naming.NamingException;
24import javax.naming.directory.SearchResult;
25import edu.vt.middleware.ldap.LdapUtil;
26import edu.vt.middleware.ldap.bean.LdapAttribute;
27import edu.vt.middleware.ldap.bean.LdapBeanFactory;
28import edu.vt.middleware.ldap.bean.LdapBeanProvider;
29import edu.vt.middleware.ldap.bean.LdapEntry;
30import edu.vt.middleware.ldap.bean.LdapResult;
31import org.apache.commons.logging.Log;
32import org.apache.commons.logging.LogFactory;
33
34/**
35 * <code>Ldif</code> contains functions for converting LDAP search result sets
36 * into LDIF.
37 *
38 * @author Middleware Services
39 * @version $Revision: 1330 $ $Date: 2010-05-24 00:10:53 +0200 (Mon, 24 May 2010) $
40 */
41public class Ldif implements Serializable
42{
43
44 /** ASCII decimal value of nul. */
45 public static final int NUL_CHAR = 0;
46
47 /** ASCII decimal value of line feed. */
48 public static final int LF_CHAR = 10;
49
50 /** ASCII decimal value of carriage return. */
51 public static final int CR_CHAR = 13;
52
53 /** ASCII decimal value of space. */
54 public static final int SP_CHAR = 32;
55
56 /** ASCII decimal value of colon. */
57 public static final int COLON_CHAR = 58;
58
59 /** ASCII decimal value of left arrow. */
60 public static final int LA_CHAR = 60;
61
62 /** ASCII decimal value of highest character. */
63 public static final int MAX_ASCII_CHAR = 127;
64
65 /** serial version uid. */
66 private static final long serialVersionUID = -3763879179455001975L;
67
68 /** Line separator. */
69 private static final String LINE_SEPARATOR = System.getProperty(
70 "line.separator");
71
72 /** Log for this class. */
73 protected final Log logger = LogFactory.getLog(this.getClass());
74
75 /** Ldap bean factory. */
76 protected LdapBeanFactory beanFactory = LdapBeanProvider.getLdapBeanFactory();
77
78
79 /**
80 * Returns the factory for creating ldap beans.
81 *
82 * @return <code>LdapBeanFactory</code>
83 */
84 public LdapBeanFactory getLdapBeanFactory()
85 {
86 return this.beanFactory;
87 }
88
89
90 /**
91 * Sets the factory for creating ldap beans.
92 *
93 * @param lbf <code>LdapBeanFactory</code>
94 */
95 public void setLdapBeanFactory(final LdapBeanFactory lbf)
96 {
97 if (lbf != null) {
98 this.beanFactory = lbf;
99 }
100 }
101
102
103 /**
104 * This will take the results of a prior LDAP query and convert it to LDIF.
105 *
106 * @param results <code>Iterator</code> of LDAP search results
107 *
108 * @return <code>String</code>
109 */
110 public String createLdif(final Iterator<SearchResult> results)
111 {
112 String ldif = "";
113 try {
114 final LdapResult lr = this.beanFactory.newLdapResult();
115 lr.addEntries(results);
116 ldif = this.createLdif(lr);
117 } catch (NamingException e) {
118 if (this.logger.isErrorEnabled()) {
119 this.logger.error("Error creating String from SearchResults", e);
120 }
121 }
122 return ldif;
123 }
124
125
126 /**
127 * This will take the results of a prior LDAP query and convert it to LDIF.
128 *
129 * @param result <code>LdapResult</code>
130 *
131 * @return <code>String</code>
132 */
133 public String createLdif(final LdapResult result)
134 {
135 // build string from results
136 final StringBuffer ldif = new StringBuffer();
137 if (result != null) {
138 for (LdapEntry le : result.getEntries()) {
139 ldif.append(createLdifEntry(le));
140 }
141 }
142
143 return ldif.toString();
144 }
145
146
147 /**
148 * This will take an LDAP entry and convert it to LDIF.
149 *
150 * @param ldapEntry <code>LdapEntry</code> to convert
151 *
152 * @return <code>String</code>
153 */
154 protected String createLdifEntry(final LdapEntry ldapEntry)
155 {
156 final StringBuffer entry = new StringBuffer();
157 if (ldapEntry != null) {
158
159 final String dn = ldapEntry.getDn();
160 if (dn != null) {
161 if (encodeData(dn)) {
162 final String encodedDn = LdapUtil.base64Encode(dn);
163 if (encodedDn != null) {
164 entry.append("dn:: ").append(dn).append(LINE_SEPARATOR);
165 }
166 } else {
167 entry.append("dn: ").append(dn).append(LINE_SEPARATOR);
168 }
169 }
170
171 for (LdapAttribute attr : ldapEntry.getLdapAttributes().getAttributes()) {
172 final String attrName = attr.getName();
173 for (Object attrValue : attr.getValues()) {
174 if (encodeData(attrValue)) {
175 String encodedAttrValue = null;
176 if (attrValue instanceof String) {
177 encodedAttrValue = LdapUtil.base64Encode((String) attrValue);
178 } else if (attrValue instanceof byte[]) {
179 encodedAttrValue = LdapUtil.base64Encode((byte[]) attrValue);
180 } else {
181 if (this.logger.isWarnEnabled()) {
182 this.logger.warn(
183 "Could not cast attribute value as a byte[]" +
184 " or a String");
185 }
186 }
187 if (encodedAttrValue != null) {
188 entry.append(attrName).append(":: ").append(encodedAttrValue)
189 .append(LINE_SEPARATOR);
190 }
191 } else {
192 entry.append(attrName).append(": ").append(attrValue).append(
193 LINE_SEPARATOR);
194 }
195 }
196 }
197 }
198
199 if (entry.length() > 0) {
200 entry.append(LINE_SEPARATOR);
201 }
202 return entry.toString();
203 }
204
205
206 /**
207 * This determines whether the supplied data should be base64 encoded. See
208 * http://www.faqs.org/rfcs/rfc2849.html for more details.
209 *
210 * @param data <code>Object</code> to inspect
211 *
212 * @return <code>boolean</code>
213 */
214 private boolean encodeData(final Object data)
215 {
216 boolean encode = false;
217 if (data instanceof String) {
218 final String stringData = (String) data;
219 final char[] dataCharArray = stringData.toCharArray();
220 for (int i = 0; i < dataCharArray.length; i++) {
221 final int charInt = (int) dataCharArray[i];
222 // check for NUL
223 if (charInt == NUL_CHAR) {
224 encode = true;
225 // check for LF
226 } else if (charInt == LF_CHAR) {
227 encode = true;
228 // check for CR
229 } else if (charInt == CR_CHAR) {
230 encode = true;
231 // check for SP at beginning or end of string
232 } else if (
233 charInt == SP_CHAR &&
234 (i == 0 || i == dataCharArray.length - 1)) {
235 encode = true;
236 // check for colon(:) at beginning of string
237 } else if (charInt == COLON_CHAR && i == 0) {
238 encode = true;
239 // check for left arrow(<) at beginning of string
240 } else if (charInt == LA_CHAR && i == 0) {
241 encode = true;
242 // check for any character above 127
243 } else if (charInt > MAX_ASCII_CHAR) {
244 encode = true;
245 }
246 }
247 } else {
248 encode = true;
249 }
250 return encode;
251 }
252
253
254 /**
255 * This will write the supplied LDAP search results to the supplied writer in
256 * LDIF form.
257 *
258 * @param results <code>Iterator</code> of LDAP search results
259 * @param writer <code>Writer</code> to write to
260 *
261 * @throws IOException if an error occurs while writing to the output stream
262 */
263 public void outputLdif(
264 final Iterator<SearchResult> results,
265 final Writer writer)
266 throws IOException
267 {
268 writer.write(createLdif(results));
269 writer.flush();
270 }
271
272
273 /**
274 * This will write the supplied LDAP search results to the supplied writer in
275 * LDIF form.
276 *
277 * @param result <code>LdapResult</code>
278 * @param writer <code>Writer</code> to write to
279 *
280 * @throws IOException if an error occurs while writing to the output stream
281 */
282 public void outputLdif(final LdapResult result, final Writer writer)
283 throws IOException
284 {
285 writer.write(createLdif(result));
286 writer.flush();
287 }
288
289
290 /**
291 * This will take a Reader containing an LDIF and convert it to an Iterator of
292 * LDAP search results. Provides a loose implementation of RFC 2849. Should
293 * not be used to validate LDIF format as it does not enforce strictness.
294 *
295 * @param reader <code>Reader</code> containing LDIF content
296 *
297 * @return <code>Iterator</code> - of LDAP search results
298 *
299 * @throws IOException if an I/O error occurs
300 */
301 public Iterator<SearchResult> importLdif(final Reader reader)
302 throws IOException
303 {
304 return this.importLdifToLdapResult(reader).toSearchResults().iterator();
305 }
306
307
308 /**
309 * This will take a Reader containing an LDIF and convert it to an <code>
310 * LdapResult</code>. Provides a loose implementation of RFC 2849. Should not
311 * be used to validate LDIF format as it does not enforce strictness.
312 *
313 * @param reader <code>Reader</code> containing LDIF content
314 *
315 * @return <code>LdapResult</code> - LDAP search results
316 *
317 * @throws IOException if an I/O error occurs
318 */
319 public LdapResult importLdifToLdapResult(final Reader reader)
320 throws IOException
321 {
322 final LdapResult ldapResult = this.beanFactory.newLdapResult();
323 final BufferedReader br = new BufferedReader(reader);
324 String line = null;
325 int lineCount = 0;
326 LdapEntry ldapEntry = null;
327 StringBuffer lineValue = new StringBuffer();
328
329 while ((line = br.readLine()) != null) {
330 lineCount++;
331 if (line.startsWith("dn:")) {
332 lineValue.append(line);
333 ldapEntry = this.beanFactory.newLdapEntry();
334 break;
335 }
336 }
337
338 boolean read = true;
339 while (read) {
340 line = br.readLine();
341 if (line == null) {
342 read = false;
343 line = "";
344 }
345 if (!line.startsWith("#")) {
346 if (line.startsWith("dn:")) {
347 ldapResult.addEntry(ldapEntry);
348 ldapEntry = this.beanFactory.newLdapEntry();
349 }
350 if (line.startsWith(" ")) {
351 lineValue.append(line.substring(1));
352 } else {
353 final String s = lineValue.toString();
354 if (s.indexOf(":") != -1) {
355 boolean isBinary = false;
356 boolean isUrl = false;
357 final String[] parts = s.split(":", 2);
358 final String attrName = parts[0];
359 String attrValue = parts[1];
360 if (attrValue.startsWith(":")) {
361 isBinary = true;
362 attrValue = attrValue.substring(1);
363 } else if (attrValue.startsWith("<")) {
364 isUrl = true;
365 attrValue = attrValue.substring(1);
366 }
367 if (attrValue.startsWith(" ")) {
368 attrValue = attrValue.substring(1);
369 }
370 if ("dn".equals(attrName)) {
371 ldapEntry.setDn(attrValue);
372 } else {
373 LdapAttribute ldapAttr = ldapEntry.getLdapAttributes()
374 .getAttribute(attrName);
375 if (ldapAttr == null) {
376 ldapAttr = this.beanFactory.newLdapAttribute();
377 ldapAttr.setName(attrName);
378 ldapEntry.getLdapAttributes().addAttribute(ldapAttr);
379 }
380 if (isBinary) {
381 ldapAttr.getValues().add(LdapUtil.base64Decode(attrValue));
382 } else if (isUrl) {
383 ldapAttr.getValues().add(LdapUtil.readURL(new URL(attrValue)));
384 } else {
385 ldapAttr.getValues().add(attrValue);
386 }
387 }
388 }
389 lineValue = new StringBuffer(line);
390 }
391 }
392 }
393 if (ldapEntry != null) {
394 ldapResult.addEntry(ldapEntry);
395 }
396 return ldapResult;
397 }
398}