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-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2011-2017 ForgeRock AS.
016 */
017package org.opends.server.util;
018
019import static org.opends.messages.UtilityMessages.*;
020import static org.opends.server.util.ServerConstants.*;
021
022import java.io.Closeable;
023import java.io.File;
024import java.io.FileInputStream;
025import java.io.FileOutputStream;
026import java.io.IOException;
027import java.io.UnsupportedEncodingException;
028import java.net.InetAddress;
029import java.net.InetSocketAddress;
030import java.net.ServerSocket;
031import java.net.Socket;
032import java.nio.ByteBuffer;
033import java.text.ParseException;
034import java.text.SimpleDateFormat;
035import java.util.ArrayList;
036import java.util.Arrays;
037import java.util.Collection;
038import java.util.Collections;
039import java.util.Date;
040import java.util.HashSet;
041import java.util.Iterator;
042import java.util.LinkedHashMap;
043import java.util.List;
044import java.util.Map;
045import java.util.TimeZone;
046
047import javax.naming.InitialContext;
048import javax.naming.NamingException;
049
050import org.forgerock.i18n.LocalizableMessage;
051import org.forgerock.i18n.LocalizableMessageBuilder;
052import org.forgerock.i18n.LocalizableMessageDescriptor;
053import org.forgerock.i18n.slf4j.LocalizedLogger;
054import org.forgerock.opendj.ldap.AVA;
055import org.forgerock.opendj.ldap.ByteSequence;
056import org.forgerock.opendj.ldap.ByteString;
057import org.forgerock.opendj.ldap.DN;
058import org.forgerock.opendj.ldap.RDN;
059import org.forgerock.opendj.ldap.schema.AttributeType;
060import org.forgerock.opendj.ldap.schema.CoreSchema;
061import org.forgerock.opendj.ldap.schema.ObjectClass;
062import org.forgerock.util.Reject;
063import org.opends.messages.ToolMessages;
064import org.opends.server.core.DirectoryServer;
065import org.opends.server.core.ServerContext;
066import org.opends.server.types.Attribute;
067import org.opends.server.types.AttributeBuilder;
068import org.opends.server.types.Entry;
069import org.opends.server.types.IdentifiedException;
070
071import com.forgerock.opendj.cli.Argument;
072import com.forgerock.opendj.cli.ArgumentException;
073
074/**
075 * This class defines a number of static utility methods that may be used
076 * throughout the server.  Note that because of the frequency with which these
077 * methods are expected to be used, very little debug logging will be performed
078 * to prevent the log from filling up with unimportant calls and to reduce the
079 * impact that debugging may have on performance.
080 */
081@org.opends.server.types.PublicAPI(
082     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
083     mayInstantiate=false,
084     mayExtend=false,
085     mayInvoke=true)
086public final class StaticUtils
087{
088  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
089
090  /** The number of bytes of a Java int. A Java int is 32 bits, i.e. 4 bytes. */
091  public static final int INT_SIZE = 4;
092  /** The number of bytes of a Java long. A Java int is 64 bits, i.e. 8 bytes. */
093  public static final int LONG_SIZE = 8;
094
095  /**
096   * Number of bytes in a Kibibyte.
097   * <p>
098   * Example usage:
099   * <pre>
100   * int _10KB = 10 * KB;
101   * </pre>
102   */
103  public static final int KB = 1024;
104  /**
105   * Number of bytes in a Mebibyte.
106   * <p>
107   * Example usage:
108   * <pre>
109   * int _10MB = 10 * MB;
110   * </pre>
111   */
112  public static final int MB = KB * KB;
113  /** The pattern that asks a search request to return all user attributes. */
114  public static final String ALL_USER_ATTRIBUTES = "*";
115  /** The pattern that asks a search request to return all operational attributes. */
116  public static final String ALL_OPERATIONAL_ATTRIBUTES = "+";
117  /** The pattern that asks a search request to return all user and operational attributes. */
118  public static final String[] ALL_USER_AND_OPERATIONAL_ATTRIBUTES =
119          { ALL_USER_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES };
120  /** Private constructor to prevent instantiation. */
121  private StaticUtils() {
122    // No implementation required.
123  }
124
125  /**
126   * Construct a byte array containing the UTF-8 encoding of the
127   * provided string. This is significantly faster
128   * than calling {@link String#getBytes(String)} for ASCII strings.
129   *
130   * @param s
131   *          The string to convert to a UTF-8 byte array.
132   * @return Returns a byte array containing the UTF-8 encoding of the
133   *         provided string.
134   */
135  public static byte[] getBytes(String s)
136  {
137    return com.forgerock.opendj.util.StaticUtils.getBytes(s);
138  }
139
140
141  /**
142   * Returns the provided byte array decoded as a UTF-8 string without throwing
143   * an UnsupportedEncodingException. This method is equivalent to:
144   *
145   * <pre>
146   * try
147   * {
148   *   return new String(bytes, &quot;UTF-8&quot;);
149   * }
150   * catch (UnsupportedEncodingException e)
151   * {
152   *   // Should never happen: UTF-8 is always supported.
153   *   throw new RuntimeException(e);
154   * }
155   * </pre>
156   *
157   * @param bytes
158   *          The byte array to be decoded as a UTF-8 string.
159   * @return The decoded string.
160   */
161  public static String decodeUTF8(final byte[] bytes)
162  {
163    Reject.ifNull(bytes);
164
165    if (bytes.length == 0)
166    {
167      return "".intern();
168    }
169
170    final StringBuilder builder = new StringBuilder(bytes.length);
171    final int sz = bytes.length;
172
173    for (int i = 0; i < sz; i++)
174    {
175      final byte b = bytes[i];
176      if ((b & 0x7f) != b)
177      {
178        try
179        {
180          builder.append(new String(bytes, i, (sz - i), "UTF-8"));
181        }
182        catch (UnsupportedEncodingException e)
183        {
184          // Should never happen: UTF-8 is always supported.
185          throw new RuntimeException(e);
186        }
187        break;
188      }
189      builder.append((char) b);
190    }
191    return builder.toString();
192  }
193
194
195
196  /**
197   * Retrieves a string representation of the provided byte in hexadecimal.
198   *
199   * @param b   The byte for which to retrieve the hexadecimal string
200   *            representation.
201   * @return The string representation of the provided byte in hexadecimal.
202   */
203
204  public static String byteToHex(final byte b)
205  {
206    return com.forgerock.opendj.util.StaticUtils.byteToHex(b);
207  }
208  /**
209   * Retrieves a string representation of the provided byte in hexadecimal.
210   *
211   * @param  b  The byte for which to retrieve the hexadecimal string
212   *            representation.
213   * @return The string representation of the provided byte in hexadecimal
214   *         using lowercase characters.
215   */
216  public static String byteToLowerHex(final byte b)
217  {
218    return com.forgerock.opendj.util.StaticUtils.byteToLowerHex(b);
219  }
220
221  /**
222   * Retrieves a string representation of the contents of the provided byte
223   * array using hexadecimal characters with no space between each byte.
224   *
225   * @param  b  The byte array containing the data.
226   *
227   * @return  A string representation of the contents of the provided byte
228   *          array using hexadecimal characters.
229   */
230  public static String bytesToHexNoSpace(byte[] b)
231  {
232    if (b == null || b.length == 0)
233    {
234      return "";
235    }
236
237    int arrayLength = b.length;
238    StringBuilder buffer = new StringBuilder(arrayLength * 2);
239
240    for (int i=0; i < arrayLength; i++)
241    {
242      buffer.append(byteToHex(b[i]));
243    }
244
245    return buffer.toString();
246  }
247
248
249
250  /**
251   * Retrieves a string representation of the contents of the provided byte
252   * array using hexadecimal characters and a space between each byte.
253   *
254   * @param  b  The byte array containing the data.
255   * @return  A string representation of the contents of the provided byte
256   *          array using hexadecimal characters.
257   */
258  public static String bytesToHex(byte[] b)
259  {
260    if (b == null || b.length == 0)
261    {
262      return "";
263    }
264
265    int arrayLength = b.length;
266    StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2);
267    buffer.append(byteToHex(b[0]));
268
269    for (int i=1; i < arrayLength; i++)
270    {
271      buffer.append(" ");
272      buffer.append(byteToHex(b[i]));
273    }
274
275    return buffer.toString();
276  }
277
278  /**
279   * Retrieves a string representation of the contents of the provided byte
280   * sequence using hexadecimal characters and a space between each byte.
281   *
282   * @param b The byte sequence containing the data.
283   * @return A string representation of the contents of the provided byte
284   *         sequence using hexadecimal characters.
285   */
286  public static String bytesToHex(ByteSequence b)
287  {
288    if (b == null || b.length() == 0)
289    {
290      return "";
291    }
292
293    int arrayLength = b.length();
294    StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2);
295    buffer.append(byteToHex(b.byteAt(0)));
296
297    for (int i=1; i < arrayLength; i++)
298    {
299      buffer.append(" ");
300      buffer.append(byteToHex(b.byteAt(i)));
301    }
302
303    return buffer.toString();
304  }
305
306
307
308  /**
309   * Retrieves a string representation of the contents of the provided byte
310   * array using hexadecimal characters and a colon between each byte.
311   *
312   * @param  b  The byte array containing the data.
313   *
314   * @return  A string representation of the contents of the provided byte
315   *          array using hexadecimal characters.
316   */
317  public static String bytesToColonDelimitedHex(byte[] b)
318  {
319    if (b == null || b.length == 0)
320    {
321      return "";
322    }
323
324    int arrayLength = b.length;
325    StringBuilder buffer = new StringBuilder((arrayLength - 1) * 3 + 2);
326    buffer.append(byteToHex(b[0]));
327
328    for (int i=1; i < arrayLength; i++)
329    {
330      buffer.append(":");
331      buffer.append(byteToHex(b[i]));
332    }
333
334    return buffer.toString();
335  }
336
337
338
339  /**
340   * Retrieves a string representation of the contents of the provided byte
341   * buffer using hexadecimal characters and a space between each byte.
342   *
343   * @param  b  The byte buffer containing the data.
344   *
345   * @return  A string representation of the contents of the provided byte
346   *          buffer using hexadecimal characters.
347   */
348  public static String bytesToHex(ByteBuffer b)
349  {
350    if (b == null)
351    {
352      return "";
353    }
354
355    int position = b.position();
356    int limit    = b.limit();
357    int length   = limit - position;
358
359    if (length == 0)
360    {
361      return "";
362    }
363
364    StringBuilder buffer = new StringBuilder((length - 1) * 3 + 2);
365    buffer.append(byteToHex(b.get()));
366
367    for (int i=1; i < length; i++)
368    {
369      buffer.append(" ");
370      buffer.append(byteToHex(b.get()));
371    }
372
373    b.position(position);
374    b.limit(limit);
375
376    return buffer.toString();
377  }
378
379
380
381  /**
382   * Appends a string representation of the provided byte array to the given
383   * buffer using the specified indent.  The data will be formatted with sixteen
384   * hex bytes in a row followed by the ASCII representation, then wrapping to a
385   * new line as necessary.
386   *
387   * @param  buffer  The buffer to which the information is to be appended.
388   * @param  b       The byte array containing the data to write.
389   * @param  indent  The number of spaces to indent the output.
390   */
391  public static void byteArrayToHexPlusAscii(StringBuilder buffer, byte[] b,
392                                             int indent)
393  {
394    StringBuilder indentBuf = new StringBuilder(indent);
395    for (int i=0 ; i < indent; i++)
396    {
397      indentBuf.append(' ');
398    }
399
400
401
402    int length = b.length;
403    int pos    = 0;
404    while (length - pos >= 16)
405    {
406      StringBuilder asciiBuf = new StringBuilder(17);
407
408      buffer.append(indentBuf);
409      buffer.append(byteToHex(b[pos]));
410      asciiBuf.append(byteToASCII(b[pos]));
411      pos++;
412
413      for (int i=1; i < 16; i++, pos++)
414      {
415        buffer.append(' ');
416        buffer.append(byteToHex(b[pos]));
417        asciiBuf.append(byteToASCII(b[pos]));
418
419        if (i == 7)
420        {
421          buffer.append("  ");
422          asciiBuf.append(' ');
423        }
424      }
425
426      buffer.append("  ");
427      buffer.append(asciiBuf);
428      buffer.append(EOL);
429    }
430
431
432    int remaining = length - pos;
433    if (remaining > 0)
434    {
435      StringBuilder asciiBuf = new StringBuilder(remaining+1);
436
437      buffer.append(indentBuf);
438      buffer.append(byteToHex(b[pos]));
439      asciiBuf.append(byteToASCII(b[pos]));
440      pos++;
441
442      for (int i=1; i < 16; i++)
443      {
444        buffer.append(' ');
445
446        if (i < remaining)
447        {
448          buffer.append(byteToHex(b[pos]));
449          asciiBuf.append(byteToASCII(b[pos]));
450          pos++;
451        }
452        else
453        {
454          buffer.append("  ");
455        }
456
457        if (i == 7)
458        {
459          buffer.append("  ");
460
461          if (i < remaining)
462          {
463            asciiBuf.append(' ');
464          }
465        }
466      }
467
468      buffer.append("  ");
469      buffer.append(asciiBuf);
470      buffer.append(EOL);
471    }
472  }
473
474  private static char byteToASCII(byte b)
475  {
476    return com.forgerock.opendj.util.StaticUtils.byteToASCII(b);
477  }
478
479  /**
480   * Compare two byte arrays for order. Returns a negative integer,
481   * zero, or a positive integer as the first argument is less than,
482   * equal to, or greater than the second.
483   *
484   * @param a
485   *          The first byte array to be compared.
486   * @param a2
487   *          The second byte array to be compared.
488   * @return Returns a negative integer, zero, or a positive integer
489   *         if the first byte array is less than, equal to, or greater
490   *         than the second.
491   */
492  public static int compare(byte[] a, byte[] a2) {
493    if (a == a2) {
494      return 0;
495    }
496
497    if (a == null) {
498      return -1;
499    }
500
501    if (a2 == null) {
502      return 1;
503    }
504
505    int minLength = Math.min(a.length, a2.length);
506    for (int i = 0; i < minLength; i++) {
507      int firstByte = 0xFF & a[i];
508      int secondByte = 0xFF & a2[i];
509      if (firstByte != secondByte) {
510        if (firstByte < secondByte) {
511          return -1;
512        } else if (firstByte > secondByte) {
513          return 1;
514        }
515      }
516    }
517
518    return a.length - a2.length;
519  }
520
521  /**
522   * Retrieves the best human-readable message for the provided exception.  For
523   * exceptions defined in the OpenDJ project, it will attempt to use the
524   * message (combining it with the message ID if available).  For some
525   * exceptions that use encapsulation (e.g., InvocationTargetException), it
526   * will be unwrapped and the cause will be treated.  For all others, the
527   *
528   *
529   * @param  t  The {@code Throwable} object for which to retrieve the message.
530   *
531   * @return  The human-readable message generated for the provided exception.
532   */
533  public static LocalizableMessage getExceptionMessage(Throwable t)
534  {
535    if (t instanceof IdentifiedException)
536    {
537      IdentifiedException ie = (IdentifiedException) t;
538
539      StringBuilder message = new StringBuilder();
540      message.append(ie.getMessage());
541      message.append(" (id=");
542      LocalizableMessage ieMsg = ie.getMessageObject();
543      if (ieMsg != null) {
544        message.append(ieMsg.resourceName()).append("-").append(ieMsg.ordinal());
545      } else {
546        message.append("-1");
547      }
548      message.append(")");
549      return LocalizableMessage.raw(message.toString());
550    }
551    else
552    {
553      return com.forgerock.opendj.util.StaticUtils.getExceptionMessage(t);
554    }
555  }
556
557
558
559  /**
560   * Retrieves a stack trace from the provided exception as a single-line
561   * string.
562   *
563   * @param  t  The exception for which to retrieve the stack trace.
564   *
565   * @return  A stack trace from the provided exception as a single-line string.
566   */
567  public static String stackTraceToSingleLineString(Throwable t)
568  {
569    return com.forgerock.opendj.util.StaticUtils.stackTraceToSingleLineString(t, DynamicConstants.DEBUG_BUILD);
570  }
571
572  /**
573   * Retrieves a string representation of the stack trace for the provided
574   * exception.
575   *
576   * @param  t  The exception for which to retrieve the stack trace.
577   *
578   * @return  A string representation of the stack trace for the provided
579   *          exception.
580   */
581  public static String stackTraceToString(Throwable t)
582  {
583    StringBuilder buffer = new StringBuilder();
584    stackTraceToString(buffer, t);
585    return buffer.toString();
586  }
587
588  /**
589   * Check if the stack trace of provided exception contains a given cause.
590   *
591   * @param throwable
592   *          exception that may contain the cause
593   * @param searchedCause
594   *          class of the cause to look for. Any subclass will match.
595   * @return true if and only if the given cause is found as a cause of any
596   *         level in the provided exception.
597   */
598  public static boolean stackTraceContainsCause(
599      Throwable throwable, Class<? extends Throwable> searchedCause)
600  {
601    Throwable t = throwable;
602    while ((t = t.getCause()) != null)
603    {
604      if (searchedCause.isAssignableFrom(t.getClass()))
605      {
606        return true;
607      }
608
609    }
610    return false;
611  }
612
613  /**
614   * Appends a string representation of the stack trace for the provided
615   * exception to the given buffer.
616   *
617   * @param  buffer  The buffer to which the information is to be appended.
618   * @param  t       The exception for which to retrieve the stack trace.
619   */
620  private static void stackTraceToString(StringBuilder buffer, Throwable t)
621  {
622    if (t == null)
623    {
624      return;
625    }
626
627    buffer.append(t);
628
629    for (StackTraceElement e : t.getStackTrace())
630    {
631      buffer.append(EOL);
632      buffer.append("  ");
633      buffer.append(e.getClassName());
634      buffer.append(".");
635      buffer.append(e.getMethodName());
636      buffer.append("(");
637      buffer.append(e.getFileName());
638      buffer.append(":");
639      buffer.append(e.getLineNumber());
640      buffer.append(")");
641    }
642
643    while (t.getCause() != null)
644    {
645      t = t.getCause();
646      buffer.append(EOL);
647      buffer.append("Caused by ");
648      buffer.append(t);
649
650      for (StackTraceElement e : t.getStackTrace())
651      {
652        buffer.append(EOL);
653        buffer.append("  ");
654        buffer.append(e.getClassName());
655        buffer.append(".");
656        buffer.append(e.getMethodName());
657        buffer.append("(");
658        buffer.append(e.getFileName());
659        buffer.append(":");
660        buffer.append(e.getLineNumber());
661        buffer.append(")");
662      }
663    }
664
665    buffer.append(EOL);
666  }
667
668
669
670  /**
671   * Retrieves a backtrace for the current thread consisting only of filenames
672   * and line numbers that may be useful in debugging the origin of problems
673   * that should not have happened.  Note that this may be an expensive
674   * operation to perform, so it should only be used for error conditions or
675   * debugging.
676   *
677   * @return  A backtrace for the current thread.
678   */
679  public static String getBacktrace()
680  {
681    StringBuilder buffer = new StringBuilder();
682
683    StackTraceElement[] elements = Thread.currentThread().getStackTrace();
684
685    if (elements.length > 1)
686    {
687      buffer.append(elements[1].getFileName());
688      buffer.append(":");
689      buffer.append(elements[1].getLineNumber());
690
691      for (int i=2; i < elements.length; i++)
692      {
693        buffer.append(" ");
694        buffer.append(elements[i].getFileName());
695        buffer.append(":");
696        buffer.append(elements[i].getLineNumber());
697      }
698    }
699
700    return buffer.toString();
701  }
702
703
704
705  /**
706   * Retrieves a backtrace for the provided exception consisting of only
707   * filenames and line numbers that may be useful in debugging the origin of
708   * problems.  This is less expensive than the call to
709   * {@code getBacktrace} without any arguments if an exception has already
710   * been thrown.
711   *
712   * @param  t  The exception for which to obtain the backtrace.
713   *
714   * @return  A backtrace from the provided exception.
715   */
716  public static String getBacktrace(Throwable t)
717  {
718    StringBuilder buffer = new StringBuilder();
719
720    StackTraceElement[] elements = t.getStackTrace();
721
722    if (elements.length > 0)
723    {
724      buffer.append(elements[0].getFileName());
725      buffer.append(":");
726      buffer.append(elements[0].getLineNumber());
727
728      for (int i=1; i < elements.length; i++)
729      {
730        buffer.append(" ");
731        buffer.append(elements[i].getFileName());
732        buffer.append(":");
733        buffer.append(elements[i].getLineNumber());
734      }
735    }
736
737    return buffer.toString();
738  }
739
740
741
742  /**
743   * Indicates whether the provided character is a numeric digit.
744   *
745   * @param  c  The character for which to make the determination.
746   *
747   * @return  {@code true} if the provided character represents a numeric
748   *          digit, or {@code false} if not.
749   */
750  public static boolean isDigit(final char c) {
751    return com.forgerock.opendj.util.StaticUtils.isDigit(c);
752  }
753
754
755
756  /**
757   * Indicates whether the provided character is an ASCII alphabetic character.
758   *
759   * @param  c  The character for which to make the determination.
760   *
761   * @return  {@code true} if the provided value is an uppercase or
762   *          lowercase ASCII alphabetic character, or {@code false} if it
763   *          is not.
764   */
765  public static boolean isAlpha(final char c) {
766    return com.forgerock.opendj.util.StaticUtils.isAlpha(c);
767  }
768
769  /**
770   * Indicates whether the provided character is a hexadecimal digit.
771   *
772   * @param  c  The character for which to make the determination.
773   *
774   * @return  {@code true} if the provided character represents a
775   *          hexadecimal digit, or {@code false} if not.
776   */
777  public static boolean isHexDigit(final char c) {
778    return com.forgerock.opendj.util.StaticUtils.isHexDigit(c);
779  }
780
781  /**
782   * Indicates whether the provided byte represents a hexadecimal digit.
783   *
784   * @param  b  The byte for which to make the determination.
785   *
786   * @return  {@code true} if the provided byte represents a hexadecimal
787   *          digit, or {@code false} if not.
788   */
789  public static boolean isHexDigit(byte b)
790  {
791    switch (b)
792    {
793      case '0':
794      case '1':
795      case '2':
796      case '3':
797      case '4':
798      case '5':
799      case '6':
800      case '7':
801      case '8':
802      case '9':
803      case 'A':
804      case 'B':
805      case 'C':
806      case 'D':
807      case 'E':
808      case 'F':
809      case 'a':
810      case 'b':
811      case 'c':
812      case 'd':
813      case 'e':
814      case 'f':
815        return true;
816      default:
817        return false;
818    }
819  }
820
821
822
823  /**
824   * Converts the provided hexadecimal string to a byte array.
825   *
826   * @param  hexString  The hexadecimal string to convert to a byte array.
827   *
828   * @return  The byte array containing the binary representation of the
829   *          provided hex string.
830   *
831   * @throws  ParseException  If the provided string contains invalid
832   *                          hexadecimal digits or does not contain an even
833   *                          number of digits.
834   */
835  public static byte[] hexStringToByteArray(String hexString)
836         throws ParseException
837  {
838    int length;
839    if (hexString == null || ((length = hexString.length()) == 0))
840    {
841      return new byte[0];
842    }
843
844
845    if ((length % 2) == 1)
846    {
847      LocalizableMessage message = ERR_HEX_DECODE_INVALID_LENGTH.get(hexString);
848      throw new ParseException(message.toString(), 0);
849    }
850
851
852    int pos = 0;
853    int arrayLength = length / 2;
854    byte[] returnArray = new byte[arrayLength];
855    for (int i=0; i < arrayLength; i++)
856    {
857      switch (hexString.charAt(pos++))
858      {
859        case '0':
860          returnArray[i] = 0x00;
861          break;
862        case '1':
863          returnArray[i] = 0x10;
864          break;
865        case '2':
866          returnArray[i] = 0x20;
867          break;
868        case '3':
869          returnArray[i] = 0x30;
870          break;
871        case '4':
872          returnArray[i] = 0x40;
873          break;
874        case '5':
875          returnArray[i] = 0x50;
876          break;
877        case '6':
878          returnArray[i] = 0x60;
879          break;
880        case '7':
881          returnArray[i] = 0x70;
882          break;
883        case '8':
884          returnArray[i] = (byte) 0x80;
885          break;
886        case '9':
887          returnArray[i] = (byte) 0x90;
888          break;
889        case 'A':
890        case 'a':
891          returnArray[i] = (byte) 0xA0;
892          break;
893        case 'B':
894        case 'b':
895          returnArray[i] = (byte) 0xB0;
896          break;
897        case 'C':
898        case 'c':
899          returnArray[i] = (byte) 0xC0;
900          break;
901        case 'D':
902        case 'd':
903          returnArray[i] = (byte) 0xD0;
904          break;
905        case 'E':
906        case 'e':
907          returnArray[i] = (byte) 0xE0;
908          break;
909        case 'F':
910        case 'f':
911          returnArray[i] = (byte) 0xF0;
912          break;
913        default:
914          LocalizableMessage message = ERR_HEX_DECODE_INVALID_CHARACTER.get(
915              hexString, hexString.charAt(pos-1));
916          throw new ParseException(message.toString(), 0);
917      }
918
919      switch (hexString.charAt(pos++))
920      {
921        case '0':
922          // No action required.
923          break;
924        case '1':
925          returnArray[i] |= 0x01;
926          break;
927        case '2':
928          returnArray[i] |= 0x02;
929          break;
930        case '3':
931          returnArray[i] |= 0x03;
932          break;
933        case '4':
934          returnArray[i] |= 0x04;
935          break;
936        case '5':
937          returnArray[i] |= 0x05;
938          break;
939        case '6':
940          returnArray[i] |= 0x06;
941          break;
942        case '7':
943          returnArray[i] |= 0x07;
944          break;
945        case '8':
946          returnArray[i] |= 0x08;
947          break;
948        case '9':
949          returnArray[i] |= 0x09;
950          break;
951        case 'A':
952        case 'a':
953          returnArray[i] |= 0x0A;
954          break;
955        case 'B':
956        case 'b':
957          returnArray[i] |= 0x0B;
958          break;
959        case 'C':
960        case 'c':
961          returnArray[i] |= 0x0C;
962          break;
963        case 'D':
964        case 'd':
965          returnArray[i] |= 0x0D;
966          break;
967        case 'E':
968        case 'e':
969          returnArray[i] |= 0x0E;
970          break;
971        case 'F':
972        case 'f':
973          returnArray[i] |= 0x0F;
974          break;
975        default:
976          LocalizableMessage message = ERR_HEX_DECODE_INVALID_CHARACTER.get(
977              hexString, hexString.charAt(pos-1));
978          throw new ParseException(message.toString(), 0);
979      }
980    }
981
982    return returnArray;
983  }
984
985
986
987  /**
988   * Indicates whether the provided value needs to be base64-encoded if it is
989   * represented in LDIF form.
990   *
991   * @param  valueBytes  The binary representation of the attribute value for
992   *                     which to make the determination.
993   *
994   * @return  {@code true} if the value needs to be base64-encoded if it is
995   *          represented in LDIF form, or {@code false} if not.
996   */
997  public static boolean needsBase64Encoding(ByteSequence valueBytes)
998  {
999    int length;
1000    if (valueBytes == null || ((length = valueBytes.length()) == 0))
1001    {
1002      return false;
1003    }
1004
1005
1006    // If the value starts with a space, colon, or less than, then it needs to
1007    // be base64-encoded.
1008    switch (valueBytes.byteAt(0))
1009    {
1010      case 0x20: // Space
1011      case 0x3A: // Colon
1012      case 0x3C: // Less-than
1013        return true;
1014    }
1015
1016
1017    // If the value ends with a space, then it needs to be base64-encoded.
1018    if (length > 1 && valueBytes.byteAt(length - 1) == 0x20)
1019    {
1020      return true;
1021    }
1022
1023
1024    // If the value contains a null, newline, or return character, then it needs
1025    // to be base64-encoded.
1026    byte b;
1027    for (int i = 0; i < valueBytes.length(); i++)
1028    {
1029      b = valueBytes.byteAt(i);
1030      if (b < 0 || 127 < b)
1031      {
1032        return true;
1033      }
1034
1035      switch (b)
1036      {
1037        case 0x00: // Null
1038        case 0x0A: // New line
1039        case 0x0D: // Carriage return
1040          return true;
1041      }
1042    }
1043
1044
1045    // If we've made it here, then there's no reason to base64-encode.
1046    return false;
1047  }
1048
1049
1050
1051  /**
1052   * Indicates whether the provided value needs to be base64-encoded if it is
1053   * represented in LDIF form.
1054   *
1055   * @param  valueString  The string representation of the attribute value for
1056   *                      which to make the determination.
1057   *
1058   * @return  {@code true} if the value needs to be base64-encoded if it is
1059   *          represented in LDIF form, or {@code false} if not.
1060   */
1061  public static boolean needsBase64Encoding(String valueString)
1062  {
1063    int length;
1064    if (valueString == null || ((length = valueString.length()) == 0))
1065    {
1066      return false;
1067    }
1068
1069
1070    // If the value starts with a space, colon, or less than, then it needs to
1071    // be base64-encoded.
1072    switch (valueString.charAt(0))
1073    {
1074      case ' ':
1075      case ':':
1076      case '<':
1077        return true;
1078    }
1079
1080
1081    // If the value ends with a space, then it needs to be base64-encoded.
1082    if (length > 1 && valueString.charAt(length - 1) == ' ')
1083    {
1084      return true;
1085    }
1086
1087
1088    // If the value contains a null, newline, or return character, then it needs
1089    // to be base64-encoded.
1090    for (int i=0; i < length; i++)
1091    {
1092      char c = valueString.charAt(i);
1093      if (c <= 0 || c == 0x0A || c == 0x0D || c > 127)
1094      {
1095        return true;
1096      }
1097    }
1098
1099
1100    // If we've made it here, then there's no reason to base64-encode.
1101    return false;
1102  }
1103
1104
1105
1106  /**
1107   * Indicates whether the use of the exec method will be allowed on this
1108   * system.  It will be allowed by default, but that capability will be removed
1109   * if the org.opends.server.DisableExec system property is set and has any
1110   * value other than "false", "off", "no", or "0".
1111   *
1112   * @return  {@code true} if the use of the exec method should be allowed,
1113   *          or {@code false} if it should not be allowed.
1114   */
1115  private static boolean mayUseExec()
1116  {
1117    return !DirectoryServer.getEnvironmentConfig().disableExec();
1118  }
1119
1120  /**
1121   * Indicates whether the provided string contains a name or OID for a schema
1122   * element like an attribute type or objectclass.
1123   *
1124   * @param  element        The string containing the substring for which to
1125   *                        make the determination.
1126   * @param  startPos       The position of the first character that is to be
1127   *                        checked.
1128   * @param  endPos         The position of the first character after the start
1129   *                        position that is not to be checked.
1130   * @param  invalidReason  The buffer to which the invalid reason is to be
1131   *                        appended if a problem is found.
1132   *
1133   * @return  {@code true} if the provided string contains a valid name or
1134   *          OID for a schema element, or {@code false} if it does not.
1135   */
1136  public static boolean isValidSchemaElement(String element, int startPos,
1137                                             int endPos,
1138                                             LocalizableMessageBuilder invalidReason)
1139  {
1140    if (element == null || startPos >= endPos)
1141    {
1142      invalidReason.append(ERR_SCHEMANAME_EMPTY_VALUE.get());
1143      return false;
1144    }
1145
1146
1147    char c = element.charAt(startPos);
1148    if (isAlpha(c))
1149    {
1150      // This can only be a name and not an OID.  The only remaining characters
1151      // must be letters, digits, dashes, and possibly the underscore.
1152      for (int i=startPos+1; i < endPos; i++)
1153      {
1154        c = element.charAt(i);
1155        if (!isAlpha(c)
1156            && !isDigit(c)
1157            && c != '-'
1158            && (c != '_' || !DirectoryServer.allowAttributeNameExceptions()))
1159        {
1160          // This is an illegal character for an attribute name.
1161          invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(element, c, i));
1162          return false;
1163        }
1164      }
1165    }
1166    else if (isDigit(c))
1167    {
1168      // This should indicate an OID, but it may also be a name if name
1169      // exceptions are enabled.  Since we don't know for sure, we'll just
1170      // hold off until we know for sure.
1171      boolean isKnown    = !DirectoryServer.allowAttributeNameExceptions();
1172      boolean isNumeric  = true;
1173      boolean lastWasDot = false;
1174
1175      for (int i=startPos+1; i < endPos; i++)
1176      {
1177        c = element.charAt(i);
1178        if (c == '.')
1179        {
1180          if (isKnown)
1181          {
1182            if (isNumeric)
1183            {
1184              // This is probably legal unless the last character was also a
1185              // period.
1186              if (lastWasDot)
1187              {
1188                invalidReason.append(ERR_SCHEMANAME_CONSECUTIVE_PERIODS.get(
1189                        element, i));
1190                return false;
1191              }
1192              lastWasDot = true;
1193            }
1194            else
1195            {
1196              // This is an illegal character.
1197              invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
1198                      element, c, i));
1199              return false;
1200            }
1201          }
1202          else
1203          {
1204            // Now we know that this must be a numeric OID and not an attribute
1205            // name with exceptions allowed.
1206            lastWasDot = true;
1207            isKnown    = true;
1208            isNumeric  = true;
1209          }
1210        }
1211        else
1212        {
1213          lastWasDot = false;
1214
1215          if (isAlpha(c) || c == '-' || c == '_')
1216          {
1217            if (isKnown)
1218            {
1219              if (isNumeric)
1220              {
1221                // This is an illegal character for a numeric OID.
1222                invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
1223                        element, c, i));
1224                return false;
1225              }
1226            }
1227            else
1228            {
1229              // Now we know that this must be an attribute name with exceptions
1230              // allowed and not a numeric OID.
1231              isKnown   = true;
1232              isNumeric = false;
1233            }
1234          }
1235          else if (! isDigit(c))
1236          {
1237            // This is an illegal character.
1238            invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
1239                    element, c, i));
1240            return false;
1241          }
1242        }
1243      }
1244    }
1245    else
1246    {
1247      // This is an illegal character.
1248      invalidReason.append(ERR_SCHEMANAME_ILLEGAL_CHAR.get(
1249              element, c, startPos));
1250      return false;
1251    }
1252
1253
1254    // If we've gotten here, then the value is fine.
1255    return true;
1256  }
1257
1258
1259
1260  /**
1261   * Indicates whether the provided TCP address is already in use.
1262   *
1263   * @param  address        IP address of the TCP address for which to make
1264   *                        the determination.
1265   * @param  port           TCP port number of the TCP address for which to
1266   *                        make the determination.
1267   * @param  allowReuse     Whether TCP address reuse is allowed when
1268   *                        making the determination.
1269   *
1270   * @return  {@code true} if the provided TCP address is already in
1271   *          use, or {@code false} otherwise.
1272   */
1273  public static boolean isAddressInUse(
1274    InetAddress address, int port,
1275    boolean allowReuse)
1276  {
1277    try {
1278      // HACK:
1279      // With dual stacks we can have a situation when INADDR_ANY/PORT
1280      // is bound in TCP4 space but available in TCP6 space and since
1281      // JavaServerSocket implementation will always use TCP46 on dual
1282      // stacks the bind below will always succeed in such cases thus
1283      // shadowing anything that is already bound to INADDR_ANY/PORT.
1284      // While technically correct, with IPv4 and IPv6 being separate
1285      // address spaces, it presents a problem to end users because a
1286      // common case scenario is to have a single service serving both
1287      // address spaces ie listening to the same port in both spaces
1288      // on wildcard addresses 0 and ::. ServerSocket implementation
1289      // does not provide any means of working with each address space
1290      // separately such as doing TCP4 or TCP6 only binds thus we have
1291      // to do a dummy connect to INADDR_ANY/PORT to check if it is
1292      // bound to something already. This is only needed for wildcard
1293      // addresses as specific IPv4 or IPv6 addresses will always be
1294      // handled in their respective address space.
1295      if (address.isAnyLocalAddress()) {
1296        try (Socket clientSocket = new Socket()) {
1297          // This might fail on some stacks but this is the best we
1298          // can do. No need for explicit timeout since it is local
1299          // address and we have to know for sure unless it fails.
1300          clientSocket.connect(new InetSocketAddress(address, port));
1301          if (clientSocket.isConnected()) {
1302            return true;
1303          }
1304        } catch (IOException ignore) {
1305          // ignore.
1306        }
1307      }
1308      try (ServerSocket serverSocket = new ServerSocket()) {
1309        serverSocket.setReuseAddress(allowReuse);
1310        serverSocket.bind(new InetSocketAddress(address, port));
1311        return false;
1312      }
1313    } catch (IOException ignore) {
1314      // no-op
1315    }
1316    return true;
1317  }
1318
1319
1320
1321  /**
1322   * Returns a lower-case string representation of a given string, verifying for null input string.
1323   *
1324   * @param s the mixed case string
1325   * @return a lower-case string
1326   * @see com.forgerock.opendj.util.StaticUtils#toLowerCase(String)
1327   */
1328  public static String toLowerCase(String s)
1329  {
1330    return (s == null ? null : com.forgerock.opendj.util.StaticUtils.toLowerCase(s));
1331  }
1332
1333  /**
1334   * Appends a lower-case string representation of a given ByteSequence to a StringBuilder,
1335   * verifying for null input.
1336   *
1337   * @param  b       The byte array for which to obtain the lowercase string
1338   *                 representation.
1339   * @param  buffer  The buffer to which the lowercase form of the string should
1340   *                 be appended.
1341   * @param  trim    Indicates whether leading and trailing spaces should be
1342   *                 omitted from the string representation.
1343   * @see com.forgerock.opendj.util.StaticUtils#toLowerCase(ByteSequence, StringBuilder)}
1344   */
1345  public static void toLowerCase(ByteSequence b, StringBuilder buffer, boolean trim)
1346  {
1347    if (b == null)
1348    {
1349      return;
1350    }
1351
1352    if (trim)
1353    {
1354      int begin = 0;
1355      int end = b.length() - 1;
1356      while (begin <= end)
1357      {
1358        if (b.byteAt(begin) == ' ')
1359        {
1360          begin++;
1361        }
1362        else if (b.byteAt(end) == ' ')
1363        {
1364          end--;
1365        }
1366        else
1367        {
1368          break;
1369        }
1370      }
1371      if (begin > 0 || end < b.length() - 1)
1372      {
1373        b = b.subSequence(begin, end + 1);
1374      }
1375    }
1376
1377    com.forgerock.opendj.util.StaticUtils.toLowerCase(b, buffer);
1378  }
1379
1380
1381
1382  /**
1383   * Retrieves an uppercase representation of the given string.  This
1384   * implementation presumes that the provided string will contain only ASCII
1385   * characters and is optimized for that case.  However, if a non-ASCII
1386   * character is encountered it will fall back on a more expensive algorithm
1387   * that will work properly for non-ASCII characters.
1388   *
1389   * @param  s  The string for which to obtain the uppercase representation.
1390   *
1391   * @return  The uppercase representation of the given string.
1392   */
1393  public static String toUpperCase(String s)
1394  {
1395    if (s == null)
1396    {
1397      return null;
1398    }
1399
1400    StringBuilder buffer = new StringBuilder(s.length());
1401    toUpperCase(s, buffer);
1402    return buffer.toString();
1403  }
1404
1405
1406
1407  /**
1408   * Appends an uppercase representation of the given string to the provided
1409   * buffer.  This implementation presumes that the provided string will contain
1410   * only ASCII characters and is optimized for that case.  However, if a
1411   * non-ASCII character is encountered it will fall back on a more expensive
1412   * algorithm that will work properly for non-ASCII characters.
1413   *
1414   * @param  s       The string for which to obtain the uppercase
1415   *                 representation.
1416   * @param  buffer  The buffer to which the uppercase form of the string should
1417   *                 be appended.
1418   */
1419  private static void toUpperCase(String s, StringBuilder buffer)
1420  {
1421    if (s == null)
1422    {
1423      return;
1424    }
1425
1426    int length = s.length();
1427    for (int i=0; i < length; i++)
1428    {
1429      char c = s.charAt(i);
1430
1431      if ((c & 0x7F) != c)
1432      {
1433        buffer.append(s.substring(i).toUpperCase());
1434        return;
1435      }
1436
1437      switch (c)
1438      {
1439        case 'a':
1440          buffer.append('A');
1441          break;
1442        case 'b':
1443          buffer.append('B');
1444          break;
1445        case 'c':
1446          buffer.append('C');
1447          break;
1448        case 'd':
1449          buffer.append('D');
1450          break;
1451        case 'e':
1452          buffer.append('E');
1453          break;
1454        case 'f':
1455          buffer.append('F');
1456          break;
1457        case 'g':
1458          buffer.append('G');
1459          break;
1460        case 'h':
1461          buffer.append('H');
1462          break;
1463        case 'i':
1464          buffer.append('I');
1465          break;
1466        case 'j':
1467          buffer.append('J');
1468          break;
1469        case 'k':
1470          buffer.append('K');
1471          break;
1472        case 'l':
1473          buffer.append('L');
1474          break;
1475        case 'm':
1476          buffer.append('M');
1477          break;
1478        case 'n':
1479          buffer.append('N');
1480          break;
1481        case 'o':
1482          buffer.append('O');
1483          break;
1484        case 'p':
1485          buffer.append('P');
1486          break;
1487        case 'q':
1488          buffer.append('Q');
1489          break;
1490        case 'r':
1491          buffer.append('R');
1492          break;
1493        case 's':
1494          buffer.append('S');
1495          break;
1496        case 't':
1497          buffer.append('T');
1498          break;
1499        case 'u':
1500          buffer.append('U');
1501          break;
1502        case 'v':
1503          buffer.append('V');
1504          break;
1505        case 'w':
1506          buffer.append('W');
1507          break;
1508        case 'x':
1509          buffer.append('X');
1510          break;
1511        case 'y':
1512          buffer.append('Y');
1513          break;
1514        case 'z':
1515          buffer.append('Z');
1516          break;
1517        default:
1518          buffer.append(c);
1519      }
1520    }
1521  }
1522
1523
1524
1525  /**
1526   * Appends an uppercase string representation of the contents of the given
1527   * byte array to the provided buffer, optionally trimming leading and trailing
1528   * spaces.  This implementation presumes that the provided string will contain
1529   * only ASCII characters and is optimized for that case.  However, if a
1530   * non-ASCII character is encountered it will fall back on a more expensive
1531   * algorithm that will work properly for non-ASCII characters.
1532   *
1533   * @param  b       The byte array for which to obtain the uppercase string
1534   *                 representation.
1535   * @param  buffer  The buffer to which the uppercase form of the string should
1536   *                 be appended.
1537   * @param  trim    Indicates whether leading and trailing spaces should be
1538   *                 omitted from the string representation.
1539   */
1540  public static void toUpperCase(byte[] b, StringBuilder buffer, boolean trim)
1541  {
1542    if (b == null)
1543    {
1544      return;
1545    }
1546
1547    int length = b.length;
1548    for (int i=0; i < length; i++)
1549    {
1550      if ((b[i] & 0x7F) != b[i])
1551      {
1552        try
1553        {
1554          buffer.append(new String(b, i, (length-i), "UTF-8").toUpperCase());
1555        }
1556        catch (Exception e)
1557        {
1558          logger.traceException(e);
1559          buffer.append(new String(b, i, (length - i)).toUpperCase());
1560        }
1561        break;
1562      }
1563
1564      int bufferLength = buffer.length();
1565      switch (b[i])
1566      {
1567        case ' ':
1568          // If we don't care about trimming, then we can always append the
1569          // space.  Otherwise, only do so if there are other characters in the value.
1570          if (trim && bufferLength == 0)
1571          {
1572            break;
1573          }
1574
1575          buffer.append(' ');
1576          break;
1577        case 'a':
1578          buffer.append('A');
1579          break;
1580        case 'b':
1581          buffer.append('B');
1582          break;
1583        case 'c':
1584          buffer.append('C');
1585          break;
1586        case 'd':
1587          buffer.append('D');
1588          break;
1589        case 'e':
1590          buffer.append('E');
1591          break;
1592        case 'f':
1593          buffer.append('F');
1594          break;
1595        case 'g':
1596          buffer.append('G');
1597          break;
1598        case 'h':
1599          buffer.append('H');
1600          break;
1601        case 'i':
1602          buffer.append('I');
1603          break;
1604        case 'j':
1605          buffer.append('J');
1606          break;
1607        case 'k':
1608          buffer.append('K');
1609          break;
1610        case 'l':
1611          buffer.append('L');
1612          break;
1613        case 'm':
1614          buffer.append('M');
1615          break;
1616        case 'n':
1617          buffer.append('N');
1618          break;
1619        case 'o':
1620          buffer.append('O');
1621          break;
1622        case 'p':
1623          buffer.append('P');
1624          break;
1625        case 'q':
1626          buffer.append('Q');
1627          break;
1628        case 'r':
1629          buffer.append('R');
1630          break;
1631        case 's':
1632          buffer.append('S');
1633          break;
1634        case 't':
1635          buffer.append('T');
1636          break;
1637        case 'u':
1638          buffer.append('U');
1639          break;
1640        case 'v':
1641          buffer.append('V');
1642          break;
1643        case 'w':
1644          buffer.append('W');
1645          break;
1646        case 'x':
1647          buffer.append('X');
1648          break;
1649        case 'y':
1650          buffer.append('Y');
1651          break;
1652        case 'z':
1653          buffer.append('Z');
1654          break;
1655        default:
1656          buffer.append((char) b[i]);
1657      }
1658    }
1659
1660    if (trim)
1661    {
1662      // Strip off any trailing spaces.
1663      for (int i=buffer.length()-1; i > 0; i--)
1664      {
1665        if (buffer.charAt(i) == ' ')
1666        {
1667          buffer.delete(i, i+1);
1668        }
1669        else
1670        {
1671          break;
1672        }
1673      }
1674    }
1675  }
1676
1677
1678
1679  /**
1680   * Append a string to a string builder, escaping any double quotes
1681   * according to the StringValue production in RFC 3641.
1682   * <p>
1683   * In RFC 3641 the StringValue production looks like this:
1684   *
1685   * <pre>
1686   *    StringValue       = dquote *SafeUTF8Character dquote
1687   *    dquote            = %x22 ; &quot; (double quote)
1688   *    SafeUTF8Character = %x00-21 / %x23-7F /   ; ASCII minus dquote
1689   *                        dquote dquote /       ; escaped double quote
1690   *                        %xC0-DF %x80-BF /     ; 2 byte UTF-8 character
1691   *                        %xE0-EF 2(%x80-BF) /  ; 3 byte UTF-8 character
1692   *                        %xF0-F7 3(%x80-BF)    ; 4 byte UTF-8 character
1693   * </pre>
1694   *
1695   * <p>
1696   * That is, strings are surrounded by double-quotes and any internal
1697   * double-quotes are doubled up.
1698   *
1699   * @param builder
1700   *          The string builder.
1701   * @param string
1702   *          The string to escape and append.
1703   * @return Returns the string builder.
1704   */
1705  public static StringBuilder toRFC3641StringValue(StringBuilder builder,
1706      String string)
1707  {
1708    // Initial double-quote.
1709    builder.append('"');
1710
1711    for (char c : string.toCharArray())
1712    {
1713      if (c == '"')
1714      {
1715        // Internal double-quotes are escaped using a double-quote.
1716        builder.append('"');
1717      }
1718      builder.append(c);
1719    }
1720
1721    // Trailing double-quote.
1722    builder.append('"');
1723
1724    return builder;
1725  }
1726
1727  /**
1728   * Retrieves an array list containing the contents of the provided array.
1729   *
1730   * @param  stringArray  The string array to convert to an array list.
1731   *
1732   * @return  An array list containing the contents of the provided array.
1733   */
1734  public static ArrayList<String> arrayToList(String... stringArray)
1735  {
1736    if (stringArray == null)
1737    {
1738      return null;
1739    }
1740
1741    ArrayList<String> stringList = new ArrayList<>(stringArray.length);
1742    Collections.addAll(stringList, stringArray);
1743    return stringList;
1744  }
1745
1746
1747  /**
1748   * Attempts to delete the specified file or directory. If it is a directory,
1749   * then any files or subdirectories that it contains will be recursively
1750   * deleted as well.
1751   *
1752   * @param file
1753   *          The file or directory to be removed.
1754   * @return {@code true} if the specified file and any subordinates are all
1755   *         successfully removed, or {@code false} if at least one element in
1756   *         the subtree could not be removed or file does not exists.
1757   */
1758  public static boolean recursiveDelete(File file)
1759  {
1760    if (file.exists())
1761    {
1762      boolean successful = true;
1763      if (file.isDirectory())
1764      {
1765        File[] childList = file.listFiles();
1766        if (childList != null)
1767        {
1768          for (File f : childList)
1769          {
1770            successful &= recursiveDelete(f);
1771          }
1772        }
1773      }
1774
1775      return successful & file.delete();
1776    }
1777    return false;
1778  }
1779
1780
1781
1782  /**
1783   * Moves the indicated file to the specified directory by creating a new file
1784   * in the target directory, copying the contents of the existing file, and
1785   * removing the existing file.  The file to move must exist and must be a
1786   * file.  The target directory must exist, must be a directory, and must not
1787   * be the directory in which the file currently resides.
1788   *
1789   * @param  fileToMove       The file to move to the target directory.
1790   * @param  targetDirectory  The directory into which the file should be moved.
1791   *
1792   * @throws  IOException  If a problem occurs while attempting to move the
1793   *                       file.
1794   */
1795  public static void moveFile(File fileToMove, File targetDirectory)
1796         throws IOException
1797  {
1798    if (! fileToMove.exists())
1799    {
1800      LocalizableMessage message = ERR_MOVEFILE_NO_SUCH_FILE.get(fileToMove.getPath());
1801      throw new IOException(message.toString());
1802    }
1803
1804    if (! fileToMove.isFile())
1805    {
1806      LocalizableMessage message = ERR_MOVEFILE_NOT_FILE.get(fileToMove.getPath());
1807      throw new IOException(message.toString());
1808    }
1809
1810    if (! targetDirectory.exists())
1811    {
1812      LocalizableMessage message =
1813          ERR_MOVEFILE_NO_SUCH_DIRECTORY.get(targetDirectory.getPath());
1814      throw new IOException(message.toString());
1815    }
1816
1817    if (! targetDirectory.isDirectory())
1818    {
1819      LocalizableMessage message =
1820          ERR_MOVEFILE_NOT_DIRECTORY.get(targetDirectory.getPath());
1821      throw new IOException(message.toString());
1822    }
1823
1824    String newFilePath = targetDirectory.getPath() + File.separator +
1825                         fileToMove.getName();
1826    FileInputStream  inputStream  = new FileInputStream(fileToMove);
1827    FileOutputStream outputStream = new FileOutputStream(newFilePath, false);
1828    byte[] buffer = new byte[8192];
1829    while (true)
1830    {
1831      int bytesRead = inputStream.read(buffer);
1832      if (bytesRead < 0)
1833      {
1834        break;
1835      }
1836
1837      outputStream.write(buffer, 0, bytesRead);
1838    }
1839
1840    outputStream.flush();
1841    outputStream.close();
1842    inputStream.close();
1843    fileToMove.delete();
1844  }
1845
1846  /**
1847   * Renames the source file to the target file.  If the target file exists
1848   * it is first deleted.  The rename and delete operation return values
1849   * are checked for success and if unsuccessful, this method throws an
1850   * exception.
1851   *
1852   * @param fileToRename The file to rename.
1853   * @param target       The file to which {@code fileToRename} will be
1854   *                     moved.
1855   * @throws IOException If a problem occurs while attempting to rename the
1856   *                     file.  On the Windows platform, this typically
1857   *                     indicates that the file is in use by this or another
1858   *                     application.
1859   */
1860  public static void renameFile(File fileToRename, File target)
1861          throws IOException {
1862    if (fileToRename != null && target != null)
1863    {
1864      synchronized(target)
1865      {
1866        if (target.exists() && !target.delete())
1867        {
1868          LocalizableMessage message =
1869              ERR_RENAMEFILE_CANNOT_DELETE_TARGET.get(target.getPath());
1870          throw new IOException(message.toString());
1871        }
1872      }
1873      if (!fileToRename.renameTo(target))
1874      {
1875        LocalizableMessage message = ERR_RENAMEFILE_CANNOT_RENAME.get(
1876            fileToRename.getPath(), target.getPath());
1877        throw new IOException(message.toString());
1878
1879      }
1880    }
1881  }
1882
1883  /**
1884   * Retrieves a {@code File} object corresponding to the specified path.
1885   * If the given path is an absolute path, then it will be used.  If the path
1886   * is relative, then it will be interpreted as if it were relative to the
1887   * Directory Server root.
1888   *
1889   * @param  path  The path string to be retrieved as a {@code File}
1890   *
1891   * @return  A {@code File} object that corresponds to the specified path.
1892   */
1893  public static File getFileForPath(String path)
1894  {
1895    File f = new File (path);
1896    return f.isAbsolute() ? f : new File(DirectoryServer.getInstanceRoot(), path);
1897  }
1898
1899  /**
1900   * Retrieves a {@code File} object corresponding to the specified path.
1901   * If the given path is an absolute path, then it will be used.  If the path
1902   * is relative, then it will be interpreted as if it were relative to the
1903   * Directory Server root.
1904   *
1905   * @param path
1906   *           The path string to be retrieved as a {@code File}.
1907   * @param serverContext
1908   *           The server context.
1909   *
1910   * @return  A {@code File} object that corresponds to the specified path.
1911   */
1912  public static File getFileForPath(String path, ServerContext serverContext)
1913  {
1914    File f = new File (path);
1915    return f.isAbsolute() ? f : new File(serverContext.getInstanceRoot(), path);
1916  }
1917
1918
1919
1920  /**
1921   * Creates a new, blank entry with the given DN.  It will contain only the
1922   * attribute(s) contained in the RDN.  The choice of objectclasses will be
1923   * based on the RDN attribute.  If there is a single RDN attribute, then the
1924   * following mapping will be used:
1925   * <BR>
1926   * <UL>
1927   *   <LI>c attribute :: country objectclass</LI>
1928   *   <LI>dc attribute :: domain objectclass</LI>
1929   *   <LI>o attribute :: organization objectclass</LI>
1930   *   <LI>ou attribute :: organizationalUnit objectclass</LI>
1931   * </UL>
1932   * <BR>
1933   * Any other single RDN attribute types, or any case in which there are
1934   * multiple RDN attributes, will use the untypedObject objectclass.  If the
1935   * RDN includes one or more attributes that are not allowed in the
1936   * untypedObject objectclass, then the extensibleObject class will also be
1937   * added.  Note that this method cannot be used to generate an entry
1938   * with an empty or null DN.
1939   *
1940   * @param  dn  The DN to use for the entry.
1941   *
1942   * @return  The entry created with the provided DN.
1943   */
1944  public static Entry createEntry(DN dn)
1945  {
1946    // If the provided DN was null or empty, then return null because we don't
1947    // support it.
1948    if (dn == null || dn.isRootDN())
1949    {
1950      return null;
1951    }
1952
1953
1954    // Get the information about the RDN attributes.
1955    RDN rdn = dn.rdn();
1956
1957    // If there is only one RDN attribute, then see which objectclass we should use.
1958    ObjectClass structuralClass = DirectoryServer.getSchema().getObjectClass(getObjectClassName(rdn));
1959
1960    // Get the top and untypedObject classes to include in the entry.
1961    LinkedHashMap<ObjectClass,String> objectClasses = new LinkedHashMap<>(3);
1962    objectClasses.put(CoreSchema.getTopObjectClass(), OC_TOP);
1963    objectClasses.put(structuralClass, structuralClass.getNameOrOID());
1964
1965
1966    // Iterate through the RDN attributes and add them to the set of user or
1967    // operational attributes.
1968    LinkedHashMap<AttributeType,List<Attribute>> userAttributes = new LinkedHashMap<>();
1969    LinkedHashMap<AttributeType,List<Attribute>> operationalAttributes = new LinkedHashMap<>();
1970
1971    boolean extensibleObjectAdded = false;
1972    for (AVA ava : rdn)
1973    {
1974      AttributeType attrType = ava.getAttributeType();
1975
1976      // First, see if this type is allowed by the untypedObject class.  If not,
1977      // then we'll need to include the extensibleObject class.
1978      if (!structuralClass.isRequiredOrOptional(attrType) && !extensibleObjectAdded)
1979      {
1980        objectClasses.put(CoreSchema.getTopObjectClass(), OC_EXTENSIBLE_OBJECT);
1981        extensibleObjectAdded = true;
1982      }
1983
1984
1985      // Create the attribute and add it to the appropriate map.
1986      addAttributeValue(attrType.isOperational() ? operationalAttributes : userAttributes, ava);
1987    }
1988
1989
1990    // Create and return the entry.
1991    return new Entry(dn, objectClasses, userAttributes, operationalAttributes);
1992  }
1993
1994  private static String getObjectClassName(RDN rdn)
1995  {
1996    if (rdn.size() == 1)
1997    {
1998      final AttributeType attrType = rdn.getFirstAVA().getAttributeType();
1999      if (attrType.hasName(ATTR_C))
2000      {
2001        return OC_COUNTRY;
2002      }
2003      else if (attrType.hasName(ATTR_DC))
2004      {
2005        return OC_DOMAIN;
2006      }
2007      else if (attrType.hasName(ATTR_O))
2008      {
2009        return OC_ORGANIZATION;
2010      }
2011      else if (attrType.hasName(ATTR_OU))
2012      {
2013        return OC_ORGANIZATIONAL_UNIT_LC;
2014      }
2015    }
2016    return OC_UNTYPED_OBJECT_LC;
2017  }
2018
2019  private static void addAttributeValue(LinkedHashMap<AttributeType, List<Attribute>> attrs, AVA ava)
2020  {
2021    AttributeType attrType = ava.getAttributeType();
2022    ByteString attrValue = ava.getAttributeValue();
2023    List<Attribute> attrList = attrs.get(attrType);
2024    if (attrList != null && !attrList.isEmpty())
2025    {
2026      AttributeBuilder builder = new AttributeBuilder(attrList.get(0));
2027      builder.add(attrValue);
2028      attrList.set(0, builder.toAttribute());
2029    }
2030    else
2031    {
2032      AttributeBuilder builder = new AttributeBuilder(attrType, ava.getAttributeName());
2033      builder.add(attrValue);
2034      attrs.put(attrType, builder.toAttributeList());
2035    }
2036  }
2037
2038  /**
2039   * Retrieves a user-friendly string that indicates the length of time (in
2040   * days, hours, minutes, and seconds) in the specified number of seconds.
2041   *
2042   * @param  numSeconds  The number of seconds to be converted to a more
2043   *                     user-friendly value.
2044   *
2045   * @return  The user-friendly representation of the specified number of
2046   *          seconds.
2047   */
2048  public static LocalizableMessage secondsToTimeString(long numSeconds)
2049  {
2050    if (numSeconds < 60)
2051    {
2052      // We can express it in seconds.
2053      return INFO_TIME_IN_SECONDS.get(numSeconds);
2054    }
2055    else if (numSeconds < 3600)
2056    {
2057      // We can express it in minutes and seconds.
2058      long m = numSeconds / 60;
2059      long s = numSeconds % 60;
2060      return INFO_TIME_IN_MINUTES_SECONDS.get(m, s);
2061    }
2062    else if (numSeconds < 86400)
2063    {
2064      // We can express it in hours, minutes, and seconds.
2065      long h = numSeconds / 3600;
2066      long m = (numSeconds % 3600) / 60;
2067      long s = numSeconds % 3600 % 60;
2068      return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s);
2069    }
2070    else
2071    {
2072      // We can express it in days, hours, minutes, and seconds.
2073      long d = numSeconds / 86400;
2074      long h = (numSeconds % 86400) / 3600;
2075      long m = (numSeconds % 86400 % 3600) / 60;
2076      long s = numSeconds % 86400 % 3600 % 60;
2077      return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s);
2078    }
2079  }
2080
2081  /**
2082   * Checks that no more that one of a set of arguments is present.  This
2083   * utility should be used after argument parser has parsed a set of
2084   * arguments.
2085   *
2086   * @param  args to test for the presence of more than one
2087   * @throws ArgumentException if more than one of {@code args} is
2088   *         present and containing an error message identifying the
2089   *         arguments in violation
2090   */
2091  public static void checkOnlyOneArgPresent(Argument... args)
2092    throws ArgumentException
2093  {
2094    if (args != null) {
2095      for (Argument arg : args) {
2096        for (Argument otherArg : args) {
2097          if (arg != otherArg && arg.isPresent() && otherArg.isPresent()) {
2098            throw new ArgumentException(
2099                    ToolMessages.ERR_INCOMPATIBLE_ARGUMENTS.get(arg.getLongIdentifier(), otherArg.getLongIdentifier()));
2100          }
2101        }
2102      }
2103    }
2104  }
2105
2106  /**
2107   * Converts a string representing a time in "yyyyMMddHHmmss.SSS'Z'" or
2108   * "yyyyMMddHHmmss" to a {@code Date}.
2109   *
2110   * @param timeStr string formatted appropriately
2111   * @return Date object; null if {@code timeStr} is null
2112   * @throws ParseException if there was a problem converting the string to
2113   *         a {@code Date}.
2114   */
2115  public static Date parseDateTimeString(String timeStr) throws ParseException
2116  {
2117    Date dateTime = null;
2118    if (timeStr != null)
2119    {
2120      if (timeStr.endsWith("Z"))
2121      {
2122        try
2123        {
2124          SimpleDateFormat dateFormat =
2125            new SimpleDateFormat(DATE_FORMAT_GENERALIZED_TIME);
2126          dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
2127          dateFormat.setLenient(true);
2128          dateTime = dateFormat.parse(timeStr);
2129        }
2130        catch (ParseException pe)
2131        {
2132          // Best effort: try with GMT time.
2133          SimpleDateFormat dateFormat =
2134            new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
2135          dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
2136          dateFormat.setLenient(true);
2137          dateTime = dateFormat.parse(timeStr);
2138        }
2139      }
2140      else
2141      {
2142        SimpleDateFormat dateFormat =
2143            new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME);
2144        dateFormat.setLenient(true);
2145        dateTime = dateFormat.parse(timeStr);
2146      }
2147    }
2148    return dateTime;
2149  }
2150
2151  /**
2152   * Formats a Date to String representation in "yyyyMMddHHmmss'Z'".
2153   *
2154   * @param date to format; null if {@code date} is null
2155   * @return string representation of the date
2156   */
2157  public static String formatDateTimeString(Date date)
2158  {
2159    String timeStr = null;
2160    if (date != null)
2161    {
2162      SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
2163      dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
2164      timeStr = dateFormat.format(date);
2165    }
2166    return timeStr;
2167  }
2168
2169  /**
2170   * Indicates whether a string represents a syntactically correct email address.
2171   *
2172   * @param addr to validate
2173   * @return boolean where {@code true} indicates that the string is a
2174   *         syntactically correct email address
2175   */
2176  public static boolean isEmailAddress(String addr) {
2177
2178    // This just does basic syntax checking.  Perhaps we
2179    // might want to be stricter about this.
2180    return addr != null && addr.contains("@") && addr.contains(".");
2181
2182  }
2183
2184  /**
2185   * Add all of the superior objectclasses to the specified objectclass
2186   * map if they don't already exist. Used by add and import-ldif to
2187   * add missing superior objectclasses to entries that don't have them.
2188   *
2189   * @param objectClasses A Map of objectclasses.
2190   */
2191  public static void addSuperiorObjectClasses(Map<ObjectClass,
2192      String> objectClasses) {
2193      HashSet<ObjectClass> additionalClasses = null;
2194      for (ObjectClass oc : objectClasses.keySet())
2195      {
2196        for(ObjectClass superiorClass : oc.getSuperiorClasses())
2197        {
2198          if (! objectClasses.containsKey(superiorClass))
2199          {
2200            if (additionalClasses == null)
2201            {
2202              additionalClasses = new HashSet<>();
2203            }
2204
2205            additionalClasses.add(superiorClass);
2206          }
2207        }
2208      }
2209
2210      if (additionalClasses != null)
2211      {
2212        for (ObjectClass oc : additionalClasses)
2213        {
2214          addObjectClassChain(oc, objectClasses);
2215        }
2216      }
2217  }
2218
2219  private static void addObjectClassChain(ObjectClass objectClass,
2220      Map<ObjectClass, String> objectClasses)
2221  {
2222    if (objectClasses != null){
2223      if (! objectClasses.containsKey(objectClass))
2224      {
2225        objectClasses.put(objectClass, objectClass.getNameOrOID());
2226      }
2227
2228      for(ObjectClass superiorClass : objectClass.getSuperiorClasses())
2229      {
2230        if (! objectClasses.containsKey(superiorClass))
2231        {
2232          addObjectClassChain(superiorClass, objectClasses);
2233        }
2234      }
2235    }
2236  }
2237
2238
2239  /**
2240   * Closes the provided {@link Closeable}'s ignoring any errors which
2241   * occurred.
2242   *
2243   * @param closeables The closeables to be closed, which may be
2244   *        {@code null}.
2245   */
2246  public static void close(Closeable... closeables)
2247  {
2248    if (closeables == null)
2249    {
2250      return;
2251    }
2252    close(Arrays.asList(closeables));
2253  }
2254
2255  /**
2256   * Closes the provided {@link Closeable}'s ignoring any errors which occurred.
2257   *
2258   * @param closeables
2259   *          The closeables to be closed, which may be {@code null}.
2260   */
2261  public static void close(Collection<? extends Closeable> closeables)
2262  {
2263    if (closeables == null)
2264    {
2265      return;
2266    }
2267    for (Closeable closeable : closeables)
2268    {
2269      if (closeable != null)
2270      {
2271        try
2272        {
2273          closeable.close();
2274        }
2275        catch (IOException ignored)
2276        {
2277          logger.traceException(ignored);
2278        }
2279      }
2280    }
2281  }
2282
2283  /**
2284   * Closes the provided {@link InitialContext}'s ignoring any errors which occurred.
2285   *
2286   * @param ctxs
2287   *          The contexts to be closed, which may be {@code null}.
2288   */
2289  public static void close(InitialContext... ctxs)
2290  {
2291    if (ctxs == null)
2292    {
2293      return;
2294    }
2295    for (InitialContext ctx : ctxs)
2296    {
2297      if (ctx != null)
2298      {
2299        try
2300        {
2301          ctx.close();
2302        }
2303        catch (NamingException ignored)
2304        {
2305          // ignore
2306        }
2307      }
2308    }
2309  }
2310
2311  /**
2312   * Calls {@link Thread#sleep(long)}, surrounding it with the mandatory
2313   * {@code try} / {@code catch(InterruptedException)} block.
2314   *
2315   * @param millis
2316   *          the length of time to sleep in milliseconds
2317   */
2318  public static void sleep(long millis)
2319  {
2320    try
2321    {
2322      Thread.sleep(millis);
2323    }
2324    catch (InterruptedException wokenUp)
2325    {
2326      // ignore
2327    }
2328  }
2329
2330  /**
2331   * Test if the provided message corresponds to the provided descriptor.
2332   *
2333   * @param msg
2334   *          The i18n message.
2335   * @param desc
2336   *          The message descriptor.
2337   * @return {@code true} if message corresponds to descriptor
2338   */
2339  public static boolean hasDescriptor(LocalizableMessage msg,
2340      LocalizableMessageDescriptor.Arg0 desc)
2341  {
2342    return msg.ordinal() == desc.ordinal()
2343        && msg.resourceName().equals(desc.resourceName());
2344  }
2345
2346  /**
2347   * Test if the provided message corresponds to the provided descriptor.
2348   *
2349   * @param msg
2350   *          The i18n message.
2351   * @param desc
2352   *          The message descriptor.
2353   * @return {@code true} if message corresponds to descriptor
2354   */
2355  public static boolean hasDescriptor(LocalizableMessage msg,
2356      LocalizableMessageDescriptor.Arg1<?> desc)
2357  {
2358    return msg.ordinal() == desc.ordinal()
2359        && msg.resourceName().equals(desc.resourceName());
2360  }
2361
2362  /**
2363   * Test if the provided message corresponds to the provided descriptor.
2364   *
2365   * @param msg
2366   *          The i18n message.
2367   * @param desc
2368   *          The message descriptor.
2369   * @return {@code true} if message corresponds to descriptor
2370   */
2371  public static boolean hasDescriptor(LocalizableMessage msg,
2372      LocalizableMessageDescriptor.Arg3<?, ?, ?> desc)
2373  {
2374    return msg.ordinal() == desc.ordinal()
2375        && msg.resourceName().equals(desc.resourceName());
2376  }
2377
2378  /**
2379   * Test if the provided message corresponds to the provided descriptor.
2380   *
2381   * @param msg
2382   *          The i18n message.
2383   * @param desc
2384   *          The message descriptor.
2385   * @return {@code true} if message corresponds to descriptor
2386   */
2387  public static boolean hasDescriptor(LocalizableMessage msg,
2388      LocalizableMessageDescriptor.Arg7<?, ?, ?, ?, ?, ?, ?> desc)
2389  {
2390    return msg.ordinal() == desc.ordinal()
2391        && msg.resourceName().equals(desc.resourceName());
2392  }
2393
2394  /**
2395   * Returns an {@link Iterable} returning the passed in {@link Iterator}. THis
2396   * allows using methods returning Iterators with foreach statements.
2397   * <p>
2398   * For example, consider a method with this signature:
2399   * <p>
2400   * <code>public Iterator&lt;String&gt; myIteratorMethod();</code>
2401   * <p>
2402   * Classical use with for or while loop:
2403   *
2404   * <pre>
2405   * for (Iterator&lt;String&gt; it = myIteratorMethod(); it.hasNext();)
2406   * {
2407   *   String s = it.next();
2408   *   // use it
2409   * }
2410   *
2411   * Iterator&lt;String&gt; it = myIteratorMethod();
2412   * while(it.hasNext();)
2413   * {
2414   *   String s = it.next();
2415   *   // use it
2416   * }
2417   * </pre>
2418   *
2419   * Improved use with foreach:
2420   *
2421   * <pre>
2422   * for (String s : StaticUtils.toIterable(myIteratorMethod()))
2423   * {
2424   * }
2425   * </pre>
2426   *
2427   * </p>
2428   *
2429   * @param <T>
2430   *          the generic type of the passed in Iterator and for the returned
2431   *          Iterable.
2432   * @param iterator
2433   *          the Iterator that will be returned by the Iterable.
2434   * @return an Iterable returning the passed in Iterator
2435   */
2436  public static <T> Iterable<T> toIterable(final Iterator<T> iterator)
2437  {
2438    return new Iterable<T>()
2439    {
2440      @Override
2441      public Iterator<T> iterator()
2442      {
2443        return iterator;
2444      }
2445    };
2446  }
2447
2448  /**
2449   * Returns true if the version of the server is an OEM one, and therefore doesn't support the JE backend.
2450   * @return {@code true} if the version of the server is an OEM version and {@code false} otherwise.
2451   */
2452  public static boolean isOEMVersion()
2453  {
2454    return !isClassAvailable("org.opends.server.backends.jeb.JEBackend");
2455  }
2456
2457  /**
2458   * Returns true if the class is available in the classpath.
2459   * @param className the string representing the class to check.
2460   * @return {@code true} if the class is available in the classpath and {@code false} otherwise.
2461   */
2462  public static boolean isClassAvailable(final String className)
2463  {
2464    try
2465    {
2466      Class.forName(className);
2467      return true;
2468    }
2469    catch (Exception e)
2470    {
2471      return false;
2472    }
2473  }
2474}
2475