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.setSafePermissions(f, 0600);
274          }
275          catch (Exception e)
276          {
277            // The file could not be created with the correct permissions.
278            LocalizableMessage message = WARN_EXPORT_LDIF_SET_PERMISSION_FAILED
279                .get(f, stackTraceToSingleLineString(e));
280            throw new IOException(message.toString());
281          }
282        }
283      }
284
285
286      // See if we should compress the output.
287      OutputStream outputStream;
288      if (compressData)
289      {
290        outputStream = new GZIPOutputStream(ldifOutputStream);
291      }
292      else
293      {
294        outputStream = ldifOutputStream;
295      }
296
297
298      // See if we should encrypt the output.
299      if (encryptData)
300      {
301        // FIXME -- Implement this.
302      }
303
304
305      // Create the writer.
306      writer = new BufferedWriter(new OutputStreamWriter(outputStream));
307    }
308
309    return writer;
310  }
311
312
313
314  /**
315   * Indicates whether the LDIF export plugins should be invoked for
316   * entries as they are exported.
317   *
318   * @return  <CODE>true</CODE> if LDIF export plugins should be
319   *          invoked for entries as they are exported, or
320   *          <CODE>false</CODE> if not.
321   */
322  public boolean invokeExportPlugins()
323  {
324    return invokeExportPlugins;
325  }
326
327
328
329  /**
330   * Specifies whether the LDIF export plugins should be invoked for
331   * entries as they are exported.
332   *
333   * @param  invokeExportPlugins  Specifies whether the LDIF export
334   *                              plugins should be invoked for
335   *                              entries as they are exported.
336   */
337  public void setInvokeExportPlugins(boolean invokeExportPlugins)
338  {
339    this.invokeExportPlugins = invokeExportPlugins;
340  }
341
342
343
344  /**
345   * Indicates whether the LDIF data should be compressed as it is
346   * written.
347   *
348   * @return  <CODE>true</CODE> if the LDIF data should be compressed
349   *          as it is written, or <CODE>false</CODE> if not.
350   */
351  public boolean compressData()
352  {
353    return compressData;
354  }
355
356
357
358  /**
359   * Specifies whether the LDIF data should be compressed as it is
360   * written.  If compression should be used, then this must be set
361   * before calling <CODE>getWriter</CODE> for the first time.
362   *
363   * @param  compressData  Indicates whether the LDIF data should be
364   *                       compressed as it is written.
365   */
366  public void setCompressData(boolean compressData)
367  {
368    this.compressData = compressData;
369  }
370
371
372
373  /**
374   * Indicates whether the LDIF data should be encrypted as it is
375   * written.
376   *
377   * @return  <CODE>true</CODE> if the LDIF data should be encrypted
378   *          as it is written, or <CODE>false</CODE> if not.
379   */
380  public boolean encryptData()
381  {
382    return encryptData;
383  }
384
385
386
387  /**
388   * Specifies whether the LDIF data should be encrypted as it is
389   * written.  If encryption should be used, then this must be set
390   * before calling <CODE>getWriter</CODE> for the first time.
391   *
392   * @param  encryptData  Indicates whether the LDIF data should be
393   *                      encrypted as it is written.
394   */
395  public void setEncryptData(boolean encryptData)
396  {
397    this.encryptData = encryptData;
398  }
399
400
401
402  /**
403   * Indicates whether to generate a cryptographic hash of the data
404   * that is written.
405   *
406   * @return  <CODE>true</CODE> if a hash should be generated as the
407   *          data is written, or <CODE>false</CODE> if not.
408   */
409  public boolean hashData()
410  {
411    return hashData;
412  }
413
414
415
416  /**
417   * Specifies whether to generate a cryptographic hash of the data
418   * that is written.  If hashing is to be used, then this must be set
419   * before calling <CODE>getWriter</CODE> for the first time.
420   *
421   * @param  hashData  Indicates whether to generate a hash of the
422   *                   data as it is written.
423   */
424  public void setHashData(boolean hashData)
425  {
426    this.hashData = hashData;
427  }
428
429
430
431  /**
432   * Indicates whether to sign the cryptographic hash of the data that
433   * is written when the export is complete.
434   *
435   * @return  <CODE>true</CODE> if the hash should be signed when the
436   *          export is complete, or <CODE>false</CODE> if not.
437   */
438  public boolean signHash()
439  {
440    return signHash;
441  }
442
443
444
445  /**
446   * Specifies whether to sign the cryptographic hash of the data that
447   * is written when the export is complete.  If the export is not
448   * configured to generate a hash, then this will be ignored.  If
449   * hashing is to be used and the hash should be signed, then this
450   * must be set before calling <CODE>getWriter</CODE> for the first
451   * time.
452   *
453   * @param  signHash  Indicates whether to generate a hash of the
454   *                   data as it is written.
455   */
456  public void setSignHash(boolean signHash)
457  {
458    this.signHash = signHash;
459  }
460
461
462
463  /**
464   * Indicates whether the LDIF generated should include attribute
465   * types (i.e., attribute names) only or both attribute types and
466   * values.
467   *
468   * @return  <CODE>true</CODE> if only attribute types should be
469   *          included in the resulting LDIF, or <CODE>false</CODE> if
470   *          both types and values should be included.
471   */
472  public boolean typesOnly()
473  {
474    return typesOnly;
475  }
476
477
478
479  /**
480   * Specifies whether the LDIF generated should include attribute
481   * types (i.e., attribute names) only or both attribute types and
482   * values.
483   *
484   * @param  typesOnly  Specifies whether the LDIF generated should
485   *                    include attribute types only or both attribute
486   *                    types and values.
487   */
488  public void setTypesOnly(boolean typesOnly)
489  {
490    this.typesOnly = typesOnly;
491  }
492
493
494
495  /**
496   * Retrieves the column at which long lines should be wrapped.
497   *
498   * @return  The column at which long lines should be wrapped, or a
499   *          value less than or equal to zero to indicate that no
500   *          wrapping should be performed.
501   */
502  public int getWrapColumn()
503  {
504    return wrapColumn;
505  }
506
507
508
509  /**
510   * Specifies the column at which long lines should be wrapped.  A
511   * value less than or equal to zero indicates that no wrapping
512   * should be performed.
513   *
514   * @param  wrapColumn  The column at which long lines should be
515   *                     wrapped.
516   */
517  public void setWrapColumn(int wrapColumn)
518  {
519    this.wrapColumn = wrapColumn;
520  }
521
522
523
524  /**
525   * Retrieves the set of base DNs that specify the set of entries to
526   * exclude from the export.  The list that is returned may be
527   * altered by the caller.
528   *
529   * @return  The set of base DNs that specify the set of entries to
530   *          exclude from the export.
531   */
532  public List<DN> getExcludeBranches()
533  {
534    return excludeBranches;
535  }
536
537
538
539  /**
540   * Specifies the set of base DNs that specify the set of entries to
541   * exclude from the export.
542   *
543   * @param  excludeBranches  The set of base DNs that specify the set
544   *                          of entries to exclude from the export.
545   */
546  public void setExcludeBranches(List<DN> excludeBranches)
547  {
548    if (excludeBranches == null)
549    {
550      this.excludeBranches = new ArrayList<>(0);
551    }
552    else
553    {
554      this.excludeBranches = excludeBranches;
555    }
556  }
557
558
559
560  /**
561   * Retrieves the set of base DNs that specify the set of entries to
562   * include in the export.  The list that is returned may be altered
563   * by the caller.
564   *
565   * @return  The set of base DNs that specify the set of entries to
566   *          include in the export.
567   */
568  public List<DN> getIncludeBranches()
569  {
570    return includeBranches;
571  }
572
573
574
575  /**
576   * Specifies the set of base DNs that specify the set of entries to
577   * include in the export.
578   *
579   * @param  includeBranches  The set of base DNs that specify the set
580   *                          of entries to include in the export.
581   */
582  public void setIncludeBranches(List<DN> includeBranches)
583  {
584    if (includeBranches == null)
585    {
586      this.includeBranches = new ArrayList<>(0);
587    }
588    else
589    {
590      this.includeBranches = includeBranches;
591    }
592  }
593
594
595
596  /**
597   * Indicates whether the set of objectclasses should be included in
598   * the entries written to LDIF.
599   *
600   * @return  <CODE>true</CODE> if the set of objectclasses should be
601   *          included in the entries written to LDIF, or
602   *          <CODE>false</CODE> if not.
603   */
604  public boolean includeObjectClasses()
605  {
606    return includeObjectClasses;
607  }
608
609
610
611  /**
612   * Indicates whether the set of operational attributes should be
613   * included in the export.
614   *
615   * @return  <CODE>true</CODE> if the set of operational attributes
616   *          should be included in the export.
617   */
618  public boolean includeOperationalAttributes()
619  {
620    return includeOperationalAttributes;
621  }
622
623
624
625  /**
626   * Specifies whether the  objectclasss attribute should be
627   * included in the export.
628   *
629   * @param  includeObjectClasses  Specifies whether the
630   *                                objectclass attribute
631   *                                should be included in the
632   *                                export.
633   */
634  public void setIncludeObjectClasses(boolean includeObjectClasses)
635  {
636    this.includeObjectClasses = includeObjectClasses;
637  }
638
639  /**
640   * Specifies whether the set of operational attributes should be
641   * included in the export.
642   *
643   * @param  includeOperationalAttributes  Specifies whether the set
644   *                                       of operational attributes
645   *                                       should be included in the
646   *                                       export.
647   */
648  public void setIncludeOperationalAttributes(
649                   boolean includeOperationalAttributes)
650  {
651    this.includeOperationalAttributes = includeOperationalAttributes;
652  }
653
654
655
656  /**
657   * Indicates whether virtual attributes should be included in the
658   * export.
659   *
660   * @return  {@code true} if virtual attributes should be included in
661   *          the export, or {@code false} if not.
662   */
663  public boolean includeVirtualAttributes()
664  {
665    return includeVirtualAttributes;
666  }
667
668
669
670  /**
671   * Specifies whether virtual attributes should be included in the
672   * export.
673   *
674   * @param  includeVirtualAttributes  Specifies whether virtual
675   *                                   attributes should be included
676   *                                   in the export.
677   */
678  public void setIncludeVirtualAttributes(
679                   boolean includeVirtualAttributes)
680  {
681    this.includeVirtualAttributes = includeVirtualAttributes;
682  }
683
684
685
686  /**
687   * Retrieves the set of attributes that should be excluded from the
688   * entries written to LDIF.  The set that is returned may be altered
689   * by the caller.
690   *
691   * @return  The set of attributes that should be excluded from the
692   *          entries written to LDIF.
693   */
694  public Set<AttributeType> getExcludeAttributes()
695  {
696    return excludeAttributes;
697  }
698
699
700
701  /**
702   * Specifies the set of attributes that should be excluded from the
703   * entries written to LDIF.
704   *
705   * @param  excludeAttributes  The set of attributes that should be
706   *                            excluded from the entries written to
707   *                            LDIF.
708   */
709  public void setExcludeAttributes(
710                   Set<AttributeType> excludeAttributes)
711  {
712    if (excludeAttributes == null)
713    {
714      this.excludeAttributes = new HashSet<>(0);
715    }
716    else
717    {
718      this.excludeAttributes = excludeAttributes;
719    }
720  }
721
722
723
724  /**
725   * Retrieves the set of attributes that should be included in the
726   * entries written to LDIF.  The set that is returned may be altered
727   * by the caller.
728   *
729   * @return  The set of attributes that should be included in the
730   *          entries written to LDIF.
731   */
732  public Set<AttributeType> getIncludeAttributes()
733  {
734    return includeAttributes;
735  }
736
737
738
739  /**
740   * Specifies the set of attributes that should be included in the
741   * entries written to LDIF.
742   *
743   * @param  includeAttributes  The set of attributes that should be
744   *                            included in the entries written to
745   *                            LDIF.
746   */
747  public void setIncludeAttributes(Set<AttributeType> includeAttributes)
748  {
749    if (includeAttributes == null)
750    {
751      this.includeAttributes = new HashSet<>(0);
752    }
753    else
754    {
755      this.includeAttributes = includeAttributes;
756    }
757  }
758
759
760
761  /**
762   * Indicates whether the specified attribute should be included in
763   * the entries written to LDIF.
764   *
765   * @param  attributeType  The attribute type for which to make the
766   *                        determination.
767   *
768   * @return  <CODE>true</CODE> if the specified attribute should be
769   *          included in the entries written to LDIF, or
770   *          <CODE>false</CODE> if not.
771   */
772  public boolean includeAttribute(AttributeType attributeType)
773  {
774    return (excludeAttributes.isEmpty()
775              || !excludeAttributes.contains(attributeType))
776        && (includeAttributes.isEmpty()
777              || includeAttributes.contains(attributeType));
778  }
779
780
781
782  /**
783   * Retrieves the set of search filters that should be used to
784   * determine which entries to exclude from the LDIF.  The list that
785   * is returned may be altered by the caller.
786   *
787   * @return  The set of search filters that should be used to
788   *          determine which entries to exclude from the LDIF.
789   */
790  public List<SearchFilter> getExcludeFilters()
791  {
792    return excludeFilters;
793  }
794
795
796
797  /**
798   * Specifies the set of search filters that should be used to
799   * determine which entries to exclude from the LDIF.
800   *
801   * @param  excludeFilters  The set of search filters that should be
802   *                         used to determine which entries to
803   *                         exclude from the LDIF.
804   */
805  public void setExcludeFilters(List<SearchFilter> excludeFilters)
806  {
807    if (excludeFilters == null)
808    {
809      this.excludeFilters = new ArrayList<>(0);
810    }
811    else
812    {
813      this.excludeFilters = excludeFilters;
814    }
815  }
816
817
818
819  /**
820   * Retrieves the set of search filters that should be used to
821   * determine which entries to include in the LDIF.  The list that is
822   * returned may be altered by the caller.
823   *
824   * @return  The set of search filters that should be used to
825   *          determine which entries to include in the LDIF.
826   */
827  public List<SearchFilter> getIncludeFilters()
828  {
829    return includeFilters;
830  }
831
832
833
834  /**
835   * Specifies the set of search filters that should be used to
836   * determine which entries to include in the LDIF.
837   *
838   * @param  includeFilters  The set of search filters that should be
839   *                         used to determine which entries to
840   *                         include in the LDIF.
841   */
842  public void setIncludeFilters(List<SearchFilter> includeFilters)
843  {
844    if (includeFilters == null)
845    {
846      this.includeFilters = new ArrayList<>(0);
847    }
848    else
849    {
850      this.includeFilters = includeFilters;
851    }
852  }
853
854
855
856  /**
857   * Indicates whether the specified entry should be included in the
858   * export based on the configured set of include and exclude
859   * filters.
860   *
861   * @param  entry  The entry for which to make the determination.
862   *
863   * @return  <CODE>true</CODE> if the specified entry should be
864   *          included in the export, or <CODE>false</CODE> if not.
865   *
866   * @throws  DirectoryException  If there is a problem with any of
867   *                              the search filters used to make the
868   *                              determination.
869   */
870  public boolean includeEntry(Entry entry)
871         throws DirectoryException
872  {
873    DN dn = entry.getName();
874    if (! excludeBranches.isEmpty())
875    {
876      for (DN excludeBranch : excludeBranches)
877      {
878        if (excludeBranch.isAncestorOf(dn))
879        {
880          return false;
881        }
882      }
883    }
884
885    checkIncludeBranches: if (! includeBranches.isEmpty())
886    {
887      for (DN includeBranch : includeBranches)
888      {
889        if (includeBranch.isAncestorOf(dn))
890        {
891          break checkIncludeBranches;
892        }
893      }
894
895      return false;
896    }
897
898    if (! excludeFilters.isEmpty())
899    {
900      for (SearchFilter filter : excludeFilters)
901      {
902        if (filter.matchesEntry(entry))
903        {
904          return false;
905        }
906      }
907    }
908
909    if (! includeFilters.isEmpty())
910    {
911      for (SearchFilter filter : includeFilters)
912      {
913        if (filter.matchesEntry(entry))
914        {
915          return true;
916        }
917      }
918      return false;
919    }
920
921    return true;
922  }
923
924
925
926  /**
927   * Closes any resources that this export config might have open.
928   */
929  @Override
930  public void close()
931  {
932    // FIXME -- Need to add code to generate a signed hash of the LDIF content.
933    StaticUtils.close(writer);
934  }
935}