PageRenderTime 81ms CodeModel.GetById 33ms app.highlight 44ms RepoModel.GetById 0ms app.codeStats 1ms

/vt-ldap/branches/vt-ldap-3/src/main/java/edu/vt/middleware/ldap/ldif/Ldif.java

http://vt-middleware.googlecode.com/
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}