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