/**
  * Encrypt database with new password
  *
  * @param oldPassword the old password
  * @param newPassword the new password
  * @param encrypt
  * @return re-encrypted database
  */
 boolean changeMasterPassword(String oldPassword, String newPassword, boolean encrypt) {
   if (!setMasterPassword(oldPassword)) {
     return false;
   }
   byte[] oldKey = key.get();
   byte[] newKey = EncryptionUtil.genPasswordKey(newPassword);
   ByteArrayWrapper testKey =
       new ByteArrayWrapper(
           EncryptionUtil.dbKey(oldKey, MasterKeyPasswordSafe.class, testKey(oldPassword)));
   HashMap<ByteArrayWrapper, byte[]> oldDb = new HashMap<ByteArrayWrapper, byte[]>();
   database.copyTo(oldDb);
   HashMap<ByteArrayWrapper, byte[]> newDb = new HashMap<ByteArrayWrapper, byte[]>();
   for (Map.Entry<ByteArrayWrapper, byte[]> e : oldDb.entrySet()) {
     if (testKey.equals(e.getKey())) {
       continue;
     }
     byte[] decryptedKey = EncryptionUtil.decryptKey(oldKey, e.getKey().unwrap());
     String decryptedText = EncryptionUtil.decryptText(oldKey, e.getValue());
     newDb.put(
         new ByteArrayWrapper(EncryptionUtil.encryptKey(newKey, decryptedKey)),
         EncryptionUtil.encryptText(newKey, decryptedText));
   }
   synchronized (database.getDbLock()) {
     resetMasterPassword(newPassword, encrypt);
     database.putAll(newDb);
   }
   return true;
 }
 /**
  * Reset password for the password safe (clears password database). The method is used from
  * plugin's UI.
  *
  * @param password the password to set
  * @param encrypt if the password should be encrypted an stored is master database
  */
 void resetMasterPassword(String password, boolean encrypt) {
   this.key.set(EncryptionUtil.genPasswordKey(password));
   database.clear();
   try {
     storePassword(null, MasterKeyPasswordSafe.class, testKey(password), TEST_PASSWORD_VALUE);
     if (encrypt) {
       database.setPasswordInfo(encryptPassword(password));
     } else {
       database.setPasswordInfo(new byte[0]);
     }
   } catch (PasswordSafeException e) {
     throw new IllegalStateException("There should be no problem with password at this point", e);
   }
 }
 @Override
 protected byte[] key(@Nullable final Project project) throws PasswordSafeException {
   ApplicationEx application = (ApplicationEx) ApplicationManager.getApplication();
   if (!isTestMode() && application.isHeadlessEnvironment()) {
     throw new MasterPasswordUnavailableException(
         "The provider is not available in headless environment");
   }
   if (key.get() == null) {
     if (isPasswordEncrypted()) {
       try {
         String s = decryptPassword(database.getPasswordInfo());
         setMasterPassword(s);
       } catch (PasswordSafeException e) {
         // ignore exception and ask password
       }
     }
     if (key.get() == null) {
       final Ref<PasswordSafeException> ex = new Ref<PasswordSafeException>();
       if (application.holdsReadLock()) {
         throw new IllegalStateException(
             "Access from read action is not allowed, because it might lead to a deadlock.");
       }
       application.invokeAndWait(
           new Runnable() {
             public void run() {
               if (key.get() == null) {
                 try {
                   if (isTestMode()) {
                     throw new MasterPasswordUnavailableException(
                         "Master password must be specified in test mode.");
                   }
                   if (database.isEmpty()) {
                     if (!ResetPasswordDialog.newPassword(project, MasterKeyPasswordSafe.this)) {
                       throw new MasterPasswordUnavailableException(
                           "Master password is required to store passwords in the database.");
                     }
                   } else {
                     MasterPasswordDialog.askPassword(project, MasterKeyPasswordSafe.this);
                   }
                 } catch (PasswordSafeException e) {
                   ex.set(e);
                 } catch (Exception e) {
                   //noinspection ThrowableInstanceNeverThrown
                   ex.set(
                       new MasterPasswordUnavailableException(
                           "The problem with retrieving the password", e));
                 }
               }
             }
           },
           ModalityState.defaultModalityState());
       //noinspection ThrowableResultOfMethodCallIgnored
       if (ex.get() != null) {
         throw ex.get();
       }
     }
   }
   return this.key.get();
 }
 /** {@inheritDoc} */
 @Override
 public void removePassword(@Nullable Project project, Class requester, String key)
     throws PasswordSafeException {
   if (database.isEmpty()) {
     return;
   }
   super.removePassword(project, requester, key);
 }
 /** {@inheritDoc} */
 @Override
 public String getPassword(@Nullable Project project, Class requester, String key)
     throws PasswordSafeException {
   if (database.isEmpty()) {
     return null;
   }
   return super.getPassword(project, requester, key);
 }
 /** @return check if provider database is empty */
 public boolean isEmpty() {
   return database.isEmpty();
 }
 /** @return true, if the password is currently encrypted in the database */
 public boolean isPasswordEncrypted() {
   byte[] i = database.getPasswordInfo();
   return i != null && i.length > 0;
 }
 /** {@inheritDoc} */
 @Override
 protected void storeEncryptedPassword(byte[] key, byte[] encryptedPassword) {
   database.put(key, encryptedPassword);
 }
 /** {@inheritDoc} */
 @Override
 protected void removeEncryptedPassword(byte[] key) {
   database.remove(key);
 }
 /** {@inheritDoc} */
 @Override
 protected byte[] getEncryptedPassword(byte[] key) {
   return database.get(key);
 }