gt:             char[] filePassword = new char[space];
context: /*
   * Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
   * Version 1.0, and under the Eclipse Public License, Version 1.0
   * (http://h2database.com/html/license.html).
   * Initial Developer: H2 Group
   */
  package org.h2.engine;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Properties;
  
  import org.h2.api.ErrorCode;
  import org.h2.command.dml.SetTypes;
  import org.h2.message.DbException;
  import org.h2.security.SHA256;
  import org.h2.store.fs.FilePathEncrypt;
  import org.h2.store.fs.FilePathRec;
  import org.h2.store.fs.FileUtils;
  import org.h2.util.New;
  import org.h2.util.SortedProperties;
  import org.h2.util.StringUtils;
  import org.h2.util.Utils;
  
  /**
   * Encapsulates the connection settings, including user name and password.
   */
  public class ConnectionInfo implements Cloneable {
      private static final HashSet<String> KNOWN_SETTINGS = New.hashSet();
  
      private Properties prop = new Properties();
      private String originalURL;
      private String url;
      private String user;
      private byte[] filePasswordHash;
      private byte[] fileEncryptionKey;
      private byte[] userPasswordHash;
  
      /**
       * The database name
       */
      private String name;
      private String nameNormalized;
      private boolean remote;
      private boolean ssl;
      private boolean persistent;
      private boolean unnamed;
  
      /**
       * Create a connection info object.
       *
       * @param name the database name (including tags), but without the
       *            "jdbc:h2:" prefix
       */
      public ConnectionInfo(String name) {
          this.name = name;
          this.url = Constants.START_URL + name;
          parseName();
      }
  
      /**
       * Create a connection info object.
       *
       * @param u the database URL (must start with jdbc:h2:)
       * @param info the connection properties
       */
      public ConnectionInfo(String u, Properties info) {
          u = remapURL(u);
          this.originalURL = u;
          if (!u.startsWith(Constants.START_URL)) {
              throw DbException.getInvalidValueException("url", u);
          }
          this.url = u;
          readProperties(info);
          readSettingsFromURL();
          setUserName(removeProperty("USER", ""));
          convertPasswords();
          name = url.substring(Constants.START_URL.length());
          parseName();
          String recoverTest = removeProperty("RECOVER_TEST", null);
          if (recoverTest != null) {
              FilePathRec.register();
              try {
                  Utils.callStaticMethod("org.h2.store.RecoverTester.init", recoverTest);
              } catch (Exception e) {
                  throw DbException.convert(e);
              }
              name = "rec:" + name;
          }
      }
  
      static {
          ArrayList<String> list = SetTypes.getTypes();
          HashSet<String> set = KNOWN_SETTINGS;
          set.addAll(list);
          String[] connectionTime = { "ACCESS_MODE_DATA", "AUTOCOMMIT", "CIPHER",
                  "CREATE", "CACHE_TYPE", "FILE_LOCK", "IGNORE_UNKNOWN_SETTINGS",
                  "IFEXISTS", "INIT", "PASSWORD", "RECOVER", "RECOVER_TEST",
                  "USER", "AUTO_SERVER", "AUTO_SERVER_PORT", "NO_UPGRADE",
                  "AUTO_RECONNECT", "OPEN_NEW", "PAGE_SIZE", "PASSWORD_HASH", "JMX" };
          for (String key : connectionTime) {
              if (SysProperties.CHECK && set.contains(key)) {
                  DbException.throwInternalError(key);
              }
              set.add(key);
          }
      }
  
      private static boolean isKnownSetting(String s) {
          return KNOWN_SETTINGS.contains(s);
      }
  
      @Override
      public ConnectionInfo clone() throws CloneNotSupportedException {
          ConnectionInfo clone = (ConnectionInfo) super.clone();
          clone.prop = (Properties) prop.clone();
          clone.filePasswordHash = Utils.cloneByteArray(filePasswordHash);
          clone.fileEncryptionKey = Utils.cloneByteArray(fileEncryptionKey);
          clone.userPasswordHash = Utils.cloneByteArray(userPasswordHash);
          return clone;
      }
  
      private void parseName() {
          if (".".equals(name)) {
              name = "mem:";
          }
          if (name.startsWith("tcp:")) {
              remote = true;
              name = name.substring("tcp:".length());
          } else if (name.startsWith("ssl:")) {
              remote = true;
              ssl = true;
              name = name.substring("ssl:".length());
          } else if (name.startsWith("mem:")) {
              persistent = false;
              if ("mem:".equals(name)) {
                  unnamed = true;
              }
          } else if (name.startsWith("file:")) {
              name = name.substring("file:".length());
              persistent = true;
          } else {
              persistent = true;
          }
          if (persistent && !remote) {
              if ("/".equals(SysProperties.FILE_SEPARATOR)) {
                  name = name.replace('\\', '/');
              } else {
                  name = name.replace('/', '\\');
              }
          }
      }
  
      /**
       * Set the base directory of persistent databases, unless the database is in
       * the user home folder (~).
       *
       * @param dir the new base directory
       */
      public void setBaseDir(String dir) {
          if (persistent) {
              String absDir = FileUtils.unwrap(FileUtils.toRealPath(dir));
              boolean absolute = FileUtils.isAbsolute(name);
              String n;
              String prefix = null;
              if (dir.endsWith(SysProperties.FILE_SEPARATOR)) {
                  dir = dir.substring(0, dir.length() - 1);
              }
              if (absolute) {
                  n = name;
              } else {
                  n  = FileUtils.unwrap(name);
                  prefix = name.substring(0, name.length() - n.length());
                  n = dir + SysProperties.FILE_SEPARATOR + n;
              }
              String normalizedName = FileUtils.unwrap(FileUtils.toRealPath(n));
              if (normalizedName.equals(absDir) || !normalizedName.startsWith(absDir)) {
                  // database name matches the baseDir or
                  // database name is clearly outside of the baseDir
                  throw DbException.get(ErrorCode.IO_EXCEPTION_1, normalizedName + " outside " +
                          absDir);
              }
              if (normalizedName.charAt(absDir.length()) != '/') {
                  // database must be within the directory
                  // (with baseDir=/test, the database name must not be
                  // /test2/x and not /test2)
                  throw DbException.get(ErrorCode.IO_EXCEPTION_1, normalizedName + " outside " +
                          absDir);
              }
              if (!absolute) {
                  name = prefix + dir + SysProperties.FILE_SEPARATOR + FileUtils.unwrap(name);
              }
          }
      }
  
      /**
       * Check if this is a remote connection.
       *
       * @return true if it is
       */
      public boolean isRemote() {
          return remote;
      }
  
      /**
       * Check if the referenced database is persistent.
       *
       * @return true if it is
       */
      public boolean isPersistent() {
          return persistent;
      }
  
      /**
       * Check if the referenced database is an unnamed in-memory database.
       *
       * @return true if it is
       */
      boolean isUnnamedInMemory() {
          return unnamed;
      }
  
      private void readProperties(Properties info) {
          Object[] list = new Object[info.size()];
          info.keySet().toArray(list);
          DbSettings s = null;
          for (Object k : list) {
              String key = StringUtils.toUpperEnglish(k.toString());
              if (prop.containsKey(key)) {
                  throw DbException.get(ErrorCode.DUPLICATE_PROPERTY_1, key);
              }
              Object value = info.get(k);
              if (isKnownSetting(key)) {
                  prop.put(key, value);
              } else {
                  if (s == null) {
                      s = getDbSettings();
                  }
                  if (s.containsKey(key)) {
                      prop.put(key, value);
                  }
              }
          }
      }
  
      private void readSettingsFromURL() {
          DbSettings dbSettings = DbSettings.getInstance(null);
          int idx = url.indexOf(';');
          if (idx >= 0) {
              String settings = url.substring(idx + 1);
              url = url.substring(0, idx);
              String[] list = StringUtils.arraySplit(settings, ';', false);
              for (String setting : list) {
                  if (setting.length() == 0) {
                      continue;
                  }
                  int equal = setting.indexOf('=');
                  if (equal < 0) {
                      throw getFormatException();
                  }
                  String value = setting.substring(equal + 1);
                  String key = setting.substring(0, equal);
                  key = StringUtils.toUpperEnglish(key);
                  if (!isKnownSetting(key) && !dbSettings.containsKey(key)) {
                      throw DbException.get(ErrorCode.UNSUPPORTED_SETTING_1, key);
                  }
                  String old = prop.getProperty(key);
                  if (old != null && !old.equals(value)) {
                      throw DbException.get(ErrorCode.DUPLICATE_PROPERTY_1, key);
                  }
                  prop.setProperty(key, value);
              }
          }
      }
  
      private char[] removePassword() {
          Object p = prop.remove("PASSWORD");
          if (p == null) {
              return new char[0];
          } else if (p instanceof char[]) {
              return (char[]) p;
          } else {
              return p.toString().toCharArray();
          }
      }
  
      /**
       * Split the password property into file password and user password if
       * necessary, and convert them to the internal hash format.
       */
      private void convertPasswords() {
          char[] password = removePassword();
          boolean passwordHash = removeProperty("PASSWORD_HASH", false);
          if (getProperty("CIPHER", null) != null) {
              // split password into (filePassword+' '+userPassword)
              int space = -1;
              for (int i = 0, len = password.length; i < len; i++) {
                  if (password[i] == ' ') {
                      space = i;
                      break;
                  }
              }
              if (space < 0) {
                  throw DbException.get(ErrorCode.WRONG_PASSWORD_FORMAT);
              }
              char[] np = new char[password.length - space - 1];
  