---
org.eclipse.jgit.ssh.apache/README.md | 61 +++++++++++++++++++
.../jgit/transport/sshd/package-info.java | 6 ++
org.eclipse.jgit.ssh.jsch/README.md | 59 ++++++++++++++++++
.../jgit/transport/ssh/jsch/package-info.java | 14 +++++
4 files changed, 140 insertions(+)
create mode 100644 org.eclipse.jgit.ssh.apache/README.md
create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/package-info.java
create mode 100644 org.eclipse.jgit.ssh.jsch/README.md
create mode 100644 org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/package-info.java
diff --git a/org.eclipse.jgit.ssh.apache/README.md b/org.eclipse.jgit.ssh.apache/README.md
new file mode 100644
index 000000000..3bbda523b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/README.md
@@ -0,0 +1,61 @@
+# JGit SSH support via Apache MINA sshd
+
+This bundle provides an implementation of git transport over SSH implemented via
+[Apache MINA sshd](https://mina.apache.org/sshd-project/).
+
+## Service registration
+
+This bundle declares a service for the `java.util.ServiceLoader` for interface
+`org.eclipse.jgit.transport.ssh.SshSessionFactory`. The core JGit bundle uses the service
+loader to pick up an implementation of that interface.
+
+Note that JGit simply uses the first `SshSessionFactory` provided by the `ServiceLoader`.
+
+If the service loader cannot find the session factory, either ensure that the service
+declaration is on the Classpath of bundle `org.eclipse.jgit`, or set the factory explicitly
+(see below).
+
+In an OSGi environment, one might need a service loader bridge, or have a little OSGi
+fragment for bundle `org.eclipse.jgit` that puts the right service declaration onto the
+Classpath of that bundle. (OSGi fragments become part of the Classpath of their host
+bundle.)
+
+## Configuring an SSH implementation for JGit
+
+The simplest way to set an SSH implementation for JGit is to install it globally via
+`SshSessionFactory.setInstance()`. This instance will be used by JGit for all SSH
+connections by default.
+
+It is also possible to set the SSH implementation individually for any git command
+that needs a transport (`TransportCommand`) via a `org.eclipse.jgit.api.TransportConfigCallback`.
+
+To do so, set the wanted `SshSessionFactory` on the SSH transport, like:
+
+```java
+SshSessionFactory customFactory = ...; // Get it from wherever
+FetchCommand fetch = git.fetch()
+ .setTransportConfigCallback(transport -> {
+ if (transport instanceof SshTransport) {
+ ((SshTransport) transport).setSshSessionFactory(customFactory);
+ }
+ })
+ ...
+ .call();
+```
+
+## Using a different SSH implementation
+
+To use a different SSH implementation:
+
+* Do not include this bundle in your product.
+* Include the bundle of the alternate implementation.
+ * If the service loader finds the alternate implementation, nothing more is needed.
+ * Otherwise ensure the service declaration from the other bundle is on the Classpath of bundle `org.eclipse.jgit`,
+ * or set the `SshSessionFactory` for JGit explicitly (see above).
+
+## Using an external SSH executable
+
+JGit has built-in support for not using any Java SSH implementation but an external SSH
+executable. To use an external SSH executable, set environment variable **GIT_SSH** to
+the path of the executable. JGit will create a sub-process to run the executable and
+communicate with this sup-process to perform the git operation.
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/package-info.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/package-info.java
new file mode 100644
index 000000000..926234a3b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Provides a JGit {@link org.eclipse.jgit.transport.SshSessionFactory}
+ * implemented via Apache MINA
+ * sshd.
+ */
+package org.eclipse.jgit.transport.sshd;
diff --git a/org.eclipse.jgit.ssh.jsch/README.md b/org.eclipse.jgit.ssh.jsch/README.md
new file mode 100644
index 000000000..48f43f0f8
--- /dev/null
+++ b/org.eclipse.jgit.ssh.jsch/README.md
@@ -0,0 +1,59 @@
+# JGit SSH support via JSch
+
+This bundle provides an implementation of git transport over SSH implemented via JSch.
+
+**This bundle should be considered deprecated**. It is essentially unmaintained, and
+the JGit project may decide anytime to remove it completely without further ado.
+
+The officially supported SSH transport is in bundle `org.eclipse.jgit.ssh.apache` and is
+built upon [Apache MINA sshd](https://mina.apache.org/sshd-project/).
+
+## Service registration
+
+This bundle declares a service for the `java.util.ServiceLoader` for interface
+`org.eclipse.jgit.transport.ssh.SshSessionFactory`. The core JGit bundle uses the service
+loader to pick up an implementation of that interface. The bundle in an OSGi fragment
+to ensure that the service loader works in an OSGi environment without the need to
+install a service loader bridge.
+
+Note that JGit simply uses the first `SshSessionFactory` provided by the `ServiceLoader`.
+
+## Using a different SSH implementation
+
+To use a different SSH implementation:
+
+* Do not include this bundle in your product.
+* Include the bundle of the alternate implementation.
+ * If the service loader finds the alternate implementation, nothing more is needed.
+ * Otherwise ensure the service declaration from the other bundle is on the Classpath of bundle `org.eclipse.jgit`,
+ * or set the `SshSessionFactory` for JGit explicitly (see below).
+
+## Configuring an SSH implementation for JGit
+
+The simplest way to set an SSH implementation for JGit is to install it globally via
+`SshSessionFactory.setInstance()`. This instance will be used by JGit for all SSH
+connections by default.
+
+It is also possible to set the SSH implementation individually for any git command
+that needs a transport (`TransportCommand`) via a `org.eclipse.jgit.api.TransportConfigCallback`.
+
+To do so, set the wanted `SshSessionFactory` on the SSH transport, like:
+
+```java
+SshSessionFactory customFactory = ...; // Get it from wherever
+FetchCommand fetch = git.fetch()
+ .setTransportConfigCallback(transport -> {
+ if (transport instanceof SshTransport) {
+ ((SshTransport) transport).setSshSessionFactory(customFactory);
+ }
+ })
+ ...
+ .call();
+```
+
+## Using an external SSH executable
+
+JGit has built-in support for not using any Java SSH implementation but an external SSH
+executable. To use an external SSH executable, set environment variable **GIT_SSH** to
+the path of the executable. JGit will create a sub-process to run the executable and
+communicate with this sup-process to perform the git operation.
diff --git a/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/package-info.java b/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/package-info.java
new file mode 100644
index 000000000..dc2915a09
--- /dev/null
+++ b/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/package-info.java
@@ -0,0 +1,14 @@
+/**
+ * Provides a JGit {@link org.eclipse.jgit.transport.SshSessionFactory}
+ * implemented via JSch.
+ *
+ * This package should be considered deprecated. It is essentially
+ * unmaintained and the JGit project may decide to remove it completely without
+ * further ado at any time.
+ *
+ *
+ * The officially supported Java SSH implementation for JGit is in bundle
+ * {@code org.eclipse.jgit.ssh.apache} and is built upon
+ * Apache MINA sshd.
+ */
+package org.eclipse.jgit.transport.ssh.jsch;
From 4f8d4346234ce28d592ae92bbfc02d4d203454b1 Mon Sep 17 00:00:00 2001
From: Matthias Sohn
Date: Wed, 3 Nov 2021 22:40:48 +0100
Subject: [PATCH 05/24] Fix target platforms
- jetty 9.4.30 moved to archive.eclipse.org
- use the final release p2 repo instead of staging for 2020-09
Change-Id: Ia096200e5983f0022c6c0da4dae035433e852807
---
.../org.eclipse.jgit.target/jgit-4.10.target | 4 ++--
.../org.eclipse.jgit.target/jgit-4.11.target | 4 ++--
.../org.eclipse.jgit.target/jgit-4.12.target | 4 ++--
.../org.eclipse.jgit.target/jgit-4.13.target | 4 ++--
.../org.eclipse.jgit.target/jgit-4.14.target | 4 ++--
.../org.eclipse.jgit.target/jgit-4.15.target | 4 ++--
.../org.eclipse.jgit.target/jgit-4.16.target | 4 ++--
.../{jgit-4.17-staging.target => jgit-4.17.target} | 6 +++---
.../{jgit-4.17-staging.tpd => jgit-4.17.tpd} | 2 +-
.../org.eclipse.jgit.target/jgit-4.6.target | 4 ++--
.../org.eclipse.jgit.target/jgit-4.7.target | 4 ++--
.../org.eclipse.jgit.target/jgit-4.8.target | 4 ++--
.../org.eclipse.jgit.target/jgit-4.9.target | 4 ++--
.../org.eclipse.jgit.target/projects/jetty-9.4.x.tpd | 2 +-
14 files changed, 27 insertions(+), 27 deletions(-)
rename org.eclipse.jgit.packaging/org.eclipse.jgit.target/{jgit-4.17-staging.target => jgit-4.17.target} (95%)
rename org.eclipse.jgit.packaging/org.eclipse.jgit.target/{jgit-4.17-staging.tpd => jgit-4.17.tpd} (72%)
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
index ad0816c04..54aeb7a82 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
index 75f893f01..40a40184c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
index 2afbcb63a..f6cdc1e66 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
index 34c497a37..6f8c2e8e8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
index 33bfc43e8..8c91bcd05 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
index 03b5cb78f..0583cda7b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
index a7470c111..c087e76a8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
similarity index 95%
rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.target
rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
index 79e021178..aa23a2662 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
@@ -88,7 +88,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
similarity index 72%
rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.tpd
rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
index 07745cb15..e7043184c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17-staging.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
@@ -3,6 +3,6 @@ target "jgit-4.17-staging" with source configurePhase
include "projects/jetty-9.4.x.tpd"
include "orbit/R20200831200620-2020-09.tpd"
-location "https://download.eclipse.org/staging/2020-09/" {
+location "https://download.eclipse.org/releases/2020-09/" {
org.eclipse.osgi lazy
}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
index 566ef3389..41070856a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
index 5a690e89e..93a3090d1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
index fe237d01c..ff3abcdd0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
index a812e7a57..c922b9fda 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
@@ -1,7 +1,7 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
index 70c426c18..f76d50030 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
@@ -1,6 +1,6 @@
target "jetty-9.4.x" with source configurePhase
-location jetty-9.4.30 "https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.30.v20200611/" {
+location jetty-9.4.30 "https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.30.v20200611/" {
org.eclipse.jetty.client [9.4.30.v20200611,9.4.30.v20200611]
org.eclipse.jetty.client.source [9.4.30.v20200611,9.4.30.v20200611]
org.eclipse.jetty.continuation [9.4.30.v20200611,9.4.30.v20200611]
From 4184ff0953b2569799221d423e77fd2f6880f77d Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Tue, 2 Nov 2021 18:47:26 +0100
Subject: [PATCH 06/24] [releng] Make the bazel build use Java 11
Make the default toolchain use Java 11, and fix two errorprone findings
introduced recently.
Change-Id: Iff51206fe8bdf096cb7d88cb1a499002550766cd
Signed-off-by: Thomas Wolf
---
.../src/org/eclipse/jgit/diff/RawText.java | 2 +-
.../org/eclipse/jgit/util/StringUtils.java | 9 +++++++--
tools/BUILD | 19 ++++++++++++++++++-
3 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
index aeb3c4563..19961a13e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
@@ -378,7 +378,7 @@ public static boolean isBinary(byte[] raw, int length, boolean complete) {
* @since 6.0
*/
public static boolean isBinary(byte curr, byte prev) {
- return curr == '\0' || curr != '\n' && prev == '\r' || prev == '\0';
+ return curr == '\0' || (curr != '\n' && prev == '\r') || prev == '\0';
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
index b77fb920e..8ab13385e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
@@ -383,7 +383,10 @@ public static long parseLongWithSuffix(@NonNull String value,
try {
return Math.multiplyExact(mul, number);
} catch (ArithmeticException e) {
- throw new NumberFormatException(e.getLocalizedMessage());
+ NumberFormatException nfe = new NumberFormatException(
+ e.getLocalizedMessage());
+ nfe.initCause(e);
+ throw nfe;
}
}
@@ -413,9 +416,11 @@ public static int parseIntWithSuffix(@NonNull String value,
try {
return Math.toIntExact(parseLongWithSuffix(value, positiveOnly));
} catch (ArithmeticException e) {
- throw new NumberFormatException(
+ NumberFormatException nfe = new NumberFormatException(
MessageFormat.format(JGitText.get().valueExceedsRange,
value, Integer.class.getSimpleName()));
+ nfe.initCause(e);
+ throw nfe;
}
}
diff --git a/tools/BUILD b/tools/BUILD
index 2b208744b..7bfab2d06 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -5,10 +5,27 @@ load(
)
load("@rules_java//java:defs.bzl", "java_package_configuration")
+JDK11_JVM_OPTS = [
+ "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
+ "--patch-module=java.compiler=$(location @bazel_tools//tools/jdk:java_compiler_jar)",
+ "--patch-module=jdk.compiler=$(location @bazel_tools//tools/jdk:jdk_compiler_jar)",
+ "--add-opens=java.base/java.nio=ALL-UNNAMED",
+ "--add-opens=java.base/java.lang=ALL-UNNAMED",
+]
+
default_java_toolchain(
name = "error_prone_warnings_toolchain",
bootclasspath = ["@bazel_tools//tools/jdk:platformclasspath.jar"],
- jvm_opts = JDK9_JVM_OPTS,
+ jvm_opts = JDK11_JVM_OPTS,
+ source_version = "11",
+ target_version = "11",
package_configuration = [
":error_prone",
],
From c04884fc9166fe491745fa51bd7540ff36ce6e7c Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Tue, 2 Nov 2021 19:20:59 +0100
Subject: [PATCH 07/24] [releng] bazel: Enable errorprone on o.e.j.ssh.apache
Fix the few issues reported. (None serious.)
Change-Id: I8d72ef7d425ab61f4c27b657c92fc021850730d6
Signed-off-by: Thomas Wolf
---
.../transport/sshd/JGitSshClient.java | 2 +-
.../sshd/auth/BasicAuthentication.java | 4 ++--
.../proxy/AbstractClientProxyConnector.java | 2 +-
.../sshd/proxy/HttpClientConnector.java | 7 ++++---
.../transport/sshd/proxy/HttpParser.java | 19 ++++++++++++++++++-
.../sshd/proxy/Socks5ClientConnector.java | 2 +-
tools/BUILD | 1 +
7 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
index 32819aea2..fdb8cde67 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
@@ -439,7 +439,7 @@ public boolean hasNext() {
@Override
public KeyPair next() {
- if (hasElement == null && !hasNext()
+ if ((hasElement == null && !hasNext())
|| !hasElement.booleanValue()) {
throw new NoSuchElementException();
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
index eae0d7535..e5f884e29 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
@@ -95,8 +95,8 @@ public final void close() {
@Override
public final void start() throws Exception {
- if (user != null && !user.isEmpty()
- || password != null && password.length > 0) {
+ if ((user != null && !user.isEmpty())
+ || (password != null && password.length > 0)) {
return;
}
askCredentials();
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
index 54e2cbceb..ae2b2b6ac 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java
@@ -31,7 +31,7 @@ public abstract class AbstractClientProxyConnector
.toMillis(30L);
/** Guards {@link #done} and {@link #bufferedCommands}. */
- private Object lock = new Object();
+ private final Object lock = new Object();
private boolean done;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
index e5d1e80f7..b7deb29dc 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java
@@ -113,8 +113,8 @@ public void sendClientProxyMetadata(ClientSession sshSession)
IoSession session = sshSession.getIoSession();
session.addCloseFutureListener(f -> close());
StringBuilder msg = connect();
- if (proxyUser != null && !proxyUser.isEmpty()
- || proxyPassword != null && proxyPassword.length > 0) {
+ if ((proxyUser != null && !proxyUser.isEmpty())
+ || (proxyPassword != null && proxyPassword.length > 0)) {
authenticator = basic;
basic.setParams(null);
basic.start();
@@ -232,7 +232,8 @@ private void handleMessage(IoSession session, List reply)
} catch (HttpParser.ParseException e) {
throw new IOException(
format(SshdText.get().proxyHttpUnexpectedReply,
- proxyAddress, reply.get(0)));
+ proxyAddress, reply.get(0)),
+ e);
}
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
index 0500a6342..ece22af1c 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java
@@ -31,6 +31,23 @@ public static class ParseException extends Exception {
private static final long serialVersionUID = -1634090143702048640L;
+ /**
+ * Creates a new {@link ParseException} without cause.
+ */
+ public ParseException() {
+ super();
+ }
+
+ /**
+ * Creates a new {@link ParseException} with the given {@code cause}.
+ *
+ * @param cause
+ * {@link Throwable} that caused this exception, or
+ * {@code null}Â if none
+ */
+ public ParseException(Throwable cause) {
+ super(cause);
+ }
}
private HttpParser() {
@@ -64,7 +81,7 @@ public static StatusLine parseStatusLine(String line)
resultCode = Integer.parseUnsignedInt(
line.substring(firstBlank + 1, secondBlank));
} catch (NumberFormatException e) {
- throw new ParseException();
+ throw new ParseException(e);
}
// Again, accept even if the reason is missing
String reason = ""; //$NON-NLS-1$
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
index 8844efa6b..bb227bbac 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java
@@ -94,7 +94,7 @@ private enum SocksAuthenticationMethod {
// JSON(9),
NONE_ACCEPTABLE(0xFF);
- private byte value;
+ private final byte value;
SocksAuthenticationMethod(int value) {
this.value = (byte) value;
diff --git a/tools/BUILD b/tools/BUILD
index 7bfab2d06..4769f4222 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -120,6 +120,7 @@ package_group(
"//org.eclipse.jgit.packaging/...",
"//org.eclipse.jgit.pgm.test/...",
"//org.eclipse.jgit.pgm/...",
+ "//org.eclipse.jgit.ssh.apache/...",
"//org.eclipse.jgit.test/...",
"//org.eclipse.jgit.ui/...",
"//org.eclipse.jgit/...",
From 68017a029cb6b8648b29ae695c9e614d1f7a9770 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Wed, 20 Oct 2021 09:51:43 +0200
Subject: [PATCH 08/24] sshd: prepare for using an SSH agent
Add interfaces Connector and ConnectorFactory. A "connector" is just
something that knows how to connect to an ssh-agent and then can make
simple synchronous RPC-style requests (request-reply).
Add a way to customize an SshdSessionFactory with a ConnectorFactory.
Provide a default setup using the Java ServiceLoader mechanism to
discover an ConnectorFactory.
Implement an SshAgentClient in the internal part. Unfortunately we
cannot re-use the implementation in Apache MINA sshd: it's hard-wired
to Apache Tomcat APR, and it's also buggy.
No behavior changes yet since there is nothing that would provide an
actual ConnectorFactory. So for Apache MINA sshd, the SshAgentFactory
remains null as before.
Change-Id: I963a3d181357df2bdb66298bc702f2b9a6607a30
Signed-off-by: Thomas Wolf
---
.../META-INF/MANIFEST.MF | 4 +-
.../transport/sshd/SshdText.properties | 5 +
.../transport/sshd/JGitSshClient.java | 20 ++
.../internal/transport/sshd/SshdText.java | 14 +
.../sshd/agent/ConnectorFactoryProvider.java | 51 ++++
.../sshd/agent/JGitSshAgentFactory.java | 72 +++++
.../transport/sshd/agent/SshAgentClient.java | 246 ++++++++++++++++++
.../transport/sshd/SshdSessionFactory.java | 21 +-
.../sshd/SshdSessionFactoryBuilder.java | 55 +++-
.../sshd/agent/AbstractConnector.java | 116 +++++++++
.../jgit/transport/sshd/agent/Connector.java | 61 +++++
.../sshd/agent/ConnectorFactory.java | 58 +++++
12 files changed, 719 insertions(+), 4 deletions(-)
create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java
create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java
create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java
create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/AbstractConnector.java
create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java
create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 1f48841bc..339ba3aae 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -23,6 +23,7 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.0.0";x-inter
org.apache.sshd.common.signature,
org.apache.sshd.common.util.buffer,
org.eclipse.jgit.transport",
+ org.eclipse.jgit.internal.transport.sshd.agent;version="6.0.0";x-internal:=true,
org.eclipse.jgit.internal.transport.sshd.auth;version="6.0.0";x-internal:=true,
org.eclipse.jgit.internal.transport.sshd.proxy;version="6.0.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
org.eclipse.jgit.transport.sshd;version="6.0.0";
@@ -31,7 +32,8 @@ Export-Package: org.eclipse.jgit.internal.transport.sshd;version="6.0.0";x-inter
org.apache.sshd.common.keyprovider,
org.eclipse.jgit.util,
org.apache.sshd.client.session,
- org.apache.sshd.client.keyverifier"
+ org.apache.sshd.client.keyverifier",
+ org.eclipse.jgit.transport.sshd.agent;version="6.0.0"
Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
org.apache.sshd.agent;version="[2.7.0,2.8.0)",
org.apache.sshd.client;version="[2.7.0,2.8.0)",
diff --git a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
index defcbdcfc..2bba736aa 100644
--- a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
+++ b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
@@ -19,6 +19,7 @@ identityFileNoKey=No keys found in identity {0}
identityFileMultipleKeys=Multiple key pairs found in identity {0}
identityFileNotFound=Skipping identity ''{0}'': file not found
identityFileUnsupportedFormat=Unsupported format in identity {0}
+invalidSignatureAlgorithm=Signature algorithm ''{0}'' is not valid for a key of type ''{1}''
kexServerKeyInvalid=Server key did not validate
keyEncryptedMsg=Key ''{0}'' is encrypted. Enter the passphrase to decrypt it.
keyEncryptedPrompt=Passphrase
@@ -84,6 +85,10 @@ serverIdTooLong=Server identification is longer than 255 characters (including l
serverIdWithNul=Server identification contains a NUL character: {0}
sessionCloseFailed=Closing the session failed
sessionWithoutUsername=SSH session created without user name; cannot authenticate
+sshAgentReplyLengthError=Invalid SSH agent reply message length {0} after command {1}
+sshAgentReplyUnexpected=Unexpected reply from ssh-agent: {0}
+sshAgentShortReadBuffer=Short read from SSH agent
+sshAgentWrongNumberOfKeys=Invalid number of SSH agent keys: {0}
sshClosingDown=Apache MINA sshd session factory is closing down; cannot create new ssh sessions on this factory
sshCommandTimeout={0} timed out after {1} seconds while opening the channel
sshProcessStillRunning={0} is not yet completed, cannot get exit code
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
index fdb8cde67..71e8e6158 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java
@@ -32,8 +32,10 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
+import org.apache.sshd.agent.SshAgentFactory;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.config.hosts.HostConfigEntry;
import org.apache.sshd.client.future.ConnectFuture;
@@ -100,6 +102,8 @@ public class JGitSshClient extends SshClient {
private ProxyDataFactory proxyDatabase;
+ private Supplier agentFactorySupplier = () -> null;
+
@Override
protected SessionFactory createSessionFactory() {
// Override the parent's default
@@ -368,6 +372,22 @@ public CredentialsProvider getCredentialsProvider() {
return credentialsProvider;
}
+ @Override
+ public SshAgentFactory getAgentFactory() {
+ return agentFactorySupplier.get();
+ }
+
+ @Override
+ protected void checkConfig() {
+ // The super class requires channel factories for agent forwarding if a
+ // factory for an SSH agent is set. We haven't implemented this yet, and
+ // we don't do SSH agent forwarding for now. Unfortunately, there is no
+ // way to bypass this check in the super class except making
+ // getAgentFactory() return null until after the check.
+ super.checkConfig();
+ agentFactorySupplier = super::getAgentFactory;
+ }
+
/**
* A {@link SessionFactory} to create our own specialized
* {@link JGitClientSession}s.
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
index c0f571962..00ee62d6d 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
@@ -1,3 +1,12 @@
+/*
+ * Copyright (C) 2018, 2021 Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
package org.eclipse.jgit.internal.transport.sshd;
import org.eclipse.jgit.nls.NLS;
@@ -39,6 +48,7 @@ public static SshdText get() {
/***/ public String identityFileMultipleKeys;
/***/ public String identityFileNotFound;
/***/ public String identityFileUnsupportedFormat;
+ /***/ public String invalidSignatureAlgorithm;
/***/ public String kexServerKeyInvalid;
/***/ public String keyEncryptedMsg;
/***/ public String keyEncryptedPrompt;
@@ -96,6 +106,10 @@ public static SshdText get() {
/***/ public String serverIdWithNul;
/***/ public String sessionCloseFailed;
/***/ public String sessionWithoutUsername;
+ /***/ public String sshAgentReplyLengthError;
+ /***/ public String sshAgentReplyUnexpected;
+ /***/ public String sshAgentShortReadBuffer;
+ /***/ public String sshAgentWrongNumberOfKeys;
/***/ public String sshClosingDown;
/***/ public String sshCommandTimeout;
/***/ public String sshProcessStillRunning;
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java
new file mode 100644
index 000000000..9984f9976
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
+
+/**
+ * Provides a {@link ConnectorFactory}Â obtained via the {@link ServiceLoader}.
+ */
+public final class ConnectorFactoryProvider {
+
+ private static final ConnectorFactory FACTORY = loadDefaultFactory();
+
+ private static ConnectorFactory loadDefaultFactory() {
+ ServiceLoader loader = ServiceLoader
+ .load(ConnectorFactory.class);
+ Iterator iter = loader.iterator();
+ while (iter.hasNext()) {
+ ConnectorFactory candidate = iter.next();
+ if (candidate.isSupported()) {
+ return candidate;
+ }
+ }
+ return null;
+
+ }
+
+ private ConnectorFactoryProvider() {
+ // No instantiation
+ }
+
+ /**
+ * Retrieves the default {@link ConnectorFactory} obtained via the
+ * {@link ServiceLoader}.
+ *
+ * @return the {@link ConnectorFactory}, or {@code null} if none.
+ */
+ public static ConnectorFactory getDefaultFactory() {
+ return FACTORY;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java
new file mode 100644
index 000000000..1ed2ab9d7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/JGitSshAgentFactory.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.SshAgentFactory;
+import org.apache.sshd.agent.SshAgentServer;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.channel.ChannelFactory;
+import org.apache.sshd.common.session.ConnectionService;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
+
+/**
+ * A factory for creating {@link SshAgentClient}s.
+ */
+public class JGitSshAgentFactory implements SshAgentFactory {
+
+ private final @NonNull ConnectorFactory factory;
+
+ private final File homeDir;
+
+ /**
+ * Creates a new {@link JGitSshAgentFactory}.
+ *
+ * @param factory
+ * {@link JGitSshAgentFactory} to wrap
+ * @param homeDir
+ * for obtaining the current local user's home directory
+ */
+ public JGitSshAgentFactory(@NonNull ConnectorFactory factory,
+ File homeDir) {
+ this.factory = factory;
+ this.homeDir = homeDir;
+ }
+
+ @Override
+ public List getChannelForwardingFactories(
+ FactoryManager manager) {
+ // No agent forwarding supported yet.
+ return Collections.emptyList();
+ }
+
+ @Override
+ public SshAgent createClient(FactoryManager manager) throws IOException {
+ // sshd 2.8.0 will pass us the session here. At that point, we can get
+ // the HostConfigEntry and extract and handle the IdentityAgent setting.
+ // For now, pass null to let the ConnectorFactory do its default
+ // behavior (Pageant on Windows, SSH_AUTH_SOCK on Unixes with the
+ // jgit-builtin factory).
+ return new SshAgentClient(factory.create(null, homeDir));
+ }
+
+ @Override
+ public SshAgentServer createServer(ConnectionService service)
+ throws IOException {
+ // This should be called in a server only.
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java
new file mode 100644
index 000000000..08483e4c2
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/SshAgentClient.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.SshAgentConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.BufferException;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.transport.sshd.agent.Connector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A client for an SSH2 agent. This client supports only querying identities and
+ * signature requests.
+ *
+ * @see SSH
+ * Agent Protocol, RFC draft
+ */
+public class SshAgentClient implements SshAgent {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(SshAgentClient.class);
+
+ // OpenSSH limit
+ private static final int MAX_NUMBER_OF_KEYS = 2048;
+
+ private final AtomicBoolean closed = new AtomicBoolean();
+
+ private final Connector connector;
+
+ /**
+ * Creates a new {@link SshAgentClient} implementing the SSH2 ssh agent
+ * protocol, using the given {@link Connector} to connect to the SSH agent
+ * and to exchange messages.
+ *
+ * @param connector
+ * {@link Connector} to use
+ */
+ public SshAgentClient(Connector connector) {
+ this.connector = connector;
+ }
+
+ private boolean open(boolean debugging) throws IOException {
+ if (closed.get()) {
+ if (debugging) {
+ LOG.debug("SSH agent connection already closed"); //$NON-NLS-1$
+ }
+ return false;
+ }
+ boolean connected = connector != null && connector.connect();
+ if (!connected) {
+ if (debugging) {
+ LOG.debug("No SSH agent (SSH_AUTH_SOCK not set)"); //$NON-NLS-1$
+ }
+ }
+ return connected;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (!closed.getAndSet(true) && connector != null) {
+ connector.close();
+ }
+ }
+
+ @Override
+ public Iterable extends Map.Entry> getIdentities()
+ throws IOException {
+ boolean debugging = LOG.isDebugEnabled();
+ if (!open(debugging)) {
+ return Collections.emptyList();
+ }
+ if (debugging) {
+ LOG.debug("Requesting identities from SSH agent"); //$NON-NLS-1$
+ }
+ try {
+ Buffer reply = rpc(
+ SshAgentConstants.SSH2_AGENTC_REQUEST_IDENTITIES);
+ byte cmd = reply.getByte();
+ if (cmd != SshAgentConstants.SSH2_AGENT_IDENTITIES_ANSWER) {
+ throw new SshException(MessageFormat.format(
+ SshdText.get().sshAgentReplyUnexpected,
+ SshAgentConstants.getCommandMessageName(cmd)));
+ }
+ int numberOfKeys = reply.getInt();
+ if (numberOfKeys < 0 || numberOfKeys > MAX_NUMBER_OF_KEYS) {
+ throw new SshException(MessageFormat.format(
+ SshdText.get().sshAgentWrongNumberOfKeys,
+ Integer.toString(numberOfKeys)));
+ }
+ if (numberOfKeys == 0) {
+ if (debugging) {
+ LOG.debug("SSH agent has no keys"); //$NON-NLS-1$
+ }
+ return Collections.emptyList();
+ }
+ if (debugging) {
+ LOG.debug("Got {} key(s) from the SSH agent", //$NON-NLS-1$
+ Integer.toString(numberOfKeys));
+ }
+ boolean tracing = LOG.isTraceEnabled();
+ List> keys = new ArrayList<>(
+ numberOfKeys);
+ for (int i = 0; i < numberOfKeys; i++) {
+ PublicKey key = reply.getPublicKey();
+ String comment = reply.getString();
+ if (tracing) {
+ LOG.trace("Got SSH agent {} key: {} {}", //$NON-NLS-1$
+ KeyUtils.getKeyType(key),
+ KeyUtils.getFingerPrint(key), comment);
+ }
+ keys.add(new AbstractMap.SimpleImmutableEntry<>(key, comment));
+ }
+ return keys;
+ } catch (BufferException e) {
+ throw new SshException(SshdText.get().sshAgentShortReadBuffer, e);
+ }
+ }
+
+ @Override
+ public Map.Entry sign(SessionContext session, PublicKey key,
+ String algorithm, byte[] data) throws IOException {
+ boolean debugging = LOG.isDebugEnabled();
+ String keyType = KeyUtils.getKeyType(key);
+ String signatureAlgorithm;
+ if (algorithm != null) {
+ if (!KeyUtils.getCanonicalKeyType(algorithm).equals(keyType)) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ SshdText.get().invalidSignatureAlgorithm, algorithm,
+ keyType));
+ }
+ signatureAlgorithm = algorithm;
+ } else {
+ signatureAlgorithm = keyType;
+ }
+ if (!open(debugging)) {
+ return null;
+ }
+ int flags = 0;
+ switch (signatureAlgorithm) {
+ case KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS:
+ case KeyUtils.RSA_SHA512_CERT_TYPE_ALIAS:
+ flags = 4;
+ break;
+ case KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS:
+ case KeyUtils.RSA_SHA256_CERT_TYPE_ALIAS:
+ flags = 2;
+ break;
+ default:
+ break;
+ }
+ ByteArrayBuffer msg = new ByteArrayBuffer();
+ msg.putInt(0);
+ msg.putByte(SshAgentConstants.SSH2_AGENTC_SIGN_REQUEST);
+ msg.putPublicKey(key);
+ msg.putBytes(data);
+ msg.putInt(flags);
+ if (debugging) {
+ LOG.debug(
+ "sign({}): signing request to SSH agent for {} key, {} signature; flags={}", //$NON-NLS-1$
+ session, keyType, signatureAlgorithm,
+ Integer.toString(flags));
+ }
+ Buffer reply = rpc(SshAgentConstants.SSH2_AGENTC_SIGN_REQUEST,
+ msg.getCompactData());
+ byte cmd = reply.getByte();
+ if (cmd != SshAgentConstants.SSH2_AGENT_SIGN_RESPONSE) {
+ throw new SshException(
+ MessageFormat.format(SshdText.get().sshAgentReplyUnexpected,
+ SshAgentConstants.getCommandMessageName(cmd)));
+ }
+ try {
+ Buffer signatureReply = new ByteArrayBuffer(reply.getBytes());
+ String actualAlgorithm = signatureReply.getString();
+ byte[] signature = signatureReply.getBytes();
+ if (LOG.isTraceEnabled()) {
+ LOG.trace(
+ "sign({}): signature reply from SSH agent for {} key: {} signature={}", //$NON-NLS-1$
+ session, keyType, actualAlgorithm,
+ BufferUtils.toHex(':', signature));
+
+ } else if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "sign({}): signature reply from SSH agent for {} key, {} signature", //$NON-NLS-1$
+ session, keyType, actualAlgorithm);
+ }
+ return new AbstractMap.SimpleImmutableEntry<>(actualAlgorithm,
+ signature);
+ } catch (BufferException e) {
+ throw new SshException(SshdText.get().sshAgentShortReadBuffer, e);
+ }
+ }
+
+ private Buffer rpc(byte command, byte[] message) throws IOException {
+ return new ByteArrayBuffer(connector.rpc(command, message));
+ }
+
+ private Buffer rpc(byte command) throws IOException {
+ return new ByteArrayBuffer(connector.rpc(command));
+ }
+
+ @Override
+ public boolean isOpen() {
+ return !closed.get();
+ }
+
+ @Override
+ public void addIdentity(KeyPair key, String comment) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeIdentity(PublicKey key) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeAllIdentities() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
index cad959c90..da99f56cb 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2020 Thomas Wolf and others
+ * Copyright (C) 2018, 2021 Thomas Wolf and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -56,11 +56,14 @@
import org.eclipse.jgit.internal.transport.sshd.OpenSshServerKeyDatabase;
import org.eclipse.jgit.internal.transport.sshd.PasswordProviderWrapper;
import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.internal.transport.sshd.agent.ConnectorFactoryProvider;
+import org.eclipse.jgit.internal.transport.sshd.agent.JGitSshAgentFactory;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.SshConfigStore;
import org.eclipse.jgit.transport.SshConstants;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
import org.eclipse.jgit.util.FS;
/**
@@ -216,6 +219,11 @@ public SshdSession getSession(URIish uri,
new JGitUserInteraction(credentialsProvider));
client.setUserAuthFactories(getUserAuthFactories());
client.setKeyIdentityProvider(defaultKeysProvider);
+ ConnectorFactory connectors = getConnectorFactory();
+ if (connectors != null) {
+ client.setAgentFactory(
+ new JGitSshAgentFactory(connectors, home));
+ }
// JGit-specific things:
JGitSshClient jgitClient = (JGitSshClient) client;
jgitClient.setKeyCache(getKeyCache());
@@ -436,6 +444,17 @@ protected ServerKeyDatabase createServerKeyDatabase(@NonNull File homeDir,
getDefaultKnownHostsFiles(sshDir));
}
+ /**
+ * Gets a {@link ConnectorFactory}. If this returns {@code null}, SSH agents
+ * are not supported.
+ *
+ * @return the factory, or {@code null}Â if no SSH agent support is desired
+ * @since 6.0
+ */
+ protected ConnectorFactory getConnectorFactory() {
+ return ConnectorFactoryProvider.getDefaultFactory();
+ }
+
/**
* Gets the list of default user known hosts files. The default returns
* ~/.ssh/known_hosts and ~/.ssh/known_hosts2. The ssh config
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactoryBuilder.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactoryBuilder.java
index 2147c2bd5..7ed9b5ea3 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactoryBuilder.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactoryBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 Thomas Wolf and others
+ * Copyright (C) 2020, 2021 Thomas Wolf and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -20,6 +20,7 @@
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.SshConfigStore;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
import org.eclipse.jgit.util.StringUtils;
/**
@@ -114,7 +115,7 @@ public SshdSessionFactoryBuilder setConfigFile(
}
/**
- * A factory interface for creating a @link SshConfigStore}.
+ * A factory interface for creating a {@link SshConfigStore}.
*/
@FunctionalInterface
public interface ConfigStoreFactory {
@@ -232,6 +233,41 @@ public SshdSessionFactoryBuilder setServerKeyDatabase(
return this;
}
+ /**
+ * Sets an explicit {@link ConnectorFactory}. If {@code null}, there will be
+ * no support for SSH agents.
+ *
+ * If not set, the created {@link SshdSessionFactory} will use the
+ * {@link java.util.ServiceLoader} to find an {@link ConnectorFactory}.
+ *
+ *
+ * @param factory
+ * {@link ConnectorFactory} to use
+ * @return this {@link SshdSessionFactoryBuilder}
+ * @since 6.0
+ */
+ public SshdSessionFactoryBuilder setConnectorFactory(
+ ConnectorFactory factory) {
+ this.state.connectorFactory = factory;
+ this.state.connectorFactorySet = true;
+ return this;
+ }
+
+ /**
+ * Removes a previously set {@link ConnectorFactory}. The created
+ * {@link SshdSessionFactory} will use the {@link java.util.ServiceLoader}
+ * to find an {@link ConnectorFactory}. This is also the default if
+ * {@link #setConnectorFactory(ConnectorFactory)} isn't called at all.
+ *
+ * @return this {@link SshdSessionFactoryBuilder}
+ * @since 6.0
+ */
+ public SshdSessionFactoryBuilder withDefaultConnectorFactory() {
+ this.state.connectorFactory = null;
+ this.state.connectorFactorySet = false;
+ return this;
+ }
+
/**
* Builds a {@link SshdSessionFactory} as configured, using the given
* {@link KeyCache} for caching keys.
@@ -277,6 +313,10 @@ private static class State {
BiFunction serverKeyDatabaseCreator;
+ ConnectorFactory connectorFactory;
+
+ boolean connectorFactorySet;
+
State copy() {
State c = new State();
c.proxyDataFactory = proxyDataFactory;
@@ -290,6 +330,8 @@ State copy() {
c.defaultKeyFileFinder = defaultKeyFileFinder;
c.defaultKeysProvider = defaultKeysProvider;
c.serverKeyDatabaseCreator = serverKeyDatabaseCreator;
+ c.connectorFactory = connectorFactory;
+ c.connectorFactorySet = connectorFactorySet;
return c;
}
@@ -388,6 +430,15 @@ protected SshConfigStore createSshConfigStore(File homeDir,
return super.createSshConfigStore(homeDir, configFile,
localUserName);
}
+
+ @Override
+ protected ConnectorFactory getConnectorFactory() {
+ if (connectorFactorySet) {
+ return connectorFactory;
+ }
+ // Use default via ServiceLoader
+ return super.getConnectorFactory();
+ }
}
}
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/AbstractConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/AbstractConnector.java
new file mode 100644
index 000000000..71ddc3b00
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/AbstractConnector.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.sshd.agent;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Objects;
+
+import org.apache.sshd.agent.SshAgentConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+
+/**
+ * Provides some utility methods for implementing {@link Connector}s.
+ *
+ * @since 6.0
+ */
+public abstract class AbstractConnector implements Connector {
+
+ // A somewhat sane lower bound for the maximum reply length
+ private static final int MIN_REPLY_LENGTH = 8 * 1024;
+
+ /**
+ * Default maximum reply length. 256kB is the OpenSSH limit.
+ */
+ protected static final int DEFAULT_MAX_REPLY_LENGTH = 256 * 1024;
+
+ private final int maxReplyLength;
+
+ /**
+ * Creates a new instance using the {@link #DEFAULT_MAX_REPLY_LENGTH}.
+ */
+ protected AbstractConnector() {
+ this(DEFAULT_MAX_REPLY_LENGTH);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param maxReplyLength
+ * maximum number of payload bytes we're ready to accept
+ */
+ protected AbstractConnector(int maxReplyLength) {
+ if (maxReplyLength < MIN_REPLY_LENGTH) {
+ throw new IllegalArgumentException(
+ "Maximum payload length too small"); //$NON-NLS-1$
+ }
+ this.maxReplyLength = maxReplyLength;
+ }
+
+ /**
+ * Retrieves the maximum message length this {@link AbstractConnector} is
+ * configured for.
+ *
+ * @return the maximum message length
+ */
+ protected int getMaximumMessageLength() {
+ return this.maxReplyLength;
+ }
+
+ /**
+ * Prepares a message for sending by inserting the command and message
+ * length.
+ *
+ * @param command
+ * SSH agent command the request is for
+ * @param message
+ * about to be sent, including the 5 spare bytes at the front
+ * @throws IllegalArgumentException
+ * if {@code message} has less than 5 bytes
+ */
+ protected void prepareMessage(byte command, byte[] message)
+ throws IllegalArgumentException {
+ Objects.requireNonNull(message);
+ if (message.length < 5) {
+ // No translation; internal error
+ throw new IllegalArgumentException("Message buffer for " //$NON-NLS-1$
+ + SshAgentConstants.getCommandMessageName(command)
+ + " must have at least 5 bytes; have only " //$NON-NLS-1$
+ + message.length);
+ }
+ BufferUtils.putUInt(message.length - 4, message);
+ message[4] = command;
+ }
+
+ /**
+ * Checks the received length of a reply.
+ *
+ * @param command
+ * SSH agent command the reply is for
+ * @param length
+ * length as received: number of payload bytes
+ * @return the length as an {@code int}
+ * @throws IOException
+ * if the length is invalid
+ */
+ protected int toLength(byte command, byte[] length)
+ throws IOException {
+ long l = BufferUtils.getUInt(length);
+ if (l <= 0 || l > maxReplyLength - 4) {
+ throw new SshException(MessageFormat.format(
+ SshdText.get().sshAgentReplyLengthError,
+ Long.toString(l),
+ SshAgentConstants.getCommandMessageName(command)));
+ }
+ return (int) l;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java
new file mode 100644
index 000000000..b6da0866a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.sshd.agent;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Simple interface for connecting to something and making RPC-style
+ * request-reply calls.
+ *
+ * @since 6.0
+ */
+public interface Connector extends Closeable {
+
+ /**
+ * Connects to an SSH agent if there is one running. If called when already
+ * connected just returns {@code true}.
+ *
+ * @return {@code true} if an SSH agent is available and connected,
+ * {@false} if no SSH agent is available
+ * @throws IOException
+ * if connecting to the SSH agent failed
+ */
+ boolean connect() throws IOException;
+
+ /**
+ * Performs a remote call to the SSH agent and returns the result.
+ *
+ * @param command
+ * to send
+ * @param message
+ * to send; must have at least 5 bytes, and must have 5 unused
+ * bytes at the front.
+ * @return the result received
+ * @throws IOException
+ * if an error occurs
+ */
+ byte[] rpc(byte command, byte[] message) throws IOException;
+
+ /**
+ * Performs a remote call sending only a command without any parameters to
+ * the SSH agent and returns the result.
+ *
+ * @param command
+ * to send
+ * @return the result received
+ * @throws IOException
+ * if an error occurs
+ */
+ default byte[] rpc(byte command) throws IOException {
+ return rpc(command, new byte[5]);
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
new file mode 100644
index 000000000..fa725ab85
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.sshd.agent;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A factory for creating {@link Connector}s.
+ *
+ * @since 6.0
+ */
+public interface ConnectorFactory {
+
+ /**
+ * Creates a new {@link Connector}.
+ *
+ * @param identityAgent
+ * identifies the wanted agent connection; if {@code null}, the
+ * factory is free to provide a {@link Connector} to a default
+ * agent. The value will typically come from the IdentityAgent
+ * setting in ~/.ssh/config.
+ * @param homeDir
+ * the current local user's home directory as configured in the
+ * {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}
+ * @return a new {@link Connector}
+ * @throws IOException
+ * if no connector can be created
+ */
+ @NonNull
+ Connector create(String identityAgent, File homeDir)
+ throws IOException;
+
+ /**
+ * Tells whether this {@link ConnectorFactory}Â is applicable on the
+ * currently running platform.
+ *
+ * @return {@code true} if the factory can be used, {@code false} otherwise
+ */
+ boolean isSupported();
+
+ /**
+ * Retrieves a name for this factory.
+ *
+ * @return the name
+ */
+ String getName();
+
+}
From 634302d2da74226cff9f78e121ad5b8216c476e6 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Tue, 2 Nov 2021 18:48:25 +0100
Subject: [PATCH 09/24] sshd: add support for ssh-agent
Add a simple SSH agent connector using JNA. Include com.sum.jna and
com.sun.jna.platform in the target platform.
JNA is used to communicate through Unix domain sockets with ssh-agent,
and if on Windows, to communicate via shared memory with Pageant.
The new bundle o.e.j.ssh.apache.agent is an OSGi fragment so that
the java.util.ServiceLoader can find the provided factory without
further ado in OSGi environments.
Adapt both maven and bazel builds to include the new bundle.
Manually tested on OS X, CentOS 7, and Win10 with Pageant 0.76. Tested
by installing JGit built from this change into freshly downloaded
Eclipse 2021-12 M1, and then doing git fetches via SSH with different
~/.ssh/config settings (explicit IdentityFile, without any but a key in
the agent, with no keys and a key in the agent and IdentitiesOnly=yes
(must fail)).
Bug: 541274
Bug: 541275
Change-Id: I34e85467293707dbad1eb44d1f40fc2e70ba3622
Signed-off-by: Thomas Wolf
---
BUILD | 1 +
WORKSPACE | 12 +
lib/BUILD | 17 +
.../org.eclipse.jgit.pgm.feature/pom.xml | 13 +
.../org.eclipse.jgit.repository/category.xml | 12 +
.../org.eclipse.jgit.repository/pom.xml | 7 +-
.../feature.xml | 7 +
.../feature.xml | 8 +
.../pom.xml | 6 +
.../org.eclipse.jgit.target/jgit-4.17.target | 6 +-
.../org.eclipse.jgit.target/jgit-4.18.target | 6 +-
.../org.eclipse.jgit.target/jgit-4.19.target | 6 +-
.../org.eclipse.jgit.target/jgit-4.20.target | 6 +-
.../org.eclipse.jgit.target/jgit-4.21.target | 6 +-
.../orbit/R20210825222808-2021-09.tpd | 4 +
org.eclipse.jgit.packaging/pom.xml | 6 +
org.eclipse.jgit.pgm/pom.xml | 6 +
org.eclipse.jgit.ssh.apache.agent/.classpath | 8 +
org.eclipse.jgit.ssh.apache.agent/.fbprefs | 125 +++++
org.eclipse.jgit.ssh.apache.agent/.project | 28 +
.../org.eclipse.core.resources.prefs | 3 +
.../.settings/org.eclipse.core.runtime.prefs | 3 +
.../.settings/org.eclipse.jdt.core.prefs | 518 ++++++++++++++++++
.../.settings/org.eclipse.jdt.ui.prefs | 66 +++
.../org.eclipse.mylyn.tasks.ui.prefs | 4 +
.../.settings/org.eclipse.mylyn.team.ui.prefs | 3 +
.../.settings/org.eclipse.pde.api.tools.prefs | 104 ++++
.../.settings/org.eclipse.pde.core.prefs | 3 +
org.eclipse.jgit.ssh.apache.agent/BUILD | 22 +
.../META-INF/MANIFEST.MF | 16 +
.../META-INF/SOURCE-MANIFEST.MF | 7 +
org.eclipse.jgit.ssh.apache.agent/about.html | 96 ++++
.../build.properties | 7 +
.../plugin.properties | 2 +
org.eclipse.jgit.ssh.apache.agent/pom.xml | 227 ++++++++
...jgit.transport.sshd.agent.ConnectorFactory | 1 +
.../sshd/agent/connector/Texts.properties | 16 +
.../sshd/agent/connector/Factory.java | 44 ++
.../sshd/agent/connector/LibraryHolder.java | 72 +++
.../agent/connector/PageantConnector.java | 56 ++
.../sshd/agent/connector/PageantLibrary.java | 240 ++++++++
.../sshd/agent/connector/Sockets.java | 78 +++
.../transport/sshd/agent/connector/Texts.java | 46 ++
.../connector/UnixDomainSocketConnector.java | 218 ++++++++
.../sshd/agent/connector/UnixSockets.java | 122 +++++
.../META-INF/MANIFEST.MF | 1 +
.../sshd/ApacheSshProtocol2Test.java | 15 +-
.../jgit/transport/sshd/ApacheSshTest.java | 15 +-
.../transport/sshd/NoFilesSshBuilderTest.java | 4 +
.../jgit/transport/sshd/NoFilesSshTest.java | 7 +
.../sshd/JGitPublicKeyAuthentication.java | 49 +-
.../transport/sshd/SshdSessionFactory.java | 17 +-
.../jgit/transport/sshd/agent/Connector.java | 1 +
.../sshd/agent/ConnectorFactory.java | 5 +-
.../transport/sshd/agent/package-info.java | 6 +
pom.xml | 1 +
tools/BUILD | 1 +
57 files changed, 2362 insertions(+), 24 deletions(-)
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.classpath
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.fbprefs
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.project
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.core.resources.prefs
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.core.runtime.prefs
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.jdt.core.prefs
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.jdt.ui.prefs
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.mylyn.tasks.ui.prefs
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.mylyn.team.ui.prefs
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.pde.api.tools.prefs
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.pde.core.prefs
create mode 100644 org.eclipse.jgit.ssh.apache.agent/BUILD
create mode 100644 org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
create mode 100644 org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
create mode 100644 org.eclipse.jgit.ssh.apache.agent/about.html
create mode 100644 org.eclipse.jgit.ssh.apache.agent/build.properties
create mode 100644 org.eclipse.jgit.ssh.apache.agent/plugin.properties
create mode 100644 org.eclipse.jgit.ssh.apache.agent/pom.xml
create mode 100644 org.eclipse.jgit.ssh.apache.agent/resources/META-INF/services/org.eclipse.jgit.transport.sshd.agent.ConnectorFactory
create mode 100644 org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties
create mode 100644 org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java
create mode 100644 org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/LibraryHolder.java
create mode 100644 org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java
create mode 100644 org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantLibrary.java
create mode 100644 org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Sockets.java
create mode 100644 org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java
create mode 100644 org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java
create mode 100644 org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixSockets.java
create mode 100644 org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/package-info.java
diff --git a/BUILD b/BUILD
index 184ab272d..b8d8b9f0b 100644
--- a/BUILD
+++ b/BUILD
@@ -14,6 +14,7 @@ genrule(
"//org.eclipse.jgit.lfs.server:jgit-lfs-server",
"//org.eclipse.jgit.junit:junit",
"//org.eclipse.jgit.ssh.apache:ssh-apache",
+ "//org.eclipse.jgit.ssh.apache.agent:ssh-apache-agent",
"//org.eclipse.jgit.ssh.jsch:ssh-jsch",
],
outs = ["all.zip"],
diff --git a/WORKSPACE b/WORKSPACE
index 1324ce46a..f4694b3ae 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -133,6 +133,18 @@ maven_jar(
sha1 = "0c9eff7145e20b338c1dd6aca36ba93ed7c0147c",
)
+maven_jar(
+ name = "jna",
+ artifact = "net.java.dev.jna:jna:5.8.0",
+ sha1 = "3551d8d827e54858214107541d3aff9c615cb615",
+)
+
+maven_jar(
+ name = "jna-platform",
+ artifact = "net.java.dev.jna:jna-platform:5.8.0",
+ sha1 = "2f12f6d7f7652270d13624cef1b82d8cd9a5398e",
+)
+
maven_jar(
name = "commons-codec",
artifact = "commons-codec:commons-codec:1.14",
diff --git a/lib/BUILD b/lib/BUILD
index 1901be8ec..00c91e3ba 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -76,6 +76,7 @@ java_library(
visibility = [
"//org.eclipse.jgit.junit.ssh:__pkg__",
"//org.eclipse.jgit.ssh.apache:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.agent:__pkg__",
"//org.eclipse.jgit.ssh.apache.test:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
],
@@ -93,6 +94,22 @@ java_library(
exports = ["@sshd-sftp//jar"],
)
+java_library(
+ name = "jna",
+ visibility = [
+ "//org.eclipse.jgit.ssh.apache.agent:__pkg__",
+ ],
+ exports = ["@jna//jar"],
+)
+
+java_library(
+ name = "jna-platform",
+ visibility = [
+ "//org.eclipse.jgit.ssh.apache.agent:__pkg__",
+ ],
+ exports = ["@jna-platform//jar"],
+)
+
java_library(
name = "javaewah",
visibility = ["//visibility:public"],
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index 813981039..22e4ebc24 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
@@ -45,6 +45,19 @@
org.eclipse.jgit.ui
${project.version}
+
+
+ org.eclipse.jgit
+ org.eclipse.jgit.ssh.apache
+ ${project.version}
+
+
+
+ org.eclipse.jgit
+ org.eclipse.jgit.ssh.apache.agent
+ ${project.version}
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index ca0935a51..a15d0fd76 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -57,6 +57,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index f36717e95..98f264da0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
@@ -91,7 +91,12 @@
org.eclipse.jgit.ssh.apache
${project.version}
-
+
+ org.eclipse.jgit
+ org.eclipse.jgit.ssh.apache.agent
+ ${project.version}
+
+
org.eclipse.jgit
org.eclipse.jgit.ssh.jsch
${project.version}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
index eb0a658e5..dcd1238f9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
@@ -110,6 +110,13 @@
version="0.0.0"
unpack="false"/>
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
index 886b9b3ce..5e0f05a85 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
@@ -39,6 +39,12 @@
${project.version}
+
+ org.eclipse.jgit
+ org.eclipse.jgit.ssh.apache.agent
+ ${project.version}
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
index ab7f63d98..5356dea1c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
@@ -1,7 +1,7 @@
-
+
@@ -31,6 +31,10 @@
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
index 611ac5bcf..cf8d12fdb 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
@@ -1,7 +1,7 @@
-
+
@@ -31,6 +31,10 @@
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
index d852f9178..3e6ee56b8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
@@ -1,7 +1,7 @@
-
+
@@ -31,6 +31,10 @@
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
index 08fb6ccc3..ce9d8bca2 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
@@ -1,7 +1,7 @@
-
+
@@ -31,6 +31,10 @@
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
index eaf975258..41954429d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
@@ -1,7 +1,7 @@
-
+
@@ -31,6 +31,10 @@
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd
index 059a5844a..99f352011 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd
@@ -8,6 +8,10 @@ location "https://download.eclipse.org/tools/orbit/downloads/drops/R202108252228
com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
+ com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
+ com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
+ com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
+ com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
javaewah [1.1.12.v20210622-2206,1.1.12.v20210622-2206]
javaewah.source [1.1.12.v20210622-2206,1.1.12.v20210622-2206]
javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 7b75c2eb6..4e1fbcb97 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -147,6 +147,12 @@
${project.version}
sources
+
+ org.eclipse.jgit
+ org.eclipse.jgit.ssh.apache.agent
+ ${project.version}
+ sources
+
org.eclipse.jgit
org.eclipse.jgit.ssh.jsch
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index c28da4d7f..11f3f7963 100644
--- a/org.eclipse.jgit.pgm/pom.xml
+++ b/org.eclipse.jgit.pgm/pom.xml
@@ -79,6 +79,12 @@
${project.version}
+
+ org.eclipse.jgit
+ org.eclipse.jgit.ssh.apache.agent
+ ${project.version}
+
+
org.eclipse.jgit
org.eclipse.jgit.ssh.jsch
diff --git a/org.eclipse.jgit.ssh.apache.agent/.classpath b/org.eclipse.jgit.ssh.apache.agent/.classpath
new file mode 100644
index 000000000..df1b324f7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.jgit.ssh.apache.agent/.fbprefs b/org.eclipse.jgit.ssh.apache.agent/.fbprefs
new file mode 100644
index 000000000..81a0767ff
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.fbprefs
@@ -0,0 +1,125 @@
+#FindBugs User Preferences
+#Mon May 04 16:24:13 PDT 2009
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|true
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorCloneIdiom=CloneIdiom|false
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorDontUseEnum=DontUseEnum|true
+detectorDroppedException=DroppedException|true
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+detectorDumbMethods=DumbMethods|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|true
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
+detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorFindOpenStream=FindOpenStream|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindReturnRef=FindReturnRef|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorIncompatMask=IncompatMask|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorInefficientToArray=InefficientToArray|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorInitializationChain=InitializationChain|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorLazyInit=LazyInit|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorMutableLock=MutableLock|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorNaming=Naming|true
+detectorNumberConstructor=NumberConstructor|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorPublicSemaphores=PublicSemaphores|false
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorStartInConstructor=StartInConstructor|true
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorStringConcatenation=StringConcatenation|true
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorURLProblems=URLProblems|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUnreadFields=UnreadFields|true
+detectorUseObjectEquals=UseObjectEquals|false
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+detectorVarArgsProblems=VarArgsProblems|true
+detectorVolatileUsage=VolatileUsage|true
+detectorWaitInLoop=WaitInLoop|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detector_threshold=2
+effort=default
+excludefilter0=findBugs/FindBugsExcludeFilter.xml
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false
+filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|
+run_at_full_build=true
diff --git a/org.eclipse.jgit.ssh.apache.agent/.project b/org.eclipse.jgit.ssh.apache.agent/.project
new file mode 100644
index 000000000..73358f4a6
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.project
@@ -0,0 +1,28 @@
+
+
+ org.eclipse.jgit.ssh.apache.agent
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 000000000..66ac15c47
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Mon Aug 11 16:46:12 PDT 2008
+eclipse.preferences.version=1
+encoding/=UTF-8
diff --git a/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 000000000..006e07ede
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Mon Mar 24 18:55:50 EDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..d1f54bbe6
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,518 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jgit.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jgit.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=11
+org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
+org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false
+org.eclipse.jdt.core.formatter.align_with_spaces=false
+org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=49
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=49
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=49
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=49
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=0
+org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=49
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assertion_message=0
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
+org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16
+org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_record_components=16
+org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0
+org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_type_annotations=0
+org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
+org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false
+org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.indent_tag_description=false
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert
+org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.text_block_indentation=0
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 000000000..5cfb8b6ac
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,66 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_JGit Format
+formatter_settings_version=21
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=true
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_type_arguments=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.mylyn.tasks.ui.prefs
new file mode 100644
index 000000000..823c0f56a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.mylyn.tasks.ui.prefs
@@ -0,0 +1,4 @@
+#Tue Jul 19 20:11:28 CEST 2011
+eclipse.preferences.version=1
+project.repository.kind=bugzilla
+project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.mylyn.team.ui.prefs
new file mode 100644
index 000000000..0cba949fb
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.mylyn.team.ui.prefs
@@ -0,0 +1,3 @@
+#Tue Jul 19 20:11:28 CEST 2011
+commit.comment.template=${task.description} \n\nBug\: ${task.key}
+eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 000000000..c0030ded7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,104 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_FIELD=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_ANNOTATION=Ignore
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Warning
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+changed_execution_env=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+incompatible_api_component_version_report_major_without_breaking_change=Warning
+incompatible_api_component_version_report_minor_without_api_change=Ignore
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 000000000..82793f2d2
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+#Thu Jan 14 14:34:32 CST 2010
+eclipse.preferences.version=1
+resolve.requirebundle=false
diff --git a/org.eclipse.jgit.ssh.apache.agent/BUILD b/org.eclipse.jgit.ssh.apache.agent/BUILD
new file mode 100644
index 000000000..0c8cf838d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/BUILD
@@ -0,0 +1,22 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//visibility:public"])
+
+SRCS = glob(["src/**/*.java"])
+
+RESOURCES = glob(["resources/**"])
+
+java_library(
+ name = "ssh-apache-agent",
+ srcs = SRCS,
+ resource_strip_prefix = "org.eclipse.jgit.ssh.apache.agent/resources",
+ resources = RESOURCES,
+ deps = [
+ "//lib:jna",
+ "//lib:jna-platform",
+ "//lib:slf4j-api",
+ "//lib:sshd-osgi",
+ "//org.eclipse.jgit:jgit",
+ "//org.eclipse.jgit.ssh.apache:ssh-apache"
+ ],
+)
diff --git a/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..19b8ee850
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
@@ -0,0 +1,16 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent;singleton:=true
+Bundle-Version: 6.0.0.qualifier
+Bundle-Vendor: %Bundle-Vendor
+Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[6.0.0,6.1.0)"
+Bundle-ActivationPolicy: lazy
+Automatic-Module-Name: org.eclipse.jgit.ssh.apache.agent
+Bundle-RequiredExecutionEnvironment: JavaSE-11
+Import-Package: org.eclipse.jgit.transport.sshd;version="[6.0.0,6.1.0)",
+ org.eclipse.jgit.nls;version="[6.0.0,6.1.0)",
+ org.eclipse.jgit.util;version="[6.0.0,6.1.0)"
+Require-Bundle: com.sun.jna;bundle-version="[5.8.0,6.0.0)",
+ com.sun.jna.platform;bundle-version="[5.8.0,6.0.0)"
+Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="6.0.0";x-internal:=true
diff --git a/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
new file mode 100644
index 000000000..afad57084
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.eclipse.jgit.ssh.apache.agent - Sources
+Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent.source
+Bundle-Vendor: Eclipse.org - JGit
+Bundle-Version: 6.0.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="6.0.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache.agent/about.html b/org.eclipse.jgit.ssh.apache.agent/about.html
new file mode 100644
index 000000000..f971af18d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/about.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+Eclipse Distribution License - Version 1.0
+
+
+
+
+
+
+Eclipse Distribution License - v 1.0
+
+Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
+
+All rights reserved.
+Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
- Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+- Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+SHA-1 UbcCheck - MIT
+
+Copyright (c) 2017:
+
+Marc Stevens
+Cryptology Group
+Centrum Wiskunde & Informatica
+P.O. Box 94079, 1090 GB Amsterdam, Netherlands
+marc@marc-stevens.nl
+
+
+Dan Shumow
+Microsoft Research
+danshu@microsoft.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+- The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+
+
diff --git a/org.eclipse.jgit.ssh.apache.agent/build.properties b/org.eclipse.jgit.ssh.apache.agent/build.properties
new file mode 100644
index 000000000..8148271ef
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/build.properties
@@ -0,0 +1,7 @@
+source.. = src/,\
+ resources/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ about.html
diff --git a/org.eclipse.jgit.ssh.apache.agent/plugin.properties b/org.eclipse.jgit.ssh.apache.agent/plugin.properties
new file mode 100644
index 000000000..86df8f2e7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/plugin.properties
@@ -0,0 +1,2 @@
+Bundle-Name=JGit Unix SSH agent client for Apache MINA sshd
+Bundle-Vendor=Eclipse JGit
diff --git a/org.eclipse.jgit.ssh.apache.agent/pom.xml b/org.eclipse.jgit.ssh.apache.agent/pom.xml
new file mode 100644
index 000000000..0b5267a40
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/pom.xml
@@ -0,0 +1,227 @@
+
+
+
+
+ 4.0.0
+
+
+ org.eclipse.jgit
+ org.eclipse.jgit-parent
+ 6.0.0-SNAPSHOT
+
+
+ org.eclipse.jgit.ssh.apache.agent
+ JGit - Apache sshd SSH agent support
+
+
+ Support for ssh-agent for the Apache MINA sshd SSH connector
+
+
+
+ 5.8.0
+
+ ${project.build.directory}/META-INF/SOURCE-MANIFEST.MF
+
+
+
+
+ org.eclipse.jgit
+ org.eclipse.jgit
+ ${project.version}
+
+
+
+ org.eclipse.jgit
+ org.eclipse.jgit.ssh.apache
+ ${project.version}
+
+
+
+ net.java.dev.jna
+ jna
+ ${jna-version}
+
+
+
+ net.java.dev.jna
+ jna-platform
+ ${jna-version}
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+ src/
+
+
+
+ .
+
+ plugin.properties
+ about.html
+
+
+
+ resources/
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ translate-source-qualifier
+ generate-resources
+
+
+
+
+
+
+
+
+
+ run
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ true
+
+
+ attach-sources
+ process-classes
+
+ jar
+
+
+
+ ${source-bundle-manifest}
+
+
+
+
+
+
+
+ maven-jar-plugin
+
+
+ ${bundle-manifest}
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.jgit.ssh.apache.agent/resources/META-INF/services/org.eclipse.jgit.transport.sshd.agent.ConnectorFactory b/org.eclipse.jgit.ssh.apache.agent/resources/META-INF/services/org.eclipse.jgit.transport.sshd.agent.ConnectorFactory
new file mode 100644
index 000000000..538888c77
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/resources/META-INF/services/org.eclipse.jgit.transport.sshd.agent.ConnectorFactory
@@ -0,0 +1 @@
+org.eclipse.jgit.internal.transport.sshd.agent.connector.Factory
diff --git a/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties b/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties
new file mode 100644
index 000000000..f884adc08
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties
@@ -0,0 +1,16 @@
+errCloseMappedFile=Cannot close mapped file: {0} - {1}
+errLastError=System message for error {0} could not be retrieved, got {1}
+errReleaseSharedMemory=Cannot release shared memory: {0} - {1}
+errUnknown=unknown error
+logErrorLoadLibrary=Cannot load socket library; SSH agent support is switched off
+msgCloseFailed=Cannot close SSH agent socket {0}
+msgConnectFailed=Could not connect to SSH agent via socket ''{0}''
+msgNoMappedFile=Could not create file mapping: {0} - {1}
+msgNoSharedMemory=Could not initialize shared memory: {0} - {1}
+msgPageantUnavailable=Could not connect to Pageant
+msgReadFailed=Reading {0} bytes from the SSH agent failed
+msgSendFailed=Sending {0} bytes to SSH agent failed; {0} bytes not written
+msgSendFailed2=Sending {0} bytes to SSH agent failed: {1} - {2}
+msgSharedMemoryFailed=Could not set up shared memory for communicating with Pageant
+msgShortRead=Short read from SSH agent, expected {0} bytes, got {1} bytes; last read() returned {2}
+
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java
new file mode 100644
index 000000000..1cbf0e41b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent.connector;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.transport.sshd.agent.Connector;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
+import org.eclipse.jgit.util.SystemReader;
+
+/**
+ * An {@link ConnectorFactory} for connecting to an OpenSSH SSH agent.
+ */
+public class Factory implements ConnectorFactory {
+
+ private static final String NAME = "jgit-builtin"; //$NON-NLS-1$
+
+ @Override
+ public Connector create(String identityAgent, File homeDir)
+ throws IOException {
+ if (SystemReader.getInstance().isWindows()) {
+ return new PageantConnector();
+ }
+ return new UnixDomainSocketConnector(identityAgent);
+ }
+
+ @Override
+ public boolean isSupported() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/LibraryHolder.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/LibraryHolder.java
new file mode 100644
index 000000000..b09b55f81
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/LibraryHolder.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent.connector;
+
+import java.text.MessageFormat;
+
+import com.sun.jna.LastErrorException;
+import com.sun.jna.platform.win32.Kernel32;
+import com.sun.jna.platform.win32.Kernel32Util;
+import com.sun.jna.platform.win32.User32;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Delay loading the native libraries until needed.
+ */
+class LibraryHolder {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(LibraryHolder.class);
+
+ private static LibraryHolder INSTANCE;
+
+ private static boolean libraryLoaded = false;
+
+ public static synchronized LibraryHolder getLibrary() {
+ if (!libraryLoaded) {
+ libraryLoaded = true;
+ try {
+ INSTANCE = new LibraryHolder();
+ } catch (Exception | UnsatisfiedLinkError
+ | NoClassDefFoundError e) {
+ LOG.error(Texts.get().logErrorLoadLibrary, e);
+ }
+ }
+ return INSTANCE;
+ }
+
+ User32 user;
+
+ Kernel32 kernel;
+
+ private LibraryHolder() {
+ user = User32.INSTANCE;
+ kernel = Kernel32.INSTANCE;
+ }
+
+ String systemError(String pattern) {
+ int lastError = kernel.GetLastError();
+ String msg;
+ try {
+ msg = Kernel32Util.formatMessageFromLastErrorCode(lastError);
+ } catch (Exception e) {
+ String err = e instanceof LastErrorException
+ ? Integer.toString(((LastErrorException) e).getErrorCode())
+ : Texts.get().errUnknown;
+ msg = MessageFormat.format(Texts.get().errLastError,
+ Integer.toString(lastError), err);
+ LOG.error(msg, e);
+ }
+ return MessageFormat.format(pattern, Integer.toString(lastError), msg);
+ }
+
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java
new file mode 100644
index 000000000..59fe2fc24
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent.connector;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.transport.sshd.agent.AbstractConnector;
+
+/**
+ * A connector using Pageant's shared memory IPC mechanism.
+ */
+public class PageantConnector extends AbstractConnector {
+
+ private final PageantLibrary lib;
+
+ /**
+ * Creates a new {@link PageantConnector}.
+ */
+ public PageantConnector() {
+ super(); // Use default maximum message size
+ this.lib = new PageantLibrary();
+ }
+
+ @Override
+ public boolean connect() throws IOException {
+ return lib.isPageantAvailable();
+ }
+
+ @Override
+ public void close() throws IOException {
+ // Nothing to do
+ }
+
+ @Override
+ public byte[] rpc(byte command, byte[] message) throws IOException {
+ try (PageantLibrary.Pipe pipe = lib
+ .createPipe(getClass().getSimpleName(),
+ getMaximumMessageLength())) {
+ prepareMessage(command, message);
+ pipe.send(message);
+ byte[] lengthBuf = new byte[4];
+ pipe.receive(lengthBuf);
+ int length = toLength(command, lengthBuf);
+ byte[] payload = new byte[length];
+ pipe.receive(payload);
+ return payload;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantLibrary.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantLibrary.java
new file mode 100644
index 000000000..9a30d804e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantLibrary.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent.connector;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.jna.Memory;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import com.sun.jna.platform.win32.WinBase;
+import com.sun.jna.platform.win32.WinDef.HWND;
+import com.sun.jna.platform.win32.WinDef.LPARAM;
+import com.sun.jna.platform.win32.WinDef.LRESULT;
+import com.sun.jna.platform.win32.WinNT;
+import com.sun.jna.platform.win32.WinNT.HANDLE;
+import com.sun.jna.platform.win32.WinUser;
+
+/**
+ * The {@link PageantLibrary} encapsulates the shared memory access and provides
+ * a simple pipe abstraction.
+ */
+public final class PageantLibrary {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(PageantLibrary.class);
+
+ /** Pageant's "class" and "window name". */
+ private static final String PAGEANT = "Pageant"; //$NON-NLS-1$
+
+ /**
+ * Magic constant from Pageant; ID for the CopyStruct used in SendMessage.
+ *
+ * @see "random goop"
+ */
+ private static final int PAGEANT_ID = 0x804e_50ba;
+
+ /**
+ * Determines whether Pageant is currently running.
+ *
+ * @return {@code true}Â if Pageant is running, {@code false} otherwise
+ */
+ boolean isPageantAvailable() {
+ LibraryHolder libs = LibraryHolder.getLibrary();
+ if (libs == null) {
+ return false;
+ }
+ HWND window = libs.user.FindWindow(PAGEANT, PAGEANT);
+ return window != null && !window.equals(WinBase.INVALID_HANDLE_VALUE);
+ }
+
+ /**
+ * An abstraction for a bi-directional pipe.
+ */
+ interface Pipe extends Closeable {
+
+ /**
+ * Send the given message.
+ *
+ * @param message
+ * to send
+ * @throws IOException
+ * on errors
+ */
+ void send(byte[] message) throws IOException;
+
+ /**
+ * Reads bytes from the pipe until {@code data} is full.
+ *
+ * @param data
+ * to read
+ * @throws IOException
+ * on errors
+ */
+ void receive(byte[] data) throws IOException;
+ }
+
+ /**
+ * Windows' COPYDATASTRUCT. Must be public for JNA.
+ */
+ public static class CopyStruct extends Structure {
+
+ /** Must be set the {@link #PAGEANT_ID}. */
+ public int dwData = PAGEANT_ID;
+
+ /** Data length; number of bytes in {@link #lpData}. */
+ public long cbData;
+
+ /** Points to {@link #cbData} bytes. */
+ public Pointer lpData;
+
+ @Override
+ protected List getFieldOrder() {
+ return List.of("dwData", "cbData", "lpData"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+
+ private static class PipeImpl implements Pipe {
+
+ private final LibraryHolder libs;
+
+ private final HWND window;
+
+ private final byte[] name;
+
+ private final HANDLE file;
+
+ private final Pointer memory;
+
+ private long readPos = 0;
+
+ PipeImpl(LibraryHolder libs, HWND window, String name, HANDLE file,
+ Pointer memory) {
+ this.libs = libs;
+ this.window = window;
+ this.name = name.getBytes(StandardCharsets.US_ASCII);
+ this.file = file;
+ this.memory = memory;
+ }
+
+ @Override
+ public void close() throws IOException {
+ PageantLibrary.close(libs, file, memory, false);
+ }
+
+ private Pointer init(CopyStruct c) {
+ c.cbData = name.length + 1;
+ c.lpData = new Memory(c.cbData);
+ c.lpData.write(0, name, 0, name.length);
+ c.lpData.setByte(name.length, (byte) 0);
+ c.write();
+ return c.getPointer();
+ }
+
+ @Override
+ public void send(byte[] message) throws IOException {
+ memory.write(0, message, 0, message.length);
+ CopyStruct c = new CopyStruct();
+ Pointer p = init(c);
+ LRESULT result = libs.user.SendMessage(window, WinUser.WM_COPYDATA,
+ null, new LPARAM(Pointer.nativeValue(p)));
+ if (result == null || result.longValue() == 0) {
+ throw new IOException(
+ libs.systemError(Texts.get().msgSendFailed2));
+ }
+ }
+
+ @Override
+ public void receive(byte[] data) throws IOException {
+ // Relies on Pageant handling the request synchronously, i.e.,
+ // SendMessage() above returning successfully only once Pageant
+ // has indeed written into the shared memory.
+ memory.read(readPos, data, 0, data.length);
+ readPos += data.length;
+ }
+ }
+
+ /**
+ * Creates a new {@link Pipe}.
+ *
+ * @param name
+ * for the pipe
+ * @param maxSize
+ * maximum size for messages
+ * @return the {@link Pipe}, or {@code null} if none created
+ * @throws IOException on errors
+ */
+ Pipe createPipe(String name, int maxSize) throws IOException {
+ LibraryHolder libs = LibraryHolder.getLibrary();
+ if (libs == null) {
+ throw new IllegalStateException("Libraries were not loaded"); //$NON-NLS-1$
+ }
+ HWND window = libs.user.FindWindow(PAGEANT, PAGEANT);
+ if (window == null || window.equals(WinBase.INVALID_HANDLE_VALUE)) {
+ throw new IOException(Texts.get().msgPageantUnavailable);
+ }
+ String fileName = name + libs.kernel.GetCurrentThreadId();
+ HANDLE file = null;
+ Pointer sharedMemory = null;
+ try {
+ file = libs.kernel.CreateFileMapping(WinBase.INVALID_HANDLE_VALUE,
+ null, WinNT.PAGE_READWRITE, 0, maxSize, fileName);
+ if (file == null || file.equals(WinBase.INVALID_HANDLE_VALUE)) {
+ throw new IOException(
+ libs.systemError(Texts.get().msgNoMappedFile));
+ }
+ sharedMemory = libs.kernel.MapViewOfFile(file,
+ WinNT.SECTION_MAP_WRITE, 0, 0, 0);
+ if (sharedMemory == null) {
+ throw new IOException(
+ libs.systemError(Texts.get().msgNoSharedMemory));
+ }
+ return new PipeImpl(libs, window, fileName, file, sharedMemory);
+ } catch (IOException e) {
+ close(libs, file, sharedMemory, true);
+ throw e;
+ } catch (Throwable e) {
+ close(libs, file, sharedMemory, true);
+ throw new IOException(Texts.get().msgSharedMemoryFailed, e);
+ }
+ }
+
+ private static void close(LibraryHolder libs, HANDLE file, Pointer memory,
+ boolean silent) throws IOException {
+ if (memory != null) {
+ if (!libs.kernel.UnmapViewOfFile(memory)) {
+ String msg = libs
+ .systemError(Texts.get().errReleaseSharedMemory);
+ if (silent) {
+ LOG.error(msg);
+ } else {
+ throw new IOException(msg);
+ }
+ }
+ }
+ if (file != null) {
+ if (!libs.kernel.CloseHandle(file)) {
+ String msg = libs.systemError(Texts.get().errCloseMappedFile);
+ if (silent) {
+ LOG.error(msg);
+ } else {
+ throw new IOException(msg);
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Sockets.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Sockets.java
new file mode 100644
index 000000000..3d95bdb51
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Sockets.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent.connector;
+
+import java.nio.charset.Charset;
+
+import com.sun.jna.Structure;
+import com.sun.jna.Structure.FieldOrder;
+
+/**
+ * Common things for socket communication.
+ */
+public final class Sockets {
+
+ private Sockets() {
+ // No instantiation
+ }
+
+ /**
+ * Default SSH agent socket environment variable name.
+ */
+ public static final String ENV_SSH_AUTH_SOCK = "SSH_AUTH_SOCK"; //$NON-NLS-1$
+
+ /**
+ * Domain for Unix domain sockets.
+ */
+ public static final int AF_UNIX = 1;
+
+ /**
+ * Socket type for duplex sockets.
+ */
+ public static final int SOCK_STREAM = 1;
+
+ /**
+ * Default protocol selector.
+ */
+ public static final int DEFAULT_PROTOCOL = 0;
+
+ /**
+ * Very simple representation of the C SockAddr type.
+ */
+ @FieldOrder(value = { "sa_family", "sa_data" })
+ public static class SockAddr extends Structure {
+ // This is a "variable length struct" in C.
+
+ // Why 108 is apparently lost in time. But the file path for a Unix
+ // domain socket cannot be longer (including the terminating NUL).
+ private static final int MAX_DATA_LENGTH = 108;
+
+ /** Socket family */
+ public short sa_family = AF_UNIX;
+
+ /** Unix domain socket path. */
+ public byte[] sa_data = new byte[MAX_DATA_LENGTH];
+
+ /**
+ * Creates a new {@link SockAddr} for the given {@code path}.
+ *
+ * @param path
+ * for the Socket
+ * @param encoding
+ * to use to decode the {@code path} to a byte sequence
+ */
+ public SockAddr(String path, Charset encoding) {
+ byte[] bytes = path.getBytes(encoding);
+ int toCopy = Math.min(sa_data.length - 1, bytes.length);
+ System.arraycopy(bytes, 0, sa_data, 0, toCopy);
+ sa_data[toCopy] = 0;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java
new file mode 100644
index 000000000..6b6626154
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent.connector;
+
+import org.eclipse.jgit.nls.NLS;
+import org.eclipse.jgit.nls.TranslationBundle;
+
+/**
+ * Externalized text messages for localization.
+ */
+public final class Texts extends TranslationBundle {
+
+ /**
+ * Get an instance of this translation bundle.
+ *
+ * @return an instance of this translation bundle
+ */
+ public static Texts get() {
+ return NLS.getBundleFor(Texts.class);
+ }
+
+ // @formatter:off
+ /***/ public String errCloseMappedFile;
+ /***/ public String errLastError;
+ /***/ public String errReleaseSharedMemory;
+ /***/ public String errUnknown;
+ /***/ public String logErrorLoadLibrary;
+ /***/ public String msgCloseFailed;
+ /***/ public String msgConnectFailed;
+ /***/ public String msgNoMappedFile;
+ /***/ public String msgNoSharedMemory;
+ /***/ public String msgPageantUnavailable;
+ /***/ public String msgReadFailed;
+ /***/ public String msgSendFailed;
+ /***/ public String msgSendFailed2;
+ /***/ public String msgSharedMemoryFailed;
+ /***/ public String msgShortRead;
+
+}
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java
new file mode 100644
index 000000000..4c698d974
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent.connector;
+
+import static org.eclipse.jgit.internal.transport.sshd.agent.connector.Sockets.AF_UNIX;
+import static org.eclipse.jgit.internal.transport.sshd.agent.connector.Sockets.DEFAULT_PROTOCOL;
+import static org.eclipse.jgit.internal.transport.sshd.agent.connector.Sockets.ENV_SSH_AUTH_SOCK;
+import static org.eclipse.jgit.internal.transport.sshd.agent.connector.Sockets.SOCK_STREAM;
+import static org.eclipse.jgit.internal.transport.sshd.agent.connector.UnixSockets.FD_CLOEXEC;
+import static org.eclipse.jgit.internal.transport.sshd.agent.connector.UnixSockets.F_SETFD;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.common.SshException;
+import org.eclipse.jgit.transport.sshd.agent.AbstractConnector;
+import org.eclipse.jgit.util.StringUtils;
+import org.eclipse.jgit.util.SystemReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.jna.LastErrorException;
+import com.sun.jna.Native;
+import com.sun.jna.platform.unix.LibCAPI;
+
+/**
+ * JNA-based implementation of communication through a Unix domain socket.
+ */
+public class UnixDomainSocketConnector extends AbstractConnector {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(UnixDomainSocketConnector.class);
+
+ private static UnixSockets library;
+
+ private static boolean libraryLoaded = false;
+
+ private static synchronized UnixSockets getLibrary() {
+ if (!libraryLoaded) {
+ libraryLoaded = true;
+ try {
+ library = Native.load(UnixSockets.LIBRARY_NAME, UnixSockets.class);
+ } catch (Exception | UnsatisfiedLinkError
+ | NoClassDefFoundError e) {
+ LOG.error(Texts.get().logErrorLoadLibrary, e);
+ }
+ }
+ return library;
+ }
+
+ private final String socketFile;
+
+ private AtomicBoolean connected = new AtomicBoolean();
+
+ private volatile int socketFd = -1;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param socketFile
+ * to use; if {@code null} or empty, use environment variable
+ * SSH_AUTH_SOCK
+ */
+ public UnixDomainSocketConnector(String socketFile) {
+ super();
+ String file = socketFile;
+ if (StringUtils.isEmptyOrNull(file)) {
+ file = SystemReader.getInstance().getenv(ENV_SSH_AUTH_SOCK);
+ }
+ this.socketFile = file;
+ }
+
+ @Override
+ public boolean connect() throws IOException {
+ if (StringUtils.isEmptyOrNull(socketFile)) {
+ return false;
+ }
+ int fd = socketFd;
+ synchronized (this) {
+ if (connected.get()) {
+ return true;
+ }
+ UnixSockets sockets = getLibrary();
+ if (sockets == null) {
+ return false;
+ }
+ try {
+ fd = sockets.socket(AF_UNIX, SOCK_STREAM, DEFAULT_PROTOCOL);
+ // OS X apparently doesn't have SOCK_CLOEXEC, so we can't set it
+ // atomically. Set it via fcntl, which exists on all systems
+ // we're interested in.
+ sockets.fcntl(fd, F_SETFD, FD_CLOEXEC);
+ Sockets.SockAddr sockAddr = new Sockets.SockAddr(socketFile,
+ StandardCharsets.UTF_8);
+ sockets.connect(fd, sockAddr, sockAddr.size());
+ connected.set(true);
+ } catch (LastErrorException e) {
+ if (fd >= 0) {
+ try {
+ sockets.close(fd);
+ } catch (LastErrorException e1) {
+ e.addSuppressed(e1);
+ }
+ }
+ throw new IOException(MessageFormat
+ .format(Texts.get().msgConnectFailed, socketFile), e);
+ }
+ }
+ socketFd = fd;
+ return connected.get();
+ }
+
+ @Override
+ public synchronized void close() throws IOException {
+ int fd = socketFd;
+ if (connected.getAndSet(false) && fd >= 0) {
+ socketFd = -1;
+ try {
+ getLibrary().close(fd);
+ } catch (LastErrorException e) {
+ throw new IOException(MessageFormat.format(
+ Texts.get().msgCloseFailed, Integer.toString(fd)), e);
+ }
+ }
+ }
+
+ @Override
+ public byte[] rpc(byte command, byte[] message) throws IOException {
+ prepareMessage(command, message);
+ int fd = socketFd;
+ if (!connected.get() || fd < 0) {
+ // No translation, internal error
+ throw new IllegalStateException("Not connected to SSH agent"); //$NON-NLS-1$
+ }
+ writeFully(fd, message);
+ // Now receive the reply
+ byte[] lengthBuf = new byte[4];
+ readFully(fd, lengthBuf);
+ int length = toLength(command, lengthBuf);
+ byte[] payload = new byte[length];
+ readFully(fd, payload);
+ return payload;
+ }
+
+ private void writeFully(int fd, byte[] message) throws IOException {
+ int toWrite = message.length;
+ try {
+ byte[] buf = message;
+ while (toWrite > 0) {
+ int written = getLibrary()
+ .write(fd, buf, new LibCAPI.size_t(buf.length))
+ .intValue();
+ if (written < 0) {
+ throw new IOException(
+ MessageFormat.format(Texts.get().msgSendFailed,
+ Integer.toString(message.length),
+ Integer.toString(toWrite)));
+ }
+ toWrite -= written;
+ if (written > 0 && toWrite > 0) {
+ buf = Arrays.copyOfRange(buf, written, buf.length);
+ }
+ }
+ } catch (LastErrorException e) {
+ throw new IOException(
+ MessageFormat.format(Texts.get().msgSendFailed,
+ Integer.toString(message.length),
+ Integer.toString(toWrite)),
+ e);
+ }
+ }
+
+ private void readFully(int fd, byte[] data) throws IOException {
+ int n = 0;
+ int offset = 0;
+ while (offset < data.length
+ && (n = read(fd, data, offset, data.length - offset)) > 0) {
+ offset += n;
+ }
+ if (offset < data.length) {
+ throw new SshException(
+ MessageFormat.format(Texts.get().msgShortRead,
+ Integer.toString(data.length),
+ Integer.toString(offset), Integer.toString(n)));
+ }
+ }
+
+ private int read(int fd, byte[] buffer, int offset, int length)
+ throws IOException {
+ try {
+ LibCAPI.size_t toRead = new LibCAPI.size_t(length);
+ if (offset == 0) {
+ return getLibrary().read(fd, buffer, toRead).intValue();
+ }
+ byte[] data = new byte[length];
+ int read = getLibrary().read(fd, data, toRead).intValue();
+ if (read > 0) {
+ System.arraycopy(data, 0, buffer, offset, read);
+ }
+ return read;
+ } catch (LastErrorException e) {
+ throw new IOException(
+ MessageFormat.format(Texts.get().msgReadFailed,
+ Integer.toString(length)),
+ e);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixSockets.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixSockets.java
new file mode 100644
index 000000000..6f8153d00
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixSockets.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd.agent.connector;
+
+import com.sun.jna.LastErrorException;
+import com.sun.jna.Library;
+import com.sun.jna.Structure;
+import com.sun.jna.platform.unix.LibCAPI;
+
+/**
+ * Low-level Unix/Linux JNA socket API.
+ */
+interface UnixSockets extends LibCAPI, Library {
+
+ /**
+ * Library to load. These functions live in libc.
+ */
+ String LIBRARY_NAME = "c"; //$NON-NLS-1$
+
+ /**
+ * Command to set the close-on-exec flag on a file descriptor via
+ * {@link #fcntl(int, int, int)}.
+ */
+ int F_SETFD = 2;
+
+ /**
+ * Specifies that a file descriptor shall not be inherited by child
+ * processes.
+ */
+ int FD_CLOEXEC = 1;
+
+ /**
+ * Creates a socket and returns a file descriptor for it.
+ *
+ * @param domain
+ * socket domain; use {@link Sockets#AF_UNIX}
+ * @param type
+ * socket type; use {@link Sockets#SOCK_STREAM}
+ * @param protocol
+ * socket communication protocol; use
+ * {@link Sockets#DEFAULT_PROTOCOL}.
+ * @return file descriptor for the socket; should be closed eventually, or
+ * -1 on error.
+ * @throws LastErrorException
+ * on errors
+ * @see LibCAPI#close(int)
+ */
+ int socket(int domain, int type, int protocol) throws LastErrorException;
+
+ /**
+ * Simple binding to fcntl; used to set the FD_CLOEXEC flag. On OS X, we
+ * cannot include SOCK_CLOEXEC in the socket() call.
+ *
+ * @param fd
+ * file descriptor to operate on
+ * @param command
+ * set to {@link #F_SETFD}
+ * @param flag
+ * zero to clear the close-on-exec flag, {@link #FD_CLOEXEC} to
+ * set it
+ * @return -1 on error, otherwise a value >= 0
+ * @throws LastErrorException
+ */
+ int fcntl(int fd, int command, int flag) throws LastErrorException;
+
+ /**
+ * Connects a file descriptor, which must refer to a socket, to a
+ * {@link Sockets.SockAddr}.
+ *
+ * @param fd
+ * file descriptor of the socket, as returned by
+ * {@link #socket(int, int, int)}
+ * @param addr
+ * address to connect to
+ * @param addrLen
+ * Length of {@code addr}, use {@link Structure#size()}
+ * @return 0 on success; -1 otherwise
+ * @throws LastErrorException
+ * on errors
+ */
+ int connect(int fd, Sockets.SockAddr addr, int addrLen)
+ throws LastErrorException;
+
+ /**
+ * Read data from a file descriptor.
+ *
+ * @param fd
+ * file descriptor to read from
+ * @param buf
+ * buffer to read into
+ * @param bufLen
+ * maximum number of bytes to read; at most length of {@code buf}
+ * @return number of bytes actually read; zero for EOF, -1 on error
+ * @throws LastErrorException
+ * on errors
+ */
+ LibCAPI.ssize_t read(int fd, byte[] buf, LibCAPI.size_t bufLen)
+ throws LastErrorException;
+
+ /**
+ * Write data to a file descriptor.
+ *
+ * @param fd
+ * file descriptor to write to
+ * @param data
+ * data to write
+ * @param dataLen
+ * number of bytes to write
+ * @return number of bytes actually written; -1 on error
+ * @throws LastErrorException
+ * on errors
+ */
+ LibCAPI.ssize_t write(int fd, byte[] data, LibCAPI.size_t dataLen)
+ throws LastErrorException;
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
index b9a7ba9dc..e5a66f1ee 100644
--- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -29,6 +29,7 @@ Import-Package: org.apache.sshd.client.config.hosts;version="[2.7.0,2.8.0)",
org.eclipse.jgit.lib;version="[6.0.0,6.1.0)",
org.eclipse.jgit.transport;version="[6.0.0,6.1.0)",
org.eclipse.jgit.transport.sshd;version="[6.0.0,6.1.0)",
+ org.eclipse.jgit.transport.sshd.agent;version="[6.0.0,6.1.0)",
org.eclipse.jgit.util;version="[6.0.0,6.1.0)",
org.hamcrest;version="[1.1.0,3.0.0)",
org.junit;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java
index 0ad96b9ac..eef0402b0 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java
@@ -26,12 +26,15 @@ public class ApacheSshProtocol2Test extends SshBasicTestBase {
@Override
protected SshSessionFactory createSessionFactory() {
- SshdSessionFactory result = new SshdSessionFactory(new JGitKeyCache(),
- null);
- // The home directory is mocked at this point!
- result.setHomeDirectory(FS.DETECTED.userHome());
- result.setSshDirectory(sshDir);
- return result;
+ return new SshdSessionFactoryBuilder()
+ // No proxies in tests
+ .setProxyDataFactory(null)
+ // No ssh-agent in tests
+ .setConnectorFactory(null)
+ // The home directory is mocked at this point!
+ .setHomeDirectory(FS.DETECTED.userHome())
+ .setSshDirectory(sshDir)
+ .build(new JGitKeyCache());
}
@Override
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
index c1f5fef3c..85626d8ee 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
@@ -65,12 +65,15 @@ public class ApacheSshTest extends SshTestBase {
@Override
protected SshSessionFactory createSessionFactory() {
- SshdSessionFactory result = new SshdSessionFactory(new JGitKeyCache(),
- null);
- // The home directory is mocked at this point!
- result.setHomeDirectory(FS.DETECTED.userHome());
- result.setSshDirectory(sshDir);
- return result;
+ return new SshdSessionFactoryBuilder()
+ // No proxies in tests
+ .setProxyDataFactory(null)
+ // No ssh-agent in tests
+ .setConnectorFactory(null)
+ // The home directory is mocked at this point!
+ .setHomeDirectory(FS.DETECTED.userHome())
+ .setSshDirectory(sshDir)
+ .build(new JGitKeyCache());
}
@Override
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/NoFilesSshBuilderTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/NoFilesSshBuilderTest.java
index 9d64adc95..fd51e0cf4 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/NoFilesSshBuilderTest.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/NoFilesSshBuilderTest.java
@@ -50,6 +50,10 @@ public class NoFilesSshBuilderTest extends SshTestHarness {
@Override
protected SshSessionFactory createSessionFactory() {
return new SshdSessionFactoryBuilder() //
+ // No proxies in tests
+ .setProxyDataFactory(null)
+ // No ssh-agent in tests
+ .setConnectorFactory(null)
.setConfigStoreFactory((h, f, u) -> null)
.setDefaultKeysProvider(f -> new KeyAuthenticator())
.setServerKeyDatabase((h, s) -> new ServerKeyDatabase() {
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/NoFilesSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/NoFilesSshTest.java
index 7b6e508c3..04c1c605d 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/NoFilesSshTest.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/NoFilesSshTest.java
@@ -33,6 +33,7 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
import org.eclipse.jgit.util.FS;
import org.junit.After;
import org.junit.Test;
@@ -80,6 +81,12 @@ public boolean accept(String connectAddress,
};
}
+ @Override
+ protected ConnectorFactory getConnectorFactory() {
+ // No ssh-agent in tests
+ return null;
+ }
+
@Override
protected Iterable getDefaultKeys(File dir) {
// This would work for this simple test case:
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
index 08da18f5a..c082a9a96 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
@@ -12,8 +12,12 @@
import static java.text.MessageFormat.format;
import static org.eclipse.jgit.transport.SshConstants.PUBKEY_ACCEPTED_ALGORITHMS;
+import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
+import org.apache.sshd.client.auth.pubkey.KeyAgentIdentity;
+import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey;
import org.apache.sshd.client.config.hosts.HostConfigEntry;
import org.apache.sshd.client.session.ClientSession;
@@ -38,7 +42,7 @@ public void init(ClientSession rawSession, String service)
throw new IllegalStateException("Wrong session type: " //$NON-NLS-1$
+ rawSession.getClass().getCanonicalName());
}
- JGitClientSession session = ((JGitClientSession) rawSession);
+ JGitClientSession session = (JGitClientSession) rawSession;
HostConfigEntry hostConfig = session.getHostConfigEntry();
// Set signature algorithms for public key authentication
String pubkeyAlgos = hostConfig.getProperty(PUBKEY_ACCEPTED_ALGORITHMS);
@@ -60,5 +64,48 @@ public void init(ClientSession rawSession, String service)
// If we don't set signature factories here, the default ones from the
// session will be used.
super.init(session, service);
+ // In sshd 2.7.0, we end up now with a key iterator that uses keys
+ // provided by an ssh-agent even if IdentitiesOnly is true. So if
+ // needed, filter out any KeyAgentIdentity.
+ if (hostConfig.isIdentitiesOnly()) {
+ Iterator original = keys;
+ // The original iterator will already have gotten the identities
+ // from the agent. Unfortunately there's nothing we can do about
+ // that; it'll have to be fixed upstream. (As will, ultimately,
+ // respecting isIdentitiesOnly().) At least we can simply not
+ // use the keys the agent provided.
+ //
+ // See https://issues.apache.org/jira/browse/SSHD-1218
+ keys = new Iterator<>() {
+
+ private PublicKeyIdentity value;
+
+ @Override
+ public boolean hasNext() {
+ if (value != null) {
+ return true;
+ }
+ PublicKeyIdentity next = null;
+ while (original.hasNext()) {
+ next = original.next();
+ if (!(next instanceof KeyAgentIdentity)) {
+ value = next;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public PublicKeyIdentity next() {
+ if (hasNext()) {
+ PublicKeyIdentity result = value;
+ value = null;
+ return result;
+ }
+ throw new NoSuchElementException();
+ }
+ };
+ }
}
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
index da99f56cb..336418009 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
@@ -63,6 +63,7 @@
import org.eclipse.jgit.transport.SshConstants;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.sshd.agent.Connector;
import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
import org.eclipse.jgit.util.FS;
@@ -105,9 +106,9 @@ public SshdSessionFactory() {
/**
* Creates a new {@link SshdSessionFactory} using the given {@link KeyCache}
- * and {@link ProxyDataFactory}. The {@code keyCache} is used for all sessions
- * created through this session factory; cached keys are destroyed when the
- * session factory is {@link #close() closed}.
+ * and {@link ProxyDataFactory}. The {@code keyCache} is used for all
+ * sessions created through this session factory; cached keys are destroyed
+ * when the session factory is {@link #close() closed}.
*
* Caching ssh keys in memory for an extended period of time is generally
* considered bad practice, but there may be circumstances where using a
@@ -117,13 +118,21 @@ public SshdSessionFactory() {
* to use a {@link #createKeyPasswordProvider(CredentialsProvider)
* KeyPasswordProvider} that has access to some secure storage and can save
* and retrieve passwords from there without user interaction. Another
- * approach is to use an ssh agent.
+ * approach is to use an SSH agent.
*
*
* Note that the underlying ssh library (Apache MINA sshd) may or may not
* keep ssh keys in memory for unspecified periods of time irrespective of
* the use of a {@link KeyCache}.
*
+ *
+ * By default, the factory uses the {@link java.util.ServiceLoader} to find
+ * a {@link ConnectorFactory} for creating a {@link Connector} to connect to
+ * a running SSH agent. If it finds one, the SSH agent is used in publickey
+ * authentication. If there is none, no SSH agent will ever be contacted.
+ * Note that one can define {@code IdentitiesOnly yes} for a host entry in
+ * the {@code ~/.ssh/config} file to bypass the SSH agent in any case.
+ *
*
* @param keyCache
* {@link KeyCache} to use for caching ssh keys, or {@code null}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java
index b6da0866a..d8dfbfc94 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/Connector.java
@@ -16,6 +16,7 @@
* Simple interface for connecting to something and making RPC-style
* request-reply calls.
*
+ * @see ConnectorFactory
* @since 6.0
*/
public interface Connector extends Closeable {
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
index fa725ab85..b351d89ef 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
@@ -15,7 +15,10 @@
import org.eclipse.jgit.annotations.NonNull;
/**
- * A factory for creating {@link Connector}s.
+ * A factory for creating {@link Connector}s. This is a service provider
+ * interface; implementations are discovered via the
+ * {@link java.util.ServiceLoader}, or can be set explicitly on a
+ * {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
*
* @since 6.0
*/
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/package-info.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/package-info.java
new file mode 100644
index 000000000..71ca43f3d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Service provider interfaces for connecting to an SSH agent. Implementations
+ * are discovered via the {@link java.util.ServiceLoader}, or can be set
+ * explicitly on a {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
+ */
+package org.eclipse.jgit.transport.sshd.agent;
diff --git a/pom.xml b/pom.xml
index 0833c7150..11789693f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -925,6 +925,7 @@
org.eclipse.jgit.http.apache
org.eclipse.jgit.http.server
org.eclipse.jgit.ssh.apache
+ org.eclipse.jgit.ssh.apache.agent
org.eclipse.jgit.ssh.jsch
org.eclipse.jgit.pgm
org.eclipse.jgit.lfs
diff --git a/tools/BUILD b/tools/BUILD
index 4769f4222..0c6b8ece7 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -121,6 +121,7 @@ package_group(
"//org.eclipse.jgit.pgm.test/...",
"//org.eclipse.jgit.pgm/...",
"//org.eclipse.jgit.ssh.apache/...",
+ "//org.eclipse.jgit.ssh.apache.agent/...",
"//org.eclipse.jgit.test/...",
"//org.eclipse.jgit.ui/...",
"//org.eclipse.jgit/...",
From c6d48ab2f8ac776eb0eb8b385a869d9c0ab9dbf8 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Wed, 13 Oct 2021 22:21:52 +0200
Subject: [PATCH 10/24] [test] test OpenSshConfigFile directly, not via the
JSch config
This is a prerequisite for removing the JSch support bundle; otherwise
OpenSshConfigFile would be left without tests.
Copy OpenSshConfigTest from the JSch support bundle and adapt all tests
to perform the equivalent checks on OpenSshConfigFile directly. Add a
new lookupDefault() method to the SshConfigStore interface and implement
it so that it behaves the same and the tests work identically.
Change-Id: I046abd9197a8484003e77005024e5d973456f1a3
---
org.eclipse.jgit.test/META-INF/MANIFEST.MF | 1 +
.../transport/ssh/OpenSshConfigFileTest.java | 605 ++++++++++++++++++
org.eclipse.jgit/META-INF/MANIFEST.MF | 3 +-
.../transport/ssh/OpenSshConfigFile.java | 38 +-
.../jgit/transport/SshConfigStore.java | 42 ++
5 files changed, 683 insertions(+), 6 deletions(-)
create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index b550bea59..91f8f74b6 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -43,6 +43,7 @@ Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
org.eclipse.jgit.internal.transport.connectivity;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.transport.http;version="[6.0.0,6.1.0)",
org.eclipse.jgit.internal.transport.parser;version="[6.0.0,6.1.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[6.0.0,6.1.0)",
org.eclipse.jgit.junit;version="[6.0.0,6.1.0)",
org.eclipse.jgit.junit.time;version="[6.0.0,6.1.0)",
org.eclipse.jgit.lfs;version="[6.0.0,6.1.0)",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java
new file mode 100644
index 000000000..27bae3747
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2008, 2021 Google Inc. and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.transport.ssh;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.transport.SshConfigStore.HostConfig;
+import org.eclipse.jgit.transport.SshConstants;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.SystemReader;
+import org.junit.Before;
+import org.junit.Test;
+
+public class OpenSshConfigFileTest extends RepositoryTestCase {
+
+ private File home;
+
+ private File configFile;
+
+ private OpenSshConfigFile osc;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ home = new File(trash, "home");
+ FileUtils.mkdir(home);
+
+ configFile = new File(new File(home, ".ssh"), Constants.CONFIG);
+ FileUtils.mkdir(configFile.getParentFile());
+
+ mockSystemReader.setProperty(Constants.OS_USER_NAME_KEY, "jex_junit");
+ mockSystemReader.setProperty("TST_VAR", "TEST");
+ osc = new OpenSshConfigFile(home, configFile, "jex_junit");
+ }
+
+ private void config(String data) throws IOException {
+ FS fs = FS.DETECTED;
+ long resolution = FS.getFileStoreAttributes(configFile.toPath())
+ .getFsTimestampResolution().toNanos();
+ Instant lastMtime = fs.lastModifiedInstant(configFile);
+ do {
+ try (final OutputStreamWriter fw = new OutputStreamWriter(
+ new FileOutputStream(configFile), UTF_8)) {
+ fw.write(data);
+ TimeUnit.NANOSECONDS.sleep(resolution);
+ } catch (InterruptedException e) {
+ Thread.interrupted();
+ }
+ } while (lastMtime.equals(fs.lastModifiedInstant(configFile)));
+ }
+
+ private HostConfig lookup(String hostname) {
+ return osc.lookupDefault(hostname, 0, null);
+ }
+
+ private void assertHost(String expected, HostConfig h) {
+ assertEquals(expected, h.getValue(SshConstants.HOST_NAME));
+ }
+
+ private void assertUser(String expected, HostConfig h) {
+ assertEquals(expected, h.getValue(SshConstants.USER));
+ }
+
+ private void assertPort(int expected, HostConfig h) {
+ assertEquals(expected,
+ OpenSshConfigFile.positive(h.getValue(SshConstants.PORT)));
+ }
+
+ private void assertIdentity(File expected, HostConfig h) {
+ String actual = h.getValue(SshConstants.IDENTITY_FILE);
+ if (expected == null) {
+ assertNull(actual);
+ } else {
+ assertEquals(expected, new File(actual));
+ }
+ }
+
+ private void assertAttempts(int expected, HostConfig h) {
+ assertEquals(expected, OpenSshConfigFile
+ .positive(h.getValue(SshConstants.CONNECTION_ATTEMPTS)));
+ }
+
+ @Test
+ public void testNoConfig() {
+ final HostConfig h = lookup("repo.or.cz");
+ assertNotNull(h);
+ assertHost("repo.or.cz", h);
+ assertUser("jex_junit", h);
+ assertPort(22, h);
+ assertAttempts(1, h);
+ assertIdentity(null, h);
+ }
+
+ @Test
+ public void testSeparatorParsing() throws Exception {
+ config("Host\tfirst\n" +
+ "\tHostName\tfirst.tld\n" +
+ "\n" +
+ "Host second\n" +
+ " HostName\tsecond.tld\n" +
+ "Host=third\n" +
+ "HostName=third.tld\n\n\n" +
+ "\t Host = fourth\n\n\n" +
+ " \t HostName\t=fourth.tld\n" +
+ "Host\t = last\n" +
+ "HostName \t last.tld");
+ assertNotNull(lookup("first"));
+ assertHost("first.tld", lookup("first"));
+ assertNotNull(lookup("second"));
+ assertHost("second.tld", lookup("second"));
+ assertNotNull(lookup("third"));
+ assertHost("third.tld", lookup("third"));
+ assertNotNull(lookup("fourth"));
+ assertHost("fourth.tld", lookup("fourth"));
+ assertNotNull(lookup("last"));
+ assertHost("last.tld", lookup("last"));
+ }
+
+ @Test
+ public void testQuoteParsing() throws Exception {
+ config("Host \"good\"\n" +
+ " HostName=\"good.tld\"\n" +
+ " Port=\"6007\"\n" +
+ " User=\"gooduser\"\n" +
+ "Host multiple unquoted and \"quoted\" \"hosts\"\n" +
+ " Port=\"2222\"\n" +
+ "Host \"spaced\"\n" +
+ "# Bad host name, but testing preservation of spaces\n" +
+ " HostName=\" spaced\ttld \"\n" +
+ "# Misbalanced quotes\n" +
+ "Host \"bad\"\n" +
+ "# OpenSSH doesn't allow this but ...\n" +
+ " HostName=bad.tld\"\n");
+ assertHost("good.tld", lookup("good"));
+ assertUser("gooduser", lookup("good"));
+ assertPort(6007, lookup("good"));
+ assertPort(2222, lookup("multiple"));
+ assertPort(2222, lookup("quoted"));
+ assertPort(2222, lookup("and"));
+ assertPort(2222, lookup("unquoted"));
+ assertPort(2222, lookup("hosts"));
+ assertHost(" spaced\ttld ", lookup("spaced"));
+ assertHost("bad.tld\"", lookup("bad"));
+ }
+
+ @Test
+ public void testCaseInsensitiveKeyLookup() throws Exception {
+ config("Host orcz\n" + "Port 29418\n"
+ + "\tHostName repo.or.cz\nStrictHostKeyChecking yes\n");
+ final HostConfig c = lookup("orcz");
+ String exactCase = c.getValue("StrictHostKeyChecking");
+ assertEquals("yes", exactCase);
+ assertEquals(exactCase, c.getValue("stricthostkeychecking"));
+ assertEquals(exactCase, c.getValue("STRICTHOSTKEYCHECKING"));
+ assertEquals(exactCase, c.getValue("sTrIcThostKEYcheckING"));
+ assertNull(c.getValue("sTrIcThostKEYcheckIN"));
+ }
+
+ @Test
+ public void testAlias_DoesNotMatch() throws Exception {
+ config("Host orcz\n" + "Port 29418\n"
+ + "\tHostName repo.or.cz\n");
+ final HostConfig h = lookup("repo.or.cz");
+ assertNotNull(h);
+ assertHost("repo.or.cz", h);
+ assertUser("jex_junit", h);
+ assertPort(22, h);
+ assertIdentity(null, h);
+ final HostConfig h2 = lookup("orcz");
+ assertHost("repo.or.cz", h);
+ assertUser("jex_junit", h);
+ assertPort(29418, h2);
+ assertIdentity(null, h);
+ }
+
+ @Test
+ public void testAlias_OptionsSet() throws Exception {
+ config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\tPort 2222\n"
+ + "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n"
+ + "\tForwardX11 no\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertHost("repo.or.cz", h);
+ assertUser("jex", h);
+ assertPort(2222, h);
+ assertIdentity(new File(home, ".ssh/id_jex"), h);
+ }
+
+ @Test
+ public void testAlias_OptionsKeywordCaseInsensitive() throws Exception {
+ config("hOsT orcz\n" + "\thOsTnAmE repo.or.cz\n" + "\tPORT 2222\n"
+ + "\tuser jex\n" + "\tidentityfile .ssh/id_jex\n"
+ + "\tForwardX11 no\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertHost("repo.or.cz", h);
+ assertUser("jex", h);
+ assertPort(2222, h);
+ assertIdentity(new File(home, ".ssh/id_jex"), h);
+ }
+
+ @Test
+ public void testAlias_OptionsInherit() throws Exception {
+ config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
+ + "\tHostName not.a.host.example.com\n" + "\tPort 2222\n"
+ + "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n"
+ + "\tForwardX11 no\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertHost("repo.or.cz", h);
+ assertUser("jex", h);
+ assertPort(2222, h);
+ assertIdentity(new File(home, ".ssh/id_jex"), h);
+ }
+
+ @Test
+ public void testAlias_PreferredAuthenticationsDefault() throws Exception {
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertNull(h.getValue(SshConstants.PREFERRED_AUTHENTICATIONS));
+ }
+
+ @Test
+ public void testAlias_PreferredAuthentications() throws Exception {
+ config("Host orcz\n" + "\tPreferredAuthentications publickey\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertEquals("publickey",
+ h.getValue(SshConstants.PREFERRED_AUTHENTICATIONS));
+ }
+
+ @Test
+ public void testAlias_InheritPreferredAuthentications() throws Exception {
+ config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
+ + "\tPreferredAuthentications publickey, hostbased\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertEquals("publickey,hostbased",
+ h.getValue(SshConstants.PREFERRED_AUTHENTICATIONS));
+ }
+
+ @Test
+ public void testAlias_BatchModeDefault() throws Exception {
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertNull(h.getValue(SshConstants.BATCH_MODE));
+ }
+
+ @Test
+ public void testAlias_BatchModeYes() throws Exception {
+ config("Host orcz\n" + "\tBatchMode yes\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertTrue(OpenSshConfigFile.flag(h.getValue(SshConstants.BATCH_MODE)));
+ }
+
+ @Test
+ public void testAlias_InheritBatchMode() throws Exception {
+ config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
+ + "\tBatchMode yes\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertTrue(OpenSshConfigFile.flag(h.getValue(SshConstants.BATCH_MODE)));
+ }
+
+ @Test
+ public void testAlias_ConnectionAttemptsDefault() throws Exception {
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertAttempts(1, h);
+ }
+
+ @Test
+ public void testAlias_ConnectionAttempts() throws Exception {
+ config("Host orcz\n" + "\tConnectionAttempts 5\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertAttempts(5, h);
+ }
+
+ @Test
+ public void testAlias_invalidConnectionAttempts() throws Exception {
+ config("Host orcz\n" + "\tConnectionAttempts -1\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertAttempts(1, h);
+ }
+
+ @Test
+ public void testAlias_badConnectionAttempts() throws Exception {
+ config("Host orcz\n" + "\tConnectionAttempts xxx\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertAttempts(1, h);
+ }
+
+ @Test
+ public void testDefaultBlock() throws Exception {
+ config("ConnectionAttempts 5\n\nHost orcz\nConnectionAttempts 3\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertAttempts(5, h);
+ }
+
+ @Test
+ public void testHostCaseInsensitive() throws Exception {
+ config("hOsT orcz\nConnectionAttempts 3\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertAttempts(3, h);
+ }
+
+ @Test
+ public void testListValueSingle() throws Exception {
+ config("Host orcz\nUserKnownHostsFile /foo/bar\n");
+ final HostConfig c = lookup("orcz");
+ assertNotNull(c);
+ assertEquals("/foo/bar", c.getValue("UserKnownHostsFile"));
+ }
+
+ @Test
+ public void testListValueMultiple() throws Exception {
+ // Tilde expansion occurs within the parser
+ config("Host orcz\nUserKnownHostsFile \"~/foo/ba z\" /foo/bar \n");
+ final HostConfig c = lookup("orcz");
+ assertNotNull(c);
+ assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(),
+ "/foo/bar" },
+ c.getValues("UserKnownHostsFile").toArray());
+ }
+
+ @Test
+ public void testRepeatedLookupsWithModification() throws Exception {
+ config("Host orcz\n" + "\tConnectionAttempts -1\n");
+ final HostConfig h1 = lookup("orcz");
+ assertNotNull(h1);
+ assertAttempts(1, h1);
+ config("Host orcz\n" + "\tConnectionAttempts 5\n");
+ final HostConfig h2 = lookup("orcz");
+ assertNotNull(h2);
+ assertNotSame(h1, h2);
+ assertAttempts(5, h2);
+ assertAttempts(1, h1);
+ assertNotSame(h1, h2);
+ }
+
+ @Test
+ public void testIdentityFile() throws Exception {
+ config("Host orcz\nIdentityFile \"~/foo/ba z\"\nIdentityFile /foo/bar");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ // Does tilde replacement
+ assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(),
+ "/foo/bar" },
+ h.getValues(SshConstants.IDENTITY_FILE).toArray());
+ }
+
+ @Test
+ public void testMultiIdentityFile() throws Exception {
+ config("IdentityFile \"~/foo/ba z\"\nHost orcz\nIdentityFile /foo/bar\nHOST *\nIdentityFile /foo/baz");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(),
+ "/foo/bar", "/foo/baz" },
+ h.getValues(SshConstants.IDENTITY_FILE).toArray());
+ }
+
+ @Test
+ public void testNegatedPattern() throws Exception {
+ config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST !*.or.cz\nIdentityFile /foo/baz");
+ final HostConfig h = lookup("repo.or.cz");
+ assertNotNull(h);
+ assertIdentity(new File(home, "foo/bar"), h);
+ assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath() },
+ h.getValues(SshConstants.IDENTITY_FILE).toArray());
+ }
+
+ @Test
+ public void testPattern() throws Exception {
+ config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz");
+ final HostConfig h = lookup("repo.or.cz");
+ assertNotNull(h);
+ assertIdentity(new File(home, "foo/bar"), h);
+ assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath(),
+ "/foo/baz" },
+ h.getValues(SshConstants.IDENTITY_FILE).toArray());
+ }
+
+ @Test
+ public void testMultiHost() throws Exception {
+ config("Host orcz *.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz");
+ final HostConfig h1 = lookup("repo.or.cz");
+ assertNotNull(h1);
+ assertIdentity(new File(home, "foo/bar"), h1);
+ assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath(),
+ "/foo/baz" },
+ h1.getValues(SshConstants.IDENTITY_FILE).toArray());
+ final HostConfig h2 = lookup("orcz");
+ assertNotNull(h2);
+ assertIdentity(new File(home, "foo/bar"), h2);
+ assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath() },
+ h2.getValues(SshConstants.IDENTITY_FILE).toArray());
+ }
+
+ @Test
+ public void testEqualsSign() throws Exception {
+ config("Host=orcz\n\tConnectionAttempts = 5\n\tUser=\t foobar\t\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertAttempts(5, h);
+ assertUser("foobar", h);
+ }
+
+ @Test
+ public void testMissingArgument() throws Exception {
+ config("Host=orcz\n\tSendEnv\nIdentityFile\t\nForwardX11\n\tUser=\t foobar\t\n");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertUser("foobar", h);
+ assertEquals("[]", h.getValues("SendEnv").toString());
+ assertIdentity(null, h);
+ assertNull(h.getValue("ForwardX11"));
+ }
+
+ @Test
+ public void testHomeDirUserReplacement() throws Exception {
+ config("Host=orcz\n\tIdentityFile %d/.ssh/%u_id_dsa");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertIdentity(new File(new File(home, ".ssh"), "jex_junit_id_dsa"), h);
+ }
+
+ @Test
+ public void testHostnameReplacement() throws Exception {
+ config("Host=orcz\nHost *.*\n\tHostname %h\nHost *\n\tHostname %h.example.org");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertHost("orcz.example.org", h);
+ }
+
+ @Test
+ public void testRemoteUserReplacement() throws Exception {
+ config("Host=orcz\n\tUser foo\n" + "Host *.*\n\tHostname %h\n"
+ + "Host *\n\tHostname %h.ex%%20ample.org\n\tIdentityFile ~/.ssh/%h_%r_id_dsa");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertIdentity(
+ new File(new File(home, ".ssh"),
+ "orcz.ex%20ample.org_foo_id_dsa"),
+ h);
+ }
+
+ @Test
+ public void testLocalhostFQDNReplacement() throws Exception {
+ String localhost = SystemReader.getInstance().getHostname();
+ config("Host=orcz\n\tIdentityFile ~/.ssh/%l_id_dsa");
+ final HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertIdentity(
+ new File(new File(home, ".ssh"), localhost + "_id_dsa"),
+ h);
+ }
+
+ @Test
+ public void testPubKeyAcceptedAlgorithms() throws Exception {
+ config("Host=orcz\n\tPubkeyAcceptedAlgorithms ^ssh-rsa");
+ HostConfig h = lookup("orcz");
+ assertEquals("^ssh-rsa",
+ h.getValue(SshConstants.PUBKEY_ACCEPTED_ALGORITHMS));
+ assertEquals("^ssh-rsa", h.getValue("PubkeyAcceptedKeyTypes"));
+ }
+
+ @Test
+ public void testPubKeyAcceptedKeyTypes() throws Exception {
+ config("Host=orcz\n\tPubkeyAcceptedKeyTypes ^ssh-rsa");
+ HostConfig h = lookup("orcz");
+ assertEquals("^ssh-rsa",
+ h.getValue(SshConstants.PUBKEY_ACCEPTED_ALGORITHMS));
+ assertEquals("^ssh-rsa", h.getValue("PubkeyAcceptedKeyTypes"));
+ }
+
+ @Test
+ public void testEolComments() throws Exception {
+ config("#Comment\nHost=orcz #Comment\n\tPubkeyAcceptedAlgorithms ^ssh-rsa # Comment\n#Comment");
+ HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertEquals("^ssh-rsa",
+ h.getValue(SshConstants.PUBKEY_ACCEPTED_ALGORITHMS));
+ }
+
+ @Test
+ public void testEnVarSubstitution() throws Exception {
+ config("Host orcz\nIdentityFile /tmp/${TST_VAR}\n"
+ + "CertificateFile /tmp/${}/foo\nUser ${TST_VAR}\nIdentityAgent /tmp/${TST_VAR/bar");
+ HostConfig h = lookup("orcz");
+ assertNotNull(h);
+ assertEquals("/tmp/TEST",
+ h.getValue(SshConstants.IDENTITY_FILE));
+ // No variable name
+ assertEquals("/tmp/${}/foo", h.getValue(SshConstants.CERTIFICATE_FILE));
+ // User doesn't get env var substitution:
+ assertUser("${TST_VAR}", h);
+ // Unterminated:
+ assertEquals("/tmp/${TST_VAR/bar",
+ h.getValue(SshConstants.IDENTITY_AGENT));
+ }
+
+ @Test
+ public void testNegativeMatch() throws Exception {
+ config("Host foo.bar !foobar.baz *.baz\n" + "Port 29418\n");
+ HostConfig h = lookup("foo.bar");
+ assertNotNull(h);
+ assertPort(29418, h);
+ h = lookup("foobar.baz");
+ assertNotNull(h);
+ assertPort(22, h);
+ h = lookup("foo.baz");
+ assertNotNull(h);
+ assertPort(29418, h);
+ }
+
+ @Test
+ public void testNegativeMatch2() throws Exception {
+ // Negative match after the positive match.
+ config("Host foo.bar *.baz !foobar.baz\n" + "Port 29418\n");
+ HostConfig h = lookup("foo.bar");
+ assertNotNull(h);
+ assertPort(29418, h);
+ h = lookup("foobar.baz");
+ assertNotNull(h);
+ assertPort(22, h);
+ h = lookup("foo.baz");
+ assertNotNull(h);
+ assertPort(29418, h);
+ }
+
+ @Test
+ public void testNoMatch() throws Exception {
+ config("Host !host1 !host2\n" + "Port 29418\n");
+ HostConfig h = lookup("host1");
+ assertNotNull(h);
+ assertPort(22, h);
+ h = lookup("host2");
+ assertNotNull(h);
+ assertPort(22, h);
+ h = lookup("host3");
+ assertNotNull(h);
+ assertPort(22, h);
+ }
+
+ @Test
+ public void testMultipleMatch() throws Exception {
+ config("Host foo.bar\nPort 29418\nIdentityFile /foo\n\n"
+ + "Host *.bar\nPort 22\nIdentityFile /bar\n"
+ + "Host foo.bar\nPort 47\nIdentityFile /baz\n");
+ HostConfig h = lookup("foo.bar");
+ assertNotNull(h);
+ assertPort(29418, h);
+ assertArrayEquals(new Object[] { "/foo", "/bar", "/baz" },
+ h.getValues(SshConstants.IDENTITY_FILE).toArray());
+ }
+
+ @Test
+ public void testWhitespace() throws Exception {
+ config("Host foo \tbar baz\nPort 29418\n");
+ HostConfig h = lookup("foo");
+ assertNotNull(h);
+ assertPort(29418, h);
+ h = lookup("bar");
+ assertNotNull(h);
+ assertPort(29418, h);
+ h = lookup("baz");
+ assertNotNull(h);
+ assertPort(29418, h);
+ h = lookup("\tbar");
+ assertNotNull(h);
+ assertPort(22, h);
+ }
+}
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index a6f9e4814..590567fe7 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -111,7 +111,8 @@ Export-Package: org.eclipse.jgit.annotations;version="6.0.0",
org.eclipse.jgit.test",
org.eclipse.jgit.internal.transport.ssh;version="6.0.0";
x-friends:="org.eclipse.jgit.ssh.apache,
- org.eclipse.jgit.ssh.jsch",
+ org.eclipse.jgit.ssh.jsch,
+ org.eclipse.jgit.test",
org.eclipse.jgit.lib;version="6.0.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util.sha1,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
index a7a143328..4cffcc5dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -151,6 +151,18 @@ public OpenSshConfigFile(@NonNull File home, @NonNull File config,
@NonNull
public HostEntry lookup(@NonNull String hostName, int port,
String userName) {
+ return lookup(hostName, port, userName, false);
+ }
+
+ @Override
+ @NonNull
+ public HostEntry lookupDefault(@NonNull String hostName, int port,
+ String userName) {
+ return lookup(hostName, port, userName, true);
+ }
+
+ private HostEntry lookup(@NonNull String hostName, int port,
+ String userName, boolean fillDefaults) {
final State cache = refresh();
String cacheKey = toCacheKey(hostName, port, userName);
HostEntry h = cache.hosts.get(cacheKey);
@@ -169,7 +181,8 @@ public HostEntry lookup(@NonNull String hostName, int port,
}
});
}
- fullConfig.substitute(hostName, port, userName, localUserName, home);
+ fullConfig.substitute(hostName, port, userName, localUserName, home,
+ fillDefaults);
cache.hosts.put(cacheKey, fullConfig);
return fullConfig;
}
@@ -725,12 +738,12 @@ private List replaceTilde(List values, File home) {
}
void substitute(String originalHostName, int port, String userName,
- String localUserName, File home) {
- int p = port >= 0 ? port : positive(getValue(SshConstants.PORT));
- if (p < 0) {
+ String localUserName, File home, boolean fillDefaults) {
+ int p = port > 0 ? port : positive(getValue(SshConstants.PORT));
+ if (p <= 0) {
p = SshConstants.SSH_DEFAULT_PORT;
}
- String u = userName != null && !userName.isEmpty() ? userName
+ String u = !StringUtils.isEmptyOrNull(userName) ? userName
: getValue(SshConstants.USER);
if (u == null || u.isEmpty()) {
u = localUserName;
@@ -747,6 +760,8 @@ void substitute(String originalHostName, int port, String userName,
options.put(SshConstants.HOST_NAME, hostName);
r.update('h', hostName);
}
+ } else if (fillDefaults) {
+ setValue(SshConstants.HOST_NAME, originalHostName);
}
if (multiOptions != null) {
List values = multiOptions
@@ -803,6 +818,19 @@ void substitute(String originalHostName, int port, String userName,
}
// Match is not implemented and would need to be done elsewhere
// anyway.
+ if (fillDefaults) {
+ String s = options.get(SshConstants.USER);
+ if (StringUtils.isEmptyOrNull(s)) {
+ options.put(SshConstants.USER, u);
+ }
+ if (positive(options.get(SshConstants.PORT)) <= 0) {
+ options.put(SshConstants.PORT, Integer.toString(p));
+ }
+ if (positive(
+ options.get(SshConstants.CONNECTION_ATTEMPTS)) <= 0) {
+ options.put(SshConstants.CONNECTION_ATTEMPTS, "1"); //$NON-NLS-1$
+ }
+ }
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigStore.java
index 04a4922bb..d98bd2307 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigStore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigStore.java
@@ -36,6 +36,48 @@ public interface SshConfigStore {
@NonNull
HostConfig lookup(@NonNull String hostName, int port, String userName);
+ /**
+ * Locate the configuration for a specific host request and if the
+ * configuration has no values for {@link SshConstants#HOST_NAME},
+ * {@link SshConstants#PORT}, {@link SshConstants#USER}, or
+ * {@link SshConstants#CONNECTION_ATTEMPTS}, fill those values with defaults
+ * from the arguments:
+ *
+ *
+ * ssh config key |
+ * value from argument |
+ *
+ *
+ * {@code HostName} |
+ * {@code hostName} |
+ *
+ *
+ * {@code Port} |
+ * {@code port > 0 ? port : 22} |
+ *
+ *
+ * {@code User} |
+ * {@code userName} |
+ *
+ *
+ * {@code ConnectionAttempts} |
+ * {@code 1} |
+ *
+ *
+ *
+ * @param hostName
+ * host name to look up
+ * @param port
+ * port number; <= 0 if none
+ * @param userName
+ * the user name, may be {@code null} or empty if none given
+ * @return the configuration for the requested name.
+ * @since 6.0
+ */
+ @NonNull
+ HostConfig lookupDefault(@NonNull String hostName, int port,
+ String userName);
+
/**
* A host entry from the ssh config. Any merging of global values and of
* several matching host entries, %-substitutions, and ~ replacement have
From 3a7db8b7820a3ed3ad2e8b6be90beb9042618e74 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Sun, 31 Oct 2021 17:29:04 +0100
Subject: [PATCH 11/24] Simplify SshdFtpChannel
Apache MINA sshd has simpler API for reading directories, and it has a
functional interface suitable for us. So no need to use our own
interface, or to deal with low-level abstractions like CloseableHandle.
Change-Id: Ic125c587535670504983f157a696b41ed6a76bb7
Signed-off-by: Thomas Wolf
---
.../META-INF/MANIFEST.MF | 1 +
.../jgit/transport/sshd/SshdSession.java | 84 +++++++------------
2 files changed, 30 insertions(+), 55 deletions(-)
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 339ba3aae..3e5feeb84 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -73,6 +73,7 @@ Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
org.apache.sshd.common.util.buffer;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.closeable;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.io;version="[2.7.0,2.8.0)",
+ org.apache.sshd.common.util.io.functors;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.io.resource;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.logging;version="[2.7.0,2.8.0)",
org.apache.sshd.common.util.net;version="[2.7.0,2.8.0)",
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
index 33b234b1f..fb7500ffd 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2020 Thomas Wolf and others
+ * Copyright (C) 2018, 2021 Thomas Wolf and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -28,7 +28,6 @@
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.regex.Pattern;
@@ -44,9 +43,9 @@
import org.apache.sshd.common.future.CloseFuture;
import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.functors.IOFunction;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.sftp.client.SftpClient;
-import org.apache.sshd.sftp.client.SftpClient.CloseableHandle;
import org.apache.sshd.sftp.client.SftpClient.CopyMode;
import org.apache.sshd.sftp.client.SftpClientFactory;
import org.apache.sshd.sftp.common.SftpException;
@@ -416,20 +415,6 @@ public void destroy() {
}
}
- /**
- * Helper interface like {@link Supplier}, but possibly raising an
- * {@link IOException}.
- *
- * @param
- * return type
- */
- @FunctionalInterface
- private interface FtpOperation {
-
- T call() throws IOException;
-
- }
-
private class SshdFtpChannel implements FtpChannel {
private SftpClient ftp;
@@ -485,9 +470,9 @@ private String absolute(String path) {
return path;
}
- private T map(FtpOperation op) throws IOException {
+ private T map(IOFunction op) throws IOException {
try {
- return op.call();
+ return op.apply(null);
} catch (IOException e) {
if (e instanceof SftpException) {
throw new FtpChannel.FtpException(e.getLocalizedMessage(),
@@ -499,7 +484,7 @@ private T map(FtpOperation op) throws IOException {
@Override
public void cd(String path) throws IOException {
- cwd = map(() -> ftp.canonicalPath(absolute(path)));
+ cwd = map(x -> ftp.canonicalPath(absolute(path)));
if (cwd.isEmpty()) {
cwd += '/';
}
@@ -512,39 +497,28 @@ public String pwd() throws IOException {
@Override
public Collection ls(String path) throws IOException {
- return map(() -> {
+ return map(x -> {
List result = new ArrayList<>();
- try (CloseableHandle handle = ftp.openDir(absolute(path))) {
- AtomicReference atEnd = new AtomicReference<>(
- Boolean.FALSE);
- while (!atEnd.get().booleanValue()) {
- List chunk = ftp.readDir(handle,
- atEnd);
- if (chunk == null) {
- break;
+ for (SftpClient.DirEntry remote : ftp.readDir(absolute(path))) {
+ result.add(new DirEntry() {
+
+ @Override
+ public String getFilename() {
+ return remote.getFilename();
}
- for (SftpClient.DirEntry remote : chunk) {
- result.add(new DirEntry() {
- @Override
- public String getFilename() {
- return remote.getFilename();
- }
-
- @Override
- public long getModifiedTime() {
- return remote.getAttributes()
- .getModifyTime().toMillis();
- }
-
- @Override
- public boolean isDirectory() {
- return remote.getAttributes().isDirectory();
- }
-
- });
+ @Override
+ public long getModifiedTime() {
+ return remote.getAttributes().getModifyTime()
+ .toMillis();
}
- }
+
+ @Override
+ public boolean isDirectory() {
+ return remote.getAttributes().isDirectory();
+ }
+
+ });
}
return result;
});
@@ -552,7 +526,7 @@ public boolean isDirectory() {
@Override
public void rmdir(String path) throws IOException {
- map(() -> {
+ map(x -> {
ftp.rmdir(absolute(path));
return null;
});
@@ -561,7 +535,7 @@ public void rmdir(String path) throws IOException {
@Override
public void mkdir(String path) throws IOException {
- map(() -> {
+ map(x -> {
ftp.mkdir(absolute(path));
return null;
});
@@ -569,17 +543,17 @@ public void mkdir(String path) throws IOException {
@Override
public InputStream get(String path) throws IOException {
- return map(() -> ftp.read(absolute(path)));
+ return map(x -> ftp.read(absolute(path)));
}
@Override
public OutputStream put(String path) throws IOException {
- return map(() -> ftp.write(absolute(path)));
+ return map(x -> ftp.write(absolute(path)));
}
@Override
public void rm(String path) throws IOException {
- map(() -> {
+ map(x -> {
ftp.remove(absolute(path));
return null;
});
@@ -587,7 +561,7 @@ public void rm(String path) throws IOException {
@Override
public void rename(String from, String to) throws IOException {
- map(() -> {
+ map(x -> {
String src = absolute(from);
String dest = absolute(to);
try {
From a92ff5369b30e4d90de0cd8ce444ac2ef7b40ed1 Mon Sep 17 00:00:00 2001
From: Matthias Sohn
Date: Fri, 5 Nov 2021 00:13:52 +0100
Subject: [PATCH 12/24] Update jetty to 9.4.44.v20210927
Change-Id: Iaa1478af0fe0ccfa1daf1cf44e4eef609e7ad8bb
---
WORKSPACE | 30 +++++++-------
.../org.eclipse.jgit.target/jgit-4.17.target | 40 +++++++++----------
.../org.eclipse.jgit.target/jgit-4.18.target | 40 +++++++++----------
.../org.eclipse.jgit.target/jgit-4.19.target | 40 +++++++++----------
.../org.eclipse.jgit.target/jgit-4.20.target | 40 +++++++++----------
.../org.eclipse.jgit.target/jgit-4.21.target | 40 +++++++++----------
.../projects/jetty-9.4.x.tpd | 38 +++++++++---------
pom.xml | 2 +-
8 files changed, 135 insertions(+), 135 deletions(-)
diff --git a/WORKSPACE b/WORKSPACE
index f4694b3ae..fb9212fa0 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -243,55 +243,55 @@ maven_jar(
sha1 = "69d9503ea0a40ee16f0bcdac7e3eaf83d0fa914a",
)
-JETTY_VER = "9.4.43.v20210629"
+JETTY_VER = "9.4.44.v20210927"
maven_jar(
name = "jetty-servlet",
artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VER,
- sha1 = "ee000c7dcdbe7b4ef94e3fa67be8f56a46915944",
- src_sha1 = "50236764fe1d3619ca07f346e148189c4f5b801a",
+ sha1 = "1cb43a0d74b7395c7207dbf3dc2ca97eac89f5fd",
+ src_sha1 = "2bbc54fc1835c963744a4e82ba2541e94fcbcf9b",
)
maven_jar(
name = "jetty-security",
artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
- sha1 = "ae1958da077c46bac61be9b8de2b45a3aa112353",
- src_sha1 = "6e5271e91da37e381f566e0db07ab4d936d86104",
+ sha1 = "ecb80b8e008daa46e95e5691b2611d4007922497",
+ src_sha1 = "d67d4705a08d9b76592b3e177e2bb1aac968d832",
)
maven_jar(
name = "jetty-server",
artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
- sha1 = "8ba04f6b5d00223983684a563a3edaa12282bcf0",
- src_sha1 = "51600567dbd082fb03feeb9c786f5e7cc9e0a17d",
+ sha1 = "0bf2de0d31925a8ca71ad80f721236850b636e0d",
+ src_sha1 = "3582cbf081cf3652f6507093585c2a0f3b8738bb",
)
maven_jar(
name = "jetty-http",
artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
- sha1 = "5171466337a6da7efbf317490b9c4574c0b4b640",
- src_sha1 = "52f477161fd0fc90869f48a145aa2c86624c496e",
+ sha1 = "37f0e30cdc02128e40d095ad63cb18e10ecb7726",
+ src_sha1 = "7f1a6e3ab54e541f33b8ed100d553d4034d2e3a9",
)
maven_jar(
name = "jetty-io",
artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
- sha1 = "acf672c64ac21851069c5b5b789e5c185a25933f",
- src_sha1 = "824d5cffce7a72af7c11d9cd87f86184e2a05c17",
+ sha1 = "a2ec01e2b5552b777a3d7085163f80756ef8c1ce",
+ src_sha1 = "6262966b3cd10ff6b98f0bed428640bbbe4f7e79",
)
maven_jar(
name = "jetty-util",
artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
- sha1 = "97306fd3c76171602caad2acc54ca779c9240d5f",
- src_sha1 = "dffff7271c248d4e21e2b1629c57896b8e631051",
+ sha1 = "3c7151c5a04a93119988b48a1577a972d90f8990",
+ src_sha1 = "f7f0420221772bc63ebae21571bb9925ca971a82",
)
maven_jar(
name = "jetty-util-ajax",
artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VER,
- sha1 = "2500d180c6e8e28eb3b75372b6ea9d457cf37658",
- src_sha1 = "682470f5ad074e64fc0e9c93bdc2784482f79362",
+ sha1 = "ed2f30e8eef939ab2825e607d83f82f85167e2c0",
+ src_sha1 = "1a48ae7a45683d20afb90784d1db314be2c73c92",
)
BOUNCYCASTLE_VER = "1.69"
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
index 5356dea1c..8c513d375 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
@@ -1,28 +1,28 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
index cf8d12fdb..a4904da44 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
@@ -1,28 +1,28 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
index 3e6ee56b8..3f65d62b7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
@@ -1,28 +1,28 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
index ce9d8bca2..2567d190c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
@@ -1,28 +1,28 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
index 41954429d..f2664a2ce 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
@@ -1,28 +1,28 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
index 9e8936a96..8a143ce24 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
@@ -1,22 +1,22 @@
target "jetty-9.4.x" with source configurePhase
-location jetty-9.4.x "https://archive.eclipse.org/jetty/updates/jetty-bundles-9.x/jetty-bundles-9.x/9.4.43.v20210629/" {
- org.eclipse.jetty.client [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.client.source [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.continuation [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.continuation.source [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.http [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.http.source [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.io [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.io.source [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.security [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.security.source [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.server [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.server.source [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.servlet [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.servlet.source [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.util [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.util.source [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.util.ajax [9.4.43.v20210629,9.4.43.v20210629]
- org.eclipse.jetty.util.ajax.source [9.4.43.v20210629,9.4.43.v20210629]
+location jetty-9.4.x "https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.44.v20210927/" {
+ org.eclipse.jetty.client [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.client.source [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.continuation [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.continuation.source [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.http [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.http.source [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.io [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.io.source [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.security [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.security.source [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.server [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.server.source [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.servlet [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.servlet.source [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.util [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.util.source [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.util.ajax [9.4.44.v20210927,9.4.44.v20210927]
+ org.eclipse.jetty.util.ajax.source [9.4.44.v20210927,9.4.44.v20210927]
}
diff --git a/pom.xml b/pom.xml
index 11789693f..c65f330ec 100644
--- a/pom.xml
+++ b/pom.xml
@@ -161,7 +161,7 @@
1.20
4.3.1
3.1.0
- 9.4.43.v20210629
+ 9.4.44.v20210927
0.15.3
4.5.13
4.4.14
From 5cbf70fd97dcc1956b7e1e375f9c3040494a28c4 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Sun, 7 Nov 2021 00:47:14 +0100
Subject: [PATCH 13/24] Update README
* Java 11 now
* Mention new bundle org.eclipse.jgit.ssh.apache.agent
* Be honest about missing features: there are quite a few
Change-Id: Ie08a2b4581024febe1983a59414cf69845ebff96
Signed-off-by: Thomas Wolf
---
README.md | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 091accd7f..f1f485adc 100644
--- a/README.md
+++ b/README.md
@@ -56,9 +56,13 @@ The CI builds use Maven and run on [Jenkins](https://ci.eclipse.org/jgit/).
- __org.eclipse.jgit.ssh.apache__
- Client support for the ssh protocol based on
+ Client support for the SSH protocol based on
[Apache Mina sshd](https://mina.apache.org/sshd-project/).
+- __org.eclipse.jgit.ssh.apache.agent__
+
+ Optional support for SSH agents for org.eclipse.jgit.ssh.apache.
+
- __org.eclipse.jgit.ui__
Simple UI for displaying git log.
@@ -83,7 +87,7 @@ __org.eclipse.jgit.junit.ssh__: Helpers for unit testing
- Only the timestamp of the index is used by JGit if the index is
dirty.
-- JGit requires at least a Java 8 JDK.
+- JGit 6.0 and newer requires at least Java 11. Older versions require at least Java 1.8.
- CRLF conversion is performed depending on the `core.autocrlf` setting,
however Git for Windows by default stores that setting during
@@ -123,7 +127,7 @@ __org.eclipse.jgit.junit.ssh__: Helpers for unit testing
- Object transport
Fetch via ssh, git, http, Amazon S3 and bundles.
- Push via ssh, git and Amazon S3. JGit does not yet deltify
+ Push via ssh, git, http, and Amazon S3. JGit does not yet deltify
the pushed packs so they may be a lot larger than C Git packs.
- Garbage collection
@@ -145,9 +149,17 @@ __org.eclipse.jgit.junit.ssh__: Helpers for unit testing
There are some missing features:
-- verifying signed commits
-- signing tags
- signing push
+- shallow and partial cloning
+- support for remote helpers
+- support for credential helpers
+- support for multiple working trees (git-worktree)
+- using external diff tools
+- support for HTTPS client certificates
+- SHA-256 object IDs
+- git protocol V2 (client side): packfile-uris
+- multi-pack index
+- split index
## Support
From a74dfb0901bdae125c351a2efec425d1721bc325 Mon Sep 17 00:00:00 2001
From: Matthias Sohn
Date: Wed, 10 Nov 2021 14:51:29 +0100
Subject: [PATCH 14/24] Update Orbit to S20211108222137
and update dependencies:
- com.google.gson to 2.8.8.v20211029-0838
- com.googlecode.javaewah to 1.1.13.v20211029-0839
- net.i2p.crypto.eddsa to 0.3.0.v20210923-1401
- org.apache.ant to 1.10.12.v20211102-1452
- org.apache.commons.compress to 1.21.0.v20211103-2100
- org.bouncycastle.bcprov to 1.69.0.v20210923-1401
- org.junit to 4.13.2.v20211018-1956
Change-Id: I90ca64f6d9f2a15c9a5d9a27d48956182f1698b4
---
WORKSPACE | 16 ++--
org.eclipse.jgit.ant/pom.xml | 1 -
.../org.eclipse.jgit.target/jgit-4.17.target | 32 ++++----
.../org.eclipse.jgit.target/jgit-4.17.tpd | 2 +-
.../org.eclipse.jgit.target/jgit-4.18.target | 32 ++++----
.../org.eclipse.jgit.target/jgit-4.18.tpd | 2 +-
.../org.eclipse.jgit.target/jgit-4.19.target | 32 ++++----
.../org.eclipse.jgit.target/jgit-4.19.tpd | 2 +-
.../org.eclipse.jgit.target/jgit-4.20.target | 32 ++++----
.../org.eclipse.jgit.target/jgit-4.20.tpd | 2 +-
.../org.eclipse.jgit.target/jgit-4.21.target | 32 ++++----
.../org.eclipse.jgit.target/jgit-4.21.tpd | 2 +-
.../orbit/S20211108222137.tpd | 73 +++++++++++++++++++
pom.xml | 15 +++-
14 files changed, 177 insertions(+), 98 deletions(-)
create mode 100644 org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20211108222137.tpd
diff --git a/WORKSPACE b/WORKSPACE
index fb9212fa0..ccd7315d4 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -105,8 +105,8 @@ maven_jar(
maven_jar(
name = "javaewah",
- artifact = "com.googlecode.javaewah:JavaEWAH:1.1.12",
- sha1 = "9feecc2b24d6bc9ff865af8d082f192238a293eb",
+ artifact = "com.googlecode.javaewah:JavaEWAH:1.1.13",
+ sha1 = "32cd724a42dc73f99ca08453d11a4bb83e0034c7",
)
maven_jar(
@@ -177,8 +177,8 @@ maven_jar(
maven_jar(
name = "commons-compress",
- artifact = "org.apache.commons:commons-compress:1.20",
- sha1 = "b8df472b31e1f17c232d2ad78ceb1c84e00c641b",
+ artifact = "org.apache.commons:commons-compress:1.21",
+ sha1 = "4ec95b60d4e86b5c95a0e919cb172a0af98011ef",
)
maven_jar(
@@ -195,8 +195,8 @@ maven_jar(
maven_jar(
name = "junit",
- artifact = "junit:junit:4.13",
- sha1 = "e49ccba652b735c93bd6e6f59760d8254cf597dd",
+ artifact = "junit:junit:4.13.2",
+ sha1 = "8ac9e16d933b6fb43bc7f576336b8f4d7eb5ba12",
)
maven_jar(
@@ -239,8 +239,8 @@ maven_jar(
maven_jar(
name = "gson",
- artifact = "com.google.code.gson:gson:2.8.7",
- sha1 = "69d9503ea0a40ee16f0bcdac7e3eaf83d0fa914a",
+ artifact = "com.google.code.gson:gson:2.8.8",
+ sha1 = "431fc3cbc0ff81abdbfde070062741089c3ba874",
)
JETTY_VER = "9.4.44.v20210927"
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index ae4b52d73..eaef00702 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -38,7 +38,6 @@
org.apache.ant
ant
- 1.10.11
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
index 8c513d375..06090e674 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
@@ -1,7 +1,7 @@
-
+
@@ -25,8 +25,8 @@
-
-
+
+
@@ -35,22 +35,22 @@
-
-
+
+
-
-
-
-
+
+
+
+
-
-
+
+
@@ -69,8 +69,8 @@
-
-
+
+
@@ -79,8 +79,8 @@
-
-
+
+
@@ -93,7 +93,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
index bab33ba2e..16527013b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
@@ -1,7 +1,7 @@
target "jgit-4.17" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/R20210825222808-2021-09.tpd"
+include "orbit/S20211108222137.tpd"
location "https://download.eclipse.org/releases/2020-09/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
index a4904da44..1d6792229 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
@@ -1,7 +1,7 @@
-
+
@@ -25,8 +25,8 @@
-
-
+
+
@@ -35,22 +35,22 @@
-
-
+
+
-
-
-
-
+
+
+
+
-
-
+
+
@@ -69,8 +69,8 @@
-
-
+
+
@@ -79,8 +79,8 @@
-
-
+
+
@@ -93,7 +93,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
index 095ea81aa..e732ad6d1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
@@ -1,7 +1,7 @@
target "jgit-4.18" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/R20210825222808-2021-09.tpd"
+include "orbit/S20211108222137.tpd"
location "https://download.eclipse.org/releases/2020-12/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
index 3f65d62b7..8b96f5269 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
@@ -1,7 +1,7 @@
-
+
@@ -25,8 +25,8 @@
-
-
+
+
@@ -35,22 +35,22 @@
-
-
+
+
-
-
-
-
+
+
+
+
-
-
+
+
@@ -69,8 +69,8 @@
-
-
+
+
@@ -79,8 +79,8 @@
-
-
+
+
@@ -93,7 +93,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
index 52e457076..0e77c8872 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
@@ -1,7 +1,7 @@
target "jgit-4.19-staging" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/R20210825222808-2021-09.tpd"
+include "orbit/S20211108222137.tpd"
location "https://download.eclipse.org/staging/2021-03/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
index 2567d190c..a3aca8d79 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
@@ -1,7 +1,7 @@
-
+
@@ -25,8 +25,8 @@
-
-
+
+
@@ -35,22 +35,22 @@
-
-
+
+
-
-
-
-
+
+
+
+
-
-
+
+
@@ -69,8 +69,8 @@
-
-
+
+
@@ -79,8 +79,8 @@
-
-
+
+
@@ -93,7 +93,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
index d2cf94f2d..081864849 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
@@ -1,7 +1,7 @@
target "jgit-4.20" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/R20210825222808-2021-09.tpd"
+include "orbit/S20211108222137.tpd"
location "https://download.eclipse.org/releases/2021-06/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
index f2664a2ce..5f4498723 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
@@ -1,7 +1,7 @@
-
+
@@ -25,8 +25,8 @@
-
-
+
+
@@ -35,22 +35,22 @@
-
-
+
+
-
-
-
-
+
+
+
+
-
-
+
+
@@ -69,8 +69,8 @@
-
-
+
+
@@ -79,8 +79,8 @@
-
-
+
+
@@ -93,7 +93,7 @@
-
+
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
index ba3ddb80c..53848b269 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
@@ -1,7 +1,7 @@
target "jgit-4.21" with source configurePhase
include "projects/jetty-9.4.x.tpd"
-include "orbit/R20210825222808-2021-09.tpd"
+include "orbit/S20211108222137.tpd"
location "https://download.eclipse.org/releases/2021-09/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20211108222137.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20211108222137.tpd
new file mode 100644
index 000000000..0f0701099
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20211108222137.tpd
@@ -0,0 +1,73 @@
+target "S20211108222137" with source configurePhase
+// see https://download.eclipse.org/tools/orbit/downloads/
+
+location "https://download.eclipse.org/tools/orbit/downloads/drops/S20211108222137/repository" {
+ com.google.gson [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
+ com.google.gson.source [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
+ com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
+ com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
+ com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
+ com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
+ com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
+ com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
+ com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
+ com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
+ javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
+ javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
+ javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
+ javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
+ net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
+ net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
+ net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
+ net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
+ net.i2p.crypto.eddsa [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
+ net.i2p.crypto.eddsa.source [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
+ org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
+ org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
+ org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
+ org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
+ org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
+ org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
+ org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
+ org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
+ org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
+ org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
+ org.apache.httpcomponents.httpcore [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
+ org.apache.httpcomponents.httpcore.source [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
+ org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
+ org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
+ org.apache.sshd.osgi [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
+ org.apache.sshd.osgi.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
+ org.apache.sshd.sftp [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
+ org.apache.sshd.sftp.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
+ org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
+ org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
+ org.bouncycastle.bcpg [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
+ org.bouncycastle.bcpg.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
+ org.bouncycastle.bcpkix [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
+ org.bouncycastle.bcpkix.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
+ org.bouncycastle.bcprov [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
+ org.bouncycastle.bcprov.source [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
+ org.bouncycastle.bcutil [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
+ org.bouncycastle.bcutil.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
+ org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
+ org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
+ org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
+ org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
+ org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
+ org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
+ org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
+ org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
+ org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
+ org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
+ org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
+ org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
+ org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
+ org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
+ org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
+ org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
+ org.slf4j.binding.log4j12 [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
+ org.slf4j.binding.log4j12.source [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
+ org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
+ org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
+}
diff --git a/pom.xml b/pom.xml
index c65f330ec..ee6b0f029 100644
--- a/pom.xml
+++ b/pom.xml
@@ -151,14 +151,15 @@
${project.build.directory}/META-INF/MANIFEST.MF
5.12.0.202106070339-r
+ 1.10.12
2.7.0
0.1.55
1.1.1
- 1.1.12
- 4.13
+ 1.1.13
+ 4.13.2
1C
2.33
- 1.20
+ 1.21
4.3.1
3.1.0
9.4.44.v20210927
@@ -169,7 +170,7 @@
1.2.15
3.3.1
2.5.0
- 2.8.7
+ 2.8.8
1.69
4.3.0
3.1.2
@@ -670,6 +671,12 @@
${servlet-api-version}
+
+ org.apache.ant
+ ant
+ ${ant-version}
+
+
org.apache.commons
commons-compress
From b84738c3693081d3ed9e8e1ba4a5db6e5ac3abf8 Mon Sep 17 00:00:00 2001
From: Matthias Sohn
Date: Wed, 10 Nov 2021 15:56:36 +0100
Subject: [PATCH 15/24] Update version of last release defining the API
baseline to 5.13.0
Change-Id: I48e0d677a466a364fdd699cdb00014a9a65d082a
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index ee6b0f029..311f0a5a6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -150,7 +150,7 @@
11
${project.build.directory}/META-INF/MANIFEST.MF
- 5.12.0.202106070339-r
+ 5.13.0.202109080827-r
1.10.12
2.7.0
0.1.55
From e7838b9c080011817ddb59c53298237a9c24a7a6 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Wed, 27 Oct 2021 14:03:10 +0200
Subject: [PATCH 16/24] [sshd agent] Introduce ConnectorDescriptor
Once a factory supports different SSH agents on the same platform,
which is planned for Windows once we use Apache MINA sshd 2.8.0,
client code may need to have a way to specify which SSH agent shall
be used when the SSH config doesn't define anything.
Add a mechanism by which a ConnectorFactory can tell what Connectors
it may provide. Client code can use this to set the identityAgent
parameter of ConnectorFactory.create() to the wanted default if it
would be null otherwise.
A ConnectorDescriptor is a pair of strings: an internal name, and a
display name. The latter is included because client code might want to
communicate agent names to the user, be it in error messages or in some
chooser dialog where a user could define which of several alternative
SSH agents should be used as default. The internal name is intended to
be used in the IdentityAgent directive in ~/.ssh/config.
Also make the ConnectorFactory discovered via the ServiceLoader
accessible and overrideable. Provide static get/setDefault() methods,
similar to the SshSessionFactory itself.
Change-Id: Ie3d077395d32dfddc72bc8627e92b23636938182
Signed-off-by: Thomas Wolf
---
.../sshd/agent/connector/Texts.properties | 3 +-
.../sshd/agent/connector/Factory.java | 24 ++++
.../agent/connector/PageantConnector.java | 17 +++
.../transport/sshd/agent/connector/Texts.java | 2 +
.../connector/UnixDomainSocketConnector.java | 17 +++
.../sshd/agent/ConnectorFactoryProvider.java | 26 ++--
.../transport/sshd/SshdSessionFactory.java | 6 +-
.../sshd/agent/ConnectorFactory.java | 116 +++++++++++++++++-
.../jgit/transport/SshSessionFactory.java | 2 +-
9 files changed, 199 insertions(+), 14 deletions(-)
diff --git a/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties b/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties
index f884adc08..6fce08366 100644
--- a/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties
+++ b/org.eclipse.jgit.ssh.apache.agent/resources/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.properties
@@ -13,4 +13,5 @@ msgSendFailed=Sending {0} bytes to SSH agent failed; {0} bytes not written
msgSendFailed2=Sending {0} bytes to SSH agent failed: {1} - {2}
msgSharedMemoryFailed=Could not set up shared memory for communicating with Pageant
msgShortRead=Short read from SSH agent, expected {0} bytes, got {1} bytes; last read() returned {2}
-
+pageant=Pageant
+unixDefaultAgent=ssh-agent
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java
index 1cbf0e41b..d7409b0c3 100644
--- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Factory.java
@@ -11,6 +11,8 @@
import java.io.File;
import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
import org.eclipse.jgit.transport.sshd.agent.Connector;
import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
@@ -41,4 +43,26 @@ public boolean isSupported() {
public String getName() {
return NAME;
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * This factory returns on Windows a
+ * {@link org.eclipse.jgit.transport.sshd.agent.ConnectorFactory.ConnectorDescriptor
+ * ConnectorDescriptor} for the internal name "pageant"; on Unix one for
+ * "SSH_AUTH_SOCK".
+ *
+ */
+ @Override
+ public Collection getSupportedConnectors() {
+ return Collections.singleton(getDefaultConnector());
+ }
+
+ @Override
+ public ConnectorDescriptor getDefaultConnector() {
+ if (SystemReader.getInstance().isWindows()) {
+ return PageantConnector.DESCRIPTOR;
+ }
+ return UnixDomainSocketConnector.DESCRIPTOR;
+ }
}
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java
index 59fe2fc24..b0e3bce72 100644
--- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/PageantConnector.java
@@ -12,12 +12,29 @@
import java.io.IOException;
import org.eclipse.jgit.transport.sshd.agent.AbstractConnector;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory.ConnectorDescriptor;
/**
* A connector using Pageant's shared memory IPC mechanism.
*/
public class PageantConnector extends AbstractConnector {
+ /**
+ * {@link ConnectorDescriptor} for the {@link PageantConnector}.
+ */
+ public static final ConnectorDescriptor DESCRIPTOR = new ConnectorDescriptor() {
+
+ @Override
+ public String getIdentityAgent() {
+ return "pageant"; //$NON-NLS-1$
+ }
+
+ @Override
+ public String getDisplayName() {
+ return Texts.get().pageant;
+ }
+ };
+
private final PageantLibrary lib;
/**
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java
index 6b6626154..fb45b30dd 100644
--- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/Texts.java
@@ -42,5 +42,7 @@ public static Texts get() {
/***/ public String msgSendFailed2;
/***/ public String msgSharedMemoryFailed;
/***/ public String msgShortRead;
+ /***/ public String pageant;
+ /***/ public String unixDefaultAgent;
}
diff --git a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java
index 4c698d974..3b75f3a7d 100644
--- a/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java
+++ b/org.eclipse.jgit.ssh.apache.agent/src/org/eclipse/jgit/internal/transport/sshd/agent/connector/UnixDomainSocketConnector.java
@@ -24,6 +24,7 @@
import org.apache.sshd.common.SshException;
import org.eclipse.jgit.transport.sshd.agent.AbstractConnector;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory.ConnectorDescriptor;
import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.SystemReader;
import org.slf4j.Logger;
@@ -38,6 +39,22 @@
*/
public class UnixDomainSocketConnector extends AbstractConnector {
+ /**
+ * {@link ConnectorDescriptor} for the {@link UnixDomainSocketConnector}.
+ */
+ public static final ConnectorDescriptor DESCRIPTOR = new ConnectorDescriptor() {
+
+ @Override
+ public String getIdentityAgent() {
+ return ENV_SSH_AUTH_SOCK;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return Texts.get().unixDefaultAgent;
+ }
+ };
+
private static final Logger LOG = LoggerFactory
.getLogger(UnixDomainSocketConnector.class);
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java
index 9984f9976..aba7a7645 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/agent/ConnectorFactoryProvider.java
@@ -19,7 +19,7 @@
*/
public final class ConnectorFactoryProvider {
- private static final ConnectorFactory FACTORY = loadDefaultFactory();
+ private static volatile ConnectorFactory INSTANCE = loadDefaultFactory();
private static ConnectorFactory loadDefaultFactory() {
ServiceLoader loader = ServiceLoader
@@ -35,17 +35,27 @@ private static ConnectorFactory loadDefaultFactory() {
}
- private ConnectorFactoryProvider() {
- // No instantiation
- }
-
/**
- * Retrieves the default {@link ConnectorFactory} obtained via the
- * {@link ServiceLoader}.
+ * Retrieves the currently set default {@link ConnectorFactory}.
*
* @return the {@link ConnectorFactory}, or {@code null} if none.
*/
public static ConnectorFactory getDefaultFactory() {
- return FACTORY;
+ return INSTANCE;
+ }
+
+ /**
+ * Sets the default {@link ConnectorFactory}.
+ *
+ * @param factory
+ * {@link ConnectorFactory} to use, or {@code null}Â to use the
+ * factory discovered via the {@link ServiceLoader}.
+ */
+ public static void setDefaultFactory(ConnectorFactory factory) {
+ INSTANCE = factory == null ? loadDefaultFactory() : factory;
+ }
+
+ private ConnectorFactoryProvider() {
+ // No instantiation
}
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
index 336418009..58cf8e1dd 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
@@ -56,7 +56,6 @@
import org.eclipse.jgit.internal.transport.sshd.OpenSshServerKeyDatabase;
import org.eclipse.jgit.internal.transport.sshd.PasswordProviderWrapper;
import org.eclipse.jgit.internal.transport.sshd.SshdText;
-import org.eclipse.jgit.internal.transport.sshd.agent.ConnectorFactoryProvider;
import org.eclipse.jgit.internal.transport.sshd.agent.JGitSshAgentFactory;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.SshConfigStore;
@@ -456,12 +455,15 @@ protected ServerKeyDatabase createServerKeyDatabase(@NonNull File homeDir,
/**
* Gets a {@link ConnectorFactory}. If this returns {@code null}, SSH agents
* are not supported.
+ *
+ * The default implementation uses {@link ConnectorFactory#getDefault()}
+ *
*
* @return the factory, or {@code null}Â if no SSH agent support is desired
* @since 6.0
*/
protected ConnectorFactory getConnectorFactory() {
- return ConnectorFactoryProvider.getDefaultFactory();
+ return ConnectorFactory.getDefault();
}
/**
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
index b351d89ef..da98ea7fe 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/agent/ConnectorFactory.java
@@ -11,8 +11,10 @@
import java.io.File;
import java.io.IOException;
+import java.util.Collection;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.agent.ConnectorFactoryProvider;
/**
* A factory for creating {@link Connector}s. This is a service provider
@@ -24,14 +26,45 @@
*/
public interface ConnectorFactory {
+ /**
+ * Retrieves the currently set default {@link ConnectorFactory}. This is the
+ * factory that is used unless overridden by the
+ * {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
+ *
+ * @return the current default factory; may be {@code null} if none is set
+ * and the {@link java.util.ServiceLoader} cannot find any suitable
+ * implementation
+ */
+ static ConnectorFactory getDefault() {
+ return ConnectorFactoryProvider.getDefaultFactory();
+ }
+
+ /**
+ * Sets a default {@link ConnectorFactory}. This is the factory that is used
+ * unless overridden by the
+ * {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}.
+ *
+ * If no default factory is set programmatically, an implementation is
+ * discovered via the {@link java.util.ServiceLoader}.
+ *
+ *
+ * @param factory
+ * {@link ConnectorFactory} to set, or {@code null}Â to revert to
+ * the default behavior of using the
+ * {@link java.util.ServiceLoader}.
+ */
+ static void setDefault(ConnectorFactory factory) {
+ ConnectorFactoryProvider.setDefaultFactory(factory);
+ }
+
/**
* Creates a new {@link Connector}.
*
* @param identityAgent
* identifies the wanted agent connection; if {@code null}, the
* factory is free to provide a {@link Connector} to a default
- * agent. The value will typically come from the IdentityAgent
- * setting in ~/.ssh/config.
+ * agent. The value will typically come from the
+ * {@code IdentityAgent} setting in {@code ~/.ssh/config}.
* @param homeDir
* the current local user's home directory as configured in the
* {@link org.eclipse.jgit.transport.sshd.SshdSessionFactory}
@@ -58,4 +91,83 @@ Connector create(String identityAgent, File homeDir)
*/
String getName();
+ /**
+ * {@link ConnectorDescriptor}s describe available {@link Connector}s a
+ * {@link ConnectorFactory} may provide.
+ *
+ * A {@link ConnectorFactory} may support connecting to different SSH
+ * agents. Agents are identified by name; a user can choose a specific agent
+ * for instance via the {@code IdentityAgent} setting in
+ * {@code ~/.ssh/config}.
+ *
+ *
+ * OpenSSH knows two built-in names: "none" for not using any agent, and
+ * "SSH_AUTH_SOCK" for using an agent that communicates over a Unix domain
+ * socket given by the value of environment variable {@code SSH_AUTH_SOCK}.
+ * Other agents can be specified in OpenSSH by specifying the socket file
+ * directly. (The "standard" OpenBSD OpenSSH knows only this communication
+ * mechanism.) "SSH_AUTH_SOCK" is also the default in OpenBSD OpenSSH if
+ * nothing is configured.
+ *
+ *
+ * A particular {@link ConnectorFactory} may support more communication
+ * mechanisms or different agents. For instance, a factory on Windows might
+ * support Pageant, Win32-OpenSSH, or even git bash ssh-agent, and might
+ * accept internal names like "pageant", "openssh", "SSH_AUTH_SOCK" in
+ * {@link ConnectorFactory#create(String, File)} to choose among them.
+ *
+ * The {@link ConnectorDescriptor} interface and the
+ * {@link ConnectorFactory#getSupportedConnectors()} and
+ * {@link ConnectorFactory#getDefaultConnector()} methods provide a way for
+ * code using a {@link ConnectorFactory} to learn what the factory supports
+ * and thus implement some way by which a user can influence the default
+ * behavior if {@code IdentityAgent} is not set or
+ * {@link ConnectorFactory#create(String, File)} is called with
+ * {@code identityAgent == null}.
+ */
+ interface ConnectorDescriptor {
+
+ /**
+ * Retrieves the internal name of a supported {@link Connector}. The
+ * internal name is the one a user can specify for instance in the
+ * {@code IdentityAgent} setting in {@code ~/.ssh/config} to select the
+ * connector.
+ *
+ * @return the internal name; not empty
+ */
+ @NonNull
+ String getIdentityAgent();
+
+ /**
+ * Retrieves a display name for a {@link Connector}, suitable for
+ * showing in a UI.
+ *
+ * @return the display name; properly localized and not empty
+ */
+ @NonNull
+ String getDisplayName();
+ }
+
+ /**
+ * Tells which kinds of SSH agents this {@link ConnectorFactory} supports.
+ *
+ * An implementation of this method should document the possible values it
+ * returns.
+ *
+ *
+ * @return an immutable collection of {@link ConnectorDescriptor}s,
+ * including {@link #getDefaultConnector()} and not including a
+ * descriptor for internal name "none"
+ */
+ @NonNull
+ Collection getSupportedConnectors();
+
+ /**
+ * Tells what kind of {@link Connector} this {@link ConnectorFactory}
+ * creates if {@link ConnectorFactory#create(String, File)} is called with
+ * {@code identityAgent == null}.
+ *
+ * @return a {@link ConnectorDescriptor} for the default connector
+ */
+ ConnectorDescriptor getDefaultConnector();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
index e216a56ac..1e98a56f7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
@@ -36,7 +36,7 @@
*/
public abstract class SshSessionFactory {
- private static SshSessionFactory INSTANCE = loadSshSessionFactory();
+ private static volatile SshSessionFactory INSTANCE = loadSshSessionFactory();
private static SshSessionFactory loadSshSessionFactory() {
ServiceLoader loader = ServiceLoader.load(SshSessionFactory.class);
From d4296d96bb13ec100f396271c3a9c9c9c9b31dad Mon Sep 17 00:00:00 2001
From: Marco Miller
Date: Thu, 11 Nov 2021 09:17:54 -0500
Subject: [PATCH 17/24] Upgrade plexus-compiler version to 2.9.0
Change-Id: I24ac698d3ccc01e79464b061068944ca82e8383b
Signed-off-by: Marco Miller
---
pom.xml | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 311f0a5a6..f7629411b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -178,7 +178,7 @@
3.0.0-M5
${maven-surefire-plugin-version}
3.8.1
- 2.8.8
+ 2.9.0
2.2
3.20.2
@@ -870,6 +870,11 @@
plexus-compiler-eclipse
${plexus-compiler-version}
+
+ org.codehaus.plexus
+ plexus-compiler-api
+ ${plexus-compiler-version}
+
org.eclipse.jdt
ecj
From ffb5cac361b1c6751f392dd2bce773cb5571b846 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Mon, 15 Nov 2021 10:37:36 +0100
Subject: [PATCH 18/24] Add missing .gitignore in o.e.j.ssh.apache.agent
Ignore /bin and /target.
Change-Id: I38f3748273b5243c54e010bfceac745084755f45
Signed-off-by: Thomas Wolf
---
org.eclipse.jgit.ssh.apache.agent/.gitignore | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 org.eclipse.jgit.ssh.apache.agent/.gitignore
diff --git a/org.eclipse.jgit.ssh.apache.agent/.gitignore b/org.eclipse.jgit.ssh.apache.agent/.gitignore
new file mode 100644
index 000000000..934e0e06f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.agent/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/target
From c4b3ec72faf891120fdd93246503d0bee339f349 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Sat, 13 Nov 2021 13:08:14 +0100
Subject: [PATCH 19/24] OpenSshConfigFile: update token replacements
It appears that the OpenSSH documentation[1] has changed; it now allows
more flags for a number of keys.
[1] https://man.openbsd.org/ssh_config.5#TOKENS
Change-Id: I55df174f86a3fd4a6ef22687dc433ac9f9ad181d
Signed-off-by: Thomas Wolf
---
.../transport/ssh/OpenSshConfigFile.java | 27 ++++++++++++++-----
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
index 4cffcc5dd..4d1864a92 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -767,13 +767,15 @@ void substitute(String originalHostName, int port, String userName,
List values = multiOptions
.get(SshConstants.IDENTITY_FILE);
if (values != null) {
- values = substitute(values, "dhlru", r, true); //$NON-NLS-1$
+ values = substitute(values, Replacer.DEFAULT_TOKENS, r,
+ true);
values = replaceTilde(values, home);
multiOptions.put(SshConstants.IDENTITY_FILE, values);
}
values = multiOptions.get(SshConstants.CERTIFICATE_FILE);
if (values != null) {
- values = substitute(values, "dhlru", r, true); //$NON-NLS-1$
+ values = substitute(values, Replacer.DEFAULT_TOKENS, r,
+ true);
values = replaceTilde(values, home);
multiOptions.put(SshConstants.CERTIFICATE_FILE, values);
}
@@ -782,6 +784,8 @@ void substitute(String originalHostName, int port, String userName,
List values = listOptions
.get(SshConstants.USER_KNOWN_HOSTS_FILE);
if (values != null) {
+ values = substitute(values, Replacer.DEFAULT_TOKENS, r,
+ true);
values = replaceTilde(values, home);
listOptions.put(SshConstants.USER_KNOWN_HOSTS_FILE, values);
}
@@ -790,29 +794,29 @@ void substitute(String originalHostName, int port, String userName,
// HOSTNAME already done above
String value = options.get(SshConstants.IDENTITY_AGENT);
if (value != null) {
- value = r.substitute(value, "dhlru", true); //$NON-NLS-1$
+ value = r.substitute(value, Replacer.DEFAULT_TOKENS, true);
value = toFile(value, home).getPath();
options.put(SshConstants.IDENTITY_AGENT, value);
}
value = options.get(SshConstants.CONTROL_PATH);
if (value != null) {
- value = r.substitute(value, "ChLlnpru", true); //$NON-NLS-1$
+ value = r.substitute(value, Replacer.DEFAULT_TOKENS, true);
value = toFile(value, home).getPath();
options.put(SshConstants.CONTROL_PATH, value);
}
value = options.get(SshConstants.LOCAL_COMMAND);
if (value != null) {
- value = r.substitute(value, "CdhlnprTu", false); //$NON-NLS-1$
+ value = r.substitute(value, "CdhLlnprTu", false); //$NON-NLS-1$
options.put(SshConstants.LOCAL_COMMAND, value);
}
value = options.get(SshConstants.REMOTE_COMMAND);
if (value != null) {
- value = r.substitute(value, "Cdhlnpru", false); //$NON-NLS-1$
+ value = r.substitute(value, Replacer.DEFAULT_TOKENS, false);
options.put(SshConstants.REMOTE_COMMAND, value);
}
value = options.get(SshConstants.PROXY_COMMAND);
if (value != null) {
- value = r.substitute(value, "hpr", false); //$NON-NLS-1$
+ value = r.substitute(value, "hnpr", false); //$NON-NLS-1$
options.put(SshConstants.PROXY_COMMAND, value);
}
}
@@ -880,6 +884,15 @@ public String toString() {
}
private static class Replacer {
+
+ /**
+ * Tokens applicable to most keys.
+ *
+ * @see man
+ * ssh_config
+ */
+ public static final String DEFAULT_TOKENS = "CdhLlnpru"; //$NON-NLS-1$
+
private final Map replacements = new HashMap<>();
public Replacer(String host, int port, String user,
From af0126e1d01100fad673b6d0a56a99633383a198 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Sat, 13 Nov 2021 13:09:01 +0100
Subject: [PATCH 20/24] OpenSshConfigFile: line comments and quoted strings
Bring our SSH config parser up-to-date with respect to changes in
OpenSSH. In particular, they fixed[1] the handling of line comments
such that #-characters inside strings are not considered. This means
that we have to parse strings with escaped quotes correctly.
[1] https://bugzilla.mindrot.org/show_bug.cgi?id=3288
Change-Id: Ifbd9014127e8d51e7c8792e237f3fc2a9a0719d2
Signed-off-by: Thomas Wolf
---
.../transport/ssh/jsch/OpenSshConfigTest.java | 4 +-
.../transport/ssh/OpenSshConfigFileTest.java | 35 +++-
.../transport/ssh/OpenSshConfigFile.java | 160 +++++++++++++-----
3 files changed, 154 insertions(+), 45 deletions(-)
diff --git a/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/OpenSshConfigTest.java b/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/OpenSshConfigTest.java
index 4399ad9be..9a61cc4c5 100644
--- a/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/OpenSshConfigTest.java
+++ b/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/OpenSshConfigTest.java
@@ -138,7 +138,7 @@ public void testQuoteParsing() throws Exception {
assertEquals(2222, osc.lookup("unquoted").getPort());
assertEquals(2222, osc.lookup("hosts").getPort());
assertEquals(" spaced\ttld ", osc.lookup("spaced").getHostName());
- assertEquals("bad.tld\"", osc.lookup("bad").getHostName());
+ assertEquals("bad.tld", osc.lookup("bad").getHostName());
}
@Test
@@ -229,7 +229,7 @@ public void testAlias_PreferredAuthentications() throws Exception {
@Test
public void testAlias_InheritPreferredAuthentications() throws Exception {
config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
- + "\tPreferredAuthentications publickey, hostbased\n");
+ + "\tPreferredAuthentications 'publickey, hostbased'\n");
final Host h = osc.lookup("orcz");
assertNotNull(h);
assertEquals("publickey,hostbased", h.getPreferredAuthentications());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java
index 27bae3747..11741b41a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFileTest.java
@@ -166,7 +166,38 @@ public void testQuoteParsing() throws Exception {
assertPort(2222, lookup("unquoted"));
assertPort(2222, lookup("hosts"));
assertHost(" spaced\ttld ", lookup("spaced"));
- assertHost("bad.tld\"", lookup("bad"));
+ assertHost("bad.tld", lookup("bad"));
+ }
+
+ @Test
+ public void testAdvancedParsing() throws Exception {
+ // Escaped quotes, and line comments
+ config("Host foo\n"
+ + " HostName=\"foo\\\"d.tld\"\n"
+ + " User= someone#foo\n"
+ + "Host bar\n"
+ + " User ' some one#two' # Comment\n"
+ + " GlobalKnownHostsFile '/a folder/with spaces/hosts' '/other/more hosts' # Comment\n"
+ + "Host foobar\n"
+ + " User a\\ u\\ thor\n"
+ + "Host backslash\n"
+ + " User some\\one\\\\\\ foo\n"
+ + "Host backslash_before_quote\n"
+ + " User \\\"someone#\"el#se\" #Comment\n"
+ + "Host backslash_in_quote\n"
+ + " User 'some\\one\\\\\\ foo'\n");
+ assertHost("foo\"d.tld", lookup("foo"));
+ assertUser("someone#foo", lookup("foo"));
+ HostConfig c = lookup("bar");
+ assertUser(" some one#two", c);
+ assertArrayEquals(
+ new Object[] { "/a folder/with spaces/hosts",
+ "/other/more hosts" },
+ c.getValues("GlobalKnownHostsFile").toArray());
+ assertUser("a u thor", lookup("foobar"));
+ assertUser("some\\one\\ foo", lookup("backslash"));
+ assertUser("\"someone#el#se", lookup("backslash_before_quote"));
+ assertUser("some\\one\\\\ foo", lookup("backslash_in_quote"));
}
@Test
@@ -258,7 +289,7 @@ public void testAlias_PreferredAuthentications() throws Exception {
@Test
public void testAlias_InheritPreferredAuthentications() throws Exception {
config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
- + "\tPreferredAuthentications publickey, hostbased\n");
+ + "\tPreferredAuthentications 'publickey, hostbased'\n");
final HostConfig h = lookup("orcz");
assertNotNull(h);
assertEquals("publickey,hostbased",
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
index 4d1864a92..2dbb7859b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -230,27 +230,39 @@ private List parse(BufferedReader reader)
String line;
while ((line = reader.readLine()) != null) {
- // OpenSsh ignores trailing comments on a line. Anything after the
- // first # on a line is trimmed away (yes, even if the hash is
- // inside quotes).
- //
- // See https://github.com/openssh/openssh-portable/commit/2bcbf679
- int i = line.indexOf('#');
- if (i >= 0) {
- line = line.substring(0, i);
- }
- line = line.trim();
+ line = line.strip();
if (line.isEmpty()) {
continue;
}
String[] parts = line.split("[ \t]*[= \t]", 2); //$NON-NLS-1$
- // Although the ssh-config man page doesn't say so, the openssh
- // parser does allow quoted keywords.
- String keyword = dequote(parts[0].trim());
+ String keyword = parts[0].strip();
+ if (keyword.isEmpty()) {
+ continue;
+ }
+ switch (keyword.charAt(0)) {
+ case '#':
+ continue;
+ case '"':
+ // Although the ssh-config man page doesn't say so, the openssh
+ // parser does allow quoted keywords.
+ List dequoted = parseList(keyword);
+ keyword = dequoted.isEmpty() ? "" : dequoted.get(0); //$NON-NLS-1$
+ break;
+ default:
+ // Keywords never contain hashes, nor whitespace
+ int i = keyword.indexOf('#');
+ if (i >= 0) {
+ keyword = keyword.substring(0, i);
+ }
+ break;
+ }
+ if (keyword.isEmpty()) {
+ continue;
+ }
// man 5 ssh-config says lines had the format "keyword arguments",
// with no indication that arguments were optional. However, let's
// not crap out on missing arguments. See bug 444319.
- String argValue = parts.length > 1 ? parts[1].trim() : ""; //$NON-NLS-1$
+ String argValue = parts.length > 1 ? parts[1].strip() : ""; //$NON-NLS-1$
if (StringUtils.equalsIgnoreCase(SshConstants.HOST, keyword)) {
current = new HostEntry(parseList(argValue));
@@ -262,7 +274,9 @@ private List parse(BufferedReader reader)
List args = validate(keyword, parseList(argValue));
current.setValue(keyword, args);
} else if (!argValue.isEmpty()) {
- argValue = validate(keyword, dequote(argValue));
+ List args = parseList(argValue);
+ String arg = args.isEmpty() ? "" : args.get(0); //$NON-NLS-1$
+ argValue = validate(keyword, arg);
current.setValue(keyword, argValue);
}
}
@@ -273,6 +287,7 @@ private List parse(BufferedReader reader)
/**
* Splits the argument into a list of whitespace-separated elements.
* Elements containing whitespace must be quoted and will be de-quoted.
+ * Backslash-escapes are handled for quotes and blanks.
*
* @param argument
* argument part of the configuration line as read from the
@@ -280,37 +295,107 @@ private List parse(BufferedReader reader)
* @return a {@link List} of elements, possibly empty and possibly
* containing empty elements, but not containing {@code null}
*/
- private List parseList(String argument) {
+ private static List parseList(String argument) {
List result = new ArrayList<>(4);
int start = 0;
int length = argument.length();
while (start < length) {
// Skip whitespace
- if (Character.isWhitespace(argument.charAt(start))) {
+ char ch = argument.charAt(start);
+ if (Character.isWhitespace(ch)) {
start++;
- continue;
- }
- if (argument.charAt(start) == '"') {
- int stop = argument.indexOf('"', ++start);
- if (stop < start) {
- // No closing double quote: skip
- break;
- }
- result.add(argument.substring(start, stop));
- start = stop + 1;
+ } else if (ch == '#') {
+ break; // Comment start
} else {
- int stop = start + 1;
- while (stop < length
- && !Character.isWhitespace(argument.charAt(stop))) {
- stop++;
- }
- result.add(argument.substring(start, stop));
- start = stop + 1;
+ // Parse one token now.
+ start = parseToken(argument, start, length, result);
}
}
return result;
}
+ /**
+ * Parses a token up to the next whitespace not inside a string quoted by
+ * single or double quotes. Inside a string, quotes can be escaped by
+ * backslash characters. Outside of a string, "\ " can be used to include a
+ * space in a token; inside a string "\ " is taken literally as '\' followed
+ * by ' '.
+ *
+ * @param argument
+ * to parse the token out of
+ * @param from
+ * index at the beginning of the token
+ * @param to
+ * index one after the last character to look at
+ * @param result
+ * a list collecting tokens to which the parsed token is added
+ * @return the index after the token
+ */
+ private static int parseToken(String argument, int from, int to,
+ List result) {
+ StringBuilder b = new StringBuilder();
+ int i = from;
+ char quote = 0;
+ boolean escaped = false;
+ SCAN: while (i < to) {
+ char ch = argument.charAt(i);
+ switch (ch) {
+ case '"':
+ case '\'':
+ if (quote == 0) {
+ if (escaped) {
+ b.append(ch);
+ } else {
+ quote = ch;
+ }
+ } else if (!escaped && quote == ch) {
+ quote = 0;
+ } else {
+ b.append(ch);
+ }
+ escaped = false;
+ break;
+ case '\\':
+ if (escaped) {
+ b.append(ch);
+ }
+ escaped = !escaped;
+ break;
+ case ' ':
+ if (quote == 0) {
+ if (escaped) {
+ b.append(ch);
+ escaped = false;
+ } else {
+ break SCAN;
+ }
+ } else {
+ if (escaped) {
+ b.append('\\');
+ }
+ b.append(ch);
+ escaped = false;
+ }
+ break;
+ default:
+ if (escaped) {
+ b.append('\\');
+ }
+ if (quote == 0 && Character.isWhitespace(ch)) {
+ break SCAN;
+ }
+ b.append(ch);
+ escaped = false;
+ break;
+ }
+ i++;
+ }
+ if (b.length() > 0) {
+ result.add(b.toString());
+ }
+ return i;
+ }
+
/**
* Hook to perform validation on a single value, or to sanitize it. If this
* throws an (unchecked) exception, parsing of the file is abandoned.
@@ -358,13 +443,6 @@ private static boolean patternMatchesHost(String pattern, String name) {
return pattern.equals(name);
}
- private static String dequote(String value) {
- if (value.startsWith("\"") && value.endsWith("\"") //$NON-NLS-1$ //$NON-NLS-2$
- && value.length() > 1)
- return value.substring(1, value.length() - 1);
- return value;
- }
-
private static String stripWhitespace(String value) {
final StringBuilder b = new StringBuilder();
int length = value.length();
From 180bc67e28f333a8b23413a0789b6563c3a5b9de Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Sat, 13 Nov 2021 13:09:58 +0100
Subject: [PATCH 21/24] ssh: use a single SecureRandom instance for hashing
hostnames
According to Spotbugs, that's better practice. It's questionable
whether it makes a big difference, though, especially since the
hash is the cryptographically weak SHA1.
Change-Id: Id293de2bad809d9cc19230bd720184786dc6c226
Signed-off-by: Thomas Wolf
---
.../internal/transport/sshd/OpenSshServerKeyDatabase.java | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
index 85e406f42..d8bf449ac 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
@@ -34,6 +34,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@@ -138,6 +139,8 @@ public class OpenSshServerKeyDatabase
private final List defaultFiles = new ArrayList<>();
+ private Random prng;
+
/**
* Creates a new {@link OpenSshServerKeyDatabase}.
*
@@ -680,7 +683,9 @@ private String createHostKeyLine(Collection patterns,
// or to Apache MINA sshd.
NamedFactory digester = KnownHostDigest.SHA1;
Mac mac = digester.create();
- SecureRandom prng = new SecureRandom();
+ if (prng == null) {
+ prng = new SecureRandom();
+ }
byte[] salt = new byte[mac.getDefaultBlockSize()];
for (SshdSocketAddress address : patterns) {
if (result.length() > 0) {
From 057f1d9123f43aceecf413acc5e759e4a97dc8e4 Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Sat, 13 Nov 2021 18:10:13 +0100
Subject: [PATCH 22/24] ssh: Handle "ProxyJump none" from SSH config file
Since OpenSSH 7.8, the ProxyJump directive accepts the value "none"[1]
to override and clear a setting that might otherwise be contributed by
another (wildcard) host entry.
[1] https://bugzilla.mindrot.org/show_bug.cgi?id=2869
Change-Id: Ia35e82c6f8c58d5c6b8040cda7a07b220f43fc21
Signed-off-by: Thomas Wolf
---
.../jgit/transport/sshd/ApacheSshTest.java | 15 +++++++++++++
.../jgit/transport/sshd/SshdSession.java | 3 ++-
.../eclipse/jgit/transport/SshConstants.java | 22 ++++++++++++++++++-
3 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
index 85626d8ee..ccaf98ced 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java
@@ -354,6 +354,21 @@ public void testJumpHost() throws Exception {
}
}
+ @Test
+ public void testJumpHostNone() throws Exception {
+ // Should not try to go through the non-existing proxy
+ cloneWith("ssh://server/doesntmatter", defaultCloneDir, null, //
+ "Host server", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "ProxyJump none", //
+ "", //
+ "Host *", //
+ "ProxyJump " + TEST_USER + "@localhost:1234");
+ }
+
@Test
public void testJumpHostWrongKeyAtProxy() throws Exception {
// Test that we find the proxy server's URI in the exception message
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
index fb7500ffd..c270b4495 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java
@@ -219,7 +219,8 @@ private List determineHops(List currentHops,
HostConfigEntry hostConfig, String host) throws IOException {
if (currentHops.isEmpty()) {
String jumpHosts = hostConfig.getProperty(SshConstants.PROXY_JUMP);
- if (!StringUtils.isEmptyOrNull(jumpHosts)) {
+ if (!StringUtils.isEmptyOrNull(jumpHosts)
+ && !SshConstants.NONE.equals(jumpHosts)) {
try {
return parseProxyJump(jumpHosts);
} catch (URISyntaxException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java
index 5cd5b334a..212a4e46c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java
@@ -191,6 +191,26 @@ private SshConstants() {
/** Flag value. */
public static final String FALSE = "false";
+ /**
+ * Property value. Some keys accept a special 'none' value to override and
+ * clear a setting otherwise contributed by another host entry, for instance
+ * {@link #PROXY_COMMAND} or {@link #PROXY_JUMP}. Example:
+ *
+ *
+ * Host bastion.example.org
+ * ProxyJump none
+ *
+ * Host *.example.org
+ * ProxyJump bastion.example.org
+ *
+ *
+ * OpenSSH supports this since OpenSSH 7.8.
+ *
+ *
+ * @since 6.0
+ */
+ public static final String NONE = "none";
+
// Default identity file names
/** Name of the default RSA private identity file. */
@@ -202,7 +222,7 @@ private SshConstants() {
/** Name of the default ECDSA private identity file. */
public static final String ID_ECDSA = "id_ecdsa";
- /** Name of the default ECDSA private identity file. */
+ /** Name of the default ED25519 private identity file. */
public static final String ID_ED25519 = "id_ed25519";
/** All known default identity file names. */
From 78b7d9e4fa1bdd3ba27b39190b45f7bdf0b61fff Mon Sep 17 00:00:00 2001
From: Thomas Wolf
Date: Sat, 13 Nov 2021 19:00:58 +0100
Subject: [PATCH 23/24] Typo fix in o.e.j.ssh.{jsch,apache}/README.md
Change-Id: Ia7da92421f8fecb2a175eb23ecfd04a67e0ca8cc
Signed-off-by: Thomas Wolf
---
org.eclipse.jgit.ssh.apache/README.md | 2 +-
org.eclipse.jgit.ssh.jsch/README.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/org.eclipse.jgit.ssh.apache/README.md b/org.eclipse.jgit.ssh.apache/README.md
index 3bbda523b..cba87ac9c 100644
--- a/org.eclipse.jgit.ssh.apache/README.md
+++ b/org.eclipse.jgit.ssh.apache/README.md
@@ -58,4 +58,4 @@ To use a different SSH implementation:
JGit has built-in support for not using any Java SSH implementation but an external SSH
executable. To use an external SSH executable, set environment variable **GIT_SSH** to
the path of the executable. JGit will create a sub-process to run the executable and
-communicate with this sup-process to perform the git operation.
+communicate with this sub-process to perform the git operation.
diff --git a/org.eclipse.jgit.ssh.jsch/README.md b/org.eclipse.jgit.ssh.jsch/README.md
index 48f43f0f8..b64bef3f7 100644
--- a/org.eclipse.jgit.ssh.jsch/README.md
+++ b/org.eclipse.jgit.ssh.jsch/README.md
@@ -56,4 +56,4 @@ FetchCommand fetch = git.fetch()
JGit has built-in support for not using any Java SSH implementation but an external SSH
executable. To use an external SSH executable, set environment variable **GIT_SSH** to
the path of the executable. JGit will create a sub-process to run the executable and
-communicate with this sup-process to perform the git operation.
+communicate with this sub-process to perform the git operation.
From ee28780bf2dfe8574905835d43b5bb0738ad81ad Mon Sep 17 00:00:00 2001
From: Matthias Sohn
Date: Mon, 1 Nov 2021 00:53:10 +0100
Subject: [PATCH 24/24] Make BinaryBlobException stackless
We use BinaryBlobException to signal a binary blob was found and never
make use of its stack trace. Suppress filling in the stack trace to
avoid the performance penalty coming with that.
See https://shipilev.net/blog/2014/exceptional-performance/
Change-Id: Iae1f1c19a1fa8aef4f6569822557171130299958
---
.../src/org/eclipse/jgit/errors/BinaryBlobException.java | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/BinaryBlobException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/BinaryBlobException.java
index 58a121403..768931ca0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/BinaryBlobException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/BinaryBlobException.java
@@ -22,4 +22,9 @@ public class BinaryBlobException extends Exception {
* Construct a BinaryBlobException.
*/
public BinaryBlobException() {}
+
+ @Override
+ public synchronized Throwable fillInStackTrace() {
+ return this;
+ }
}