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