001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2016 ForgeRock AS.
016 */
017package org.opends.server.types;
018
019import static org.opends.messages.UtilityMessages.*;
020import static org.opends.server.util.StaticUtils.*;
021
022import java.io.BufferedWriter;
023import java.io.Closeable;
024import java.io.File;
025import java.io.FileOutputStream;
026import java.io.IOException;
027import java.io.OutputStream;
028import java.io.OutputStreamWriter;
029import java.util.ArrayList;
030import java.util.HashSet;
031import java.util.List;
032import java.util.Set;
033import java.util.zip.GZIPOutputStream;
034
035import org.forgerock.i18n.LocalizableMessage;
036import org.forgerock.opendj.ldap.DN;
037import org.forgerock.opendj.ldap.schema.AttributeType;
038import org.opends.server.util.StaticUtils;
039
040/**
041 * This class defines a data structure for holding configuration
042 * information to use when performing an LDIF export.
043 */
044@org.opends.server.types.PublicAPI(
045     stability=org.opends.server.types.StabilityLevel.VOLATILE,
046     mayInstantiate=true,
047     mayExtend=false,
048     mayInvoke=true)
049public final class LDIFExportConfig extends OperationConfig
050  implements Closeable
051{
052  /** Indicates whether the data should be compressed as it is written. */
053  private boolean compressData;
054  /** Indicates whether the data should be encrypted as it is written. */
055  private boolean encryptData;
056  /** Indicates whether to generate a cryptographic hash of the data as it is written. */
057  private boolean hashData;
058  /** Indicates whether to include the objectclasses in the entries written in the export. */
059  private boolean includeObjectClasses;
060  /** Indicates whether to include operational attributes in the export. */
061  private boolean includeOperationalAttributes;
062  /** Indicates whether to include virtual attributes in the export. */
063  private boolean includeVirtualAttributes;
064  /** Indicates whether to invoke LDIF export plugins on entries being exported. */
065  private boolean invokeExportPlugins;
066
067  /**
068   * Indicates whether to digitally sign the hash when the export is complete.
069   */
070  private boolean signHash;
071
072  /** Indicates whether to include attribute types (i.e., names) only or both types and values. */
073  private boolean typesOnly;
074
075  /** The path to the LDIF file that should be written. */
076  private final String ldifFile;
077  /** The buffered writer to which the LDIF data should be written. */
078  private BufferedWriter writer;
079  /** The output stream to which the LDIF data should be written. */
080  private OutputStream ldifOutputStream;
081
082  /**
083   * The behavior that should be used when writing an LDIF file and a file with
084   * the same name already exists.
085   */
086  private final ExistingFileBehavior existingFileBehavior;
087
088  /** The column number at which long lines should be wrapped. */
089  private int wrapColumn;
090
091  /** The set of base DNs to exclude from the export. */
092  private List<DN> excludeBranches;
093  /** The set of base DNs to include from the export. */
094  private List<DN> includeBranches;
095
096  /** The set of search filters for entries to exclude from the export. */
097  private List<SearchFilter> excludeFilters;
098  /** The set of search filters for entries to include in the export. */
099  private List<SearchFilter> includeFilters;
100
101  /** The set of attribute types that should be excluded from the export. */
102  private Set<AttributeType> excludeAttributes;
103  /** The set of attribute types that should be included in the export. */
104  private Set<AttributeType> includeAttributes;
105
106  /**
107   * Creates a new LDIF export configuration that will write to the
108   * specified LDIF file.
109   *
110   * @param  ldifFile              The path to the LDIF file to
111   *                               export.
112   * @param  existingFileBehavior  Indicates how to proceed if the
113   *                               specified file already exists.
114   */
115  public LDIFExportConfig(String ldifFile,
116                          ExistingFileBehavior existingFileBehavior)
117  {
118    this.ldifFile                = ldifFile;
119    this.existingFileBehavior    = existingFileBehavior;
120    ldifOutputStream             = null;
121
122    excludeBranches              = new ArrayList<>();
123    includeBranches              = new ArrayList<>();
124    excludeFilters               = new ArrayList<>();
125    includeFilters               = new ArrayList<>();
126    compressData                 = false;
127    encryptData                  = false;
128    hashData                     = false;
129    includeObjectClasses         = true;
130    includeOperationalAttributes = true;
131    includeVirtualAttributes     = false;
132    invokeExportPlugins          = false;
133    signHash                     = false;
134    typesOnly                    = false;
135    writer                       = null;
136    excludeAttributes            = new HashSet<>();
137    includeAttributes            = new HashSet<>();
138    wrapColumn                   = -1;
139  }
140
141  /**
142   * Creates a new LDIF export configuration that will write to the
143   * provided output stream.
144   *
145   * @param  ldifOutputStream  The output stream to which the LDIF
146   *                           data should be written.
147   */
148  public LDIFExportConfig(OutputStream ldifOutputStream)
149  {
150    this.ldifOutputStream        = ldifOutputStream;
151    ldifFile                     = null;
152    existingFileBehavior         = ExistingFileBehavior.FAIL;
153
154    excludeBranches              = new ArrayList<>();
155    includeBranches              = new ArrayList<>();
156    excludeFilters               = new ArrayList<>();
157    includeFilters               = new ArrayList<>();
158    compressData                 = false;
159    encryptData                  = false;
160    hashData                     = false;
161    includeObjectClasses         = true;
162    includeOperationalAttributes = true;
163    includeVirtualAttributes     = false;
164    invokeExportPlugins          = false;
165    signHash                     = false;
166    typesOnly                    = false;
167    writer                       = null;
168    excludeAttributes            = new HashSet<>();
169    includeAttributes            = new HashSet<>();
170    wrapColumn                   = -1;
171  }
172
173  /**
174   * Retrieves the writer that should be used to write the LDIF data.
175   * If compression or encryption are to be used, then they must be
176   * enabled before the first call to this method.
177   *
178   * @return  The writer that should be used to write the LDIF data.
179   *
180   * @throws  IOException  If a problem occurs while preparing the
181   *                       writer.
182   */
183  public BufferedWriter getWriter()
184         throws IOException
185  {
186    if (writer == null)
187    {
188      if (ldifOutputStream == null)
189      {
190        File f = new File(ldifFile);
191        boolean mustSetPermissions = false;
192
193        switch (existingFileBehavior)
194        {
195        case APPEND:
196          // Create new file if it doesn't exist ensuring that we can
197          // set its permissions.
198          if (!f.exists())
199          {
200            f.createNewFile();
201            mustSetPermissions = true;
202          }
203          ldifOutputStream = new FileOutputStream(ldifFile, true);
204          break;
205        case OVERWRITE:
206          // Create new file if it doesn't exist ensuring that we can
207          // set its permissions.
208          if (!f.exists())
209          {
210            f.createNewFile();
211            mustSetPermissions = true;
212          }
213          ldifOutputStream = new FileOutputStream(ldifFile, false);
214          break;
215        case FAIL:
216          if (f.exists())
217          {
218            LocalizableMessage message = ERR_LDIF_FILE_EXISTS.get(ldifFile);
219            throw new IOException(message.toString());
220          }
221          // Create new file ensuring that we can set its permissions.
222          f.createNewFile();
223          mustSetPermissions = true;
224          ldifOutputStream = new FileOutputStream(ldifFile);
225          break;
226        }
227
228        if (mustSetPermissions)
229        {
230          try
231          {
232            // Ignore
233            FilePermission.setSafePermissions(f, 0600);
234          }
235          catch (Exception e)
236          {
237            // The file could not be created with the correct permissions.
238            LocalizableMessage message = WARN_EXPORT_LDIF_SET_PERMISSION_FAILED
239                .get(f, stackTraceToSingleLineString(e));
240            throw new IOException(message.toString());
241          }
242        }
243      }
244
245      // See if we should compress the output.
246      OutputStream outputStream;
247      if (compressData)
248      {
249        outputStream = new GZIPOutputStream(ldifOutputStream);
250      }
251      else
252      {
253        outputStream = ldifOutputStream;
254      }
255
256
257      // See if we should encrypt the output.
258      if (encryptData)
259      {
260        // FIXME -- To be implemented: See OPENDJ-448.
261      }
262
263
264      // Create the writer.
265      writer = new BufferedWriter(new OutputStreamWriter(outputStream));
266    }
267
268    return writer;
269  }
270
271  /**
272   * Indicates whether the LDIF export plugins should be invoked for
273   * entries as they are exported.
274   *
275   * @return  <CODE>true</CODE> if LDIF export plugins should be
276   *          invoked for entries as they are exported, or
277   *          <CODE>false</CODE> if not.
278   */
279  public boolean invokeExportPlugins()
280  {
281    return invokeExportPlugins;
282  }
283
284  /**
285   * Specifies whether the LDIF export plugins should be invoked for
286   * entries as they are exported.
287   *
288   * @param  invokeExportPlugins  Specifies whether the LDIF export
289   *                              plugins should be invoked for
290   *                              entries as they are exported.
291   */
292  public void setInvokeExportPlugins(boolean invokeExportPlugins)
293  {
294    this.invokeExportPlugins = invokeExportPlugins;
295  }
296
297  /**
298   * Specifies whether the LDIF data should be compressed as it is
299   * written.  If compression should be used, then this must be set
300   * before calling <CODE>getWriter</CODE> for the first time.
301   *
302   * @param  compressData  Indicates whether the LDIF data should be
303   *                       compressed as it is written.
304   */
305  public void setCompressData(boolean compressData)
306  {
307    this.compressData = compressData;
308  }
309
310
311
312  /**
313   * Indicates whether the LDIF data should be encrypted as it is
314   * written.
315   *
316   * @return  <CODE>true</CODE> if the LDIF data should be encrypted
317   *          as it is written, or <CODE>false</CODE> if not.
318   */
319  public boolean encryptData()
320  {
321    return encryptData;
322  }
323
324
325
326  /**
327   * Specifies whether the LDIF data should be encrypted as it is
328   * written.  If encryption should be used, then this must be set
329   * before calling <CODE>getWriter</CODE> for the first time.
330   *
331   * @param  encryptData  Indicates whether the LDIF data should be
332   *                      encrypted as it is written.
333   */
334  public void setEncryptData(boolean encryptData)
335  {
336    this.encryptData = encryptData;
337  }
338
339
340
341  /**
342   * Indicates whether to generate a cryptographic hash of the data
343   * that is written.
344   *
345   * @return  <CODE>true</CODE> if a hash should be generated as the
346   *          data is written, or <CODE>false</CODE> if not.
347   */
348  public boolean hashData()
349  {
350    return hashData;
351  }
352
353
354
355  /**
356   * Specifies whether to generate a cryptographic hash of the data
357   * that is written.  If hashing is to be used, then this must be set
358   * before calling <CODE>getWriter</CODE> for the first time.
359   *
360   * @param  hashData  Indicates whether to generate a hash of the
361   *                   data as it is written.
362   */
363  public void setHashData(boolean hashData)
364  {
365    this.hashData = hashData;
366  }
367
368
369
370  /**
371   * Indicates whether to sign the cryptographic hash of the data that
372   * is written when the export is complete.
373   *
374   * @return  <CODE>true</CODE> if the hash should be signed when the
375   *          export is complete, or <CODE>false</CODE> if not.
376   */
377  public boolean signHash()
378  {
379    return signHash;
380  }
381
382
383
384  /**
385   * Specifies whether to sign the cryptographic hash of the data that
386   * is written when the export is complete.  If the export is not
387   * configured to generate a hash, then this will be ignored.  If
388   * hashing is to be used and the hash should be signed, then this
389   * must be set before calling <CODE>getWriter</CODE> for the first
390   * time.
391   *
392   * @param  signHash  Indicates whether to generate a hash of the
393   *                   data as it is written.
394   */
395  public void setSignHash(boolean signHash)
396  {
397    this.signHash = signHash;
398  }
399
400
401
402  /**
403   * Indicates whether the LDIF generated should include attribute
404   * types (i.e., attribute names) only or both attribute types and
405   * values.
406   *
407   * @return  <CODE>true</CODE> if only attribute types should be
408   *          included in the resulting LDIF, or <CODE>false</CODE> if
409   *          both types and values should be included.
410   */
411  public boolean typesOnly()
412  {
413    return typesOnly;
414  }
415
416  /**
417   * Specifies whether the LDIF generated should include attribute
418   * types (i.e., attribute names) only or both attribute types and
419   * values.
420   *
421   * @param  typesOnly  Specifies whether the LDIF generated should
422   *                    include attribute types only or both attribute
423   *                    types and values.
424   */
425  public void setTypesOnly(boolean typesOnly)
426  {
427    this.typesOnly = typesOnly;
428  }
429
430  /**
431   * Retrieves the column at which long lines should be wrapped.
432   *
433   * @return  The column at which long lines should be wrapped, or a
434   *          value less than or equal to zero to indicate that no
435   *          wrapping should be performed.
436   */
437  public int getWrapColumn()
438  {
439    return wrapColumn;
440  }
441
442  /**
443   * Specifies the column at which long lines should be wrapped.  A
444   * value less than or equal to zero indicates that no wrapping
445   * should be performed.
446   *
447   * @param  wrapColumn  The column at which long lines should be
448   *                     wrapped.
449   */
450  public void setWrapColumn(int wrapColumn)
451  {
452    this.wrapColumn = wrapColumn;
453  }
454
455  /**
456   * Retrieves the set of base DNs that specify the set of entries to
457   * exclude from the export.  The list that is returned may be
458   * altered by the caller.
459   *
460   * @return  The set of base DNs that specify the set of entries to
461   *          exclude from the export.
462   */
463  public List<DN> getExcludeBranches()
464  {
465    return excludeBranches;
466  }
467
468  /**
469   * Specifies the set of base DNs that specify the set of entries to
470   * exclude from the export.
471   *
472   * @param  excludeBranches  The set of base DNs that specify the set
473   *                          of entries to exclude from the export.
474   */
475  public void setExcludeBranches(List<DN> excludeBranches)
476  {
477    if (excludeBranches != null)
478    {
479      this.excludeBranches = excludeBranches;
480    }
481    else
482    {
483      this.excludeBranches = new ArrayList<>(0);
484    }
485  }
486
487  /**
488   * Retrieves the set of base DNs that specify the set of entries to
489   * include in the export.  The list that is returned may be altered
490   * by the caller.
491   *
492   * @return  The set of base DNs that specify the set of entries to
493   *          include in the export.
494   */
495  public List<DN> getIncludeBranches()
496  {
497    return includeBranches;
498  }
499
500  /**
501   * Specifies the set of base DNs that specify the set of entries to
502   * include in the export.
503   *
504   * @param  includeBranches  The set of base DNs that specify the set
505   *                          of entries to include in the export.
506   */
507  public void setIncludeBranches(List<DN> includeBranches)
508  {
509    if (includeBranches != null)
510    {
511      this.includeBranches = includeBranches;
512    }
513    else
514    {
515      this.includeBranches = new ArrayList<>(0);
516    }
517  }
518
519  /**
520   * Indicates whether the set of objectclasses should be included in
521   * the entries written to LDIF.
522   *
523   * @return  <CODE>true</CODE> if the set of objectclasses should be
524   *          included in the entries written to LDIF, or
525   *          <CODE>false</CODE> if not.
526   */
527  public boolean includeObjectClasses()
528  {
529    return includeObjectClasses;
530  }
531
532  /**
533   * Indicates whether the set of operational attributes should be
534   * included in the export.
535   *
536   * @return  <CODE>true</CODE> if the set of operational attributes
537   *          should be included in the export.
538   */
539  public boolean includeOperationalAttributes()
540  {
541    return includeOperationalAttributes;
542  }
543
544  /**
545   * Specifies whether the  objectclasss attribute should be
546   * included in the export.
547   *
548   * @param  includeObjectClasses  Specifies whether the
549   *                                objectclass attribute
550   *                                should be included in the
551   *                                export.
552   */
553  public void setIncludeObjectClasses(boolean includeObjectClasses)
554  {
555    this.includeObjectClasses = includeObjectClasses;
556  }
557
558  /**
559   * Specifies whether the set of operational attributes should be
560   * included in the export.
561   *
562   * @param  includeOperationalAttributes  Specifies whether the set
563   *                                       of operational attributes
564   *                                       should be included in the
565   *                                       export.
566   */
567  public void setIncludeOperationalAttributes(
568                   boolean includeOperationalAttributes)
569  {
570    this.includeOperationalAttributes = includeOperationalAttributes;
571  }
572
573  /**
574   * Indicates whether virtual attributes should be included in the
575   * export.
576   *
577   * @return  {@code true} if virtual attributes should be included in
578   *          the export, or {@code false} if not.
579   */
580  public boolean includeVirtualAttributes()
581  {
582    return includeVirtualAttributes;
583  }
584
585  /**
586   * Specifies whether virtual attributes should be included in the
587   * export.
588   *
589   * @param  includeVirtualAttributes  Specifies whether virtual
590   *                                   attributes should be included
591   *                                   in the export.
592   */
593  public void setIncludeVirtualAttributes(
594                   boolean includeVirtualAttributes)
595  {
596    this.includeVirtualAttributes = includeVirtualAttributes;
597  }
598
599  /**
600   * Retrieves the set of attributes that should be excluded from the
601   * entries written to LDIF.  The set that is returned may be altered
602   * by the caller.
603   *
604   * @return  The set of attributes that should be excluded from the
605   *          entries written to LDIF.
606   */
607  public Set<AttributeType> getExcludeAttributes()
608  {
609    return excludeAttributes;
610  }
611
612  /**
613   * Specifies the set of attributes that should be excluded from the
614   * entries written to LDIF.
615   *
616   * @param  excludeAttributes  The set of attributes that should be
617   *                            excluded from the entries written to
618   *                            LDIF.
619   */
620  public void setExcludeAttributes(
621                   Set<AttributeType> excludeAttributes)
622  {
623    if (excludeAttributes != null)
624    {
625      this.excludeAttributes = excludeAttributes;
626    }
627    else
628    {
629      this.excludeAttributes = new HashSet<>(0);
630    }
631  }
632
633  /**
634   * Retrieves the set of attributes that should be included in the
635   * entries written to LDIF.  The set that is returned may be altered
636   * by the caller.
637   *
638   * @return  The set of attributes that should be included in the
639   *          entries written to LDIF.
640   */
641  public Set<AttributeType> getIncludeAttributes()
642  {
643    return includeAttributes;
644  }
645
646  /**
647   * Specifies the set of attributes that should be included in the
648   * entries written to LDIF.
649   *
650   * @param  includeAttributes  The set of attributes that should be
651   *                            included in the entries written to
652   *                            LDIF.
653   */
654  public void setIncludeAttributes(Set<AttributeType> includeAttributes)
655  {
656    if (includeAttributes != null)
657    {
658      this.includeAttributes = includeAttributes;
659    }
660    else
661    {
662      this.includeAttributes = new HashSet<>(0);
663    }
664  }
665
666  /**
667   * Indicates whether the specified attribute should be included in
668   * the entries written to LDIF.
669   *
670   * @param  attributeType  The attribute type for which to make the
671   *                        determination.
672   *
673   * @return  <CODE>true</CODE> if the specified attribute should be
674   *          included in the entries written to LDIF, or
675   *          <CODE>false</CODE> if not.
676   */
677  public boolean includeAttribute(AttributeType attributeType)
678  {
679    return (excludeAttributes.isEmpty()
680              || !excludeAttributes.contains(attributeType))
681        && (includeAttributes.isEmpty()
682              || includeAttributes.contains(attributeType));
683  }
684
685  /**
686   * Retrieves the set of search filters that should be used to
687   * determine which entries to exclude from the LDIF.  The list that
688   * is returned may be altered by the caller.
689   *
690   * @return  The set of search filters that should be used to
691   *          determine which entries to exclude from the LDIF.
692   */
693  public List<SearchFilter> getExcludeFilters()
694  {
695    return excludeFilters;
696  }
697
698  /**
699   * Specifies the set of search filters that should be used to
700   * determine which entries to exclude from the LDIF.
701   *
702   * @param  excludeFilters  The set of search filters that should be
703   *                         used to determine which entries to
704   *                         exclude from the LDIF.
705   */
706  public void setExcludeFilters(List<SearchFilter> excludeFilters)
707  {
708    if (excludeFilters != null)
709    {
710      this.excludeFilters = excludeFilters;
711    }
712    else
713    {
714      this.excludeFilters = new ArrayList<>(0);
715    }
716  }
717
718  /**
719   * Retrieves the set of search filters that should be used to
720   * determine which entries to include in the LDIF.  The list that is
721   * returned may be altered by the caller.
722   *
723   * @return  The set of search filters that should be used to
724   *          determine which entries to include in the LDIF.
725   */
726  public List<SearchFilter> getIncludeFilters()
727  {
728    return includeFilters;
729  }
730
731  /**
732   * Specifies the set of search filters that should be used to
733   * determine which entries to include in the LDIF.
734   *
735   * @param  includeFilters  The set of search filters that should be
736   *                         used to determine which entries to
737   *                         include in the LDIF.
738   */
739  public void setIncludeFilters(List<SearchFilter> includeFilters)
740  {
741    if (includeFilters != null)
742    {
743      this.includeFilters = includeFilters;
744    }
745    else
746    {
747      this.includeFilters = new ArrayList<>(0);
748    }
749  }
750
751  /**
752   * Indicates whether the specified entry should be included in the
753   * export based on the configured set of include and exclude
754   * filters.
755   *
756   * @param  entry  The entry for which to make the determination.
757   *
758   * @return  <CODE>true</CODE> if the specified entry should be
759   *          included in the export, or <CODE>false</CODE> if not.
760   *
761   * @throws  DirectoryException  If there is a problem with any of
762   *                              the search filters used to make the
763   *                              determination.
764   */
765  public boolean includeEntry(Entry entry)
766         throws DirectoryException
767  {
768    DN dn = entry.getName();
769    if (! excludeBranches.isEmpty())
770    {
771      for (DN excludeBranch : excludeBranches)
772      {
773        if (excludeBranch.isSuperiorOrEqualTo(dn))
774        {
775          return false;
776        }
777      }
778    }
779
780    checkIncludeBranches: if (! includeBranches.isEmpty())
781    {
782      for (DN includeBranch : includeBranches)
783      {
784        if (includeBranch.isSuperiorOrEqualTo(dn))
785        {
786          break checkIncludeBranches;
787        }
788      }
789
790      return false;
791    }
792
793    if (! excludeFilters.isEmpty())
794    {
795      for (SearchFilter filter : excludeFilters)
796      {
797        if (filter.matchesEntry(entry))
798        {
799          return false;
800        }
801      }
802    }
803
804    if (! includeFilters.isEmpty())
805    {
806      for (SearchFilter filter : includeFilters)
807      {
808        if (filter.matchesEntry(entry))
809        {
810          return true;
811        }
812      }
813      return false;
814    }
815
816    return true;
817  }
818
819  /** Closes any resources that this export config might have open. */
820  @Override
821  public void close()
822  {
823    // FIXME -- Need to add code to generate a signed hash of the LDIF content.
824    StaticUtils.close(writer);
825  }
826}