SFTP: Memory leak because AbstractFileProvider#findFileSystem fails to
detect equality of SFTP FileSystemOptions #272
- Normalize File object inputs to absolute Files.
- Add Eclipse-generated hashCode() and equals() methods.
- Fix camel-casing of "passPhrase" -> "passphrase".
- Make defensive copies of byte[]s.
- Normalize Javadoc comments
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/BytesIdentityInfo.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/BytesIdentityInfo.java
index 07b1794..419ecf1 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/BytesIdentityInfo.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/BytesIdentityInfo.java
@@ -1,92 +1,126 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.vfs2.provider.sftp;
-
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-
-/**
- * Structure for an identity based on byte arrays.
- *
- * @since 2.4
- */
-public class BytesIdentityInfo implements IdentityProvider {
-
- private final byte[] passPhrase;
-
- private final byte[] privateKey;
-
- private final byte[] publicKey;
-
- /**
- * Constructs an identity info with private and passphrase for the private key.
- *
- * @param privateKey Private key bytes
- * @param passPhrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
- */
- public BytesIdentityInfo(final byte[] privateKey, final byte[] passPhrase) {
- this.privateKey = privateKey;
- this.publicKey = null;
- this.passPhrase = passPhrase;
- }
-
- /**
- * Constructs an identity info with private and public key and passphrase for the private key.
- *
- * @param privateKey Private key bytes
- * @param publicKey The public key part used for connections with exchange of certificates (can be {@code null})
- * @param passPhrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
- */
- public BytesIdentityInfo(final byte[] privateKey, final byte[] publicKey, final byte[] passPhrase) {
- this.privateKey = privateKey;
- this.publicKey = publicKey;
- this.passPhrase = passPhrase;
- }
-
- @Override
- public void addIdentity(final JSch jsch) throws JSchException {
- jsch.addIdentity("PrivateKey", privateKey, publicKey, passPhrase);
- }
-
- /**
- * Gets the passphrase.
- *
- * @return the passphrase.
- */
- public byte[] getPassPhrase() {
- return passPhrase;
- }
-
- /**
- * Gets the private key.
- *
- * @return the private key.
- */
- public byte[] getPrivateKeyBytes() {
- return privateKey;
- }
-
- /**
- * Gets the public key.
- *
- * @return the public key.
- */
- public byte[] getPublicKeyBytes() {
- return publicKey;
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.vfs2.provider.sftp;
+
+import java.util.Arrays;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+
+/**
+ * Structure for an identity based on byte arrays.
+ *
+ * @since 2.4
+ */
+public class BytesIdentityInfo implements IdentityProvider {
+
+ private final byte[] passphrase;
+
+ private final byte[] privateKey;
+
+ private final byte[] publicKey;
+
+ /**
+ * Constructs an identity info with private and passphrase for the private key.
+ *
+ * @param privateKey Private key bytes
+ * @param passphrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
+ */
+ public BytesIdentityInfo(final byte[] privateKey, final byte[] passphrase) {
+ this(privateKey, null, passphrase);
+ }
+
+ /**
+ * Constructs an identity info with private and public key and passphrase for the private key.
+ *
+ * @param privateKey Private key bytes
+ * @param publicKey The public key part used for connections with exchange of certificates (can be {@code null})
+ * @param passphrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
+ */
+ public BytesIdentityInfo(final byte[] privateKey, final byte[] publicKey, final byte[] passphrase) {
+ this.privateKey = Utils.clone(privateKey);
+ this.publicKey = Utils.clone(publicKey);
+ this.passphrase = Utils.clone(passphrase);
+ }
+
+ @Override
+ public void addIdentity(final JSch jsch) throws JSchException {
+ jsch.addIdentity("PrivateKey", privateKey, publicKey, passphrase);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof BytesIdentityInfo)) {
+ return false;
+ }
+ final BytesIdentityInfo other = (BytesIdentityInfo) obj;
+ return Arrays.equals(passphrase, other.passphrase) && Arrays.equals(privateKey, other.privateKey) && Arrays.equals(publicKey, other.publicKey);
+ }
+
+ /**
+ * Gets the passphrase.
+ *
+ * @return the passphrase.
+ * @since 2.10.0
+ */
+ public byte[] getPassphrase() {
+ return Utils.clone(passphrase);
+ }
+
+ /**
+ * Gets the passphrase.
+ *
+ * @return the passphrase.
+ * @deprecated Use {@link #getPassphrase()}.
+ */
+ @Deprecated
+ public byte[] getPassPhrase() {
+ return Utils.clone(passphrase);
+ }
+
+ /**
+ * Gets the private key.
+ *
+ * @return the private key.
+ */
+ public byte[] getPrivateKeyBytes() {
+ return Utils.clone(privateKey);
+ }
+
+ /**
+ * Gets the public key.
+ *
+ * @return the public key.
+ */
+ public byte[] getPublicKeyBytes() {
+ return Utils.clone(publicKey);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(passphrase);
+ result = prime * result + Arrays.hashCode(privateKey);
+ result = prime * result + Arrays.hashCode(publicKey);
+ return result;
+ }
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/IdentityInfo.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/IdentityInfo.java
index e3319d9..266bc8f 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/IdentityInfo.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/IdentityInfo.java
@@ -1,123 +1,162 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.vfs2.provider.sftp;
-
-import java.io.File;
-
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-
-/**
- * Structure for an identity based on Files.
- *
- * @since 2.1
- */
-public class IdentityInfo implements IdentityProvider {
-
- private final byte[] passPhrase;
- private final File privateKey;
- private final File publicKey;
-
- /**
- * Constructs an identity info with private key.
- * <p>
- * The key is not passphrase protected.
- * </p>
- * <p>
- * We use java.io.File because JSch cannot deal with VFS FileObjects.
- * </p>
- *
- * @param privateKey The file with the private key
- * @since 2.1
- */
- public IdentityInfo(final File privateKey) {
- this(privateKey, null, null);
- }
-
- /**
- * Constructs an identity info with private key and its passphrase.
- * <p>
- * We use java.io.File because JSch cannot deal with VFS FileObjects.
- * </p>
- *
- * @param privateKey The file with the private key
- * @param passPhrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
- * @since 2.1
- */
- public IdentityInfo(final File privateKey, final byte[] passPhrase) {
- this(privateKey, null, passPhrase);
- }
-
- /**
- * Constructs an identity info with private and public key and passphrase for the private key.
- * <p>
- * We use java.io.File because JSch cannot deal with VFS FileObjects.
- * </p>
- *
- * @param privateKey The file with the private key
- * @param publicKey The public key part used for connections with exchange of certificates (can be {@code null})
- * @param passPhrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
- * @since 2.1
- */
- public IdentityInfo(final File privateKey, final File publicKey, final byte[] passPhrase) {
- this.privateKey = privateKey;
- this.publicKey = publicKey;
- this.passPhrase = passPhrase;
- }
-
- /**
- * @since 2.4
- */
- @Override
- public void addIdentity(final JSch jsch) throws JSchException {
- jsch.addIdentity(getAbsolutePath(privateKey), getAbsolutePath(publicKey), passPhrase);
- }
-
- private String getAbsolutePath(final File file) {
- return file != null ? file.getAbsolutePath() : null;
- }
-
- /**
- * Get the passphrase of the private key.
- *
- * @return the passphrase
- * @since 2.1
- */
- public byte[] getPassPhrase() {
- return passPhrase;
- }
-
- /**
- * Get the file with the private key.
- *
- * @return the file
- * @since 2.1
- */
- public File getPrivateKey() {
- return privateKey;
- }
-
- /**
- * Get the file with the public key.
- *
- * @return the file
- * @since 2.1
- */
- public File getPublicKey() {
- return publicKey;
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.vfs2.provider.sftp;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Objects;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+
+/**
+ * Structure for an identity based on Files.
+ *
+ * @since 2.1
+ */
+public class IdentityInfo implements IdentityProvider {
+
+ private final byte[] passphrase;
+ private final File privateKey;
+ private final File publicKey;
+
+ /**
+ * Constructs an identity info with private key.
+ * <p>
+ * The key is not passphrase protected.
+ * </p>
+ * <p>
+ * We use java.io.File because JSch cannot deal with VFS FileObjects.
+ * </p>
+ *
+ * @param privateKey The file with the private key
+ * @since 2.1
+ */
+ public IdentityInfo(final File privateKey) {
+ this(privateKey, null, null);
+ }
+
+ /**
+ * Constructs an identity info with private key and its passphrase.
+ * <p>
+ * We use java.io.File because JSch cannot deal with VFS FileObjects.
+ * </p>
+ *
+ * @param privateKey The file with the private key
+ * @param passphrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
+ * @since 2.1
+ */
+ public IdentityInfo(final File privateKey, final byte[] passphrase) {
+ this(privateKey, null, passphrase);
+ }
+
+ /**
+ * Constructs an identity info with private and public key and passphrase for the private key.
+ * <p>
+ * We use java.io.File because JSch cannot deal with VFS FileObjects.
+ * </p>
+ *
+ * @param privateKey The file with the private key
+ * @param publicKey The public key part used for connections with exchange of certificates (can be {@code null})
+ * @param passphrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
+ * @since 2.1
+ */
+ public IdentityInfo(final File privateKey, final File publicKey, final byte[] passphrase) {
+ this.privateKey = getAbsoluteFile(privateKey);
+ this.publicKey = getAbsoluteFile(publicKey);
+ this.passphrase = Utils.clone(passphrase);
+ }
+
+ /**
+ * @since 2.4
+ */
+ @Override
+ public void addIdentity(final JSch jsch) throws JSchException {
+ jsch.addIdentity(getAbsolutePath(privateKey), getAbsolutePath(publicKey), passphrase);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof IdentityInfo)) {
+ return false;
+ }
+ IdentityInfo other = (IdentityInfo) obj;
+ return Arrays.equals(passphrase, other.passphrase) && Objects.equals(privateKey, other.privateKey) && Objects.equals(publicKey, other.publicKey);
+ }
+
+ private File getAbsoluteFile(final File privateKey) {
+ return privateKey != null ? privateKey.getAbsoluteFile() : null;
+ }
+
+ private String getAbsolutePath(final File file) {
+ return file != null ? file.getAbsolutePath() : null;
+ }
+
+ /**
+ * Gets the passphrase of the private key.
+ *
+ * @return the passphrase
+ * @since 2.10.0
+ */
+ public byte[] getPassphrase() {
+ return Utils.clone(passphrase);
+ }
+
+ /**
+ * Gets the passphrase of the private key.
+ *
+ * @return the passphrase
+ * @since 2.1
+ * @deprecated Use {@link #getPassphrase()}.
+ */
+ @Deprecated
+ public byte[] getPassPhrase() {
+ return Utils.clone(passphrase);
+ }
+
+ /**
+ * Gets the file with the private key.
+ *
+ * @return the file
+ * @since 2.1
+ */
+ public File getPrivateKey() {
+ return privateKey;
+ }
+
+ /**
+ * Gets the file with the public key.
+ *
+ * @return the file
+ * @since 2.1
+ */
+ public File getPublicKey() {
+ return publicKey;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(passphrase);
+ result = prime * result + Objects.hash(privateKey, publicKey);
+ return result;
+ }
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/Utils.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/Utils.java
new file mode 100644
index 0000000..d84b0ef
--- /dev/null
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/Utils.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.vfs2.provider.sftp;
+
+final class Utils {
+
+ private Utils() {
+ // never
+ }
+
+ static byte[] clone(final byte[] array) {
+ return array != null ? array.clone() : null;
+ }
+
+}