diff options
507 files changed, 23256 insertions, 2814 deletions
@@ -1,10 +1,18 @@ -Han-Wen Nienhuys <hanwen@google.com> Han-Wen NIenhuys <hanwen@google.com> -Mark Ingram <markdingram@gmail.com> markdingram <markdingram@gmail.com> -Roberto Tyley <roberto.tyley@guardian.co.uk> roberto <roberto.tyley@guardian.co.uk> -Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com> -Saša Živkov <sasa.zivkov@sap.com> Saša Živkov <zivkov@gmail.com> -Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <zivkov@gmail.com> -Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <sop@google.com> -Shawn Pearce <spearce@spearce.org> Shawn Pearce <sop@google.com> -Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <spearce@spearce.org> -Terry Parker <tparker@google.com> tparker <tparker@google.com> +Chris Aniszczyk <caniszczyk@gmail.com> Chris Aniszczyk <zx@eclipsesource.com> +Christian Halstrick <christian.halstrick@sap.com> Christian Halstrick <christian.halstrick@gmail.com> +Dani Megert <Daniel_Megert@ch.ibm.com> Daniel Megert <daniel_megert@ch.ibm.com> +David Pursehouse <david.pursehouse@gmail.com> David Pursehouse <david.pursehouse@sonymobile.com> +Han-Wen Nienhuys <hanwen@google.com> Han-Wen NIenhuys <hanwen@google.com> +Hector Oswaldo Caballero <hector.caballero@ericsson.com> Hector Caballero <hector.caballero@ericsson.com> +Lars Vogel <Lars.Vogel@vogella.com> Lars Vogel <Lars.Vogel@gmail.com> +Mark Ingram <markdingram@gmail.com> markdingram <markdingram@gmail.com> +Markus Duft <markus.duft@ssi-schaefer.com> Markus Duft <markus.duft@salomon.at> +Michael Keppler <michael.keppler@gmx.de> Michael Keppler <Michael.Keppler@gmx.de> +Roberto Tyley <roberto.tyley@guardian.co.uk> roberto <roberto.tyley@guardian.co.uk> +Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com> +Saša Živkov <sasa.zivkov@sap.com> Saša Živkov <zivkov@gmail.com> +Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <zivkov@gmail.com> +Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <sop@google.com> +Shawn Pearce <spearce@spearce.org> Shawn Pearce <sop@google.com> +Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <spearce@spearce.org> +Terry Parker <tparker@google.com> tparker <tparker@google.com> @@ -1,5 +1,18 @@ workspace(name = "jgit") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "bazel_skylib", + sha256 = "bbccf674aa441c266df9894182d80de104cabd19be98be002f6d478aaa31574d", + strip_prefix = "bazel-skylib-2169ae1c374aab4a09aa90e65efe1a3aad4e279b", + urls = ["https://github.com/bazelbuild/bazel-skylib/archive/2169ae1c374aab4a09aa90e65efe1a3aad4e279b.tar.gz"], +) + +load("@bazel_skylib//lib:versions.bzl", "versions") + +versions.check(minimum_bazel_version = "0.19.0") + load("//tools:bazlets.bzl", "load_bazlets") load_bazlets(commit = "3afbeab55ece585dbfc7a980bf7214b24ddbbe86") @@ -10,6 +23,12 @@ load( ) maven_jar( + name = "eddsa", + artifact = "net.i2p.crypto:eddsa:0.3.0", + sha1 = "1901c8d4d8bffb7d79027686cfb91e704217c3e1", +) + +maven_jar( name = "jsch", artifact = "com.jcraft:jsch:0.1.54", sha1 = "da3584329a263616e277e15462b387addd1b208d", @@ -40,6 +59,18 @@ maven_jar( ) maven_jar( + name = "sshd-core", + artifact = "org.apache.sshd:sshd-core:2.0.0", + sha1 = "f4275079a2463cfd2bf1548a80e1683288a8e86b", +) + +maven_jar( + name = "sshd-sftp", + artifact = "org.apache.sshd:sshd-sftp:2.0.0", + sha1 = "a12d64dc2d5d23271a4dc58075e55f9c64a68494", +) + +maven_jar( name = "commons-codec", artifact = "commons-codec:commons-codec:1.10", sha1 = "4b95f4897fa13f2cd904aee711aeafc0c5295cd8", @@ -71,8 +102,8 @@ maven_jar( maven_jar( name = "commons-compress", - artifact = "org.apache.commons:commons-compress:1.15", - sha1 = "b686cd04abaef1ea7bc5e143c080563668eec17e", + artifact = "org.apache.commons:commons-compress:1.18", + sha1 = "1191f9f2bc0c47a8cce69193feb1ff0a8bcb37d5", ) maven_jar( @@ -106,6 +137,32 @@ maven_jar( ) maven_jar( + name = "mockito", + artifact = "org.mockito:mockito-core:2.13.0", + sha1 = "8e372943974e4a121fb8617baced8ebfe46d54f0", +) + +BYTE_BUDDY_VERSION = "1.7.9" + +maven_jar( + name = "byte_buddy", + artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION, + sha1 = "51218a01a882c04d0aba8c028179cce488bbcb58", +) + +maven_jar( + name = "byte_buddy_agent", + artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION, + sha1 = "a6c65f9da7f467ee1f02ff2841ffd3155aee2fc9", +) + +maven_jar( + name = "objenesis", + artifact = "org.objenesis:objenesis:2.6", + sha1 = "639033469776fd37c08358c6b92a4761feb2af4b", +) + +maven_jar( name = "gson", artifact = "com.google.code.gson:gson:2.8.2", sha1 = "3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf", @@ -23,12 +23,20 @@ java_library( java_library( name = "commons-logging", - testonly = 1, visibility = ["//visibility:public"], exports = ["@commons-logging//jar"], ) java_library( + name = "eddsa", + visibility = [ + "//org.eclipse.jgit.ssh.apache:__pkg__", + "//org.eclipse.jgit.ssh.apache.test:__pkg__", + ], + exports = ["@eddsa//jar"], +) + +java_library( name = "gson", visibility = [ "//org.eclipse.jgit.lfs:__pkg__", @@ -60,6 +68,28 @@ java_library( ) java_library( + name = "sshd-core", + visibility = [ + "//org.eclipse.jgit.junit.ssh:__pkg__", + "//org.eclipse.jgit.ssh.apache:__pkg__", + "//org.eclipse.jgit.ssh.apache.test:__pkg__", + "//org.eclipse.jgit.test:__pkg__", + ], + exports = ["@sshd-core//jar"], +) + +java_library( + name = "sshd-sftp", + visibility = [ + "//org.eclipse.jgit.junit.ssh:__pkg__", + "//org.eclipse.jgit.ssh.apache:__pkg__", + "//org.eclipse.jgit.ssh.apache.test:__pkg__", + "//org.eclipse.jgit.test:__pkg__", + ], + exports = ["@sshd-sftp//jar"], +) + +java_library( name = "javaewah", visibility = ["//visibility:public"], exports = ["@javaewah//jar"], @@ -138,6 +168,18 @@ java_library( ) java_library( + name = "mockito", + testonly = 1, + visibility = ["//visibility:public"], + exports = [ + "@byte_buddy//jar", + "@byte_buddy_agent//jar", + "@mockito//jar", + "@objenesis//jar", + ], +) + +java_library( name = "servlet-api", visibility = [ "//org.eclipse.jgit.http.apache:__pkg__", diff --git a/org.eclipse.jgit.ant.test/.classpath b/org.eclipse.jgit.ant.test/.classpath index eca7bdba8f..3e5654f17e 100644 --- a/org.eclipse.jgit.ant.test/.classpath +++ b/org.eclipse.jgit.ant.test/.classpath @@ -2,6 +2,10 @@ <classpath> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="src"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs index 794592dee1..2ca78ff2d0 100644 --- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF index 76770f3f58..ad6407874d 100644 --- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF @@ -4,13 +4,13 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.ant.test Bundle-SymbolicName: org.eclipse.jgit.ant.test -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.ant.tasks;version="[5.1.9,5.2.0)", - org.eclipse.jgit.junit;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", + org.eclipse.jgit.ant.tasks;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)" diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml index 3c133833d6..d75acdba28 100644 --- a/org.eclipse.jgit.ant.test/pom.xml +++ b/org.eclipse.jgit.ant.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ant.test</artifactId> @@ -105,7 +105,7 @@ <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>@{argLine} -Xmx512m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine> + <argLine>-Xmx256m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine> </configuration> </plugin> </plugins> diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs index 565b75c658..94a1c4f10b 100644 --- a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.ant/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.ant/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF index 99ca3b2c85..f0bbb7ef95 100644 --- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF @@ -3,11 +3,11 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.ant Bundle-SymbolicName: org.eclipse.jgit.ant -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)" + org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)" Bundle-Localization: plugin Bundle-Vendor: %Provider-Name -Export-Package: org.eclipse.jgit.ant.tasks;version="5.1.9"; +Export-Package: org.eclipse.jgit.ant.tasks;version="5.2.3"; uses:="org.apache.tools.ant.types,org.apache.tools.ant" diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml index f12d4173de..f68a0152d7 100644 --- a/org.eclipse.jgit.ant/pom.xml +++ b/org.eclipse.jgit.ant/pom.xml @@ -48,7 +48,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ant</artifactId> @@ -72,7 +72,7 @@ <dependency> <groupId>org.apache.ant</groupId> <artifactId>ant</artifactId> - <version>1.9.6</version> + <version>1.10.5</version> </dependency> </dependencies> diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs index 13c32a6d94..ef6f5e732f 100644 --- a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.archive/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.archive/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF index d1ed4da662..8c854b8d93 100644 --- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.archive Bundle-SymbolicName: org.eclipse.jgit.archive -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -13,15 +13,15 @@ Import-Package: org.apache.commons.compress.archivers;version="[1.4,2.0)", org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)", org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)", org.apache.commons.compress.compressors.xz;version="[1.4,2.0)", - org.eclipse.jgit.api;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.nls;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", + org.eclipse.jgit.api;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", org.osgi.framework;version="[1.3.0,2.0.0)" Bundle-ActivationPolicy: lazy Bundle-Activator: org.eclipse.jgit.archive.FormatActivator -Export-Package: org.eclipse.jgit.archive;version="5.1.9"; +Export-Package: org.eclipse.jgit.archive;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.api, org.apache.commons.compress.archivers, diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF index 5c19758097..aab58a6300 100644 --- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.archive - Sources Bundle-SymbolicName: org.eclipse.jgit.archive.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 5.1.9.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.1.9.qualifier";roots="." +Bundle-Version: 5.2.3.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.archive;version="5.2.3.qualifier";roots="." diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml index cb1e66b08a..f36a3a6587 100644 --- a/org.eclipse.jgit.archive/pom.xml +++ b/org.eclipse.jgit.archive/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.archive</artifactId> diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java index 9ed60d941a..aee80d8307 100644 --- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java +++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java @@ -42,7 +42,7 @@ */ package org.eclipse.jgit.archive; -import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING; +import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.io.OutputStream; @@ -85,7 +85,7 @@ public final class TarFormat extends BaseFormat implements public ArchiveOutputStream createArchiveOutputStream(OutputStream s, Map<String, Object> o) throws IOException { TarArchiveOutputStream out = new TarArchiveOutputStream(s, - CHARACTER_ENCODING); + UTF_8.name()); out.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); out.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); return applyFormatOptions(out, o); @@ -99,8 +99,7 @@ public final class TarFormat extends BaseFormat implements if (mode == FileMode.SYMLINK) { final TarArchiveEntry entry = new TarArchiveEntry( path, TarConstants.LF_SYMLINK); - entry.setLinkName(new String( - loader.getCachedBytes(100), CHARACTER_ENCODING)); + entry.setLinkName(new String(loader.getCachedBytes(100), UTF_8)); out.putArchiveEntry(entry); out.closeArchiveEntry(); return; diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs index 565b75c658..94a1c4f10b 100644 --- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF index 5177a604c9..fb3dec75ca 100644 --- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit.http.apache Bundle-SymbolicName: org.eclipse.jgit.http.apache -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-Localization: plugin Bundle-Vendor: %Provider-Name @@ -23,10 +23,11 @@ Import-Package: org.apache.http;version="[4.3.0,5.0.0)", org.apache.http.impl.client;version="[4.3.0,5.0.0)", org.apache.http.impl.conn;version="[4.3.0,5.0.0)", org.apache.http.params;version="[4.3.0,5.0.0)", - org.eclipse.jgit.nls;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)" -Export-Package: org.eclipse.jgit.transport.http.apache;version="5.1.9"; + org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)" +Export-Package: org.eclipse.jgit.transport.http.apache;version="5.2.3"; uses:="org.apache.http.client, org.eclipse.jgit.transport.http, org.apache.http.entity, diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml index b9e11f7e97..a1cb74c008 100644 --- a/org.eclipse.jgit.http.apache/pom.xml +++ b/org.eclipse.jgit.http.apache/pom.xml @@ -48,7 +48,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.apache</artifactId> diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java index 77c5dc0f3e..4ac81a54df 100644 --- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java +++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java @@ -58,10 +58,13 @@ import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; @@ -90,6 +93,7 @@ import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.BasicHttpClientConnectionManager; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.transport.http.HttpConnection; import org.eclipse.jgit.transport.http.apache.internal.HttpApacheText; import org.eclipse.jgit.util.TemporaryBuffer; @@ -347,11 +351,17 @@ public class HttpClientConnection implements HttpConnection { // will return only the first field /** {@inheritDoc} */ @Override - public String getHeaderField(String name) { + public String getHeaderField(@NonNull String name) { Header header = resp.getFirstHeader(name); return (header == null) ? null : header.getValue(); } + @Override + public List<String> getHeaderFields(@NonNull String name) { + return Collections.unmodifiableList(Arrays.asList(resp.getHeaders(name)) + .stream().map(Header::getValue).collect(Collectors.toList())); + } + /** {@inheritDoc} */ @Override public int getContentLength() { diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs index 565b75c658..94a1c4f10b 100644 --- a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.http.server/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF index dd469806e2..b65ee844d5 100644 --- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF @@ -3,13 +3,13 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.http.server Bundle-SymbolicName: org.eclipse.jgit.http.server -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name -Export-Package: org.eclipse.jgit.http.server;version="5.1.9", - org.eclipse.jgit.http.server.glue;version="5.1.9"; +Export-Package: org.eclipse.jgit.http.server;version="5.2.3", + org.eclipse.jgit.http.server.glue;version="5.2.3"; uses:="javax.servlet,javax.servlet.http", - org.eclipse.jgit.http.server.resolver;version="5.1.9"; + org.eclipse.jgit.http.server.resolver;version="5.2.3"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.lib, org.eclipse.jgit.transport, @@ -18,12 +18,13 @@ Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: javax.servlet;version="[2.5.0,3.2.0)", javax.servlet.http;version="[2.5.0,3.2.0)", - org.eclipse.jgit.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.nls;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)" + org.eclipse.jgit.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.transport.parser;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)" diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml index 472c942507..78191ef3cf 100644 --- a/org.eclipse.jgit.http.server/pom.xml +++ b/org.eclipse.jgit.http.server/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.server</artifactId> diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ClientVersionUtil.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ClientVersionUtil.java index 38a9ea75e7..18b9b2a484 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ClientVersionUtil.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ClientVersionUtil.java @@ -43,18 +43,12 @@ package org.eclipse.jgit.http.server; -import static org.eclipse.jgit.http.server.ServletUtils.isChunked; - import javax.servlet.http.HttpServletRequest; /** * Parses Git client User-Agent strings. */ public class ClientVersionUtil { - private static final int[] v1_7_5 = { 1, 7, 5 }; - private static final int[] v1_7_8_6 = { 1, 7, 8, 6 }; - private static final int[] v1_7_9 = { 1, 7, 9 }; - /** * An invalid version of Git * @@ -174,17 +168,11 @@ public class ClientVersionUtil { * @param version * parsed version of the Git client software. * @return true if the bug is present. + * @deprecated no widely used Git versions need this any more */ + @Deprecated public static boolean hasPushStatusBug(int[] version) { - int cmp = compare(version, v1_7_8_6); - if (cmp < 0) - return true; // Everything before 1.7.8.6 is known broken. - else if (cmp == 0) - return false; // 1.7.8.6 contained the bug fix. - - if (compare(version, v1_7_9) <= 0) - return true; // 1.7.9 shipped before 1.7.8.6 and has the bug. - return false; // 1.7.9.1 and later are fixed. + return false; } /** @@ -198,10 +186,12 @@ public class ClientVersionUtil { * @param request * incoming HTTP request. * @return true if the client has the chunked encoding bug. + * @deprecated no widely used Git versions need this any more */ + @Deprecated public static boolean hasChunkedEncodingRequestBug( int[] version, HttpServletRequest request) { - return compare(version, v1_7_5) == 0 && isChunked(request); + return false; } private ClientVersionUtil() { diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java index b9ca12bf3d..ee4b32efb7 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java @@ -63,6 +63,7 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jgit.internal.transport.parser.FirstWant; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.transport.PacketLineIn; import org.eclipse.jgit.transport.PacketLineOut; @@ -246,9 +247,9 @@ public class GitSmartHttpTools { // not have an UploadPack, or it might not have read any of the request. // So, cheat and read the first line. String line = new PacketLineIn(req.getInputStream()).readString(); - UploadPack.FirstLine parsed = new UploadPack.FirstLine(line); - return (parsed.getOptions().contains(OPTION_SIDE_BAND) - || parsed.getOptions().contains(OPTION_SIDE_BAND_64K)); + FirstWant parsed = FirstWant.fromLine(line); + return (parsed.getCapabilities().contains(OPTION_SIDE_BAND) + || parsed.getCapabilities().contains(OPTION_SIDE_BAND_64K)); } catch (IOException e) { // Probably the connection is closed and a subsequent write will fail, but // try it just in case. diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java index 1a9d192450..b084b0db54 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java @@ -69,7 +69,7 @@ class InfoRefsServlet extends HttpServlet { // Assume a dumb client and send back the dumb client // version of the info/refs file. rsp.setContentType(HttpSupport.TEXT_PLAIN); - rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING); + rsp.setCharacterEncoding(UTF_8.name()); final Repository db = getRepository(req); try (OutputStreamWriter out = new OutputStreamWriter( diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java index a46652ee42..aed36560a8 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java @@ -43,14 +43,10 @@ package org.eclipse.jgit.http.server; -import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; -import static org.eclipse.jgit.http.server.ClientVersionUtil.hasChunkedEncodingRequestBug; -import static org.eclipse.jgit.http.server.ClientVersionUtil.hasPushStatusBug; -import static org.eclipse.jgit.http.server.ClientVersionUtil.parseVersion; import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK; import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_REQUEST_TYPE; import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_RESULT_TYPE; @@ -174,13 +170,6 @@ class ReceivePackServlet extends HttpServlet { return; } - int[] version = parseVersion(req.getHeader(HDR_USER_AGENT)); - if (hasChunkedEncodingRequestBug(version, req)) { - GitSmartHttpTools.sendError(req, rsp, SC_BAD_REQUEST, "\n\n" - + HttpServerText.get().clientHas175ChunkedEncodingBug); - return; - } - SmartOutputStream out = new SmartOutputStream(req, rsp, false) { @Override public void flush() throws IOException { @@ -191,7 +180,6 @@ class ReceivePackServlet extends HttpServlet { ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER); try { rp.setBiDirectionalPipe(false); - rp.setEchoCommandFailures(hasPushStatusBug(version)); rsp.setContentType(RECEIVE_PACK_RESULT_TYPE); rp.receive(getInputStream(req), out, null); diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java index 9601c8cafa..b6d73b5591 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.http.server; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP; import static org.eclipse.jgit.util.HttpSupport.ENCODING_X_GZIP; import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING; @@ -191,9 +192,9 @@ public final class ServletUtils { public static void sendPlainText(final String content, final HttpServletRequest req, final HttpServletResponse rsp) throws IOException { - final byte[] raw = content.getBytes(Constants.CHARACTER_ENCODING); + final byte[] raw = content.getBytes(UTF_8); rsp.setContentType(TEXT_PLAIN); - rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING); + rsp.setCharacterEncoding(UTF_8.name()); send(raw, req, rsp); } diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java index ad5e8d479e..06bdce679d 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java @@ -105,6 +105,7 @@ class SmartOutputStream extends TemporaryBuffer { // If output hasn't started yet, the entire thing fit into our // buffer. Try to use a proper Content-Length header, and also // deflate the response with gzip if it will be smaller. + @SuppressWarnings("resource") TemporaryBuffer out = this; if (256 < out.length() && acceptsGzipEncoding(req)) { diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java index ca6b749e75..0f4037144a 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java @@ -43,13 +43,10 @@ package org.eclipse.jgit.http.server; -import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; -import static org.eclipse.jgit.http.server.ClientVersionUtil.hasChunkedEncodingRequestBug; -import static org.eclipse.jgit.http.server.ClientVersionUtil.parseVersion; import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK; import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_REQUEST_TYPE; import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_RESULT_TYPE; @@ -193,13 +190,6 @@ class UploadPackServlet extends HttpServlet { return; } - int[] version = parseVersion(req.getHeader(HDR_USER_AGENT)); - if (hasChunkedEncodingRequestBug(version, req)) { - GitSmartHttpTools.sendError(req, rsp, SC_BAD_REQUEST, "\n\n" - + HttpServerText.get().clientHas175ChunkedEncodingBug); - return; - } - SmartOutputStream out = new SmartOutputStream(req, rsp, false) { @Override public void flush() throws IOException { diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java index b2b4748989..2a03633580 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinder.java @@ -57,7 +57,7 @@ public interface ServletBinder { * the filter to trigger while processing the path. * @return {@code this}. */ - public ServletBinder through(Filter filter); + ServletBinder through(Filter filter); /** * Set the servlet to execute on this path @@ -65,5 +65,5 @@ public interface ServletBinder { * @param servlet * the servlet to execute on this path. */ - public void with(HttpServlet servlet); + void with(HttpServlet servlet); } diff --git a/org.eclipse.jgit.http.test/.classpath b/org.eclipse.jgit.http.test/.classpath index e6014c7122..b25838024a 100644 --- a/org.eclipse.jgit.http.test/.classpath +++ b/org.eclipse.jgit.http.test/.classpath @@ -2,7 +2,15 @@ <classpath> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry kind="src" path="tst"/> - <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="tst"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="src" path="src"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs index 794592dee1..2ca78ff2d0 100644 --- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.http.test/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index 4b8bff81b6..495d36d6b5 100644 --- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.http.test Bundle-SymbolicName: org.eclipse.jgit.http.test -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -28,25 +28,25 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)", org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)", - org.eclipse.jgit.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.http.server;version="[5.1.9,5.2.0)", - org.eclipse.jgit.http.server.glue;version="[5.1.9,5.2.0)", - org.eclipse.jgit.http.server.resolver;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.reftable;version="[5.1.9,5.2.0)", - org.eclipse.jgit.junit;version="[5.1.9,5.2.0)", - org.eclipse.jgit.junit.http;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.nls;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.http.apache;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", + org.eclipse.jgit.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.http.server;version="[5.2.3,5.3.0)", + org.eclipse.jgit.http.server.glue;version="[5.2.3,5.3.0)", + org.eclipse.jgit.http.server.resolver;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.reftable;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit.http;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.http.apache;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", org.hamcrest;version="[1.1.0,2.0.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)", diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml index fa0d8ea217..e8e697e1ac 100644 --- a/org.eclipse.jgit.http.test/pom.xml +++ b/org.eclipse.jgit.http.test/pom.xml @@ -51,7 +51,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.http.test</artifactId> @@ -139,7 +139,7 @@ <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory} -Xmx512m</argLine> + <argLine>-Djava.io.tmpdir=${project.build.directory} -Xmx300m</argLine> <includes> <include>**/*Test.java</include> <include>**/*Tests.java</include> diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/server/ClientVersionUtilTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/server/ClientVersionUtilTest.java index c7260e325b..9dca279912 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/server/ClientVersionUtilTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/server/ClientVersionUtilTest.java @@ -43,11 +43,8 @@ package org.eclipse.jgit.http.server; -import static org.eclipse.jgit.http.server.ClientVersionUtil.hasPushStatusBug; import static org.eclipse.jgit.http.server.ClientVersionUtil.invalidVersion; import static org.eclipse.jgit.http.server.ClientVersionUtil.parseVersion; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import org.junit.Assert; import org.junit.Test; @@ -67,18 +64,6 @@ public class ClientVersionUtilTest { assertEquals(ClientVersionUtil.toString(invalidVersion()), parseVersion("foo")); } - @Test - public void testPushStatusBug() { - assertTrue(hasPushStatusBug(parseVersion("git/1.6.6"))); - assertTrue(hasPushStatusBug(parseVersion("git/1.6.6.1"))); - assertTrue(hasPushStatusBug(parseVersion("git/1.7.9"))); - - assertFalse(hasPushStatusBug(parseVersion("git/1.7.8.6"))); - assertFalse(hasPushStatusBug(parseVersion("git/1.7.9.1"))); - assertFalse(hasPushStatusBug(parseVersion("git/1.7.9.2"))); - assertFalse(hasPushStatusBug(parseVersion("git/1.7.10"))); - } - private static void assertEquals(String exp, int[] act) { Assert.assertEquals(exp, ClientVersionUtil.toString(act)); } diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java index 82e79b8e50..553c340a73 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java @@ -147,6 +147,7 @@ public class FileResolverTest extends LocalDiskRepositoryTestCase { try { resolver.open(null, name); + fail("opened non-git repository"); } catch (RepositoryNotFoundException e) { assertEquals("repository not found: " + name, e.getMessage()); diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java index 725a5902df..59c2b4e677 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java @@ -43,11 +43,14 @@ package org.eclipse.jgit.http.test; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.URI; @@ -82,7 +85,8 @@ public class RegexPipelineTest extends HttpTestCase { protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { res.setStatus(200); - PrintWriter out = new PrintWriter(res.getOutputStream()); + PrintWriter out = new PrintWriter(new BufferedWriter( + new OutputStreamWriter(res.getOutputStream(), UTF_8))); out.write(name); out.write("\n"); out.write(String.valueOf(req.getServletPath())); @@ -120,7 +124,8 @@ public class RegexPipelineTest extends HttpTestCase { c = ((HttpURLConnection) uri.resolve("/a").toURL() .openConnection()); assertEquals(200, c.getResponseCode()); - r = new BufferedReader(new InputStreamReader(c.getInputStream())); + r = new BufferedReader( + new InputStreamReader(c.getInputStream(), UTF_8)); assertEquals("test", r.readLine()); assertEquals("", r.readLine()); assertEquals("/a", r.readLine()); @@ -129,7 +134,8 @@ public class RegexPipelineTest extends HttpTestCase { c = ((HttpURLConnection) uri.resolve("/b").toURL() .openConnection()); assertEquals(200, c.getResponseCode()); - r = new BufferedReader(new InputStreamReader(c.getInputStream())); + r = new BufferedReader( + new InputStreamReader(c.getInputStream(), UTF_8)); assertEquals("test", r.readLine()); assertEquals("", r.readLine()); assertEquals("/b", r.readLine()); @@ -155,7 +161,8 @@ public class RegexPipelineTest extends HttpTestCase { c = ((HttpURLConnection) uri.resolve("/a").toURL() .openConnection()); assertEquals(200, c.getResponseCode()); - r = new BufferedReader(new InputStreamReader(c.getInputStream())); + r = new BufferedReader( + new InputStreamReader(c.getInputStream(), UTF_8)); assertEquals("test1", r.readLine()); assertEquals("", r.readLine()); assertEquals("/a", r.readLine()); @@ -183,7 +190,8 @@ public class RegexPipelineTest extends HttpTestCase { c = ((HttpURLConnection) uri.resolve("/a/b").toURL() .openConnection()); assertEquals(200, c.getResponseCode()); - r = new BufferedReader(new InputStreamReader(c.getInputStream())); + r = new BufferedReader( + new InputStreamReader(c.getInputStream(), UTF_8)); assertEquals("test1", r.readLine()); assertEquals("", r.readLine()); // No RegexGroupFilter defaults to first group. @@ -193,7 +201,8 @@ public class RegexPipelineTest extends HttpTestCase { c = ((HttpURLConnection) uri.resolve("/c/d").toURL() .openConnection()); assertEquals(200, c.getResponseCode()); - r = new BufferedReader(new InputStreamReader(c.getInputStream())); + r = new BufferedReader( + new InputStreamReader(c.getInputStream(), UTF_8)); assertEquals("test2", r.readLine()); assertEquals("", r.readLine()); assertEquals("/c", r.readLine()); @@ -202,7 +211,8 @@ public class RegexPipelineTest extends HttpTestCase { c = ((HttpURLConnection) uri.resolve("/e/f/g").toURL() .openConnection()); assertEquals(200, c.getResponseCode()); - r = new BufferedReader(new InputStreamReader(c.getInputStream())); + r = new BufferedReader( + new InputStreamReader(c.getInputStream(), UTF_8)); assertEquals("test3", r.readLine()); assertEquals("/e/f", r.readLine()); assertEquals("/g", r.readLine()); diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java index 2b4a2511ed..b26324d4f2 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java @@ -298,7 +298,7 @@ public class SmartClientSmartServerTest extends HttpTestCase { throws IOException, ServletException { final HttpServletResponse r = (HttpServletResponse) response; r.setContentType("text/plain"); - r.setCharacterEncoding(Constants.CHARACTER_ENCODING); + r.setCharacterEncoding(UTF_8.name()); try (PrintWriter w = r.getWriter()) { w.print("OK"); } diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/transport/http/apache/HttpClientConnectionTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/transport/http/apache/HttpClientConnectionTest.java index 72c4921de6..8a0d59c748 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/transport/http/apache/HttpClientConnectionTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/transport/http/apache/HttpClientConnectionTest.java @@ -76,7 +76,7 @@ public class HttpClientConnectionTest { assertTrue(headerValues.contains("NTLM")); } - private class HttpResponseMock extends AbstractHttpMessage + private static class HttpResponseMock extends AbstractHttpMessage implements HttpResponse { @Override public StatusLine getStatusLine() { diff --git a/org.eclipse.jgit.junit.http/.classpath b/org.eclipse.jgit.junit.http/.classpath index b862a296d3..3e5654f17e 100644 --- a/org.eclipse.jgit.junit.http/.classpath +++ b/org.eclipse.jgit.junit.http/.classpath @@ -1,7 +1,11 @@ -<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs index 794592dee1..2ca78ff2d0 100644 --- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF index 8d6357e792..8432c451bc 100644 --- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.junit.http Bundle-SymbolicName: org.eclipse.jgit.junit.http -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy @@ -22,16 +22,16 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)", org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.ssl;version="[9.4.5,10.0.0)", - org.eclipse.jgit.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.http.server;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.junit;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)", + org.eclipse.jgit.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.http.server;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)", org.junit;version="[4.12,5.0.0)" -Export-Package: org.eclipse.jgit.junit.http;version="5.1.9"; +Export-Package: org.eclipse.jgit.junit.http;version="5.2.3"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.junit, javax.servlet.http, diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml index cda2f66236..b766974442 100644 --- a/org.eclipse.jgit.junit.http/pom.xml +++ b/org.eclipse.jgit.junit.http/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit.http</artifactId> diff --git a/org.eclipse.jgit.junit.ssh/.classpath b/org.eclipse.jgit.junit.ssh/.classpath new file mode 100644 index 0000000000..eca7bdba8f --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.jgit.junit.ssh/.gitignore b/org.eclipse.jgit.junit.ssh/.gitignore new file mode 100644 index 0000000000..934e0e06ff --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.gitignore @@ -0,0 +1,2 @@ +/bin +/target diff --git a/org.eclipse.jgit.junit.ssh/.project b/org.eclipse.jgit.junit.ssh/.project new file mode 100644 index 0000000000..3a0c494155 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.project @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.jgit.junit.ssh</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..f77db3b723 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Sat Dec 20 21:21:24 CET 2008 +eclipse.preferences.version=1 +encoding/<project>=UTF-8 diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000000..9f733eeea7 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,3 @@ +#Mon Mar 24 18:55:56 EDT 2008 +eclipse.preferences.version=1 +line.separator=\n diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..2ca78ff2d0 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,399 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +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=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.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=ignore +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=ignore +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=warning +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=warning +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.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=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error +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.source=1.8 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +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_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +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_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +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_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=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_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_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=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_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_method_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.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.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.insert_new_line_before_root_tags=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=true +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_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_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=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_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_and_in_type_parameter=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_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_superinterfaces=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_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_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_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=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_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_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_superinterfaces=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_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_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_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_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_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_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_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +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_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +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.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000000..fef3713825 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.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=12 +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=<?xml version\="1.0" encoding\="UTF-8"?><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.junit.ssh/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 0000000000..823c0f56ae --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.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.junit.ssh/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.mylyn.team.ui.prefs new file mode 100644 index 0000000000..2fca432276 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.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.junit.ssh/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.api.tools.prefs new file mode 100644 index 0000000000..c0030ded71 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.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.junit.ssh/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.junit.ssh/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 0000000000..82793f2d27 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/.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.junit.ssh/BUILD b/org.eclipse.jgit.junit.ssh/BUILD new file mode 100644 index 0000000000..e9a04c7173 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/BUILD @@ -0,0 +1,15 @@ +package(default_visibility = ["//visibility:public"]) + +java_library( + name = "junit-ssh", + testonly = 1, + srcs = glob(["src/**/*.java"]), + resource_strip_prefix = "org.eclipse.jgit.junit.ssh/resources", + resources = glob(["resources/**"]), + deps = [ + "//lib:sshd-core", + "//lib:sshd-sftp", + # We want these deps to be provided_deps + "//org.eclipse.jgit:jgit", + ], +) diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..14674996cd --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF @@ -0,0 +1,36 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %plugin_name +Automatic-Module-Name: org.eclipse.jgit.junit.ssh +Bundle-SymbolicName: org.eclipse.jgit.junit.ssh +Bundle-Version: 5.2.3.qualifier +Bundle-Localization: plugin +Bundle-Vendor: %provider_name +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Import-Package: org.apache.sshd.common;version="[2.0.0,2.1.0)", + org.apache.sshd.common.config.keys;version="[2.0.0,2.1.0)", + org.apache.sshd.common.file.virtualfs;version="[2.0.0,2.1.0)", + org.apache.sshd.common.helpers;version="[2.0.0,2.1.0)", + org.apache.sshd.common.io;version="[2.0.0,2.1.0)", + org.apache.sshd.common.kex;version="[2.0.0,2.1.0)", + org.apache.sshd.common.keyprovider;version="[2.0.0,2.1.0)", + org.apache.sshd.common.session;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util.buffer;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util.logging;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util.security;version="[2.0.0,2.1.0)", + org.apache.sshd.server;version="[2.0.0,2.1.0)", + org.apache.sshd.server.auth;version="[2.0.0,2.1.0)", + org.apache.sshd.server.auth.gss;version="[2.0.0,2.1.0)", + org.apache.sshd.server.auth.keyboard;version="[2.0.0,2.1.0)", + org.apache.sshd.server.auth.password;version="[2.0.0,2.1.0)", + org.apache.sshd.server.command;version="[2.0.0,2.1.0)", + org.apache.sshd.server.session;version="[2.0.0,2.1.0)", + org.apache.sshd.server.shell;version="[2.0.0,2.1.0)", + org.apache.sshd.server.subsystem;version="[2.0.0,2.1.0)", + org.apache.sshd.server.subsystem.sftp;version="[2.0.0,2.1.0)", + org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.slf4j;version="[1.7.0,2.0.0)" +Export-Package: org.eclipse.jgit.junit.ssh;version="5.2.3" diff --git a/org.eclipse.jgit.junit.ssh/build.properties b/org.eclipse.jgit.junit.ssh/build.properties new file mode 100644 index 0000000000..aa1a008269 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.properties diff --git a/org.eclipse.jgit.junit.ssh/plugin.properties b/org.eclipse.jgit.junit.ssh/plugin.properties new file mode 100644 index 0000000000..5689b9472f --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/plugin.properties @@ -0,0 +1,2 @@ +plugin_name=JGit JUnit Ssh Utility Classes +provider_name=Eclipse JGit diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml new file mode 100644 index 0000000000..4cd68eb255 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/pom.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2010, Jens Baumgart <jens.baumgart@sap.com> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit-parent</artifactId> + <version>5.2.3-SNAPSHOT</version> + </parent> + + <artifactId>org.eclipse.jgit.junit.ssh</artifactId> + <name>JGit - JUnit Ssh Utility Classes</name> + + <description> + Utility classes to support Ssh based JUnit testing of JGit applications. + </description> + + <properties> + <translate-qualifier/> + </properties> + + <dependencies> + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-core</artifactId> + <version>${apache-sshd-version}</version> + </dependency> + + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-sftp</artifactId> + <version>${apache-sshd-version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <sourceDirectory>src/</sourceDirectory> + + <resources> + <resource> + <directory>.</directory> + <includes> + <include>plugin.properties</include> + </includes> + </resource> + </resources> + + <plugins> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestFile>${bundle-manifest}</manifestFile> + </archive> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java new file mode 100644 index 0000000000..f5af2e5ce1 --- /dev/null +++ b/org.eclipse.jgit.junit.ssh/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.junit.ssh; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.PublicKey; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.SshConstants; +import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; +import org.apache.sshd.common.config.keys.KeyUtils; +import org.apache.sshd.common.config.keys.PublicKeyEntryResolver; +import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory; +import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.util.buffer.Buffer; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.server.ServerAuthenticationManager; +import org.apache.sshd.server.SshServer; +import org.apache.sshd.server.auth.UserAuth; +import org.apache.sshd.server.auth.gss.GSSAuthenticator; +import org.apache.sshd.server.auth.gss.UserAuthGSS; +import org.apache.sshd.server.auth.gss.UserAuthGSSFactory; +import org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator; +import org.apache.sshd.server.command.AbstractCommandSupport; +import org.apache.sshd.server.command.Command; +import org.apache.sshd.server.session.ServerSession; +import org.apache.sshd.server.shell.UnknownCommand; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.ReceivePack; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.UploadPack; + +/** + * A simple ssh/sftp git <em>test</em> server based on Apache MINA sshd. + * <p> + * Supports only a single repository. Authenticates only the given test user + * against his given test public key. Supports fetch and push. + * </p> + * + * @since 5.2 + */ +public class SshTestGitServer { + + @NonNull + protected final String testUser; + + @NonNull + protected final Repository repository; + + @NonNull + protected final List<KeyPair> hostKeys = new ArrayList<>(); + + protected final SshServer server; + + @NonNull + protected PublicKey testKey; + + private final ExecutorService executorService = Executors + .newFixedThreadPool(2); + + /** + * Creates a ssh git <em>test</em> server. It serves one single repository, + * and accepts public-key authentication for exactly one test user. + * + * @param testUser + * user name of the test user + * @param testKey + * <em>private</em> key file of the test user; the server will + * only user the public key from it + * @param repository + * to serve + * @param hostKey + * the unencrypted private key to use as host key + * @throws IOException + * @throws GeneralSecurityException + */ + public SshTestGitServer(@NonNull String testUser, @NonNull Path testKey, + @NonNull Repository repository, @NonNull byte[] hostKey) + throws IOException, GeneralSecurityException { + this.testUser = testUser; + setTestUserPublicKey(testKey); + this.repository = repository; + server = SshServer.setUpDefaultServer(); + // Set host key + try (ByteArrayInputStream in = new ByteArrayInputStream(hostKey)) { + hostKeys.add(SecurityUtils.loadKeyPairIdentity("", in, null)); + } catch (IOException | GeneralSecurityException e) { + // Ignore. + } + server.setKeyPairProvider(() -> hostKeys); + + configureAuthentication(); + + List<NamedFactory<Command>> subsystems = configureSubsystems(); + if (!subsystems.isEmpty()) { + server.setSubsystemFactories(subsystems); + } + + configureShell(); + + server.setCommandFactory(command -> { + if (command.startsWith(RemoteConfig.DEFAULT_UPLOAD_PACK)) { + return new GitUploadPackCommand(command, executorService); + } else if (command.startsWith(RemoteConfig.DEFAULT_RECEIVE_PACK)) { + return new GitReceivePackCommand(command, executorService); + } + return new UnknownCommand(command); + }); + } + + private static class FakeUserAuthGSS extends UserAuthGSS { + @Override + protected Boolean doAuth(Buffer buffer, boolean initial) + throws Exception { + // We always reply that we did do this, but then we fail at the + // first token message. That way we can test that the client-side + // sends the correct initial request and then is skipped correctly, + // even if it causes a GSSException if Kerberos isn't configured at + // all. + if (initial) { + ServerSession session = getServerSession(); + Buffer b = session.createBuffer( + SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST); + b.putBytes(KRB5_MECH.getDER()); + session.writePacket(b); + return null; + } + return Boolean.FALSE; + } + } + + private List<NamedFactory<UserAuth>> getAuthFactories() { + List<NamedFactory<UserAuth>> authentications = new ArrayList<>(); + authentications.add(new UserAuthGSSFactory() { + @Override + public UserAuth create() { + return new FakeUserAuthGSS(); + } + }); + authentications.add( + ServerAuthenticationManager.DEFAULT_USER_AUTH_PUBLIC_KEY_FACTORY); + authentications.add( + ServerAuthenticationManager.DEFAULT_USER_AUTH_KB_INTERACTIVE_FACTORY); + authentications.add( + ServerAuthenticationManager.DEFAULT_USER_AUTH_PASSWORD_FACTORY); + return authentications; + } + + /** + * Configures the authentication mechanisms of this test server. Invoked + * from the constructor. The default sets up public key authentication for + * the test user, and a gssapi-with-mic authenticator that pretends to + * support this mechanism, but that then refuses to authenticate anyone. + */ + protected void configureAuthentication() { + server.setUserAuthFactories(getAuthFactories()); + // Disable some authentications + server.setPasswordAuthenticator(null); + server.setKeyboardInteractiveAuthenticator(null); + server.setHostBasedAuthenticator(null); + // Pretend we did gssapi-with-mic. + server.setGSSAuthenticator(new GSSAuthenticator() { + @Override + public boolean validateInitialUser(ServerSession session, + String user) { + return false; + } + }); + // Accept only the test user/public key + server.setPublickeyAuthenticator((userName, publicKey, session) -> { + return SshTestGitServer.this.testUser.equals(userName) && KeyUtils + .compareKeys(SshTestGitServer.this.testKey, publicKey); + }); + } + + /** + * Configures the test server's subsystems (sftp, scp). Invoked from the + * constructor. The default provides a simple SFTP setup with the root + * directory as the given repository's .git directory's parent. (I.e., at + * the directory containing the .git directory.) + * + * @return A possibly empty collection of subsystems. + */ + @NonNull + protected List<NamedFactory<Command>> configureSubsystems() { + // SFTP. + server.setFileSystemFactory(new VirtualFileSystemFactory() { + + @Override + protected Path computeRootDir(Session session) throws IOException { + return SshTestGitServer.this.repository.getDirectory() + .getParentFile().getAbsoluteFile().toPath(); + } + }); + return Collections + .singletonList((new SftpSubsystemFactory.Builder()).build()); + } + + /** + * Configures shell access for the test server. The default provides no + * shell at all. + */ + protected void configureShell() { + // No shell + server.setShellFactory(null); + } + + /** + * Adds an additional host key to the server. + * + * @param key + * path to the private key file; should not be encrypted + * @param inFront + * whether to add the new key before other existing keys + * @throws IOException + * if the file denoted by the {@link Path} {@code key} cannot be + * read + * @throws GeneralSecurityException + * if the key contained in the file cannot be read + */ + public void addHostKey(@NonNull Path key, boolean inFront) + throws IOException, GeneralSecurityException { + try (InputStream in = Files.newInputStream(key)) { + KeyPair pair = SecurityUtils.loadKeyPairIdentity(key.toString(), in, + null); + if (inFront) { + hostKeys.add(0, pair); + } else { + hostKeys.add(pair); + } + } + } + + /** + * Enable password authentication. The server will accept the test user's + * name, converted to all upper-case, as password. + */ + public void enablePasswordAuthentication() { + server.setPasswordAuthenticator((user, pwd, session) -> { + return testUser.equals(user) + && testUser.toUpperCase(Locale.ROOT).equals(pwd); + }); + } + + /** + * Enable keyboard-interactive authentication. The server will accept the + * test user's name, converted to all upper-case, as password. + */ + public void enableKeyboardInteractiveAuthentication() { + server.setPasswordAuthenticator((user, pwd, session) -> { + return testUser.equals(user) + && testUser.toUpperCase(Locale.ROOT).equals(pwd); + }); + server.setKeyboardInteractiveAuthenticator( + DefaultKeyboardInteractiveAuthenticator.INSTANCE); + } + + /** + * Starts the test server, listening on a random port. + * + * @return the port the server listens on; test clients should connect to + * that port + * @throws IOException + */ + public int start() throws IOException { + server.start(); + return server.getPort(); + } + + /** + * Stops the test server. + * + * @throws IOException + */ + public void stop() throws IOException { + executorService.shutdownNow(); + server.stop(true); + } + + public void setTestUserPublicKey(Path key) + throws IOException, GeneralSecurityException { + this.testKey = AuthorizedKeyEntry.readAuthorizedKeys(key).get(0) + .resolvePublicKey(PublicKeyEntryResolver.IGNORING); + } + + private class GitUploadPackCommand extends AbstractCommandSupport { + + protected GitUploadPackCommand(String command, + ExecutorService executorService) { + super(command, executorService, false); + } + + @Override + public void run() { + UploadPack uploadPack = new UploadPack(repository); + String gitProtocol = getEnvironment().getEnv().get("GIT_PROTOCOL"); + if (gitProtocol != null) { + uploadPack + .setExtraParameters(Collections.singleton(gitProtocol)); + } + try { + uploadPack.upload(getInputStream(), getOutputStream(), + getErrorStream()); + onExit(0); + } catch (IOException e) { + log.warn( + MessageFormat.format("Could not run {0}", getCommand()), + e); + onExit(-1, e.toString()); + } + } + + } + + private class GitReceivePackCommand extends AbstractCommandSupport { + + protected GitReceivePackCommand(String command, + ExecutorService executorService) { + super(command, executorService, false); + } + + @Override + public void run() { + try { + new ReceivePack(repository).receive(getInputStream(), + getOutputStream(), getErrorStream()); + onExit(0); + } catch (IOException e) { + log.warn( + MessageFormat.format("Could not run {0}", getCommand()), + e); + onExit(-1, e.toString()); + } + } + + } +} diff --git a/org.eclipse.jgit.junit/.settings/.api_filters b/org.eclipse.jgit.junit/.settings/.api_filters deleted file mode 100644 index e5de787fa6..0000000000 --- a/org.eclipse.jgit.junit/.settings/.api_filters +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<component id="org.eclipse.jgit.junit" version="2"> - <resource path="src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java" type="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase"> - <filter comment="Don't care about non-API in tests." id="643842064"> - <message_arguments> - <message_argument value="FileRepository"/> - <message_argument value="LocalDiskRepositoryTestCase"/> - <message_argument value="createBareRepository()"/> - </message_arguments> - </filter> - <filter comment="Don't care about non-API in tests." id="643842064"> - <message_arguments> - <message_argument value="FileRepository"/> - <message_argument value="LocalDiskRepositoryTestCase"/> - <message_argument value="createRepository(boolean, boolean)"/> - </message_arguments> - </filter> - <filter comment="Don't care about non-API in tests." id="643842064"> - <message_arguments> - <message_argument value="FileRepository"/> - <message_argument value="LocalDiskRepositoryTestCase"/> - <message_argument value="createWorkRepository()"/> - </message_arguments> - </filter> - </resource> - <resource path="src/org/eclipse/jgit/junit/RepositoryTestCase.java" type="org.eclipse.jgit.junit.RepositoryTestCase"> - <filter comment="Don't care about non-API in tests." id="627060751"> - <message_arguments> - <message_argument value="FileRepository"/> - <message_argument value="RepositoryTestCase"/> - <message_argument value="db"/> - </message_arguments> - </filter> - </resource> -</component> diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs index 794592dee1..2ca78ff2d0 100644 --- a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.junit/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.junit/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF index b9cd4a3f29..a416ae760c 100644 --- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF @@ -3,31 +3,34 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.junit Bundle-SymbolicName: org.eclipse.jgit.junit -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.jgit.api;version="[5.1.9,5.2.0)", - org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.dircache;version="[5.1.9,5.2.0)", - org.eclipse.jgit.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.pack;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.merge;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util.time;version="[5.1.9,5.2.0)", +Import-Package: org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)", + org.eclipse.jgit.api;version="[5.2.3,5.3.0)", + org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.dircache;version="[5.2.3,5.3.0)", + org.eclipse.jgit.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.pack;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.merge;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="5.2.3", + org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util.time;version="[5.2.3,5.3.0)", org.junit;version="[4.12,5.0.0)", org.junit.rules;version="[4.12,5.0.0)", org.junit.runner;version="[4.12,5.0.0)", - org.junit.runners.model;version="[4.12,5.0.0)" -Export-Package: org.eclipse.jgit.junit;version="5.1.9"; + org.junit.runners.model;version="[4.12,5.0.0)", + org.slf4j;version="[1.7.0,2.0.0)" +Export-Package: org.eclipse.jgit.junit;version="5.2.3"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, @@ -35,5 +38,9 @@ Export-Package: org.eclipse.jgit.junit;version="5.1.9"; org.eclipse.jgit.treewalk, org.eclipse.jgit.util, org.eclipse.jgit.storage.file, - org.eclipse.jgit.api", - org.eclipse.jgit.junit.time;version="5.1.9" + org.eclipse.jgit.api, + org.junit.rules, + org.junit.runners.model, + org.junit.runner, + org.eclipse.jgit.util.time", + org.eclipse.jgit.junit.time;version="5.2.3";uses:="org.eclipse.jgit.util.time" diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml index ab2000204d..e198193f5e 100644 --- a/org.eclipse.jgit.junit/pom.xml +++ b/org.eclipse.jgit.junit/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.junit</artifactId> diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java index d3d7d682e8..8de386e344 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java @@ -70,7 +70,7 @@ import org.eclipse.jgit.util.time.ProposedTimestamp; * Mock {@link org.eclipse.jgit.util.SystemReader} for tests. */ public class MockSystemReader extends SystemReader { - private final class MockConfig extends FileBasedConfig { + private static final class MockConfig extends FileBasedConfig { private MockConfig(File cfgLocation, FS fs) { super(cfgLocation, fs); } diff --git a/org.eclipse.jgit.lfs.server.test/.classpath b/org.eclipse.jgit.lfs.server.test/.classpath index 4f9f6bf22f..f08af0a4e9 100644 --- a/org.eclipse.jgit.lfs.server.test/.classpath +++ b/org.eclipse.jgit.lfs.server.test/.classpath @@ -2,6 +2,10 @@ <classpath> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry kind="src" path="tst"/> + <classpathentry kind="src" path="tst"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs index 794592dee1..2ca78ff2d0 100644 --- a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs index ce7a0f0478..984263dd94 100644 --- a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,2 +1,2 @@ -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF index 4f05e9da38..32bdf98b41 100644 --- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.lfs.server.test Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -28,24 +28,24 @@ Import-Package: javax.servlet;version="[3.1.0,4.0.0)", org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)", - org.eclipse.jgit.api;version="[5.1.9,5.2.0)", - org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.junit;version="[5.1.9,5.2.0)", - org.eclipse.jgit.junit.http;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.server;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.server.fs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.test;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", + org.eclipse.jgit.api;version="[5.2.3,5.3.0)", + org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit.http;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.server;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.server.fs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.test;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)", org.junit.rules;version="[4.12,5.0.0)", diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml index b2cb9cbb05..1e26b0a919 100644 --- a/org.eclipse.jgit.lfs.server.test/pom.xml +++ b/org.eclipse.jgit.lfs.server.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.server.test</artifactId> @@ -137,7 +137,7 @@ <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory} -Xmx512m</argLine> + <argLine>-Djava.io.tmpdir=${project.build.directory} -Xmx300m</argLine> </configuration> </plugin> </plugins> diff --git a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java index b081a8ef73..4eb4a0da54 100644 --- a/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java +++ b/org.eclipse.jgit.lfs.server.test/tst/org/eclipse/jgit/lfs/server/fs/PushTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.lfs.server.fs; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.InputStream; @@ -149,7 +150,7 @@ public class PushTest extends LfsServerTest { new String(IO .readWholeStream(is, (int) ldr.getSize()) - .array())); + .array(), UTF_8)); } } diff --git a/org.eclipse.jgit.lfs.server/.settings/.api_filters b/org.eclipse.jgit.lfs.server/.settings/.api_filters index 84e6b19b61..3e60680cf7 100644 --- a/org.eclipse.jgit.lfs.server/.settings/.api_filters +++ b/org.eclipse.jgit.lfs.server/.settings/.api_filters @@ -3,8 +3,8 @@ <resource path="META-INF/MANIFEST.MF"> <filter id="924844039"> <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="5.1.0"/> + <message_argument value="5.2.3"/> + <message_argument value="5.2.0"/> </message_arguments> </filter> </resource> diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs index 89394eca41..525ac67142 100644 --- a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs index ce7a0f0478..984263dd94 100644 --- a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,2 +1,2 @@ -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF index 0dec217147..7bed13371b 100644 --- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF @@ -3,19 +3,19 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.lfs.server Bundle-SymbolicName: org.eclipse.jgit.lfs.server -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name -Export-Package: org.eclipse.jgit.lfs.server;version="5.1.9"; +Export-Package: org.eclipse.jgit.lfs.server;version="5.2.3"; uses:="javax.servlet.http, org.eclipse.jgit.lfs.lib", - org.eclipse.jgit.lfs.server.fs;version="5.1.9"; + org.eclipse.jgit.lfs.server.fs;version="5.2.3"; uses:="javax.servlet, javax.servlet.http, org.eclipse.jgit.lfs.server, org.eclipse.jgit.lfs.lib", - org.eclipse.jgit.lfs.server.internal;version="5.1.9";x-internal:=true, - org.eclipse.jgit.lfs.server.s3;version="5.1.9"; + org.eclipse.jgit.lfs.server.internal;version="5.2.3";x-internal:=true, + org.eclipse.jgit.lfs.server.s3;version="5.2.3"; uses:="org.eclipse.jgit.lfs.server, org.eclipse.jgit.lfs.lib" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -25,15 +25,15 @@ Import-Package: com.google.gson;version="[2.8.0,3.0.0)", javax.servlet.http;version="[3.1.0,4.0.0)", org.apache.http;version="[4.3.0,5.0.0)", org.apache.http.client;version="[4.3.0,5.0.0)", - org.eclipse.jgit.annotations;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.internal;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.nls;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.http.apache;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", + org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.internal;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.http.apache;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", org.slf4j;version="[1.7.0,2.0.0)" diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml index 543420c776..4a2b38ac18 100644 --- a/org.eclipse.jgit.lfs.server/pom.xml +++ b/org.eclipse.jgit.lfs.server/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.server</artifactId> diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java index cfa53af9cd..c2439adf62 100644 --- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LargeFileRepository.java @@ -61,7 +61,7 @@ public interface LargeFileRepository { * id of the object to download * @return Action for downloading the object */ - public Response.Action getDownloadAction(AnyLongObjectId id); + Response.Action getDownloadAction(AnyLongObjectId id); /** * Get upload action @@ -72,7 +72,7 @@ public interface LargeFileRepository { * size of the object to be uploaded * @return Action for uploading the object */ - public Response.Action getUploadAction(AnyLongObjectId id, long size); + Response.Action getUploadAction(AnyLongObjectId id, long size); /** * Get verify action @@ -82,7 +82,8 @@ public interface LargeFileRepository { * @return Action for verifying the object, or {@code null} if the server * doesn't support or require verification */ - public @Nullable Response.Action getVerifyAction(AnyLongObjectId id); + @Nullable + Response.Action getVerifyAction(AnyLongObjectId id); /** * Get size of an object @@ -93,5 +94,5 @@ public interface LargeFileRepository { * exist * @throws java.io.IOException */ - public long getSize(AnyLongObjectId id) throws IOException; + long getSize(AnyLongObjectId id) throws IOException; } diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java index 55d9093241..0a7c37ca55 100644 --- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/FileLfsRepository.java @@ -99,7 +99,8 @@ public class FileLfsRepository implements LargeFileRepository { /** {@inheritDoc} */ @Override - public @Nullable Action getVerifyAction(AnyLongObjectId id) { + @Nullable + public Action getVerifyAction(AnyLongObjectId id) { return null; } diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java index b21c94e4e6..7b76cecf0c 100644 --- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java @@ -411,7 +411,8 @@ class SignerV4 { String stringToSign = stringToSign(SCHEME, ALGORITHM, dateTimeStamp, scope, canonicalRequest); - byte[] signature = (SCHEME + bucketConfig.getSecretKey()).getBytes(); + byte[] signature = (SCHEME + bucketConfig.getSecretKey()) + .getBytes(UTF_8); signature = sign(dateStamp, signature); signature = sign(bucketConfig.getRegion(), signature); signature = sign(S3, signature); diff --git a/org.eclipse.jgit.lfs.test/.classpath b/org.eclipse.jgit.lfs.test/.classpath index e43ae76c50..e79b7c763a 100644 --- a/org.eclipse.jgit.lfs.test/.classpath +++ b/org.eclipse.jgit.lfs.test/.classpath @@ -2,7 +2,15 @@ <classpath> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry kind="src" path="src"/> - <classpathentry kind="src" path="tst"/> + <classpathentry kind="src" path="src"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="src" path="tst"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs index 794592dee1..2ca78ff2d0 100644 --- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs index ce7a0f0478..984263dd94 100644 --- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,2 +1,2 @@ -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF index 8e03dd84d0..2863967868 100644 --- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF @@ -3,23 +3,23 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.lfs.test Bundle-SymbolicName: org.eclipse.jgit.lfs.test -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.junit;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", +Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)", org.junit.runner;version="[4.12,5.0.0)", org.junit.runners;version="[4.12,5.0.0)" -Export-Package: org.eclipse.jgit.lfs.test;version="5.1.9";x-friends:="org.eclipse.jgit.lfs.server.test" +Export-Package: org.eclipse.jgit.lfs.test;version="5.2.3";x-friends:="org.eclipse.jgit.lfs.server.test" diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml index c7734f2579..37d11f91ce 100644 --- a/org.eclipse.jgit.lfs.test/pom.xml +++ b/org.eclipse.jgit.lfs.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs.test</artifactId> @@ -111,7 +111,7 @@ <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>@{argLine} -Djava.io.tmpdir=${project.build.directory} -Xmx512m</argLine> + <argLine>-Djava.io.tmpdir=${project.build.directory} -Xmx300m</argLine> </configuration> </plugin> </plugins> diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java index a1283ddf41..146a25ed3d 100644 --- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java +++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LFSPointerTest.java @@ -43,7 +43,7 @@ package org.eclipse.jgit.lfs.lib; -import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.ByteArrayOutputStream; @@ -66,7 +66,7 @@ public class LFSPointerTest { assertEquals( "version https://git-lfs.github.com/spec/v1\noid sha256:" + s + "\nsize 4\n", - baos.toString(CHARACTER_ENCODING)); + baos.toString(UTF_8.name())); } } } diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs index 89394eca41..525ac67142 100644 --- a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs index ce7a0f0478..984263dd94 100644 --- a/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,2 +1,2 @@ -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF index f731c54919..209b2b2e52 100644 --- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF @@ -3,33 +3,33 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.lfs Bundle-SymbolicName: org.eclipse.jgit.lfs -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name -Export-Package: org.eclipse.jgit.lfs;version="5.1.9", - org.eclipse.jgit.lfs.errors;version="5.1.9", - org.eclipse.jgit.lfs.internal;version="5.1.9";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server", - org.eclipse.jgit.lfs.lib;version="5.1.9" +Export-Package: org.eclipse.jgit.lfs;version="5.2.3", + org.eclipse.jgit.lfs.errors;version="5.2.3", + org.eclipse.jgit.lfs.internal;version="5.2.3";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server", + org.eclipse.jgit.lfs.lib;version="5.2.3" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.google.gson;version="[2.8.2,3.0.0)", com.google.gson.stream;version="[2.8.2,3.0.0)", org.apache.http.impl.client;version="[4.2.6,5.0.0)", org.apache.http.impl.conn;version="[4.2.6,5.0.0)", - org.eclipse.jgit.annotations;version="[5.1.9,5.2.0)";resolution:=optional, - org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.attributes;version="[5.1.9,5.2.0)", - org.eclipse.jgit.diff;version="[5.1.9,5.2.0)", - org.eclipse.jgit.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.hooks;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.nls;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.pack;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)" + org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)";resolution:=optional, + org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.attributes;version="[5.2.3,5.3.0)", + org.eclipse.jgit.diff;version="[5.2.3,5.3.0)", + org.eclipse.jgit.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.hooks;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.pack;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)" diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml index 311b6ceead..f7cad33523 100644 --- a/org.eclipse.jgit.lfs/pom.xml +++ b/org.eclipse.jgit.lfs/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.lfs</artifactId> diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java index 415caa9859..56e3a12ddc 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/BuiltinLFS.java @@ -99,7 +99,8 @@ public class BuiltinLFS extends LfsFactory { } @Override - public @Nullable PrePushHook getPrePushHook(Repository repo, + @Nullable + public PrePushHook getPrePushHook(Repository repo, PrintStream outputStream) { if (isEnabled(repo)) { return new LfsPrePushHook(repo, outputStream); diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java index fac87c177e..7bacf49269 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java @@ -222,7 +222,10 @@ public class SmudgeFilter extends FilterCommand { Integer.valueOf(responseCode))); } Path path = lfs.getMediaFile(ptr.getOid()); - path.getParent().toFile().mkdirs(); + Path parent = path.getParent(); + if (parent != null) { + parent.toFile().mkdirs(); + } try (InputStream contentIn = contentServerConn .getInputStream()) { long bytesCopied = Files.copy(contentIn, path); diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java index 0762ac5f14..317d68a980 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java @@ -106,7 +106,8 @@ public class AtomicObjectOutputStream extends OutputStream { * stream. May return {@code null} if called before closing this * stream. */ - public @Nullable AnyLongObjectId getId() { + @Nullable + public AnyLongObjectId getId() { return id; } diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java index 955eca0c3f..feb8b4ae5a 100644 --- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java +++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsConnectionFactory.java @@ -227,7 +227,8 @@ public class LfsConnectionFactory { * @throws IOException * in case of any error. */ - public static @NonNull HttpConnection getLfsContentConnection( + @NonNull + public static HttpConnection getLfsContentConnection( Repository repo, Protocol.Action action, String method) throws IOException { URL contentUrl = new URL(action.href); diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml index 2a25885802..ad4da8cdf0 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit" label="%featureName" - version="5.1.9.qualifier" + version="5.2.3.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml index cfbdee70c5..576756bf99 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml index d58fc2aae5..e721ddcebf 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.http.apache" label="%featureName" - version="5.1.9.qualifier" + version="5.2.3.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml index 81ffd3a789..c356b34869 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml index 3edf069777..0abc1acd17 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.junit" label="%featureName" - version="5.1.9.qualifier" + version="5.2.3.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -41,6 +41,13 @@ unpack="false"/> <plugin + id="org.eclipse.jgit.junit.ssh" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin id="org.eclipse.jgit.http.server" download-size="0" install-size="0" @@ -54,4 +61,18 @@ version="0.0.0" unpack="false"/> + <plugin + id="org.apache.sshd.core" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="org.apache.sshd.sftp" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + </feature> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml index 783af2d24c..da11f324b9 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> @@ -72,6 +72,11 @@ </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.junit.ssh</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.server</artifactId> <version>${project.version}</version> </dependency> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml index bfb95c3e45..d287505f28 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.lfs" label="%featureName" - version="5.1.9.qualifier" + version="5.2.3.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml index 57ad03113b..79576005eb 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml index b5dddaf5fe..c74326e3a6 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.pgm" label="%featureName" - version="5.1.9.qualifier" + version="5.2.3.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> @@ -31,8 +31,9 @@ version="0.0.0"/> <requires> - <import feature="org.eclipse.jgit" version="5.1.9" match="equivalent"/> - <import feature="org.eclipse.jgit.lfs" version="5.1.9" match="equivalent"/> + <import feature="org.eclipse.jgit" version="5.2.3" match="equivalent"/> + <import feature="org.eclipse.jgit.lfs" version="5.2.3" match="equivalent"/> + <import feature="org.eclipse.jgit.ssh.apache" version="5.2.3" match="equivalent"/> </requires> <plugin 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 80424c11f7..e45d79dbef 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 @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml index 902a904d27..2179699abf 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.pgm.source" label="%featureName" - version="5.1.9.qualifier" + version="5.2.3.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml index 2af5eb824b..221f0316ae 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 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 01490295ae..995bd21b18 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml @@ -9,12 +9,18 @@ <feature url="features/org.eclipse.jgit.pgm_0.0.0.qualifier.jar" id="org.eclipse.jgit.pgm" version="0.0.0" patch="true"> <category name="JGit"/> </feature> + <feature url="features/org.eclipse.jgit.ssh.apache_0.0.0.qualifier.jar" id="org.eclipse.jgit.ssh.apache" version="0.0.0" patch="true"> + <category name="JGit"/> + </feature> <feature url="features/org.eclipse.jgit.source_0.0.0.qualifier.jar" id="org.eclipse.jgit.source" version="0.0.0" patch="true"> <category name="JGit"/> </feature> <feature url="features/org.eclipse.jgit.pgm.source_0.0.0.qualifier.jar" id="org.eclipse.jgit.pgm.source" version="0.0.0" patch="true"> <category name="JGit"/> </feature> + <feature url="features/org.eclipse.jgit.ssh.apache.source_0.0.0.qualifier.jar" id="org.eclipse.jgit.ssh.apache.source" version="0.0.0" patch="true"> + <category name="JGit"/> + </feature> <feature url="features/org.eclipse.jgit.junit_0.0.0.qualifier.jar" id="org.eclipse.jgit.junit" version="0.0.0" patch="true"> <category name="JGit"/> </feature> 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 fb1a55627f..e258eb253e 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.repository</artifactId> @@ -96,8 +96,18 @@ </dependency> <dependency> <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.junit.ssh</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.http.server</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.ssh.apache</artifactId> + <version>${project.version}</version> + </dependency> </dependencies> </project> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 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 f9ae9d0a35..cee8946e06 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 @@ -2,7 +2,7 @@ <feature id="org.eclipse.jgit.source" label="%featureName" - version="5.1.9.qualifier" + version="5.2.3.qualifier" provider-name="%providerName"> <description url="http://www.eclipse.org/jgit/"> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml index 4621e1bbe3..60b2c5e28b 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <groupId>org.eclipse.jgit.feature</groupId> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.gitignore b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.gitignore new file mode 100644 index 0000000000..2f7896d1d1 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.project new file mode 100644 index 0000000000..77876e13b2 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.jgit.ssh.apache.feature</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.pde.FeatureBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.FeatureNature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..14bdc2c705 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Fri Jun 18 23:33:45 CEST 2010 +eclipse.preferences.version=1 +encoding/<project>=UTF-8 diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000000..898252b4d6 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,3 @@ +#Fri Jun 18 23:33:45 CEST 2010 +eclipse.preferences.version=1 +line.separator=\n diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 0000000000..823c0f56ae --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.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.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.settings/org.eclipse.mylyn.team.ui.prefs new file mode 100644 index 0000000000..2fca432276 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/.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.packaging/org.eclipse.jgit.ssh.apache.feature/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/build.properties new file mode 100644 index 0000000000..b4a8dde9e5 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/build.properties @@ -0,0 +1,4 @@ +bin.includes = feature.xml,\ + edl-v10.html,\ + feature.properties,\ + license.html diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/edl-v10.html new file mode 100644 index 0000000000..1826b47af8 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/edl-v10.html @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> +<title>Eclipse Distribution License - Version 1.0</title> +<style type="text/css"> + body { + size: 8.5in 11.0in; + margin: 0.25in 0.5in 0.25in 0.5in; + tab-interval: 0.5in; + } + p { + margin-left: auto; + margin-top: 0.5em; + margin-bottom: 0.5em; + } + p.list { + margin-left: 0.5in; + margin-top: 0.05em; + margin-bottom: 0.05em; + } + </style> + +</head> + +<body lang="EN-US"> + +<p><b>Eclipse Distribution License - v 1.0</b></p> + +<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p> + +<p>All rights reserved.</p> +<p>Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: +<ul><li>Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer.</li> +<li>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.</li> +<li>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.</li></ul> +</p> +<p>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.</p> + +</body> + +</html> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties new file mode 100644 index 0000000000..2b086129ff --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.properties @@ -0,0 +1,177 @@ +############################################################################### +# Copyright (c) 2000, 2010 IBM Corporation and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +############################################################################### + +featureName=Java implementation of Git - ssh support using Apache MINA sshd +providerName=Eclipse JGit + +updateSiteName=Eclipse JGit Update Site + +# description property - text of the "Feature Description" +description=\ +Ssh support using Apache MINA sshd.\n +################ end of description property ################################## + +# "copyright" property - text of the "Feature Update Copyright" +copyright=\ +Copyright (c) 2018 Thomas Wolf and others.\n\ +All rights reserved. This program and the accompanying materials\n\ +are made available under the terms of the Eclipse Distribution License v1.0\n\ +which accompanies this distribution, and is available at\n\ +http://www.eclipse.org/org/documents/edl-v10.html\n +################ end of copyright property #################################### + +# "licenseURL" property - URL of the "Feature License" +# do not translate value - just change to point to a locale-specific HTML page +licenseURL=license.html + +# "license" property - text of the "Feature Update License" +# should be plain text version of license agreement pointed to be "licenseURL" +license=\ +Eclipse Foundation Software User Agreement\n\ +\n\ +November 22, 2017\n\ +\n\ +Usage Of Content\n\ +\n\ +THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION\n\ +AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT"). USE OF\n\ +THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE\n\ +TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\ +BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED\n\ +BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE\n\ +AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\ +TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS OF ANY\n\ +APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU\n\ +MAY NOT USE THE CONTENT.\n\ +\n\ +Applicable Licenses\n\ +\n\ +Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\ +is provided to you under the terms and conditions of the Eclipse Public License\n\ +Version 2.0 ("EPL"). A copy of the EPL is provided with this Content and is also\n\ +available at http://www.eclipse.org/legal/epl-2.0. For purposes of the EPL,\n\ +"Program" will mean the Content.\n\ +\n\ +Content includes, but is not limited to, source code, object code, documentation\n\ +and other files maintained in the Eclipse Foundation source code repository\n\ +("Repository") in software modules ("Modules") and made available as\n\ +downloadable archives ("Downloads").\n\ +\n\ +- Content may be structured and packaged into modules to facilitate\n\ + delivering, extending, and upgrading the Content. Typical modules may\n\ + include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and\n\ + features ("Features").\n\ +- Each Plug-in or Fragment may be packaged as a sub-directory or JAR\n\ + (Javaâ„¢ ARchive) in a directory named "plugins".\n\ +- A Feature is a bundle of one or more Plug-ins and/or Fragments and\n\ + associated material. Each Feature may be packaged as a sub-directory in a\n\ + directory named "features". Within a Feature, files named "feature.xml" may\n\ + contain a list of the names and version numbers of the Plug-ins and/or\n\ + Fragments associated with that Feature.\n\ +- Features may also include other Features ("Included Features"). Within a\n\ + Feature, files named "feature.xml" may contain a list of the names and\n\ + version numbers of Included Features.\n\ +\n\ +The terms and conditions governing Plug-ins and Fragments should be contained in\n\ +files named "about.html" ("Abouts"). The terms and conditions governing Features\n\ +and Included Features should be contained in files named "license.html"\n\ +("Feature Licenses"). Abouts and Feature Licenses may be located in any\n\ +directory of a Download or Module including, but not limited to the following\n\ +locations:\n\ +\n\ +- The top-level (root) directory\n\ +- Plug-in and Fragment directories\n\ +- Inside Plug-ins and Fragments packaged as JARs\n\ +- Sub-directories of the directory named "src" of certain Plug-ins\n\ +- Feature directories\n\ +\n\ +Note: if a Feature made available by the Eclipse Foundation is installed using\n\ +the Provisioning Technology (as defined below), you must agree to a license\n\ +("Feature Update License") during the installation process. If the Feature\n\ +contains Included Features, the Feature Update License should either provide you\n\ +with the terms and conditions governing the Included Features or inform you\n\ +where you can locate them. Feature Update Licenses may be found in the "license"\n\ +property of files named "feature.properties" found within a Feature. Such\n\ +Abouts, Feature Licenses, and Feature Update Licenses contain the terms and\n\ +conditions (or references to such terms and conditions) that govern your use of\n\ +the associated Content in that directory.\n\ +\n\ +THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL\n\ +OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE\n\ +OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\ +\n\ +- Eclipse Public License Version 1.0 (available at\n\ + http://www.eclipse.org/legal/epl-v10.html)\n\ +- Eclipse Distribution License Version 1.0 (available at\n\ + http://www.eclipse.org/licenses/edl-v1.0.html)\n\ +- Common Public License Version 1.0 (available at\n\ + http://www.eclipse.org/legal/cpl-v10.html)\n\ +- Apache Software License 1.1 (available at\n\ + http://www.apache.org/licenses/LICENSE)\n\ +- Apache Software License 2.0 (available at\n\ + http://www.apache.org/licenses/LICENSE-2.0)\n\ +- Mozilla Public License Version 1.1 (available at\n\ + http://www.mozilla.org/MPL/MPL-1.1.html)\n\ +\n\ +IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO\n\ +USE OF THE CONTENT. If no About, Feature License, or Feature Update License is\n\ +provided, please contact the Eclipse Foundation to determine what terms and\n\ +conditions govern that particular Content.\n\ +\n\ +Use of Provisioning Technology\n\ +\n\ +The Eclipse Foundation makes available provisioning software, examples of which\n\ +include, but are not limited to, p2 and the Eclipse Update Manager\n\ +("Provisioning Technology") for the purpose of allowing users to install\n\ +software, documentation, information and/or other materials (collectively\n\ +"Installable Software"). This capability is provided with the intent of allowing\n\ +such users to install, extend and update Eclipse-based products. Information\n\ +about packaging Installable Software is available at\n\ +http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\ +\n\ +You may use Provisioning Technology to allow other parties to install\n\ +Installable Software. You shall be responsible for enabling the applicable\n\ +license agreements relating to the Installable Software to be presented to, and\n\ +accepted by, the users of the Provisioning Technology in accordance with the\n\ +Specification. By using Provisioning Technology in such a manner and making it\n\ +available in accordance with the Specification, you further acknowledge your\n\ +agreement to, and the acquisition of all necessary rights to permit the\n\ +following:\n\ +\n\ +1. A series of actions may occur ("Provisioning Process") in which a user may\n\ + execute the Provisioning Technology on a machine ("Target Machine") with the\n\ + intent of installing, extending or updating the functionality of an\n\ + Eclipse-based product.\n\ +2. During the Provisioning Process, the Provisioning Technology may cause third\n\ + party Installable Software or a portion thereof to be accessed and copied to\n\ + the Target Machine.\n\ +3. Pursuant to the Specification, you will provide to the user the terms and\n\ + conditions that govern the use of the Installable Software ("Installable\n\ + Software Agreement") and such Installable Software Agreement shall be\n\ + accessed from the Target Machine in accordance with the Specification. Such\n\ + Installable Software Agreement must inform the user of the terms and\n\ + conditions that govern the Installable Software and must solicit acceptance\n\ + by the end user in the manner prescribed in such Installable\n\ + Software Agreement. Upon such indication of agreement by the user, the\n\ + provisioning Technology will complete installation of the\n\ + Installable Software.\n\ +\n\ +Cryptography\n\ +\n\ +Content may contain encryption software. The country in which you are currently\n\ +may have restrictions on the import, possession, and use, and/or re-export to\n\ +another country, of encryption software. BEFORE using any encryption software,\n\ +please check the country's laws, regulations and policies concerning the import,\n\ +possession, or use, and re-export of encryption software, to see if this is\n\ +permitted.\n\ +\n\ +Java and all Java-based trademarks are trademarks of Oracle Corporation in the\n\ +United States, other countries, or both.\n +########### end of license property ########################################## diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml new file mode 100644 index 0000000000..2ce712ccd0 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feature + id="org.eclipse.jgit.ssh.apache" + label="%featureName" + version="5.2.3.qualifier" + provider-name="%providerName"> + + <description url="http://www.eclipse.org/jgit/"> + %description + </description> + + <copyright> + %copyright + </copyright> + + <license url="%licenseURL"> + %license + </license> + + <url> + <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/> + <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/> + </url> + + <requires> + <import plugin="org.eclipse.jgit"/> + </requires> + + <plugin + id="org.eclipse.jgit.ssh.apache" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="org.apache.sshd.core" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="org.apache.sshd.sftp" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="net.i2p.crypto.eddsa" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + +</feature> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html new file mode 100644 index 0000000000..008b8018db --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/license.html @@ -0,0 +1,189 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> +<title>Eclipse Foundation Software User Agreement</title> +</head> + +<body lang="EN-US"> + <h2>Eclipse Foundation Software User Agreement</h2> + <p>November 22, 2017</p> + + <h3>Usage Of Content</h3> + + <p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, + INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS + (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY + THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND + CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED + BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS + GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY + APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED + BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS + AGREEMENT AND THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE + AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT + USE THE CONTENT.</p> + + <h3>Applicable Licenses</h3> + + <p> + Unless otherwise indicated, all Content made available by the Eclipse + Foundation is provided to you under the terms and conditions of the + Eclipse Public License Version 2.0 ("EPL"). A copy of the + EPL is provided with this Content and is also available at <a + href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>. + For purposes of the EPL, "Program" will mean the Content. + </p> + + <p>Content includes, but is not limited to, source code, object + code, documentation and other files maintained in the Eclipse + Foundation source code repository ("Repository") in software + modules ("Modules") and made available as downloadable + archives ("Downloads").</p> + + <ul> + <li>Content may be structured and packaged into modules to + facilitate delivering, extending, and upgrading the Content. Typical + modules may include plug-ins ("Plug-ins"), plug-in + fragments ("Fragments"), and features + ("Features").</li> + <li>Each Plug-in or Fragment may be packaged as a sub-directory + or JAR (Java™ ARchive) in a directory named + "plugins".</li> + <li>A Feature is a bundle of one or more Plug-ins and/or + Fragments and associated material. Each Feature may be packaged as a + sub-directory in a directory named "features". Within a + Feature, files named "feature.xml" may contain a list of + the names and version numbers of the Plug-ins and/or Fragments + associated with that Feature.</li> + <li>Features may also include other Features ("Included + Features"). Within a Feature, files named + "feature.xml" may contain a list of the names and version + numbers of Included Features.</li> + </ul> + + <p>The terms and conditions governing Plug-ins and Fragments should + be contained in files named "about.html" + ("Abouts"). The terms and conditions governing Features and + Included Features should be contained in files named + "license.html" ("Feature Licenses"). Abouts and + Feature Licenses may be located in any directory of a Download or + Module including, but not limited to the following locations:</p> + + <ul> + <li>The top-level (root) directory</li> + <li>Plug-in and Fragment directories</li> + <li>Inside Plug-ins and Fragments packaged as JARs</li> + <li>Sub-directories of the directory named "src" of + certain Plug-ins</li> + <li>Feature directories</li> + </ul> + + <p>Note: if a Feature made available by the Eclipse Foundation is + installed using the Provisioning Technology (as defined below), you + must agree to a license ("Feature Update License") during + the installation process. If the Feature contains Included Features, + the Feature Update License should either provide you with the terms + and conditions governing the Included Features or inform you where you + can locate them. Feature Update Licenses may be found in the + "license" property of files named + "feature.properties" found within a Feature. Such Abouts, + Feature Licenses, and Feature Update Licenses contain the terms and + conditions (or references to such terms and conditions) that govern + your use of the associated Content in that directory.</p> + + <p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY + REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND + CONDITIONS. SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT + ARE NOT LIMITED TO):</p> + + <ul> + <li>Eclipse Public License Version 1.0 (available at <a + href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>) + </li> + <li>Eclipse Distribution License Version 1.0 (available at <a + href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>) + </li> + <li>Common Public License Version 1.0 (available at <a + href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>) + </li> + <li>Apache Software License 1.1 (available at <a + href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>) + </li> + <li>Apache Software License 2.0 (available at <a + href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>) + </li> + <li>Mozilla Public License Version 1.1 (available at <a + href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>) + </li> + </ul> + + <p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND + CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, + or Feature Update License is provided, please contact the Eclipse + Foundation to determine what terms and conditions govern that + particular Content.</p> + + + <h3>Use of Provisioning Technology</h3> + + <p> + The Eclipse Foundation makes available provisioning software, examples + of which include, but are not limited to, p2 and the Eclipse Update + Manager ("Provisioning Technology") for the purpose of + allowing users to install software, documentation, information and/or + other materials (collectively "Installable Software"). This + capability is provided with the intent of allowing such users to + install, extend and update Eclipse-based products. Information about + packaging Installable Software is available at <a + href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a> + ("Specification"). + </p> + + <p>You may use Provisioning Technology to allow other parties to + install Installable Software. You shall be responsible for enabling + the applicable license agreements relating to the Installable Software + to be presented to, and accepted by, the users of the Provisioning + Technology in accordance with the Specification. By using Provisioning + Technology in such a manner and making it available in accordance with + the Specification, you further acknowledge your agreement to, and the + acquisition of all necessary rights to permit the following:</p> + + <ol> + <li>A series of actions may occur ("Provisioning + Process") in which a user may execute the Provisioning + Technology on a machine ("Target Machine") with the intent + of installing, extending or updating the functionality of an + Eclipse-based product.</li> + <li>During the Provisioning Process, the Provisioning Technology + may cause third party Installable Software or a portion thereof to be + accessed and copied to the Target Machine.</li> + <li>Pursuant to the Specification, you will provide to the user + the terms and conditions that govern the use of the Installable + Software ("Installable Software Agreement") and such + Installable Software Agreement shall be accessed from the Target + Machine in accordance with the Specification. Such Installable + Software Agreement must inform the user of the terms and conditions + that govern the Installable Software and must solicit acceptance by + the end user in the manner prescribed in such Installable Software + Agreement. Upon such indication of agreement by the user, the + provisioning Technology will complete installation of the Installable + Software.</li> + </ol> + + <h3>Cryptography</h3> + + <p>Content may contain encryption software. The country in which + you are currently may have restrictions on the import, possession, and + use, and/or re-export to another country, of encryption software. + BEFORE using any encryption software, please check the country's laws, + regulations and policies concerning the import, possession, or use, + and re-export of encryption software, to see if this is permitted.</p> + + <p> + <small>Java and all Java-based trademarks are trademarks of + Oracle Corporation in the United States, other countries, or both.</small> + </p> +</body> +</html> 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 new file mode 100644 index 0000000000..869fb171c3 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 201x84, Thomas Wolf <thomas.wolf@paranor.ch> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>jgit.tycho.parent</artifactId> + <version>5.2.3-SNAPSHOT</version> + </parent> + + <groupId>org.eclipse.jgit.feature</groupId> + <artifactId>org.eclipse.jgit.ssh.apache</artifactId> + <packaging>eclipse-feature</packaging> + + <name>JGit - Ssh support using Apache MINA sshd</name> + <dependencies> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.ssh.apache</artifactId> + <version>${project.version}</version> + </dependency> + + </dependencies> + +</project> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore new file mode 100644 index 0000000000..eb5a316cbd --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.gitignore @@ -0,0 +1 @@ +target diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project new file mode 100644 index 0000000000..55620844ff --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.jgit.ssh.apache.source.feature</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.pde.FeatureBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.FeatureNature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..6f96ce809a --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000000..1a32dba177 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,3 @@ +#Fri Jun 18 23:33:45 CEST 2010
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 0000000000..823c0f56ae --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.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.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.settings/org.eclipse.mylyn.team.ui.prefs new file mode 100644 index 0000000000..2fca432276 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/.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.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties new file mode 100644 index 0000000000..b4a8dde9e5 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/build.properties @@ -0,0 +1,4 @@ +bin.includes = feature.xml,\ + edl-v10.html,\ + feature.properties,\ + license.html diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html new file mode 100644 index 0000000000..8caddac569 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/edl-v10.html @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> +<title>Eclipse Distribution License - Version 1.0</title> +<style type="text/css"> + body { + size: 8.5in 11.0in; + margin: 0.25in 0.5in 0.25in 0.5in; + tab-interval: 0.5in; + } + p { + margin-left: auto; + margin-top: 0.5em; + margin-bottom: 0.5em; + } + p.list { + margin-left: 0.5in; + margin-top: 0.05em; + margin-bottom: 0.05em; + } +</style> + +</head> + +<body lang="EN-US"> + +<p><b>Eclipse Distribution License - v 1.0</b></p> + +<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p> + +<p>All rights reserved.</p> +<p>Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: +<ul><li>Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. </li> +<li>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. </li> +<li>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. </li></ul> +</p> +<p>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.</p> + +</body> + +</html> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties new file mode 100644 index 0000000000..9935431824 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.properties @@ -0,0 +1,178 @@ +############################################################################### +# Copyright (c) 2000, 2010 IBM Corporation and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +############################################################################### + +featureName=Java implementation of Git - ssh support using Apache MINA sshd - Source Code +providerName=Eclipse JGit + +updateSiteName=Eclipse JGit Update Site + +# description property - text of the "Feature Description" +description=\ +Do not install in your IDE: this feature is meant to provision Target Platforms.\n\ +Source code for the JGit ssh support using Apache MINA sshd.\n +################ end of description property ################################## + +# "copyright" property - text of the "Feature Update Copyright" +copyright=\ +Copyright (c) 2018 Thomas Wolf and others\n\ +All rights reserved. This program and the accompanying materials\n\ +are made available under the terms of the Eclipse Distribution License v1.0\n\ +which accompanies this distribution, and is available at\n\ +http://www.eclipse.org/org/documents/edl-v10.html\n +################ end of copyright property #################################### + +# "licenseURL" property - URL of the "Feature License" +# do not translate value - just change to point to a locale-specific HTML page +licenseURL=license.html + +# "license" property - text of the "Feature Update License" +# should be plain text version of license agreement pointed to be "licenseURL" +license=\ +Eclipse Foundation Software User Agreement\n\ +\n\ +November 22, 2017\n\ +\n\ +Usage Of Content\n\ +\n\ +THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION\n\ +AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT"). USE OF\n\ +THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE\n\ +TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\ +BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED\n\ +BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE\n\ +AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\ +TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS OF ANY\n\ +APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU\n\ +MAY NOT USE THE CONTENT.\n\ +\n\ +Applicable Licenses\n\ +\n\ +Unless otherwise indicated, all Content made available by the Eclipse Foundation\n\ +is provided to you under the terms and conditions of the Eclipse Public License\n\ +Version 2.0 ("EPL"). A copy of the EPL is provided with this Content and is also\n\ +available at http://www.eclipse.org/legal/epl-2.0. For purposes of the EPL,\n\ +"Program" will mean the Content.\n\ +\n\ +Content includes, but is not limited to, source code, object code, documentation\n\ +and other files maintained in the Eclipse Foundation source code repository\n\ +("Repository") in software modules ("Modules") and made available as\n\ +downloadable archives ("Downloads").\n\ +\n\ +- Content may be structured and packaged into modules to facilitate\n\ + delivering, extending, and upgrading the Content. Typical modules may\n\ + include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and\n\ + features ("Features").\n\ +- Each Plug-in or Fragment may be packaged as a sub-directory or JAR\n\ + (Javaâ„¢ ARchive) in a directory named "plugins".\n\ +- A Feature is a bundle of one or more Plug-ins and/or Fragments and\n\ + associated material. Each Feature may be packaged as a sub-directory in a\n\ + directory named "features". Within a Feature, files named "feature.xml" may\n\ + contain a list of the names and version numbers of the Plug-ins and/or\n\ + Fragments associated with that Feature.\n\ +- Features may also include other Features ("Included Features"). Within a\n\ + Feature, files named "feature.xml" may contain a list of the names and\n\ + version numbers of Included Features.\n\ +\n\ +The terms and conditions governing Plug-ins and Fragments should be contained in\n\ +files named "about.html" ("Abouts"). The terms and conditions governing Features\n\ +and Included Features should be contained in files named "license.html"\n\ +("Feature Licenses"). Abouts and Feature Licenses may be located in any\n\ +directory of a Download or Module including, but not limited to the following\n\ +locations:\n\ +\n\ +- The top-level (root) directory\n\ +- Plug-in and Fragment directories\n\ +- Inside Plug-ins and Fragments packaged as JARs\n\ +- Sub-directories of the directory named "src" of certain Plug-ins\n\ +- Feature directories\n\ +\n\ +Note: if a Feature made available by the Eclipse Foundation is installed using\n\ +the Provisioning Technology (as defined below), you must agree to a license\n\ +("Feature Update License") during the installation process. If the Feature\n\ +contains Included Features, the Feature Update License should either provide you\n\ +with the terms and conditions governing the Included Features or inform you\n\ +where you can locate them. Feature Update Licenses may be found in the "license"\n\ +property of files named "feature.properties" found within a Feature. Such\n\ +Abouts, Feature Licenses, and Feature Update Licenses contain the terms and\n\ +conditions (or references to such terms and conditions) that govern your use of\n\ +the associated Content in that directory.\n\ +\n\ +THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL\n\ +OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE\n\ +OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\ +\n\ +- Eclipse Public License Version 1.0 (available at\n\ + http://www.eclipse.org/legal/epl-v10.html)\n\ +- Eclipse Distribution License Version 1.0 (available at\n\ + http://www.eclipse.org/licenses/edl-v1.0.html)\n\ +- Common Public License Version 1.0 (available at\n\ + http://www.eclipse.org/legal/cpl-v10.html)\n\ +- Apache Software License 1.1 (available at\n\ + http://www.apache.org/licenses/LICENSE)\n\ +- Apache Software License 2.0 (available at\n\ + http://www.apache.org/licenses/LICENSE-2.0)\n\ +- Mozilla Public License Version 1.1 (available at\n\ + http://www.mozilla.org/MPL/MPL-1.1.html)\n\ +\n\ +IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO\n\ +USE OF THE CONTENT. If no About, Feature License, or Feature Update License is\n\ +provided, please contact the Eclipse Foundation to determine what terms and\n\ +conditions govern that particular Content.\n\ +\n\ +Use of Provisioning Technology\n\ +\n\ +The Eclipse Foundation makes available provisioning software, examples of which\n\ +include, but are not limited to, p2 and the Eclipse Update Manager\n\ +("Provisioning Technology") for the purpose of allowing users to install\n\ +software, documentation, information and/or other materials (collectively\n\ +"Installable Software"). This capability is provided with the intent of allowing\n\ +such users to install, extend and update Eclipse-based products. Information\n\ +about packaging Installable Software is available at\n\ +http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\ +\n\ +You may use Provisioning Technology to allow other parties to install\n\ +Installable Software. You shall be responsible for enabling the applicable\n\ +license agreements relating to the Installable Software to be presented to, and\n\ +accepted by, the users of the Provisioning Technology in accordance with the\n\ +Specification. By using Provisioning Technology in such a manner and making it\n\ +available in accordance with the Specification, you further acknowledge your\n\ +agreement to, and the acquisition of all necessary rights to permit the\n\ +following:\n\ +\n\ +1. A series of actions may occur ("Provisioning Process") in which a user may\n\ + execute the Provisioning Technology on a machine ("Target Machine") with the\n\ + intent of installing, extending or updating the functionality of an\n\ + Eclipse-based product.\n\ +2. During the Provisioning Process, the Provisioning Technology may cause third\n\ + party Installable Software or a portion thereof to be accessed and copied to\n\ + the Target Machine.\n\ +3. Pursuant to the Specification, you will provide to the user the terms and\n\ + conditions that govern the use of the Installable Software ("Installable\n\ + Software Agreement") and such Installable Software Agreement shall be\n\ + accessed from the Target Machine in accordance with the Specification. Such\n\ + Installable Software Agreement must inform the user of the terms and\n\ + conditions that govern the Installable Software and must solicit acceptance\n\ + by the end user in the manner prescribed in such Installable\n\ + Software Agreement. Upon such indication of agreement by the user, the\n\ + provisioning Technology will complete installation of the\n\ + Installable Software.\n\ +\n\ +Cryptography\n\ +\n\ +Content may contain encryption software. The country in which you are currently\n\ +may have restrictions on the import, possession, and use, and/or re-export to\n\ +another country, of encryption software. BEFORE using any encryption software,\n\ +please check the country's laws, regulations and policies concerning the import,\n\ +possession, or use, and re-export of encryption software, to see if this is\n\ +permitted.\n\ +\n\ +Java and all Java-based trademarks are trademarks of Oracle Corporation in the\n\ +United States, other countries, or both.\n +########### end of license property ########################################## diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml new file mode 100644 index 0000000000..08777443a4 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/feature.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feature + id="org.eclipse.jgit.ssh.apache.source" + label="%featureName" + version="5.2.3.qualifier" + provider-name="%providerName"> + + <description url="http://www.eclipse.org/jgit/"> + %description + </description> + + <copyright> + %copyright + </copyright> + + <license url="%licenseURL"> + %license + </license> + + <url> + <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/> + <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/> + </url> + + <plugin + id="org.eclipse.jgit.ssh.apache.source" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> +</feature> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html new file mode 100644 index 0000000000..008b8018db --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/license.html @@ -0,0 +1,189 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> +<title>Eclipse Foundation Software User Agreement</title> +</head> + +<body lang="EN-US"> + <h2>Eclipse Foundation Software User Agreement</h2> + <p>November 22, 2017</p> + + <h3>Usage Of Content</h3> + + <p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, + INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS + (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY + THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND + CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED + BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS + GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY + APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED + BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS + AGREEMENT AND THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE + AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT + USE THE CONTENT.</p> + + <h3>Applicable Licenses</h3> + + <p> + Unless otherwise indicated, all Content made available by the Eclipse + Foundation is provided to you under the terms and conditions of the + Eclipse Public License Version 2.0 ("EPL"). A copy of the + EPL is provided with this Content and is also available at <a + href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>. + For purposes of the EPL, "Program" will mean the Content. + </p> + + <p>Content includes, but is not limited to, source code, object + code, documentation and other files maintained in the Eclipse + Foundation source code repository ("Repository") in software + modules ("Modules") and made available as downloadable + archives ("Downloads").</p> + + <ul> + <li>Content may be structured and packaged into modules to + facilitate delivering, extending, and upgrading the Content. Typical + modules may include plug-ins ("Plug-ins"), plug-in + fragments ("Fragments"), and features + ("Features").</li> + <li>Each Plug-in or Fragment may be packaged as a sub-directory + or JAR (Java™ ARchive) in a directory named + "plugins".</li> + <li>A Feature is a bundle of one or more Plug-ins and/or + Fragments and associated material. Each Feature may be packaged as a + sub-directory in a directory named "features". Within a + Feature, files named "feature.xml" may contain a list of + the names and version numbers of the Plug-ins and/or Fragments + associated with that Feature.</li> + <li>Features may also include other Features ("Included + Features"). Within a Feature, files named + "feature.xml" may contain a list of the names and version + numbers of Included Features.</li> + </ul> + + <p>The terms and conditions governing Plug-ins and Fragments should + be contained in files named "about.html" + ("Abouts"). The terms and conditions governing Features and + Included Features should be contained in files named + "license.html" ("Feature Licenses"). Abouts and + Feature Licenses may be located in any directory of a Download or + Module including, but not limited to the following locations:</p> + + <ul> + <li>The top-level (root) directory</li> + <li>Plug-in and Fragment directories</li> + <li>Inside Plug-ins and Fragments packaged as JARs</li> + <li>Sub-directories of the directory named "src" of + certain Plug-ins</li> + <li>Feature directories</li> + </ul> + + <p>Note: if a Feature made available by the Eclipse Foundation is + installed using the Provisioning Technology (as defined below), you + must agree to a license ("Feature Update License") during + the installation process. If the Feature contains Included Features, + the Feature Update License should either provide you with the terms + and conditions governing the Included Features or inform you where you + can locate them. Feature Update Licenses may be found in the + "license" property of files named + "feature.properties" found within a Feature. Such Abouts, + Feature Licenses, and Feature Update Licenses contain the terms and + conditions (or references to such terms and conditions) that govern + your use of the associated Content in that directory.</p> + + <p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY + REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND + CONDITIONS. SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT + ARE NOT LIMITED TO):</p> + + <ul> + <li>Eclipse Public License Version 1.0 (available at <a + href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>) + </li> + <li>Eclipse Distribution License Version 1.0 (available at <a + href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>) + </li> + <li>Common Public License Version 1.0 (available at <a + href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>) + </li> + <li>Apache Software License 1.1 (available at <a + href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>) + </li> + <li>Apache Software License 2.0 (available at <a + href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>) + </li> + <li>Mozilla Public License Version 1.1 (available at <a + href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>) + </li> + </ul> + + <p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND + CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, + or Feature Update License is provided, please contact the Eclipse + Foundation to determine what terms and conditions govern that + particular Content.</p> + + + <h3>Use of Provisioning Technology</h3> + + <p> + The Eclipse Foundation makes available provisioning software, examples + of which include, but are not limited to, p2 and the Eclipse Update + Manager ("Provisioning Technology") for the purpose of + allowing users to install software, documentation, information and/or + other materials (collectively "Installable Software"). This + capability is provided with the intent of allowing such users to + install, extend and update Eclipse-based products. Information about + packaging Installable Software is available at <a + href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a> + ("Specification"). + </p> + + <p>You may use Provisioning Technology to allow other parties to + install Installable Software. You shall be responsible for enabling + the applicable license agreements relating to the Installable Software + to be presented to, and accepted by, the users of the Provisioning + Technology in accordance with the Specification. By using Provisioning + Technology in such a manner and making it available in accordance with + the Specification, you further acknowledge your agreement to, and the + acquisition of all necessary rights to permit the following:</p> + + <ol> + <li>A series of actions may occur ("Provisioning + Process") in which a user may execute the Provisioning + Technology on a machine ("Target Machine") with the intent + of installing, extending or updating the functionality of an + Eclipse-based product.</li> + <li>During the Provisioning Process, the Provisioning Technology + may cause third party Installable Software or a portion thereof to be + accessed and copied to the Target Machine.</li> + <li>Pursuant to the Specification, you will provide to the user + the terms and conditions that govern the use of the Installable + Software ("Installable Software Agreement") and such + Installable Software Agreement shall be accessed from the Target + Machine in accordance with the Specification. Such Installable + Software Agreement must inform the user of the terms and conditions + that govern the Installable Software and must solicit acceptance by + the end user in the manner prescribed in such Installable Software + Agreement. Upon such indication of agreement by the user, the + provisioning Technology will complete installation of the Installable + Software.</li> + </ol> + + <h3>Cryptography</h3> + + <p>Content may contain encryption software. The country in which + you are currently may have restrictions on the import, possession, and + use, and/or re-export to another country, of encryption software. + BEFORE using any encryption software, please check the country's laws, + regulations and policies concerning the import, possession, or use, + and re-export of encryption software, to see if this is permitted.</p> + + <p> + <small>Java and all Java-based trademarks are trademarks of + Oracle Corporation in the United States, other countries, or both.</small> + </p> +</body> +</html> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml new file mode 100644 index 0000000000..6fd255a9b0 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.source.feature/pom.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2018 Thomas Wolf <thomas.wolf@paranor.ch> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>jgit.tycho.parent</artifactId> + <version>5.2.3-SNAPSHOT</version> + </parent> + + <groupId>org.eclipse.jgit.feature</groupId> + <artifactId>org.eclipse.jgit.ssh.apache.source</artifactId> + <packaging>eclipse-feature</packaging> + + <name>JGit Apache MINA ssh Source Feature</name> + +</project> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF index d6787015bb..b869d8d716 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF @@ -2,4 +2,4 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: JGit Target Platform Bundle Bundle-SymbolicName: org.eclipse.jgit.target -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.target index bdabdffa28..98a2a6b0c0 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.target @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> -<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.9-staging" sequenceNumber="1536522948"> +<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> +<target name="jgit-4.9-staging" sequenceNumber="1544018574"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/> @@ -23,12 +23,12 @@ <repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> - <unit id="org.apache.ant" version="1.9.6.v201510161327"/> - <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/> + <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/> + <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/> <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/> <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/> - <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/> - <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/> + <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/> + <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/> <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/> <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> <unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/> @@ -46,10 +46,14 @@ <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> <unit id="javaewah" version="1.1.6.v20160919-1400"/> <unit id="javaewah.source" version="1.1.6.v20160919-1400"/> - <unit id="org.objenesis" version="1.0.0.v201505121915"/> - <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> - <unit id="org.mockito" version="1.8.4.v201303031500"/> - <unit id="org.mockito.source" version="1.8.4.v201303031500"/> + <unit id="org.objenesis" version="2.6.0.v20180420-1519"/> + <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/> + <unit id="org.mockito" version="2.13.0.v20180426-1843"/> + <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/> + <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/> <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> @@ -66,11 +70,17 @@ <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> - <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/> + <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> - <repository location="http://download.eclipse.org/staging/2018-09/"/> + <repository location="http://download.eclipse.org/staging/2018-12/"/> </location> </locations> </target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.tpd index 6f963c7ceb..07594eafd7 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9-staging.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10-staging.tpd @@ -1,8 +1,8 @@ target "jgit-4.9-staging" with source configurePhase include "projects/jetty-9.4.11.tpd" -include "orbit/R20180905201904-2018-09.tpd" +include "orbit/R20181128170323-2018-12.tpd" -location "http://download.eclipse.org/staging/2018-09/" { +location "http://download.eclipse.org/staging/2018-12/" { org.eclipse.osgi lazy }
\ No newline at end of file diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target index 28ebdbf703..10ff3bea0d 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> -<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.5" sequenceNumber="1536523000"> +<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> +<target name="jgit-4.5" sequenceNumber="1544018556"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/> @@ -23,12 +23,12 @@ <repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> - <unit id="org.apache.ant" version="1.9.6.v201510161327"/> - <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/> + <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/> + <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/> <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/> <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/> - <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/> - <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/> + <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/> + <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/> <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/> <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> <unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/> @@ -46,10 +46,14 @@ <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> <unit id="javaewah" version="1.1.6.v20160919-1400"/> <unit id="javaewah.source" version="1.1.6.v20160919-1400"/> - <unit id="org.objenesis" version="1.0.0.v201505121915"/> - <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> - <unit id="org.mockito" version="1.8.4.v201303031500"/> - <unit id="org.mockito.source" version="1.8.4.v201303031500"/> + <unit id="org.objenesis" version="2.6.0.v20180420-1519"/> + <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/> + <unit id="org.mockito" version="2.13.0.v20180426-1843"/> + <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/> + <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/> <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> @@ -66,7 +70,13 @@ <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> - <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/> + <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd index 8e65c9820e..f099b34998 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd @@ -1,7 +1,7 @@ target "jgit-4.5" with source configurePhase include "projects/jetty-9.4.11.tpd" -include "orbit/R20180905201904-2018-09.tpd" +include "orbit/R20181128170323-2018-12.tpd" location "http://download.eclipse.org/releases/mars/" { 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 8490e55b27..11fd7fd69d 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 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> -<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.6" sequenceNumber="1536522989"> +<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> +<target name="jgit-4.6" sequenceNumber="1544018561"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/> @@ -23,12 +23,12 @@ <repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> - <unit id="org.apache.ant" version="1.9.6.v201510161327"/> - <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/> + <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/> + <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/> <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/> <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/> - <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/> - <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/> + <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/> + <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/> <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/> <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> <unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/> @@ -46,10 +46,14 @@ <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> <unit id="javaewah" version="1.1.6.v20160919-1400"/> <unit id="javaewah.source" version="1.1.6.v20160919-1400"/> - <unit id="org.objenesis" version="1.0.0.v201505121915"/> - <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> - <unit id="org.mockito" version="1.8.4.v201303031500"/> - <unit id="org.mockito.source" version="1.8.4.v201303031500"/> + <unit id="org.objenesis" version="2.6.0.v20180420-1519"/> + <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/> + <unit id="org.mockito" version="2.13.0.v20180426-1843"/> + <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/> + <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/> <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> @@ -66,7 +70,13 @@ <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> - <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/> + <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd index 44c846e0bf..39f166cc1c 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.tpd @@ -1,7 +1,7 @@ target "jgit-4.6" with source configurePhase include "projects/jetty-9.4.11.tpd" -include "orbit/R20180905201904-2018-09.tpd" +include "orbit/R20181128170323-2018-12.tpd" location "http://download.eclipse.org/releases/neon/" { org.eclipse.osgi lazy 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 a6651b837b..061439cd93 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 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> -<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.7" sequenceNumber="1536522970"> +<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> +<target name="jgit-4.7" sequenceNumber="1544018548"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/> @@ -23,12 +23,12 @@ <repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> - <unit id="org.apache.ant" version="1.9.6.v201510161327"/> - <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/> + <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/> + <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/> <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/> <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/> - <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/> - <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/> + <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/> + <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/> <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/> <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> <unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/> @@ -46,10 +46,14 @@ <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> <unit id="javaewah" version="1.1.6.v20160919-1400"/> <unit id="javaewah.source" version="1.1.6.v20160919-1400"/> - <unit id="org.objenesis" version="1.0.0.v201505121915"/> - <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> - <unit id="org.mockito" version="1.8.4.v201303031500"/> - <unit id="org.mockito.source" version="1.8.4.v201303031500"/> + <unit id="org.objenesis" version="2.6.0.v20180420-1519"/> + <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/> + <unit id="org.mockito" version="2.13.0.v20180426-1843"/> + <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/> + <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/> <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> @@ -66,7 +70,13 @@ <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> - <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/> + <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd index b63955f2af..b1f05680bc 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.tpd @@ -1,7 +1,7 @@ target "jgit-4.7" with source configurePhase include "projects/jetty-9.4.11.tpd" -include "orbit/R20180905201904-2018-09.tpd" +include "orbit/R20181128170323-2018-12.tpd" location "http://download.eclipse.org/releases/oxygen/" { org.eclipse.osgi lazy 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 7adb7dafd6..da66fb9475 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 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <?pde?> -<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform --> -<target name="jgit-4.8" sequenceNumber="1536522959"> +<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> +<target name="jgit-4.8" sequenceNumber="1544018536"> <locations> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/> @@ -23,12 +23,12 @@ <repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> - <unit id="org.apache.ant" version="1.9.6.v201510161327"/> - <unit id="org.apache.ant.source" version="1.9.6.v201510161327"/> + <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/> + <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/> <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/> <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/> - <unit id="org.apache.commons.compress" version="1.15.0.v20180119-1613"/> - <unit id="org.apache.commons.compress.source" version="1.15.0.v20180119-1613"/> + <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/> + <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/> <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/> <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> <unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/> @@ -46,10 +46,14 @@ <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> <unit id="javaewah" version="1.1.6.v20160919-1400"/> <unit id="javaewah.source" version="1.1.6.v20160919-1400"/> - <unit id="org.objenesis" version="1.0.0.v201505121915"/> - <unit id="org.objenesis.source" version="1.0.0.v201505121915"/> - <unit id="org.mockito" version="1.8.4.v201303031500"/> - <unit id="org.mockito.source" version="1.8.4.v201303031500"/> + <unit id="org.objenesis" version="2.6.0.v20180420-1519"/> + <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/> + <unit id="org.mockito" version="2.13.0.v20180426-1843"/> + <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/> + <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/> <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> @@ -66,7 +70,13 @@ <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> - <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/> + <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/> </location> <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> <unit id="org.eclipse.osgi" version="0.0.0"/> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd index 4e41ad9a04..959abaeb56 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.tpd @@ -1,7 +1,7 @@ target "jgit-4.8" with source configurePhase include "projects/jetty-9.4.11.tpd" -include "orbit/R20180905201904-2018-09.tpd" +include "orbit/R20181128170323-2018-12.tpd" location "http://download.eclipse.org/releases/photon/" { org.eclipse.osgi lazy 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 new file mode 100644 index 0000000000..025bb1fa1c --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?pde?> +<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> +<target name="jgit-4.9" sequenceNumber="1544017335"> + <locations> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="org.eclipse.jetty.client" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.client.source" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.continuation" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.continuation.source" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.http" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.http.source" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.io" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.io.source" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.security" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.security.source" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.server" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.server.source" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.servlet" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.servlet.source" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.util" version="9.4.11.v20180605"/> + <unit id="org.eclipse.jetty.util.source" version="9.4.11.v20180605"/> + <repository id="jetty-9.4.11" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.11.v20180605"/> + </location> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="org.apache.ant" version="1.10.5.v20180808-0324"/> + <unit id="org.apache.ant.source" version="1.10.5.v20180808-0324"/> + <unit id="org.apache.commons.codec" version="1.10.0.v20180409-1845"/> + <unit id="org.apache.commons.codec.source" version="1.10.0.v20180409-1845"/> + <unit id="org.apache.commons.compress" version="1.18.0.v20181121-2221"/> + <unit id="org.apache.commons.compress.source" version="1.18.0.v20181121-2221"/> + <unit id="org.apache.commons.logging" version="1.2.0.v20180409-1502"/> + <unit id="org.apache.commons.logging.source" version="1.2.0.v20180409-1502"/> + <unit id="org.apache.httpcomponents.httpclient" version="4.5.5.v20180409-1525"/> + <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.5.v20180409-1525"/> + <unit id="org.apache.httpcomponents.httpcore" version="4.4.9.v20180409-1525"/> + <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> + <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> + <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> + <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> + <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> + <unit id="org.hamcrest" version="1.1.0.v20090501071000"/> + <unit id="org.hamcrest.core" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.core.source" version="1.3.0.v20180420-1519"/> + <unit id="org.hamcrest.library" version="1.3.0.v20180524-2246"/> + <unit id="org.hamcrest.library.source" version="1.3.0.v20180524-2246"/> + <unit id="javaewah" version="1.1.6.v20160919-1400"/> + <unit id="javaewah.source" version="1.1.6.v20160919-1400"/> + <unit id="org.objenesis" version="2.6.0.v20180420-1519"/> + <unit id="org.objenesis.source" version="2.6.0.v20180420-1519"/> + <unit id="org.mockito" version="2.13.0.v20180426-1843"/> + <unit id="org.mockito.source" version="2.13.0.v20180426-1843"/> + <unit id="net.bytebuddy.byte-buddy" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy.source" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent" version="1.7.9.v20180420-1519"/> + <unit id="net.bytebuddy.byte-buddy-agent.source" version="1.7.9.v20180420-1519"/> + <unit id="com.google.gson" version="2.8.2.v20180104-1110"/> + <unit id="com.google.gson.source" version="2.8.2.v20180104-1110"/> + <unit id="com.jcraft.jsch" version="0.1.54.v20170116-1932"/> + <unit id="com.jcraft.jsch.source" version="0.1.54.v20170116-1932"/> + <unit id="org.junit" version="4.12.0.v201504281640"/> + <unit id="org.junit.source" version="4.12.0.v201504281640"/> + <unit id="javax.servlet" version="3.1.0.v201410161800"/> + <unit id="javax.servlet.source" version="3.1.0.v201410161800"/> + <unit id="org.tukaani.xz" version="1.6.0.v20170629-1752"/> + <unit id="org.tukaani.xz.source" version="1.6.0.v20170629-1752"/> + <unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/> + <unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/> + <unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/> + <unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/> + <unit id="com.jcraft.jzlib" version="1.1.1.v201205102305"/> + <unit id="com.jcraft.jzlib.source" version="1.1.1.v201205102305"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0.v20181102-1323"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0.v20181102-1323"/> + <unit id="org.apache.sshd.core" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.core.source" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp" version="2.0.0.v20181102-1323"/> + <unit id="org.apache.sshd.sftp.source" version="2.0.0.v20181102-1323"/> + <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository"/> + </location> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="org.eclipse.osgi" version="0.0.0"/> + <repository location="http://download.eclipse.org/releases/2018-09/"/> + </location> + </locations> +</target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd new file mode 100644 index 0000000000..0b6277d271 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.tpd @@ -0,0 +1,8 @@ +target "jgit-4.9" with source configurePhase + +include "projects/jetty-9.4.11.tpd" +include "orbit/R20181128170323-2018-12.tpd" + +location "http://download.eclipse.org/releases/2018-09/" { + org.eclipse.osgi lazy +}
\ No newline at end of file diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20181128170323-2018-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20181128170323-2018-12.tpd new file mode 100644 index 0000000000..d5a257d1df --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20181128170323-2018-12.tpd @@ -0,0 +1,58 @@ +target "R20181128170323-2018-12" with source configurePhase +// see http://download.eclipse.org/tools/orbit/downloads/ + +location "http://download.eclipse.org/tools/orbit/downloads/drops/R20181128170323/repository" { + org.apache.ant [1.10.5.v20180808-0324,1.10.5.v20180808-0324] + org.apache.ant.source [1.10.5.v20180808-0324,1.10.5.v20180808-0324] + org.apache.commons.codec [1.10.0.v20180409-1845,1.10.0.v20180409-1845] + org.apache.commons.codec.source [1.10.0.v20180409-1845,1.10.0.v20180409-1845] + org.apache.commons.compress [1.18.0.v20181121-2221,1.18.0.v20181121-2221] + org.apache.commons.compress.source [1.18.0.v20181121-2221,1.18.0.v20181121-2221] + 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.5.v20180409-1525,4.5.5.v20180409-1525] + org.apache.httpcomponents.httpclient.source [4.5.5.v20180409-1525,4.5.5.v20180409-1525] + org.apache.httpcomponents.httpcore [4.4.9.v20180409-1525,4.4.9.v20180409-1525] + org.apache.httpcomponents.httpcore.source [4.4.9.v20180409-1525,4.4.9.v20180409-1525] + org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815] + org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815] + 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.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000] + 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] + javaewah [1.1.6.v20160919-1400,1.1.6.v20160919-1400] + javaewah.source [1.1.6.v20160919-1400,1.1.6.v20160919-1400] + 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.mockito [2.13.0.v20180426-1843,2.13.0.v20180426-1843] + org.mockito.source [2.13.0.v20180426-1843,2.13.0.v20180426-1843] + net.bytebuddy.byte-buddy [1.7.9.v20180420-1519,1.7.9.v20180420-1519] + net.bytebuddy.byte-buddy.source [1.7.9.v20180420-1519,1.7.9.v20180420-1519] + net.bytebuddy.byte-buddy-agent [1.7.9.v20180420-1519,1.7.9.v20180420-1519] + net.bytebuddy.byte-buddy-agent.source [1.7.9.v20180420-1519,1.7.9.v20180420-1519] + com.google.gson [2.8.2.v20180104-1110,2.8.2.v20180104-1110] + com.google.gson.source [2.8.2.v20180104-1110,2.8.2.v20180104-1110] + com.jcraft.jsch [0.1.54.v20170116-1932,0.1.54.v20170116-1932] + com.jcraft.jsch.source [0.1.54.v20170116-1932,0.1.54.v20170116-1932] + org.junit [4.12.0.v201504281640,4.12.0.v201504281640] + org.junit.source [4.12.0.v201504281640,4.12.0.v201504281640] + javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800] + javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800] + org.tukaani.xz [1.6.0.v20170629-1752,1.6.0.v20170629-1752] + org.tukaani.xz.source [1.6.0.v20170629-1752,1.6.0.v20170629-1752] + org.slf4j.api [1.7.2.v20121108-1250,1.7.2.v20121108-1250] + org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250] + org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200] + org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200] + com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305] + com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305] + net.i2p.crypto.eddsa [0.3.0.v20181102-1323,0.3.0.v20181102-1323] + net.i2p.crypto.eddsa.source [0.3.0.v20181102-1323,0.3.0.v20181102-1323] + org.apache.sshd.core [2.0.0.v20181102-1323,2.0.0.v20181102-1323] + org.apache.sshd.core.source [2.0.0.v20181102-1323,2.0.0.v20181102-1323] + org.apache.sshd.sftp [2.0.0.v20181102-1323,2.0.0.v20181102-1323] + org.apache.sshd.sftp.source [2.0.0.v20181102-1323,2.0.0.v20181102-1323] +} diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml index f25796f13d..dba70d3238 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml @@ -49,7 +49,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.target</artifactId> @@ -82,4 +82,4 @@ </plugin> </plugins> </build> -</project>
\ No newline at end of file +</project> diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml index d763cb03b9..bc069c6265 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -53,7 +53,7 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>jgit.tycho.parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> <packaging>pom</packaging> <name>JGit Tycho Parent</name> @@ -79,10 +79,12 @@ <module>org.eclipse.jgit.target</module> <module>org.eclipse.jgit.feature</module> <module>org.eclipse.jgit.http.apache.feature</module> + <module>org.eclipse.jgit.ssh.apache.feature</module> <module>org.eclipse.jgit.lfs.feature</module> <module>org.eclipse.jgit.pgm.feature</module> <module>org.eclipse.jgit.source.feature</module> <module>org.eclipse.jgit.pgm.source.feature</module> + <module>org.eclipse.jgit.ssh.apache.source.feature</module> <module>org.eclipse.jgit.junit.feature</module> <module>org.eclipse.jgit.repository</module> </modules> @@ -115,6 +117,12 @@ <version>${project.version}</version> <classifier>sources</classifier> </dependency> + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.ssh.apache</artifactId> + <version>${project.version}</version> + <classifier>sources</classifier> + </dependency> </dependencies> <build> diff --git a/org.eclipse.jgit.pgm.test/.classpath b/org.eclipse.jgit.pgm.test/.classpath index b26f4c45d1..1334739b76 100644 --- a/org.eclipse.jgit.pgm.test/.classpath +++ b/org.eclipse.jgit.pgm.test/.classpath @@ -1,7 +1,15 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="src" path="tst"/> - <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="tst"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="src" path="src"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="output" path="bin"/> diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs index 794592dee1..2ca78ff2d0 100644 --- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF index 97a7ba22d8..90398a956c 100644 --- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF @@ -3,28 +3,28 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.pgm.test Bundle-SymbolicName: org.eclipse.jgit.pgm.test -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.jgit.api;version="[5.1.9,5.2.0)", - org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.diff;version="[5.1.9,5.2.0)", - org.eclipse.jgit.dircache;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="5.1.9", - org.eclipse.jgit.junit;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.merge;version="[5.1.9,5.2.0)", - org.eclipse.jgit.pgm;version="[5.1.9,5.2.0)", - org.eclipse.jgit.pgm.internal;version="[5.1.9,5.2.0)", - org.eclipse.jgit.pgm.opt;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)", +Import-Package: org.eclipse.jgit.api;version="[5.2.3,5.3.0)", + org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.diff;version="[5.2.3,5.3.0)", + org.eclipse.jgit.dircache;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="5.2.3", + org.eclipse.jgit.junit;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.merge;version="[5.2.3,5.3.0)", + org.eclipse.jgit.pgm;version="[5.2.3,5.3.0)", + org.eclipse.jgit.pgm.internal;version="[5.2.3,5.3.0)", + org.eclipse.jgit.pgm.opt;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)", org.hamcrest.core;bundle-version="[1.1.0,2.0.0)", org.junit;version="[4.12,5.0.0)", org.junit.rules;version="[4.12,5.0.0)", diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml index d5641bec1c..ced7168068 100644 --- a/org.eclipse.jgit.pgm.test/pom.xml +++ b/org.eclipse.jgit.pgm.test/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm.test</artifactId> @@ -109,7 +109,7 @@ <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>@{argLine} -Xmx512m -Djava.io.tmpdir=${project.build.directory}</argLine> + <argLine>-Djava.io.tmpdir=${project.build.directory}</argLine> </configuration> </plugin> </plugins> diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java index 81875f11bc..9ad22ddf2e 100644 --- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java +++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java @@ -42,11 +42,13 @@ */ package org.eclipse.jgit.pgm; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertNull; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -153,7 +155,8 @@ public class CLIGitCommand extends Main { @Override PrintWriter createErrorWriter() { - return new PrintWriter(result.err); + return new PrintWriter(new OutputStreamWriter( + result.err, UTF_8)); } @Override @@ -249,19 +252,19 @@ public class CLIGitCommand extends Main { } public String outString() { - return out.toString(); + return new String(out.toByteArray(), UTF_8); } public List<String> outLines() { - return IO.readLines(out.toString()); + return IO.readLines(outString()); } public String errString() { - return err.toString(); + return new String(err.toByteArray(), UTF_8); } public List<String> errLines() { - return IO.readLines(err.toString()); + return IO.readLines(errString()); } } diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ProxyConfigTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ProxyConfigTest.java index 40a223d92f..42530f3a34 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ProxyConfigTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ProxyConfigTest.java @@ -37,6 +37,7 @@ */ package org.eclipse.jgit.pgm; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.ByteArrayOutputStream; @@ -47,7 +48,6 @@ import java.net.MalformedURLException; import java.util.List; import java.util.Map; -import org.eclipse.jgit.lib.Constants; import org.junit.Before; import org.junit.Test; @@ -204,7 +204,7 @@ public class ProxyConfigTest { while ((length = inputStream.read(buffer)) != -1) { result.write(buffer, 0, length); } - return result.toString(Constants.CHARACTER_ENCODING); + return result.toString(UTF_8.name()); } } } diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs index 13c32a6d94..ef6f5e732f 100644 --- a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.pgm/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.pgm/BUILD b/org.eclipse.jgit.pgm/BUILD index ddc84bea06..1a115dd427 100644 --- a/org.eclipse.jgit.pgm/BUILD +++ b/org.eclipse.jgit.pgm/BUILD @@ -7,6 +7,7 @@ java_library( deps = [ ":services", "//lib:args4j", + "//lib:commons-logging", "//lib:httpclient", "//lib:httpcore", "//lib:jetty-http", @@ -21,6 +22,7 @@ java_library( "//org.eclipse.jgit:jgit", "//org.eclipse.jgit.lfs:jgit-lfs", "//org.eclipse.jgit.lfs.server:jgit-lfs-server", + "//org.eclipse.jgit.ssh.apache:ssh-apache", "//org.eclipse.jgit.ui:ui", ], ) diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF index ce4d6efc75..e10a772a8c 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.pgm Bundle-SymbolicName: org.eclipse.jgit.pgm -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-Localization: plugin @@ -28,49 +28,50 @@ Import-Package: javax.servlet;version="[3.1.0,4.0.0)", org.eclipse.jetty.util.log;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.security;version="[9.4.5,10.0.0)", org.eclipse.jetty.util.thread;version="[9.4.5,10.0.0)", - org.eclipse.jgit.api;version="[5.1.9,5.2.0)", - org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.archive;version="[5.1.9,5.2.0)", - org.eclipse.jgit.awtui;version="[5.1.9,5.2.0)", - org.eclipse.jgit.blame;version="[5.1.9,5.2.0)", - org.eclipse.jgit.diff;version="[5.1.9,5.2.0)", - org.eclipse.jgit.dircache;version="[5.1.9,5.2.0)", - org.eclipse.jgit.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.gitrepo;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.ketch;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.io;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.pack;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.reftable;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.reftree;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.server;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.server.fs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs.server.s3;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.merge;version="[5.1.9,5.2.0)", - org.eclipse.jgit.nls;version="[5.1.9,5.2.0)", - org.eclipse.jgit.notes;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revplot;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk.filter;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.pack;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.http.apache;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)", + org.eclipse.jgit.api;version="[5.2.3,5.3.0)", + org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.archive;version="[5.2.3,5.3.0)", + org.eclipse.jgit.awtui;version="[5.2.3,5.3.0)", + org.eclipse.jgit.blame;version="[5.2.3,5.3.0)", + org.eclipse.jgit.diff;version="[5.2.3,5.3.0)", + org.eclipse.jgit.dircache;version="[5.2.3,5.3.0)", + org.eclipse.jgit.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.gitrepo;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.ketch;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.io;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.pack;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.reftable;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.reftree;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.server;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.server.fs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs.server.s3;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.merge;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.notes;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revplot;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk.filter;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.pack;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.http.apache;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.sshd;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)", org.kohsuke.args4j;version="[2.33.0,3.0.0)", org.kohsuke.args4j.spi;version="[2.33.0,3.0.0)" -Export-Package: org.eclipse.jgit.console;version="5.1.9"; +Export-Package: org.eclipse.jgit.console;version="5.2.3"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util", - org.eclipse.jgit.pgm;version="5.1.9"; + org.eclipse.jgit.pgm;version="5.2.3"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.pgm.opt, @@ -81,11 +82,11 @@ Export-Package: org.eclipse.jgit.console;version="5.1.9"; org.eclipse.jgit.treewalk, javax.swing, org.eclipse.jgit.transport", - org.eclipse.jgit.pgm.debug;version="5.1.9"; + org.eclipse.jgit.pgm.debug;version="5.2.3"; uses:="org.eclipse.jgit.util.io, org.eclipse.jgit.pgm", - org.eclipse.jgit.pgm.internal;version="5.1.9";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test", - org.eclipse.jgit.pgm.opt;version="5.1.9"; + org.eclipse.jgit.pgm.internal;version="5.2.3";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test", + org.eclipse.jgit.pgm.opt;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.kohsuke.args4j.spi, diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF index 2c4fba8083..9831e1347e 100644 --- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.pgm - Sources Bundle-SymbolicName: org.eclipse.jgit.pgm.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 5.1.9.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.1.9.qualifier";roots="." +Bundle-Version: 5.2.3.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="5.2.3.qualifier";roots="." diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml index 98f579a9fb..81ef1b3f70 100644 --- a/org.eclipse.jgit.pgm/pom.xml +++ b/org.eclipse.jgit.pgm/pom.xml @@ -50,7 +50,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.pgm</artifactId> @@ -101,6 +101,12 @@ </dependency> <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.ssh.apache</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties index 8ff9c6bb10..d7d895ab31 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties @@ -402,6 +402,7 @@ usage_showNotes=Add this ref to the list of note branches from which notes are d usage_showTimeInMilliseconds=Show mtime in milliseconds usage_squash=Squash commits as if a real merge happened, but do not make a commit or move the HEAD. usage_srcPrefix=show the source prefix instead of "a/" +usage_sshDriver=Selects the built-in ssh library to use, JSch or Apache MINA sshd. usage_symbolicVersionForTheProject=Symbolic version for the project usage_tags=fetch all tags usage_notags=do not fetch tags @@ -411,7 +412,7 @@ usage_untrackedFilesMode=show untracked files usage_updateRef=reference to update usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository usage_useNameInsteadOfOriginToTrackUpstream=use <name> instead of 'origin' to track upstream -usage_checkoutBranchAfterClone=checkout named branch instead of remotes's HEAD +usage_checkoutBranchAfterClone=checkout named branch instead of remote's HEAD usage_viewCommitHistory=View commit history -usage_orphan=Create a new orphan branch. The first commit made on this new branch will have no parents amd it will be the root of a new history totally disconnected from other branches and commits. +usage_orphan=Create a new orphan branch. The first commit made on this new branch will have no parents and it will be the root of a new history totally disconnected from other branches and commits. usernameFor=Username for {0}: diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java index 7e5b5451b6..c4b4018b8f 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java @@ -44,6 +44,9 @@ package org.eclipse.jgit.pgm; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SECTION_I18N; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_LOG_OUTPUT_ENCODING; import static org.eclipse.jgit.lib.Constants.R_HEADS; import static org.eclipse.jgit.lib.Constants.R_REMOTES; import static org.eclipse.jgit.lib.Constants.R_TAGS; @@ -56,14 +59,20 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.nio.charset.Charset; import java.text.MessageFormat; import java.util.ResourceBundle; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.pgm.internal.CLIText; +import org.eclipse.jgit.pgm.internal.SshDriver; import org.eclipse.jgit.pgm.opt.CmdLineParser; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.SshSessionFactory; +import org.eclipse.jgit.transport.sshd.DefaultProxyDataFactory; +import org.eclipse.jgit.transport.sshd.JGitKeyCache; +import org.eclipse.jgit.transport.sshd.SshdSessionFactory; import org.eclipse.jgit.util.io.ThrowingPrintWriter; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.Option; @@ -85,6 +94,9 @@ public abstract class TextBuiltin { @Option(name = "--help", usage = "usage_displayThisHelpText", aliases = { "-h" }) private boolean help; + @Option(name = "--ssh", usage = "usage_sshDriver") + private SshDriver sshDriver = SshDriver.JSCH; + /** * Input stream, typically this is standard input. * @@ -168,6 +180,30 @@ public abstract class TextBuiltin { } /** + * Get the log output encoding specified in the repository's + * {@code i18n.logOutputEncoding} configuration. + * + * @param repository + * the repository. + * @return Charset corresponding to {@code i18n.logOutputEncoding}, or + * {@code UTF_8}. + */ + private Charset getLogOutputEncodingCharset(Repository repository) { + if (repository != null) { + String logOutputEncoding = repository.getConfig().getString( + CONFIG_SECTION_I18N, null, CONFIG_KEY_LOG_OUTPUT_ENCODING); + if (logOutputEncoding != null) { + try { + return Charset.forName(logOutputEncoding); + } catch (IllegalArgumentException e) { + throw die(CLIText.get().cannotCreateOutputStream); + } + } + } + return UTF_8; + } + + /** * Initialize the command to work with a repository. * * @param repository @@ -177,32 +213,18 @@ public abstract class TextBuiltin { * {@code repository} is null. */ protected void init(Repository repository, String gitDir) { - try { - final String outputEncoding = repository != null ? repository - .getConfig().getString("i18n", null, "logOutputEncoding") : null; //$NON-NLS-1$ //$NON-NLS-2$ - if (ins == null) - ins = new FileInputStream(FileDescriptor.in); - if (outs == null) - outs = new FileOutputStream(FileDescriptor.out); - if (errs == null) - errs = new FileOutputStream(FileDescriptor.err); - BufferedWriter outbufw; - if (outputEncoding != null) - outbufw = new BufferedWriter(new OutputStreamWriter(outs, - outputEncoding)); - else - outbufw = new BufferedWriter(new OutputStreamWriter(outs)); - outw = new ThrowingPrintWriter(outbufw); - BufferedWriter errbufw; - if (outputEncoding != null) - errbufw = new BufferedWriter(new OutputStreamWriter(errs, - outputEncoding)); - else - errbufw = new BufferedWriter(new OutputStreamWriter(errs)); - errw = new ThrowingPrintWriter(errbufw); - } catch (IOException e) { - throw die(CLIText.get().cannotCreateOutputStream); - } + Charset charset = getLogOutputEncodingCharset(repository); + + if (ins == null) + ins = new FileInputStream(FileDescriptor.in); + if (outs == null) + outs = new FileOutputStream(FileDescriptor.out); + if (errs == null) + errs = new FileOutputStream(FileDescriptor.err); + outw = new ThrowingPrintWriter(new BufferedWriter( + new OutputStreamWriter(outs, charset))); + errw = new ThrowingPrintWriter(new BufferedWriter( + new OutputStreamWriter(errs, charset))); if (repository != null && repository.getDirectory() != null) { db = repository; @@ -225,6 +247,20 @@ public abstract class TextBuiltin { */ public final void execute(String[] args) throws Exception { parseArguments(args); + switch (sshDriver) { + case APACHE: { + SshdSessionFactory factory = new SshdSessionFactory( + new JGitKeyCache(), new DefaultProxyDataFactory()); + Runtime.getRuntime() + .addShutdownHook(new Thread(() -> factory.close())); + SshSessionFactory.setInstance(factory); + break; + } + case JSCH: + default: + SshSessionFactory.setInstance(null); + break; + } run(); } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java index bb51b50487..300a01d246 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java @@ -474,7 +474,7 @@ class TextHashFunctions extends TextBuiltin { } /** Utility to help us identify unique lines in a file. */ - private class Line { + private static class Line { private final RawText txt; private final int pos; diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/SshDriver.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/SshDriver.java new file mode 100644 index 0000000000..7d0423b014 --- /dev/null +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/SshDriver.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.pgm.internal; + +/** + * Simple enumeration for the available built-in ssh clients. + */ +public enum SshDriver { + + /** Default client: use JSch. */ + JSCH, + + /** Use the Apache MINA sshd client from org.eclipse.jgit.ssh.apache. */ + APACHE; + +} diff --git a/org.eclipse.jgit.ssh.apache.test/.classpath b/org.eclipse.jgit.ssh.apache.test/.classpath new file mode 100644 index 0000000000..f08af0a4e9 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.classpath @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="tst"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.jgit.ssh.apache.test/.gitignore b/org.eclipse.jgit.ssh.apache.test/.gitignore new file mode 100644 index 0000000000..934e0e06ff --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.gitignore @@ -0,0 +1,2 @@ +/bin +/target diff --git a/org.eclipse.jgit.ssh.apache.test/.project b/org.eclipse.jgit.ssh.apache.test/.project new file mode 100644 index 0000000000..0aafb730fb --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.jgit.ssh.apache.test</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..f77db3b723 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Sat Dec 20 21:21:24 CET 2008 +eclipse.preferences.version=1 +encoding/<project>=UTF-8 diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000000..9f733eeea7 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,3 @@ +#Mon Mar 24 18:55:56 EDT 2008 +eclipse.preferences.version=1 +line.separator=\n diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..794592dee1 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,399 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +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=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.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=ignore +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=ignore +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=warning +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=warning +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.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=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error +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.source=1.8 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +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_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +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_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +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_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=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_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_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=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_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_method_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.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.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.insert_new_line_before_root_tags=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=true +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_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_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=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_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_and_in_type_parameter=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_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_superinterfaces=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_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_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_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=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_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_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_superinterfaces=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_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_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_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_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_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_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_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +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_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +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.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000000..fef3713825 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.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=12 +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=<?xml version\="1.0" encoding\="UTF-8"?><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.test/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 0000000000..823c0f56ae --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.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.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.mylyn.team.ui.prefs new file mode 100644 index 0000000000..0cba949fb7 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.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.test/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.api.tools.prefs new file mode 100644 index 0000000000..c0030ded71 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.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.test/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.ssh.apache.test/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 0000000000..82793f2d27 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/.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.test/BUILD b/org.eclipse.jgit.ssh.apache.test/BUILD new file mode 100644 index 0000000000..a13cf0b30f --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/BUILD @@ -0,0 +1,19 @@ +load( + "@com_googlesource_gerrit_bazlets//tools:junit.bzl", + "junit_tests", +) + +junit_tests( + name = "sshd_apache", + srcs = glob(["tst/**/*.java"]), + tags = ["sshd"], + deps = [ + "//lib:eddsa", + "//lib:junit", + "//lib:sshd-core", + "//lib:sshd-sftp", + "//org.eclipse.jgit:jgit", + "//org.eclipse.jgit.ssh.apache:ssh-apache", + "//org.eclipse.jgit.test:sshd-helpers", + ], +) diff --git a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..5456949947 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF @@ -0,0 +1,19 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %Bundle-Name +Automatic-Module-Name: org.eclipse.jgit.ssh.apache.test +Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.test +Bundle-Version: 5.2.3.qualifier +Bundle-Vendor: %Provider-Name +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Import-Package: org.eclipse.jgit.internal.transport.sshd.proxy;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit.ssh;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.ssh;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.sshd;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", + org.junit;version="[4.12,5.0.0)", + org.junit.experimental.theories;version="[4.12,5.0.0)", + org.junit.runner;version="[4.12,5.0.0)" diff --git a/org.eclipse.jgit.ssh.apache.test/about.html b/org.eclipse.jgit.ssh.apache.test/about.html new file mode 100644 index 0000000000..f971af18d0 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/about.html @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> +<title>Eclipse Distribution License - Version 1.0</title> +<style type="text/css"> + body { + size: 8.5in 11.0in; + margin: 0.25in 0.5in 0.25in 0.5in; + tab-interval: 0.5in; + } + p { + margin-left: auto; + margin-top: 0.5em; + margin-bottom: 0.5em; + } + p.list { + margin-left: 0.5in; + margin-top: 0.05em; + margin-bottom: 0.05em; + } + .ubc-name { + margin-left: 0.5in; + white-space: pre; + } + </style> + +</head> + +<body lang="EN-US"> + +<p><b>Eclipse Distribution License - v 1.0</b></p> + +<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p> + +<p>All rights reserved.</p> +<p>Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: +<ul><li>Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. </li> +<li>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. </li> +<li>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. </li></ul> +</p> +<p>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.</p> + +<hr> +<p><b>SHA-1 UbcCheck - MIT</b></p> + +<p>Copyright (c) 2017:</p> +<div class="ubc-name"> +Marc Stevens +Cryptology Group +Centrum Wiskunde & Informatica +P.O. Box 94079, 1090 GB Amsterdam, Netherlands +marc@marc-stevens.nl +</div> +<div class="ubc-name"> +Dan Shumow +Microsoft Research +danshu@microsoft.com +</div> +<p>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: +</p> +<ul><li>The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software.</li></ul> +<p>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.</p> + +</body> + +</html> diff --git a/org.eclipse.jgit.ssh.apache.test/build.properties b/org.eclipse.jgit.ssh.apache.test/build.properties new file mode 100644 index 0000000000..9ffa0caf78 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/build.properties @@ -0,0 +1,5 @@ +source.. = tst/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.properties diff --git a/org.eclipse.jgit.ssh.apache.test/plugin.properties b/org.eclipse.jgit.ssh.apache.test/plugin.properties new file mode 100644 index 0000000000..67c296d9e5 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/plugin.properties @@ -0,0 +1,2 @@ +plugin_name=JGit Tests for SSH with Apache MINA sshd +provider_name=Eclipse JGit diff --git a/org.eclipse.jgit.ssh.apache.test/pom.xml b/org.eclipse.jgit.ssh.apache.test/pom.xml new file mode 100644 index 0000000000..a03f4ececd --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/pom.xml @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit-parent</artifactId> + <version>5.2.3-SNAPSHOT</version> + </parent> + + <artifactId>org.eclipse.jgit.ssh.apache.test</artifactId> + <name>JGit - Apache sshd SSH Tests</name> + + <description> + JUnit tests for the JGit SSH support based on Apache MINA sshd. + </description> + + <properties> + <maven.javadoc.skip>true</maven.javadoc.skip> + </properties> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.junit</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.ssh.apache</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.test</artifactId> + <version>${project.version}</version> + </dependency> + + </dependencies> + + <profiles> + <!-- Profile provides a property which enables long running tests. --> + <profile> + <id>test.long</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine>-Djgit.test.long=true</argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + + <build> + <sourceDirectory>src/</sourceDirectory> + <testSourceDirectory>tst/</testSourceDirectory> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <argLine>-Xmx1024m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine> + <includes> + <include>**/*Test.java</include> + <include>**/*Tests.java</include> + </includes> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParserTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParserTest.java new file mode 100644 index 0000000000..b8e85493aa --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParserTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.proxy; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +public class HttpParserTest { + + private static final String STATUS_LINE = "HTTP/1.1. 407 Authentication required"; + + @Test + public void testEmpty() throws Exception { + String[] lines = { STATUS_LINE }; + List<AuthenticationChallenge> challenges = HttpParser + .getAuthenticationHeaders(Arrays.asList(lines), + "WWW-Authenticate:"); + assertTrue("No challenges expected", challenges.isEmpty()); + } + + @Test + public void testRFC7235Example() throws Exception { + // The example from RFC 7235, sec. 4.1, slightly modified ("kind" + // argument with whitespace around '=') + String[] lines = { STATUS_LINE, + "WWW-Authenticate: Newauth realm=\"apps\", type=1 , kind = \t2 ", + " \t title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"" }; + List<AuthenticationChallenge> challenges = HttpParser + .getAuthenticationHeaders(Arrays.asList(lines), + "WWW-Authenticate:"); + assertEquals("Unexpected number of challenges", 2, challenges.size()); + assertNull("No token expected", challenges.get(0).getToken()); + assertNull("No token expected", challenges.get(1).getToken()); + assertEquals("Unexpected mechanism", "Newauth", + challenges.get(0).getMechanism()); + assertEquals("Unexpected mechanism", "Basic", + challenges.get(1).getMechanism()); + Map<String, String> expectedArguments = new LinkedHashMap<>(); + expectedArguments.put("realm", "apps"); + expectedArguments.put("type", "1"); + expectedArguments.put("kind", "2"); + expectedArguments.put("title", "Login to \"apps\""); + assertEquals("Unexpected arguments", expectedArguments, + challenges.get(0).getArguments()); + expectedArguments.clear(); + expectedArguments.put("realm", "simple"); + assertEquals("Unexpected arguments", expectedArguments, + challenges.get(1).getArguments()); + } + + @Test + public void testMultipleHeaders() { + String[] lines = { STATUS_LINE, + "Server: Apache", + "WWW-Authenticate: Newauth realm=\"apps\", type=1 , kind = \t2 ", + " \t title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"", + "Content-Type: text/plain", + "WWW-Authenticate: Other 0123456789=== , YetAnother, ", + "WWW-Authenticate: Negotiate ", + "WWW-Authenticate: Negotiate a87421000492aa874209af8bc028" }; + List<AuthenticationChallenge> challenges = HttpParser + .getAuthenticationHeaders(Arrays.asList(lines), + "WWW-Authenticate:"); + assertEquals("Unexpected number of challenges", 6, challenges.size()); + assertEquals("Mismatched challenge", "Other", + challenges.get(2).getMechanism()); + assertEquals("Token expected", "0123456789===", + challenges.get(2).getToken()); + assertEquals("Mismatched challenge", "YetAnother", + challenges.get(3).getMechanism()); + assertNull("No token expected", challenges.get(3).getToken()); + assertTrue("No arguments expected", + challenges.get(3).getArguments().isEmpty()); + assertEquals("Mismatched challenge", "Negotiate", + challenges.get(4).getMechanism()); + assertNull("No token expected", challenges.get(4).getToken()); + assertEquals("Mismatched challenge", "Negotiate", + challenges.get(5).getMechanism()); + assertEquals("Token expected", "a87421000492aa874209af8bc028", + challenges.get(5).getToken()); + } + + @Test + public void testStopOnEmptyLine() { + String[] lines = { STATUS_LINE, "Server: Apache", + "WWW-Authenticate: Newauth realm=\"apps\", type=1 , kind = \t2 ", + " \t title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"", + "Content-Type: text/plain", + "WWW-Authenticate: Other 0123456789===", "", + // Not headers anymore; this would be the body + "WWW-Authenticate: Negotiate ", + "WWW-Authenticate: Negotiate a87421000492aa874209af8bc028" }; + List<AuthenticationChallenge> challenges = HttpParser + .getAuthenticationHeaders(Arrays.asList(lines), + "WWW-Authenticate:"); + assertEquals("Unexpected number of challenges", 3, challenges.size()); + } +} 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 new file mode 100644 index 0000000000..ee58083a5a --- /dev/null +++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.util.Arrays; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.transport.SshSessionFactory; +import org.eclipse.jgit.transport.ssh.SshTestBase; +import org.eclipse.jgit.transport.sshd.SshdSessionFactory; +import org.eclipse.jgit.util.FS; +import org.junit.Test; +import org.junit.experimental.theories.Theories; +import org.junit.runner.RunWith; + +@RunWith(Theories.class) +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; + } + + @Override + protected void installConfig(String... config) { + File configFile = new File(sshDir, Constants.CONFIG); + if (config != null) { + try { + Files.write(configFile.toPath(), Arrays.asList(config)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + + // Using an ed25519 (unencrypted) user key is tested in the super class in + // testSshKeys(). sshd 2.0.0 cannot yet read encrypted ed25519 keys. + + @Test + public void testEd25519HostKey() throws Exception { + File newHostKey = new File(getTemporaryDirectory(), "newhostkey"); + copyTestResource("id_ed25519", newHostKey); + server.addHostKey(newHostKey.toPath(), true); + File newHostKeyPub = new File(getTemporaryDirectory(), + "newhostkey.pub"); + copyTestResource("id_ed25519.pub", newHostKeyPub); + createKnownHostsFile(knownHosts, "localhost", testPort, newHostKeyPub); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + +} diff --git a/org.eclipse.jgit.ssh.apache/.classpath b/org.eclipse.jgit.ssh.apache/.classpath new file mode 100644 index 0000000000..110168ffa1 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.classpath @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="resources"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.jgit.ssh.apache/.fbprefs b/org.eclipse.jgit.ssh.apache/.fbprefs new file mode 100644 index 0000000000..81a0767ff6 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.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/.gitignore b/org.eclipse.jgit.ssh.apache/.gitignore new file mode 100644 index 0000000000..934e0e06ff --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.gitignore @@ -0,0 +1,2 @@ +/bin +/target diff --git a/org.eclipse.jgit.ssh.apache/.project b/org.eclipse.jgit.ssh.apache/.project new file mode 100644 index 0000000000..a7bbd6bafd --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.project @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.jgit.ssh.apache</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..66ac15c47c --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Mon Aug 11 16:46:12 PDT 2008 +eclipse.preferences.version=1 +encoding/<project>=UTF-8 diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000000..006e07ede5 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.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/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..13c32a6d94 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,399 @@ +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=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.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.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=ignore +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.source=1.8 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +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_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +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_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +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_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=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_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_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=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_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_method_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.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.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.insert_new_line_before_root_tags=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=true +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_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_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=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_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_and_in_type_parameter=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_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_superinterfaces=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_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_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_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=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_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_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_superinterfaces=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_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_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_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_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_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_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_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +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_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +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.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000000..fef3713825 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.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=12 +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=<?xml version\="1.0" encoding\="UTF-8"?><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/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 0000000000..823c0f56ae --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.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/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.mylyn.team.ui.prefs new file mode 100644 index 0000000000..0cba949fb7 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.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/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.api.tools.prefs new file mode 100644 index 0000000000..c0030ded71 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.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/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.ssh.apache/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 0000000000..82793f2d27 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/.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/BUILD b/org.eclipse.jgit.ssh.apache/BUILD new file mode 100644 index 0000000000..a1a6c8e24c --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/BUILD @@ -0,0 +1,19 @@ +package(default_visibility = ["//visibility:public"]) + +SRCS = glob(["src/**/*.java"]) + +RESOURCES = glob(["resources/**"]) + +java_library( + name = "ssh-apache", + srcs = SRCS, + resource_strip_prefix = "org.eclipse.jgit.ssh.apache/resources", + resources = RESOURCES, + deps = [ + "//lib:eddsa", + "//lib:slf4j-api", + "//lib:sshd-core", + "//lib:sshd-sftp", + "//org.eclipse.jgit:jgit", + ], +) diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..29a12bbbe9 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF @@ -0,0 +1,83 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %Bundle-Name +Automatic-Module-Name: org.eclipse.jgit.ssh.apache +Bundle-SymbolicName: org.eclipse.jgit.ssh.apache +Bundle-Vendor: %Provider-Name +Bundle-ActivationPolicy: lazy +Bundle-Version: 5.2.3.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: org.eclipse.jgit.internal.transport.sshd;version="5.2.3";x-internal:=true; + uses:="org.apache.sshd.client, + org.apache.sshd.client.auth, + org.apache.sshd.client.auth.keyboard, + org.apache.sshd.client.auth.pubkey, + org.apache.sshd.client.config.hosts, + org.apache.sshd.client.future, + org.apache.sshd.client.keyverifier, + org.apache.sshd.client.session, + org.apache.sshd.common.config.keys, + org.apache.sshd.common.io, + org.apache.sshd.common.keyprovider, + org.apache.sshd.common.signature, + org.apache.sshd.common.util.buffer, + org.eclipse.jgit.transport", + org.eclipse.jgit.internal.transport.sshd.auth;version="5.2.3";x-internal:=true, + org.eclipse.jgit.internal.transport.sshd.proxy;version="5.2.3";x-friends:="org.eclipse.jgit.ssh.apache.test", + org.eclipse.jgit.transport.sshd;version="5.2.3"; + uses:="org.eclipse.jgit.transport, + org.apache.sshd.client.config.hosts, + org.apache.sshd.common.keyprovider, + org.eclipse.jgit.util, + org.apache.sshd.client.session, + org.apache.sshd.client.keyverifier" +Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)", + org.apache.sshd.agent;version="[2.0.0,2.1.0)", + org.apache.sshd.client;version="[2.0.0,2.1.0)", + org.apache.sshd.client.auth;version="[2.0.0,2.1.0)", + org.apache.sshd.client.auth.keyboard;version="[2.0.0,2.1.0)", + org.apache.sshd.client.auth.password;version="[2.0.0,2.1.0)", + org.apache.sshd.client.auth.pubkey;version="[2.0.0,2.1.0)", + org.apache.sshd.client.channel;version="[2.0.0,2.1.0)", + org.apache.sshd.client.config.hosts;version="[2.0.0,2.1.0)", + org.apache.sshd.client.config.keys;version="[2.0.0,2.1.0)", + org.apache.sshd.client.future;version="[2.0.0,2.1.0)", + org.apache.sshd.client.keyverifier;version="[2.0.0,2.1.0)", + org.apache.sshd.client.session;version="[2.0.0,2.1.0)", + org.apache.sshd.client.subsystem.sftp;version="[2.0.0,2.1.0)", + org.apache.sshd.common;version="[2.0.0,2.1.0)", + org.apache.sshd.common.auth;version="[2.0.0,2.1.0)", + org.apache.sshd.common.channel;version="[2.0.0,2.1.0)", + org.apache.sshd.common.compression;version="[2.0.0,2.1.0)", + org.apache.sshd.common.config.keys;version="[2.0.0,2.1.0)", + org.apache.sshd.common.config.keys.loader;version="[2.0.0,2.1.0)", + org.apache.sshd.common.digest;version="[2.0.0,2.1.0)", + org.apache.sshd.common.forward;version="[2.0.0,2.1.0)", + org.apache.sshd.common.future;version="[2.0.0,2.1.0)", + org.apache.sshd.common.helpers;version="[2.0.0,2.1.0)", + org.apache.sshd.common.io;version="[2.0.0,2.1.0)", + org.apache.sshd.common.kex;version="[2.0.0,2.1.0)", + org.apache.sshd.common.keyprovider;version="[2.0.0,2.1.0)", + org.apache.sshd.common.mac;version="[2.0.0,2.1.0)", + org.apache.sshd.common.random;version="[2.0.0,2.1.0)", + org.apache.sshd.common.session;version="[2.0.0,2.1.0)", + org.apache.sshd.common.session.helpers;version="[2.0.0,2.1.0)", + org.apache.sshd.common.signature;version="[2.0.0,2.1.0)", + org.apache.sshd.common.subsystem.sftp;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util.buffer;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util.closeable;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util.io;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util.logging;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util.net;version="[2.0.0,2.1.0)", + org.apache.sshd.common.util.security;version="[2.0.0,2.1.0)", + org.apache.sshd.server.auth;version="[2.0.0,2.1.0)", + org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)", + org.eclipse.jgit.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.fnmatch;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.transport.ssh;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", + org.slf4j;version="[1.7.0,2.0.0)" diff --git a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF new file mode 100644 index 0000000000..a16ebd3f97 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF @@ -0,0 +1,7 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: org.eclipse.jgit.ssh.apache - Sources +Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.source +Bundle-Vendor: Eclipse.org - JGit +Bundle-Version: 5.2.3.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="5.2.3.qualifier";roots="." diff --git a/org.eclipse.jgit.ssh.apache/about.html b/org.eclipse.jgit.ssh.apache/about.html new file mode 100644 index 0000000000..f971af18d0 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/about.html @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> +<title>Eclipse Distribution License - Version 1.0</title> +<style type="text/css"> + body { + size: 8.5in 11.0in; + margin: 0.25in 0.5in 0.25in 0.5in; + tab-interval: 0.5in; + } + p { + margin-left: auto; + margin-top: 0.5em; + margin-bottom: 0.5em; + } + p.list { + margin-left: 0.5in; + margin-top: 0.05em; + margin-bottom: 0.05em; + } + .ubc-name { + margin-left: 0.5in; + white-space: pre; + } + </style> + +</head> + +<body lang="EN-US"> + +<p><b>Eclipse Distribution License - v 1.0</b></p> + +<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p> + +<p>All rights reserved.</p> +<p>Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: +<ul><li>Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. </li> +<li>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. </li> +<li>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. </li></ul> +</p> +<p>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.</p> + +<hr> +<p><b>SHA-1 UbcCheck - MIT</b></p> + +<p>Copyright (c) 2017:</p> +<div class="ubc-name"> +Marc Stevens +Cryptology Group +Centrum Wiskunde & Informatica +P.O. Box 94079, 1090 GB Amsterdam, Netherlands +marc@marc-stevens.nl +</div> +<div class="ubc-name"> +Dan Shumow +Microsoft Research +danshu@microsoft.com +</div> +<p>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: +</p> +<ul><li>The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software.</li></ul> +<p>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.</p> + +</body> + +</html> diff --git a/org.eclipse.jgit.ssh.apache/build.properties b/org.eclipse.jgit.ssh.apache/build.properties new file mode 100644 index 0000000000..8148271ef3 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/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/plugin.properties b/org.eclipse.jgit.ssh.apache/plugin.properties new file mode 100644 index 0000000000..8f3540c0f0 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/plugin.properties @@ -0,0 +1,2 @@ +plugin_name=JGit SSH support based on Apache MINA sshd +provider_name=Eclipse JGit diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml new file mode 100644 index 0000000000..22541339a5 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/pom.xml @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + and other copyright owners as documented in the project's IP log. + + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + + 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit-parent</artifactId> + <version>5.2.3-SNAPSHOT</version> + </parent> + + <artifactId>org.eclipse.jgit.ssh.apache</artifactId> + <name>JGit - Apache sshd-based SSH support</name> + + <description> + SSH support for JGit based on Apache MINA sshd + </description> + + <properties> + <translate-qualifier/> + <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest> + <eddsa-version>0.3.0</eddsa-version> + </properties> + + <dependencies> + <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-core</artifactId> + <version>${apache-sshd-version}</version> + </dependency> + + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-sftp</artifactId> + <version>${apache-sshd-version}</version> + </dependency> + + <dependency> + <groupId>net.i2p.crypto</groupId> + <artifactId>eddsa</artifactId> + <version>${eddsa-version}</version> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + </dependencies> + + <build> + <sourceDirectory>src/</sourceDirectory> + + <resources> + <resource> + <directory>.</directory> + <includes> + <include>plugin.properties</include> + <include>about.html</include> + </includes> + </resource> + <resource> + <directory>resources/</directory> + </resource> + </resources> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <id>translate-source-qualifier</id> + <phase>generate-resources</phase> + <configuration> + <target> + <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true" /> + <replace file="${source-bundle-manifest}"> + <replacefilter token=".qualifier" value=".${maven.build.timestamp}" /> + </replace> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <inherited>true</inherited> + <executions> + <execution> + <id>attach-sources</id> + <phase>process-classes</phase> + <goals> + <goal>jar</goal> + </goals> + <configuration> + <archive> + <manifestFile>${source-bundle-manifest}</manifestFile> + </archive> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestFile>${bundle-manifest}</manifestFile> + </archive> + </configuration> + </plugin> + + <plugin> + <groupId>com.github.siom79.japicmp</groupId> + <artifactId>japicmp-maven-plugin</artifactId> + <version>${japicmp-version}</version> + <configuration> + <oldVersion> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>${project.artifactId}</artifactId> + <version>${jgit-last-release-version}</version> + </dependency> + </oldVersion> + <newVersion> + <file> + <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path> + </file> + </newVersion> + <parameter> + <onlyModified>true</onlyModified> + <includes> + <include>org.eclipse.jgit.*</include> + </includes> + <accessModifier>public</accessModifier> + <breakBuildOnModifications>false</breakBuildOnModifications> + <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications> + <onlyBinaryIncompatible>false</onlyBinaryIncompatible> + <includeSynthetic>false</includeSynthetic> + <ignoreMissingClasses>false</ignoreMissingClasses> + <skipPomModules>true</skipPomModules> + </parameter> + <skip>true</skip><!-- TODO: Enable after the first release --> + </configuration> + <executions> + <execution> + <phase>verify</phase> + <goals> + <goal>cmp</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <reporting> + <plugins> + <plugin> + <groupId>com.github.siom79.japicmp</groupId> + <artifactId>japicmp-maven-plugin</artifactId> + <version>${japicmp-version}</version> + <reportSets> + <reportSet> + <reports> + <report>cmp-report</report> + </reports> + </reportSet> + </reportSets> + <configuration> + <oldVersion> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>${project.artifactId}</artifactId> + <version>${jgit-last-release-version}</version> + </dependency> + </oldVersion> + <newVersion> + <file> + <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path> + </file> + </newVersion> + <parameter> + <onlyModified>true</onlyModified> + <includes> + <include>org.eclipse.jgit.*</include> + </includes> + <accessModifier>public</accessModifier> + <breakBuildOnModifications>false</breakBuildOnModifications> + <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications> + <onlyBinaryIncompatible>false</onlyBinaryIncompatible> + <includeSynthetic>false</includeSynthetic> + <ignoreMissingClasses>false</ignoreMissingClasses> + <skipPomModules>true</skipPomModules> + </parameter> + <skip>true</skip><!-- TODO: Enable after the first release --> + </configuration> + </plugin> + </plugins> + </reporting> +</project> 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 new file mode 100644 index 0000000000..aa4e4ccec3 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties @@ -0,0 +1,77 @@ +authenticationCanceled=Authentication canceled: no password +closeListenerFailed=Ssh session close listener failed +configInvalidPath=Invalid path in ssh config key {0}: {1} +configInvalidPattern=Invalid pattern in ssh config key {0}: {1} +configInvalidPositive=Ssh config entry {0} must be a strictly positive number but is ''{1}'' +configNoKnownHostKeyAlgorithms=No implementations for any of the algorithms ''{0}'' given in HostKeyAlgorithms in the ssh config; using the default. +configNoRemainingHostKeyAlgorithms=Ssh config removed all host key algorithms: HostKeyAlgorithms ''{0}'' +ftpCloseFailed=Closing the SFTP channel failed +gssapiFailure=GSS-API error for mechanism OID {0} +gssapiInitFailure=GSS-API initialization failure for mechanism {0} +gssapiUnexpectedMechanism=Server {0} replied with unknown mechanism name ''{1}'' in {2} authentication +gssapiUnexpectedMessage=Received unexpected ssh message {1} in {0} authentication +identityFileCannotDecrypt=Given passphrase cannot decrypt identity {0} +identityFileNoKey=No keys found in identity {0} +identityFileMultipleKeys=Multiple key pairs found in identity {0} +identityFileUnsupportedFormat=Unsupported format in identity {0} +kexServerKeyInvalid=Server key did not validate +keyEncryptedMsg=Key ''{0}'' is encrypted. Enter the passphrase to decrypt it. +keyEncryptedPrompt=Passphrase +keyEncryptedRetry=Encrypted key ''{0}'' could not be decrypted. Enter the passphrase again. +keyLoadFailed=Could not load key ''{0}'' +knownHostsCouldNotUpdate=Could not update known hosts file {0} +knownHostsFileLockedRead=Could not read known hosts file (locked) {0} +knownHostsFileLockedUpdate=Could not update known hosts file (locked) {0} +knownHostsFileReadFailed=Failed to read known hosts file {0} +knownHostsInvalidLine=Known hosts file {0} contains invalid line {1} +knownHostsInvalidPath=Invalid path for known hosts file {0} +knownHostsKeyFingerprints=The {0} key''s fingerprints are: +knownHostsModifiedKeyAcceptPrompt=Accept this key and continue connecting all the same? +knownHostsModifiedKeyDenyMsg=To resolve this add the correct host key to your known hosts file {0} +knownHostsModifiedKeyStorePrompt=If so, also store the new key? +knownHostsModifiedKeyWarning=WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n\ +The connection might be compromised (man-in-the-middle attack).\n\ +It is also possible that the {0} key of the host has just been changed.\n\ +The expected {1} key for host ''{2}'' has the fingerprints:\n\ +{3}\n\ +{4}\n\ +The {0} key actually received has the fingerprints:\n\ +{5}\n\ +{6} +knownHostsRevokedKeyMsg=Host ''{0}'' sent a key that is marked as revoked in the known hosts file {1}. +knownHostsUnknownKeyMsg=The authenticity of host ''{0}'' cannot be established. +knownHostsUnknownKeyPrompt=Accept and store this key, and continue connecting? +knownHostsUnknownKeyType=Cannot read server key from known hosts file {0}; line {1} +knownHostsUserAskCreationMsg=File {0} does not exist. +knownHostsUserAskCreationPrompt=Create file {0} ? +passwordPrompt=Password +proxyCannotAuthenticate=Cannot authenticate to proxy {0} +proxyHttpFailure=HTTP Proxy connection to {0} failed with code {1}: {2} +proxyHttpInvalidUserName=HTTP proxy connection {0} with invalid user name; must not contain colons: {1} +proxyHttpUnexpectedReply=Unexpected HTTP proxy response from {0}: {1} +proxyHttpUnspecifiedFailureReason=unspecified reason +proxyPasswordPrompt=Proxy password +proxySocksAuthenticationFailed=Authentication to SOCKS5 proxy {0} failed +proxySocksFailureForbidden=SOCKS5 proxy {0}: connection to {1} not allowed by ruleset +proxySocksFailureGeneral=SOCKS5 proxy {0}: general failure +proxySocksFailureHostUnreachable=SOCKS5 proxy {0}: host unreachable {1} +proxySocksFailureNetworkUnreachable=SOCKS5 proxy {0}: network unreachable {1} +proxySocksFailureRefused=SOCKS5 proxy {0}: connection refused {1} +proxySocksFailureTTL=TTL expired in SOCKS5 proxy connection {0} +proxySocksFailureUnspecified=Unspecified failure in SOCKS5 proxy connection {0} +proxySocksFailureUnsupportedAddress=SOCKS5 proxy {0} does not support address type +proxySocksFailureUnsupportedCommand=SOCKS5 proxy {0} does not support CONNECT command +proxySocksGssApiFailure=Cannot authenticate with GSS-API to SOCKS5 proxy {0} +proxySocksGssApiMessageTooShort=SOCKS5 proxy {0} sent too short message +proxySocksGssApiUnknownMessage=SOCKS5 proxy {0} sent unexpected GSS-API message type, expected 1, got {1} +proxySocksGssApiVersionMismatch=SOCKS5 proxy {0} sent wrong GSS-API version number, expected 1, got {1} +proxySocksNoRemoteHostName=Could not send remote address {0} +proxySocksPasswordTooLong=Password for proxy {0} must be at most 255 bytes long, is {1} bytes +proxySocksUnexpectedMessage=Unexpected message received from SOCKS5 proxy {0}; client state {1}: {2} +proxySocksUnexpectedVersion=Expected SOCKS version 5, got {0} +proxySocksUsernameTooLong=User name for proxy {0} must be at most 255 bytes long, is {1} bytes: {2} +sessionCloseFailed=Closing the session failed +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 +unknownProxyProtocol=Ignoring unknown proxy protocol {0}
\ No newline at end of file diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java new file mode 100644 index 0000000000..ad2ff5256c --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/CachingKeyPairProvider.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.nio.file.Path; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; + +import org.eclipse.jgit.transport.sshd.KeyCache; + +/** + * A {@link EncryptedFileKeyPairProvider} that uses an external + * {@link KeyCache}. + */ +public class CachingKeyPairProvider extends EncryptedFileKeyPairProvider { + + private final KeyCache cache; + + /** + * Creates a new {@link CachingKeyPairProvider} using the given + * {@link KeyCache}. If the cache is {@code null}, this is a simple + * {@link EncryptedFileKeyPairProvider}. + * + * @param paths + * to load keys from + * @param cache + * to use, may be {@code null} if no external caching is desired + */ + public CachingKeyPairProvider(List<Path> paths, KeyCache cache) { + super(paths); + this.cache = cache; + } + + @Override + protected Iterable<KeyPair> loadKeys(Collection<? extends Path> resources) { + if (resources.isEmpty()) { + return Collections.emptyList(); + } + return () -> new CancellingKeyPairIterator(resources); + } + + @Override + protected KeyPair doLoadKey(Path resource) + throws IOException, GeneralSecurityException { + // By calling doLoadKey(String, Path, FilePasswordProvider) instead of + // super.doLoadKey(Path) we can bypass the key caching in + // AbstractResourceKeyPairProvider, over which we have no real control. + String resourceId = resource.toString(); + if (cache == null) { + return doLoadKey(resourceId, resource, getPasswordFinder()); + } + Throwable t[] = { null }; + KeyPair key = cache.get(resource, p -> { + try { + return doLoadKey(resourceId, p, getPasswordFinder()); + } catch (IOException | GeneralSecurityException e) { + t[0] = e; + return null; + } + }); + if (t[0] != null) { + if (t[0] instanceof CancellationException) { + throw (CancellationException) t[0]; + } + throw new IOException( + format(SshdText.get().keyLoadFailed, resource), t[0]); + } + return key; + } + + private class CancellingKeyPairIterator implements Iterator<KeyPair> { + + private final Iterator<Path> paths; + + private KeyPair nextItem; + + private boolean nextSet; + + public CancellingKeyPairIterator(Collection<? extends Path> resources) { + List<Path> copy = new ArrayList<>(resources.size()); + copy.addAll(resources); + paths = copy.iterator(); + } + + @Override + public boolean hasNext() { + if (nextSet) { + return nextItem != null; + } + nextSet = true; + while (nextItem == null && paths.hasNext()) { + try { + nextItem = doLoadKey(paths.next()); + } catch (CancellationException cancelled) { + throw cancelled; + } catch (Exception other) { + log.warn(other.toString()); + } + } + return nextItem != null; + } + + @Override + public KeyPair next() { + if (!nextSet && !hasNext()) { + throw new NoSuchElementException(); + } + KeyPair result = nextItem; + nextItem = null; + nextSet = false; + if (result == null) { + throw new NoSuchElementException(); + } + return result; + } + + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java new file mode 100644 index 0000000000..ff81989991 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/EncryptedFileKeyPairProvider.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import javax.security.auth.DestroyFailedException; + +import org.apache.sshd.common.config.keys.FilePasswordProvider; +import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser; +import org.apache.sshd.common.keyprovider.FileKeyPairProvider; +import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.eclipse.jgit.internal.transport.sshd.RepeatingFilePasswordProvider.ResourceDecodeResult; + +/** + * A {@link FileKeyPairProvider} that asks repeatedly for a passphrase for an + * encrypted private key if the {@link FilePasswordProvider} is a + * {@link RepeatingFilePasswordProvider}. + */ +public class EncryptedFileKeyPairProvider extends FileKeyPairProvider { + + // TODO: remove this class once we're based on sshd > 2.1.0. See upstream + // issue SSHD-850 https://issues.apache.org/jira/browse/SSHD-850 and commit + // https://github.com/apache/mina-sshd/commit/f19bd2e34 + + /** + * Creates a new {@link EncryptedFileKeyPairProvider} for the given + * {@link Path}s. + * + * @param paths + * to read keys from + */ + public EncryptedFileKeyPairProvider(List<Path> paths) { + super(paths); + } + + @Override + protected KeyPair doLoadKey(String resourceKey, InputStream inputStream, + FilePasswordProvider provider) + throws IOException, GeneralSecurityException { + if (!(provider instanceof RepeatingFilePasswordProvider)) { + return super.doLoadKey(resourceKey, inputStream, provider); + } + KeyPairResourceParser parser = SecurityUtils.getKeyPairResourceParser(); + if (parser == null) { + // This is an internal configuration error, thus no translation. + throw new NoSuchProviderException( + "No registered key-pair resource parser"); //$NON-NLS-1$ + } + RepeatingFilePasswordProvider realProvider = (RepeatingFilePasswordProvider) provider; + // Read the stream now so that we can process the content several + // times. + List<String> lines = IoUtils.readAllLines(inputStream); + Collection<KeyPair> ids = null; + while (ids == null) { + try { + ids = parser.loadKeyPairs(resourceKey, realProvider, lines); + realProvider.handleDecodeAttemptResult(resourceKey, "", null); //$NON-NLS-1$ + // No exception; success. Exit the loop even if ids is still + // null! + break; + } catch (IOException | GeneralSecurityException + | RuntimeException e) { + ResourceDecodeResult loadResult = realProvider + .handleDecodeAttemptResult(resourceKey, "", e); //$NON-NLS-1$ + if (loadResult == null + || loadResult == ResourceDecodeResult.TERMINATE) { + throw e; + } else if (loadResult == ResourceDecodeResult.RETRY) { + continue; + } + // IGNORE doesn't make any sense here, but OK, let's ignore it. + // ids == null, so we'll throw an exception below. + break; + } + } + if (ids == null) { + // The javadoc on loadKeyPairs says it might return null if no + // key pair found. Bad API. + throw new InvalidKeyException( + format(SshdText.get().identityFileNoKey, resourceKey)); + } + Iterator<KeyPair> keys = ids.iterator(); + if (!keys.hasNext()) { + throw new InvalidKeyException(format( + SshdText.get().identityFileUnsupportedFormat, resourceKey)); + } + KeyPair result = keys.next(); + if (keys.hasNext()) { + log.warn(format(SshdText.get().identityFileMultipleKeys, + resourceKey)); + keys.forEachRemaining(k -> { + PrivateKey pk = k.getPrivate(); + if (pk != null) { + try { + pk.destroy(); + } catch (DestroyFailedException e) { + // Ignore + } + } + }); + } + return result; + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiMechanisms.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiMechanisms.java new file mode 100644 index 0000000000..cf68eac5a7 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiMechanisms.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.jgit.annotations.NonNull; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; + +/** + * Global repository of GSS-API mechanisms that we can use. + */ +public class GssApiMechanisms { + + private GssApiMechanisms() { + // No instantiation + } + + /** Prefix to use with {@link GSSName#NT_HOSTBASED_SERVICE}. */ + public static final String GSSAPI_HOST_PREFIX = "host@"; //$NON-NLS-1$ + + /** The {@link Oid} of Kerberos 5. */ + public static final Oid KERBEROS_5 = createOid("1.2.840.113554.1.2.2"); //$NON-NLS-1$ + + /** SGNEGO is not to be used with ssh. */ + public static final Oid SPNEGO = createOid("1.3.6.1.5.5.2"); //$NON-NLS-1$ + + /** Protects {@link #supportedMechanisms}. */ + private static final Object LOCK = new Object(); + + /** + * The {@link AtomicBoolean} is set to {@code true} when the mechanism could + * be initialized successfully at least once. + */ + private static Map<Oid, Boolean> supportedMechanisms; + + /** + * Retrieves an immutable collection of the supported mechanisms. + * + * @return the supported mechanisms + */ + @NonNull + public static Collection<Oid> getSupportedMechanisms() { + synchronized (LOCK) { + if (supportedMechanisms == null) { + GSSManager manager = GSSManager.getInstance(); + Oid[] mechs = manager.getMechs(); + Map<Oid, Boolean> mechanisms = new LinkedHashMap<>(); + if (mechs != null) { + for (Oid oid : mechs) { + mechanisms.put(oid, Boolean.FALSE); + } + } + supportedMechanisms = mechanisms; + } + return Collections.unmodifiableSet(supportedMechanisms.keySet()); + } + } + + /** + * Report that this mechanism was used successfully. + * + * @param mechanism + * that worked + */ + public static void worked(@NonNull Oid mechanism) { + synchronized (LOCK) { + supportedMechanisms.put(mechanism, Boolean.TRUE); + } + } + + /** + * Mark the mechanisms as failed. + * + * @param mechanism + * to mark + */ + public static void failed(@NonNull Oid mechanism) { + synchronized (LOCK) { + Boolean worked = supportedMechanisms.get(mechanism); + if (worked != null && !worked.booleanValue()) { + // If it never worked, remove it + supportedMechanisms.remove(mechanism); + } + } + } + + /** + * Resolves an {@link InetSocketAddress}. + * + * @param remote + * to resolve + * @return the resolved {@link InetAddress}, or {@code null} if unresolved. + */ + public static InetAddress resolve(@NonNull InetSocketAddress remote) { + InetAddress address = remote.getAddress(); + if (address == null) { + try { + address = InetAddress.getByName(remote.getHostString()); + } catch (UnknownHostException e) { + return null; + } + } + return address; + } + + /** + * Determines a canonical host name for use use with GSS-API. + * + * @param remote + * to get the host name from + * @return the canonical host name, if it can be determined, otherwise the + * {@link InetSocketAddress#getHostString() unprocessed host name}. + */ + @NonNull + public static String getCanonicalName(@NonNull InetSocketAddress remote) { + InetAddress address = resolve(remote); + if (address == null) { + return remote.getHostString(); + } + return address.getCanonicalHostName(); + } + + /** + * Creates a {@link GSSContext} for the given mechanism to authenticate with + * the host given by {@code fqdn}. + * + * @param mechanism + * {@link Oid} of the mechanism to use + * @param fqdn + * fully qualified domain name of the host to authenticate with + * @return the context, if the mechanism is available and the context could + * be created, or {@code null} otherwise + */ + public static GSSContext createContext(@NonNull Oid mechanism, + @NonNull String fqdn) { + GSSContext context = null; + try { + GSSManager manager = GSSManager.getInstance(); + context = manager.createContext( + manager.createName( + GssApiMechanisms.GSSAPI_HOST_PREFIX + fqdn, + GSSName.NT_HOSTBASED_SERVICE), + mechanism, null, GSSContext.DEFAULT_LIFETIME); + } catch (GSSException e) { + closeContextSilently(context); + failed(mechanism); + return null; + } + worked(mechanism); + return context; + } + + /** + * Closes (disposes of) a {@link GSSContext} ignoring any + * {@link GSSException}s. + * + * @param context + * to dispose + */ + public static void closeContextSilently(GSSContext context) { + if (context != null) { + try { + context.dispose(); + } catch (GSSException e) { + // Ignore + } + } + } + + private static Oid createOid(String rep) { + try { + return new Oid(rep); + } catch (GSSException e) { + // Does not occur + return null; + } + } + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthFactory.java new file mode 100644 index 0000000000..ba5630516f --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthFactory.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import org.apache.sshd.client.auth.AbstractUserAuthFactory; +import org.apache.sshd.client.auth.UserAuth; + +/** + * Factory to create {@link GssApiWithMicAuthentication} handlers. + */ +public class GssApiWithMicAuthFactory extends AbstractUserAuthFactory { + + /** The authentication identifier for GSSApi-with-MIC. */ + public static final String NAME = "gssapi-with-mic"; //$NON-NLS-1$ + + /** The singleton {@link GssApiWithMicAuthFactory}. */ + public static final GssApiWithMicAuthFactory INSTANCE = new GssApiWithMicAuthFactory(); + + private GssApiWithMicAuthFactory() { + super(NAME); + } + + @Override + public UserAuth create() { + return new GssApiWithMicAuthentication(); + } + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java new file mode 100644 index 0000000000..aef263d7f4 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/GssApiWithMicAuthentication.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.Iterator; + +import org.apache.sshd.client.auth.AbstractUserAuth; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.SshConstants; +import org.apache.sshd.common.util.buffer.Buffer; +import org.apache.sshd.common.util.buffer.ByteArrayBuffer; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.MessageProp; +import org.ietf.jgss.Oid; + +/** + * GSSAPI-with-MIC authentication handler (Kerberos 5). + * + * @see <a href="https://tools.ietf.org/html/rfc4462">RFC 4462</a> + */ +public class GssApiWithMicAuthentication extends AbstractUserAuth { + + /** Synonym used in RFC 4462. */ + private static final byte SSH_MSG_USERAUTH_GSSAPI_RESPONSE = SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST; + + /** Synonym used in RFC 4462. */ + private static final byte SSH_MSG_USERAUTH_GSSAPI_TOKEN = SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE; + + private enum ProtocolState { + STARTED, TOKENS, MIC_SENT, FAILED + } + + private Collection<Oid> mechanisms; + + private Iterator<Oid> nextMechanism; + + private Oid currentMechanism; + + private ProtocolState state; + + private GSSContext context; + + /** Creates a new {@link GssApiWithMicAuthentication}. */ + public GssApiWithMicAuthentication() { + super(GssApiWithMicAuthFactory.NAME); + } + + @Override + protected boolean sendAuthDataRequest(ClientSession session, String service) + throws Exception { + if (mechanisms == null) { + mechanisms = GssApiMechanisms.getSupportedMechanisms(); + nextMechanism = mechanisms.iterator(); + } + if (context != null) { + close(false); + } + if (!nextMechanism.hasNext()) { + return false; + } + state = ProtocolState.STARTED; + currentMechanism = nextMechanism.next(); + // RFC 4462 states that SPNEGO must not be used with ssh + while (GssApiMechanisms.SPNEGO.equals(currentMechanism)) { + if (!nextMechanism.hasNext()) { + return false; + } + currentMechanism = nextMechanism.next(); + } + try { + String hostName = getHostName(session); + context = GssApiMechanisms.createContext(currentMechanism, + hostName); + context.requestMutualAuth(true); + context.requestConf(true); + context.requestInteg(true); + context.requestCredDeleg(true); + context.requestAnonymity(false); + } catch (GSSException | NullPointerException e) { + close(true); + if (log.isDebugEnabled()) { + log.debug(format(SshdText.get().gssapiInitFailure, + currentMechanism.toString())); + } + currentMechanism = null; + state = ProtocolState.FAILED; + return false; + } + Buffer buffer = session + .createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST); + buffer.putString(session.getUsername()); + buffer.putString(service); + buffer.putString(getName()); + buffer.putInt(1); + buffer.putBytes(currentMechanism.getDER()); + session.writePacket(buffer); + return true; + } + + @Override + protected boolean processAuthDataRequest(ClientSession session, + String service, Buffer in) throws Exception { + // SSH_MSG_USERAUTH_FAILURE and SSH_MSG_USERAUTH_SUCCESS, as well as + // SSH_MSG_USERAUTH_BANNER are handled by the framework. + int command = in.getUByte(); + if (context == null) { + return false; + } + try { + switch (command) { + case SSH_MSG_USERAUTH_GSSAPI_RESPONSE: { + if (state != ProtocolState.STARTED) { + return unexpectedMessage(command); + } + // Initial reply from the server with the mechanism to use. + Oid mechanism = new Oid(in.getBytes()); + if (!currentMechanism.equals(mechanism)) { + return false; + } + replyToken(session, service, new byte[0]); + return true; + } + case SSH_MSG_USERAUTH_GSSAPI_TOKEN: { + if (context.isEstablished() || state != ProtocolState.TOKENS) { + return unexpectedMessage(command); + } + // Server sent us a token + replyToken(session, service, in.getBytes()); + return true; + } + default: + return unexpectedMessage(command); + } + } catch (GSSException e) { + log.warn(format(SshdText.get().gssapiFailure, + currentMechanism.toString()), e); + state = ProtocolState.FAILED; + return false; + } + } + + @Override + public void destroy() { + try { + close(false); + } finally { + super.destroy(); + } + } + + private void close(boolean silent) { + try { + if (context != null) { + context.dispose(); + context = null; + } + } catch (GSSException e) { + if (!silent) { + log.warn(SshdText.get().gssapiFailure, e); + } + } + } + + private void sendToken(ClientSession session, byte[] receivedToken) + throws IOException, GSSException { + state = ProtocolState.TOKENS; + byte[] token = context.initSecContext(receivedToken, 0, + receivedToken.length); + if (token != null) { + Buffer buffer = session.createBuffer(SSH_MSG_USERAUTH_GSSAPI_TOKEN); + buffer.putBytes(token); + session.writePacket(buffer); + } + } + + private void sendMic(ClientSession session, String service) + throws IOException, GSSException { + state = ProtocolState.MIC_SENT; + // Produce MIC + Buffer micBuffer = new ByteArrayBuffer(); + micBuffer.putBytes(session.getSessionId()); + micBuffer.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST); + micBuffer.putString(session.getUsername()); + micBuffer.putString(service); + micBuffer.putString(getName()); + byte[] micBytes = micBuffer.getCompactData(); + byte[] mic = context.getMIC(micBytes, 0, micBytes.length, + new MessageProp(0, true)); + Buffer buffer = session + .createBuffer(SshConstants.SSH_MSG_USERAUTH_GSSAPI_MIC); + buffer.putBytes(mic); + session.writePacket(buffer); + } + + private void replyToken(ClientSession session, String service, byte[] bytes) + throws IOException, GSSException { + sendToken(session, bytes); + if (context.isEstablished()) { + sendMic(session, service); + } + } + + private String getHostName(ClientSession session) { + SocketAddress remote = session.getConnectAddress(); + if (remote instanceof InetSocketAddress) { + InetAddress address = GssApiMechanisms + .resolve((InetSocketAddress) remote); + if (address != null) { + return address.getCanonicalHostName(); + } + } + if (session instanceof JGitClientSession) { + String hostName = ((JGitClientSession) session).getHostConfigEntry() + .getHostName(); + try { + hostName = InetAddress.getByName(hostName) + .getCanonicalHostName(); + } catch (UnknownHostException e) { + // Ignore here; try with the non-canonical name + } + return hostName; + } + throw new IllegalStateException( + "Wrong session class :" + session.getClass().getName()); //$NON-NLS-1$ + } + + private boolean unexpectedMessage(int command) { + log.warn(format(SshdText.get().gssapiUnexpectedMessage, getName(), + Integer.toString(command))); + return false; + } + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java new file mode 100644 index 0000000000..dcf330a2a4 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitClientSession.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.net.SocketAddress; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.sshd.client.ClientFactoryManager; +import org.apache.sshd.client.config.hosts.HostConfigEntry; +import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair; +import org.apache.sshd.client.keyverifier.ServerKeyVerifier; +import org.apache.sshd.client.session.ClientSessionImpl; +import org.apache.sshd.common.FactoryManager; +import org.apache.sshd.common.SshException; +import org.apache.sshd.common.config.keys.KeyUtils; +import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.common.io.IoWriteFuture; +import org.apache.sshd.common.util.Readable; +import org.eclipse.jgit.errors.InvalidPatternException; +import org.eclipse.jgit.fnmatch.FileNameMatcher; +import org.eclipse.jgit.internal.transport.sshd.proxy.StatefulProxyConnector; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.SshConstants; + +/** + * A {@link org.apache.sshd.client.session.ClientSession ClientSession} that can + * be associated with the {@link HostConfigEntry} the session was created for. + * The {@link JGitSshClient} creates such sessions and sets this association. + * <p> + * Also provides for associating a JGit {@link CredentialsProvider} with a + * session. + * </p> + */ +public class JGitClientSession extends ClientSessionImpl { + + private HostConfigEntry hostConfig; + + private CredentialsProvider credentialsProvider; + + private StatefulProxyConnector proxyHandler; + + /** + * @param manager + * @param session + * @throws Exception + */ + public JGitClientSession(ClientFactoryManager manager, IoSession session) + throws Exception { + super(manager, session); + } + + /** + * Retrieves the {@link HostConfigEntry} this session was created for. + * + * @return the {@link HostConfigEntry}, or {@code null} if none set + */ + public HostConfigEntry getHostConfigEntry() { + return hostConfig; + } + + /** + * Sets the {@link HostConfigEntry} this session was created for. + * + * @param hostConfig + * the {@link HostConfigEntry} + */ + public void setHostConfigEntry(HostConfigEntry hostConfig) { + this.hostConfig = hostConfig; + } + + /** + * Sets the {@link CredentialsProvider} for this session. + * + * @param provider + * to set + */ + public void setCredentialsProvider(CredentialsProvider provider) { + credentialsProvider = provider; + } + + /** + * Retrieves the {@link CredentialsProvider} set for this session. + * + * @return the provider, or {@code null}Â if none is set. + */ + public CredentialsProvider getCredentialsProvider() { + return credentialsProvider; + } + + /** + * Sets a {@link StatefulProxyConnector} to handle proxy connection + * protocols. + * + * @param handler + * to set + */ + public void setProxyHandler(StatefulProxyConnector handler) { + proxyHandler = handler; + } + + @Override + protected IoWriteFuture sendIdentification(String ident) + throws IOException { + StatefulProxyConnector proxy = proxyHandler; + if (proxy != null) { + try { + // We must not block here; the framework starts reading messages + // from the peer only once the initial sendKexInit() following + // this call to sendIdentification() has returned! + proxy.runWhenDone(() -> { + JGitClientSession.super.sendIdentification(ident); + return null; + }); + // Called only from the ClientSessionImpl constructor, where the + // return value is ignored. + return null; + } catch (IOException e) { + throw e; + } catch (Exception other) { + throw new IOException(other.getLocalizedMessage(), other); + } + } else { + return super.sendIdentification(ident); + } + } + + @Override + protected byte[] sendKexInit() throws IOException { + StatefulProxyConnector proxy = proxyHandler; + if (proxy != null) { + try { + // We must not block here; the framework starts reading messages + // from the peer only once the initial sendKexInit() has + // returned! + proxy.runWhenDone(() -> { + JGitClientSession.super.sendKexInit(); + return null; + }); + // This is called only from the ClientSessionImpl + // constructor, where the return value is ignored. + return null; + } catch (IOException e) { + throw e; + } catch (Exception other) { + throw new IOException(other.getLocalizedMessage(), other); + } + } else { + return super.sendKexInit(); + } + } + + /** + * {@inheritDoc} + * + * As long as we're still setting up the proxy connection, diverts messages + * to the {@link StatefulProxyConnector}. + */ + @Override + public void messageReceived(Readable buffer) throws Exception { + StatefulProxyConnector proxy = proxyHandler; + if (proxy != null) { + proxy.messageReceived(getIoSession(), buffer); + } else { + super.messageReceived(buffer); + } + } + + @Override + protected void checkKeys() throws SshException { + ServerKeyVerifier serverKeyVerifier = getServerKeyVerifier(); + // The super implementation always uses + // getIoSession().getRemoteAddress(). In case of a proxy connection, + // that would be the address of the proxy! + SocketAddress remoteAddress = getConnectAddress(); + PublicKey serverKey = getKex().getServerKey(); + if (!serverKeyVerifier.verifyServerKey(this, remoteAddress, + serverKey)) { + throw new SshException( + org.apache.sshd.common.SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, + SshdText.get().kexServerKeyInvalid); + } + } + + @Override + protected String resolveAvailableSignaturesProposal( + FactoryManager manager) { + Set<String> defaultSignatures = new LinkedHashSet<>(); + defaultSignatures.addAll(getSignatureFactoriesNames()); + HostConfigEntry config = resolveAttribute( + JGitSshClient.HOST_CONFIG_ENTRY); + String hostKeyAlgorithms = config + .getProperty(SshConstants.HOST_KEY_ALGORITHMS); + if (hostKeyAlgorithms != null && !hostKeyAlgorithms.isEmpty()) { + char first = hostKeyAlgorithms.charAt(0); + if (first == '+') { + // Additions make not much sense -- it's either in + // defaultSignatures already, or we have no implementation for + // it. No point in proposing it. + return String.join(",", defaultSignatures); //$NON-NLS-1$ + } else if (first == '-') { + // This takes wildcard patterns! + removeFromList(defaultSignatures, + SshConstants.HOST_KEY_ALGORITHMS, + hostKeyAlgorithms.substring(1)); + if (defaultSignatures.isEmpty()) { + // Too bad: user config error. Warn here, and then fail + // later. + log.warn(format( + SshdText.get().configNoRemainingHostKeyAlgorithms, + hostKeyAlgorithms)); + } + return String.join(",", defaultSignatures); //$NON-NLS-1$ + } else { + // Default is overridden -- only accept the ones for which we do + // have an implementation. + List<String> newNames = filteredList(defaultSignatures, + hostKeyAlgorithms); + if (newNames.isEmpty()) { + log.warn(format( + SshdText.get().configNoKnownHostKeyAlgorithms, + hostKeyAlgorithms)); + // Use the default instead. + } else { + return String.join(",", newNames); //$NON-NLS-1$ + } + } + } + // No HostKeyAlgorithms; using default -- change order to put existing + // keys first. + ServerKeyVerifier verifier = getServerKeyVerifier(); + if (verifier instanceof ServerKeyLookup) { + SocketAddress remoteAddress = resolvePeerAddress( + resolveAttribute(JGitSshClient.ORIGINAL_REMOTE_ADDRESS)); + List<HostEntryPair> allKnownKeys = ((ServerKeyLookup) verifier) + .lookup(this, remoteAddress); + Set<String> reordered = new LinkedHashSet<>(); + for (HostEntryPair h : allKnownKeys) { + PublicKey key = h.getServerKey(); + if (key != null) { + String keyType = KeyUtils.getKeyType(key); + if (keyType != null) { + reordered.add(keyType); + } + } + } + reordered.addAll(defaultSignatures); + return String.join(",", reordered); //$NON-NLS-1$ + } + return String.join(",", defaultSignatures); //$NON-NLS-1$ + } + + private void removeFromList(Set<String> current, String key, + String patterns) { + for (String toRemove : patterns.split("\\s*,\\s*")) { //$NON-NLS-1$ + if (toRemove.indexOf('*') < 0 && toRemove.indexOf('?') < 0) { + current.remove(toRemove); + continue; + } + try { + FileNameMatcher matcher = new FileNameMatcher(toRemove, null); + for (Iterator<String> i = current.iterator(); i.hasNext();) { + matcher.reset(); + matcher.append(i.next()); + if (matcher.isMatch()) { + i.remove(); + } + } + } catch (InvalidPatternException e) { + log.warn(format(SshdText.get().configInvalidPattern, key, + toRemove)); + } + } + } + + private List<String> filteredList(Set<String> known, String values) { + List<String> newNames = new ArrayList<>(); + for (String newValue : values.split("\\s*,\\s*")) { //$NON-NLS-1$ + if (known.contains(newValue)) { + newNames.add(newValue); + } + } + return newNames; + } + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java new file mode 100644 index 0000000000..a0705f25f5 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitHostConfigEntry.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.sshd.client.config.hosts.HostConfigEntry; +import org.eclipse.jgit.annotations.NonNull; + +/** + * A {@link HostConfigEntry} that provides access to the multi-valued keys as + * lists of strings. The super class treats them as single strings containing + * comma-separated lists. + * + * @since 5.2 + */ +public class JGitHostConfigEntry extends HostConfigEntry { + + private Map<String, List<String>> multiValuedOptions; + + @Override + public String getProperty(String name, String defaultValue) { + // Upstream bug fix (SSHD-867): if there are _no_ properties at all, the + // super implementation returns always null even if a default value is + // given. + // + // See https://issues.apache.org/jira/projects/SSHD/issues/SSHD-867 + // + // TODO: remove this override once we're based on sshd > 2.1.0 + Map<String, String> properties = getProperties(); + if (properties == null || properties.isEmpty()) { + return defaultValue; + } + return super.getProperty(name, defaultValue); + } + + /** + * Sets the multi-valued options. + * + * @param options + * to set, may be {@code null} to set an empty map + */ + public void setMultiValuedOptions(Map<String, List<String>> options) { + multiValuedOptions = options; + } + + /** + * Retrieves all multi-valued options. + * + * @return an unmodifiable map + */ + @NonNull + public Map<String, List<String>> getMultiValuedOptions() { + Map<String, List<String>> options = multiValuedOptions; + if (options == null) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(options); + } + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthFactory.java new file mode 100644 index 0000000000..315d0258d6 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import org.apache.sshd.client.auth.AbstractUserAuthFactory; +import org.apache.sshd.client.auth.UserAuth; +import org.apache.sshd.client.auth.password.UserAuthPasswordFactory; + +/** + * A customized {@link UserAuthPasswordFactory} that creates instance of + * {@link JGitPasswordAuthentication}. + */ +public class JGitPasswordAuthFactory extends AbstractUserAuthFactory { + + /** The singleton {@link JGitPasswordAuthFactory}. */ + public static final JGitPasswordAuthFactory INSTANCE = new JGitPasswordAuthFactory(); + + private JGitPasswordAuthFactory() { + super(UserAuthPasswordFactory.NAME); + } + + @Override + public UserAuth create() { + return new JGitPasswordAuthentication(); + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java new file mode 100644 index 0000000000..9a6ed39c68 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPasswordAuthentication.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.util.concurrent.CancellationException; + +import org.apache.sshd.client.ClientAuthenticationManager; +import org.apache.sshd.client.auth.keyboard.UserInteraction; +import org.apache.sshd.client.auth.password.UserAuthPassword; +import org.apache.sshd.client.session.ClientSession; + +/** + * A password authentication handler that uses the {@link JGitUserInteraction} + * to ask the user for the password. It also respects the + * {@code NumberOfPasswordPrompts} ssh config. + */ +public class JGitPasswordAuthentication extends UserAuthPassword { + + private int maxAttempts; + + private int attempts; + + @Override + public void init(ClientSession session, String service) throws Exception { + super.init(session, service); + maxAttempts = Math.max(1, + session.getIntProperty( + ClientAuthenticationManager.PASSWORD_PROMPTS, + ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS)); + attempts = 0; + } + + @Override + protected boolean sendAuthDataRequest(ClientSession session, String service) + throws Exception { + if (++attempts > maxAttempts) { + return false; + } + UserInteraction interaction = session.getUserInteraction(); + if (!interaction.isInteractionAllowed(session)) { + return false; + } + String password = getPassword(session, interaction); + if (password == null) { + throw new CancellationException(); + } + // sendPassword takes a buffer as first argument, but actually doesn't + // use it and creates its own buffer... + sendPassword(null, session, password, password); + return true; + } + + private String getPassword(ClientSession session, + UserInteraction interaction) { + String[] results = interaction.interactive(session, null, null, "", //$NON-NLS-1$ + new String[] { SshdText.get().passwordPrompt }, + new boolean[] { false }); + return (results == null || results.length == 0) ? null : results[0]; + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java new file mode 100644 index 0000000000..0b3de4ace3 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthFactory.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.util.List; + +import org.apache.sshd.client.auth.AbstractUserAuthFactory; +import org.apache.sshd.client.auth.UserAuth; +import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.signature.Signature; +import org.apache.sshd.common.signature.SignatureFactoriesManager; + +/** + * A customized authentication factory for public key user authentication. The + * default implementation {@link UserAuthPublicKeyFactory} ends up doing some + * crazy stream "magic" that loads too many keys too early. + */ +public class JGitPublicKeyAuthFactory extends AbstractUserAuthFactory + implements SignatureFactoriesManager { + + /** The singleton {@link JGitPublicKeyAuthFactory}. */ + public static final JGitPublicKeyAuthFactory INSTANCE = new JGitPublicKeyAuthFactory(); + + private JGitPublicKeyAuthFactory() { + super(UserAuthPublicKeyFactory.NAME); + } + + @Override + public UserAuth create() { + return new JGitPublicKeyAuthentication(getSignatureFactories()); + } + + @Override + public List<NamedFactory<Signature>> getSignatureFactories() { + return null; + } + + @Override + public void setSignatureFactories(List<NamedFactory<Signature>> factories) { + throw new UnsupportedOperationException(); + } +} 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 new file mode 100644 index 0000000000..63b3990b13 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.util.List; + +import org.apache.sshd.client.auth.pubkey.UserAuthPublicKey; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.signature.Signature; + +/** + * A specialized public key authentication handler that uses our own + * {@link JGitPublicKeyIterator}. The super class creates in + * {@link #init(ClientSession, String)} a + * {@link org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator}, which + * in its constructor does some strange {@link java.util.stream.Stream} "magic" + * that ends up loading keys prematurely. + */ +public class JGitPublicKeyAuthentication extends UserAuthPublicKey { + + private ClientSession clientSession; + + private String serviceName; + + /** + * Creates a new {@link JGitPublicKeyAuthentication}. + * + * @param factories + * signature factories to use + */ + public JGitPublicKeyAuthentication( + List<NamedFactory<Signature>> factories) { + super(factories); + } + + @Override + public void init(ClientSession session, String service) throws Exception { + // Do *not* call super.init(); it'll create a UserAuthPublicKeyIterator + // and that's where things then go wrong. Instead, do the whole + // initialization directly here. + clientSession = session; + serviceName = service; + releaseKeys(); + // Use our own iterator! + keys = new JGitPublicKeyIterator(session, this); + } + + @Override + public ClientSession getClientSession() { + return clientSession; + } + + @Override + public ClientSession getSession() { + return clientSession; + } + + @Override + public String getService() { + return serviceName; + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java new file mode 100644 index 0000000000..cda12623d8 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyIterator.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.io.IOException; +import java.nio.channels.Channel; +import java.security.KeyPair; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.sshd.agent.SshAgent; +import org.apache.sshd.agent.SshAgentFactory; +import org.apache.sshd.client.auth.pubkey.AbstractKeyPairIterator; +import org.apache.sshd.client.auth.pubkey.KeyAgentIdentity; +import org.apache.sshd.client.auth.pubkey.KeyPairIdentity; +import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity; +import org.apache.sshd.client.config.hosts.HostConfigEntry; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.FactoryManager; +import org.apache.sshd.common.keyprovider.KeyIdentityProvider; +import org.apache.sshd.common.signature.SignatureFactoriesManager; + +/** + * A new iterator over key pairs that we use instead of the default + * {@link org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyIterator}, which + * in its constructor does some strange {@link java.util.stream.Stream} "magic" + * that ends up loading keys prematurely. This class uses plain + * {@link Iterator}s instead to avoid that problem. Used in + * {@link JGitPublicKeyAuthentication}. + * + * @see <a href= + * "https://issues.apache.org/jira/projects/SSHD/issues/SSHD-860">Upstream + * issue SSHD-860</a> + */ +public class JGitPublicKeyIterator + extends AbstractKeyPairIterator<PublicKeyIdentity> implements Channel { + + // Re: the cause for the problem mentioned above has not been determined. + // It looks as if either the Apache code inadvertently calls + // GenericUtils.isEmpty() on all streams (which would load the first key + // of each stream), or the Java stream implementation does some prefetching. + // It's not entirely clear. Using Iterators we have more control over + // what happens when. + + private final AtomicBoolean open = new AtomicBoolean(true); + + private SshAgent agent; + + private final List<Iterator<PublicKeyIdentity>> keys = new ArrayList<>(3); + + private final Iterator<Iterator<PublicKeyIdentity>> keyIter; + + private Iterator<PublicKeyIdentity> current; + + private Boolean hasElement; + + /** + * Creates a new {@link JGitPublicKeyIterator}. + * + * @param session + * we're trying to authenticate + * @param signatureFactories + * to use + * @throws Exception + * if an {@link SshAgentFactory} is configured and getting + * identities from the agent fails + */ + public JGitPublicKeyIterator(ClientSession session, + SignatureFactoriesManager signatureFactories) throws Exception { + super(session); + boolean useAgent = true; + if (session instanceof JGitClientSession) { + HostConfigEntry config = ((JGitClientSession) session) + .getHostConfigEntry(); + useAgent = !config.isIdentitiesOnly(); + } + if (useAgent) { + FactoryManager manager = session.getFactoryManager(); + SshAgentFactory factory = manager == null ? null + : manager.getAgentFactory(); + if (factory != null) { + try { + agent = factory.createClient(manager); + keys.add(new AgentIdentityIterator(agent)); + } catch (IOException e) { + try { + closeAgent(); + } catch (IOException err) { + e.addSuppressed(err); + } + throw e; + } + } + } + keys.add( + new KeyPairIdentityIterator(session.getRegisteredIdentities(), + session, signatureFactories)); + keys.add(new KeyPairIdentityIterator(session.getKeyPairProvider(), + session, signatureFactories)); + keyIter = keys.iterator(); + } + + @Override + public boolean isOpen() { + return open.get(); + } + + @Override + public void close() throws IOException { + if (open.getAndSet(false)) { + closeAgent(); + } + } + + @Override + public boolean hasNext() { + if (!isOpen()) { + return false; + } + if (hasElement != null) { + return hasElement.booleanValue(); + } + while (current == null || !current.hasNext()) { + if (keyIter.hasNext()) { + current = keyIter.next(); + } else { + current = null; + hasElement = Boolean.FALSE; + return false; + } + } + hasElement = Boolean.TRUE; + return true; + } + + @Override + public PublicKeyIdentity next() { + if (!isOpen() || hasElement == null && !hasNext() + || !hasElement.booleanValue()) { + throw new NoSuchElementException(); + } + hasElement = null; + PublicKeyIdentity result; + try { + result = current.next(); + } catch (NoSuchElementException e) { + result = null; + } + return result; + } + + private void closeAgent() throws IOException { + if (agent == null) { + return; + } + try { + agent.close(); + } finally { + agent = null; + } + } + + /** + * An {@link Iterator} that maps the data obtained from an agent to + * {@link PublicKeyIdentity}. + */ + private static class AgentIdentityIterator + implements Iterator<PublicKeyIdentity> { + + private final SshAgent agent; + + private final Iterator<? extends Map.Entry<PublicKey, String>> iter; + + public AgentIdentityIterator(SshAgent agent) throws IOException { + this.agent = agent; + iter = agent == null ? null : agent.getIdentities().iterator(); + } + + @Override + public boolean hasNext() { + return iter != null && iter.hasNext(); + } + + @Override + public PublicKeyIdentity next() { + if (iter == null) { + throw new NoSuchElementException(); + } + Map.Entry<PublicKey, String> entry = iter.next(); + return new KeyAgentIdentity(agent, entry.getKey(), + entry.getValue()); + } + } + + /** + * An {@link Iterator} that maps {@link KeyPair} to + * {@link PublicKeyIdentity}. + */ + private static class KeyPairIdentityIterator + implements Iterator<PublicKeyIdentity> { + + private final Iterator<KeyPair> keyPairs; + + private final ClientSession session; + + private final SignatureFactoriesManager signatureFactories; + + public KeyPairIdentityIterator(KeyIdentityProvider provider, + ClientSession session, + SignatureFactoriesManager signatureFactories) { + this.session = session; + this.signatureFactories = signatureFactories; + keyPairs = provider == null ? null : provider.loadKeys().iterator(); + } + + @Override + public boolean hasNext() { + return keyPairs != null && keyPairs.hasNext(); + } + + @Override + public PublicKeyIdentity next() { + if (keyPairs == null) { + throw new NoSuchElementException(); + } + KeyPair key = keyPairs.next(); + return new KeyPairIdentity(signatureFactories, session, key); + } + } +} 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 new file mode 100644 index 0000000000..212b67fe33 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshClient.java @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static java.text.MessageFormat.format; +import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.positive; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyPair; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.config.hosts.HostConfigEntry; +import org.apache.sshd.client.future.ConnectFuture; +import org.apache.sshd.client.future.DefaultConnectFuture; +import org.apache.sshd.client.session.ClientSessionImpl; +import org.apache.sshd.client.session.SessionFactory; +import org.apache.sshd.common.config.keys.FilePasswordProvider; +import org.apache.sshd.common.future.SshFutureListener; +import org.apache.sshd.common.io.IoConnectFuture; +import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.common.keyprovider.FileKeyPairProvider; +import org.apache.sshd.common.keyprovider.KeyPairProvider; +import org.apache.sshd.common.session.helpers.AbstractSession; +import org.apache.sshd.common.util.ValidateUtils; +import org.eclipse.jgit.internal.transport.sshd.proxy.HttpClientConnector; +import org.eclipse.jgit.internal.transport.sshd.proxy.Socks5ClientConnector; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.SshConstants; +import org.eclipse.jgit.transport.sshd.KeyCache; +import org.eclipse.jgit.transport.sshd.ProxyData; +import org.eclipse.jgit.transport.sshd.ProxyDataFactory; + +/** + * Customized {@link SshClient} for JGit. It creates specialized + * {@link JGitClientSession}s that know about the {@link HostConfigEntry} they + * were created for, and it loads all KeyPair identities lazily. + */ +public class JGitSshClient extends SshClient { + + /** + * We need access to this during the constructor of the ClientSession, + * before setConnectAddress() can have been called. So we have to remember + * it in an attribute on the SshClient, from where we can then retrieve it. + */ + static final AttributeKey<HostConfigEntry> HOST_CONFIG_ENTRY = new AttributeKey<>(); + + static final AttributeKey<InetSocketAddress> ORIGINAL_REMOTE_ADDRESS = new AttributeKey<>(); + + /** + * An attribute key for the comma-separated list of default preferred + * authentication mechanisms. + */ + public static final AttributeKey<String> PREFERRED_AUTHENTICATIONS = new AttributeKey<>(); + + private KeyCache keyCache; + + private CredentialsProvider credentialsProvider; + + private ProxyDataFactory proxyDatabase; + + @Override + protected SessionFactory createSessionFactory() { + // Override the parent's default + return new JGitSessionFactory(this); + } + + @Override + public ConnectFuture connect(HostConfigEntry hostConfig) + throws IOException { + if (connector == null) { + throw new IllegalStateException("SshClient not started."); //$NON-NLS-1$ + } + Objects.requireNonNull(hostConfig, "No host configuration"); //$NON-NLS-1$ + String host = ValidateUtils.checkNotNullAndNotEmpty( + hostConfig.getHostName(), "No target host"); //$NON-NLS-1$ + int port = hostConfig.getPort(); + ValidateUtils.checkTrue(port > 0, "Invalid port: %d", port); //$NON-NLS-1$ + String userName = hostConfig.getUsername(); + InetSocketAddress address = new InetSocketAddress(host, port); + ConnectFuture connectFuture = new DefaultConnectFuture( + userName + '@' + address, null); + SshFutureListener<IoConnectFuture> listener = createConnectCompletionListener( + connectFuture, userName, address, hostConfig); + // sshd needs some entries from the host config already in the + // constructor of the session. Put those as properties on this client, + // where it will find them. We can set the host config only once the + // session object has been created. + copyProperty( + hostConfig.getProperty(SshConstants.PREFERRED_AUTHENTICATIONS, + getAttribute(PREFERRED_AUTHENTICATIONS)), + PREFERRED_AUTHS); + setAttribute(HOST_CONFIG_ENTRY, hostConfig); + setAttribute(ORIGINAL_REMOTE_ADDRESS, address); + // Proxy support + ProxyData proxy = getProxyData(address); + if (proxy != null) { + address = configureProxy(proxy, address); + proxy.clearPassword(); + } + connector.connect(address).addListener(listener); + return connectFuture; + } + + private void copyProperty(String value, String key) { + if (value != null && !value.isEmpty()) { + getProperties().put(key, value); + } + } + + private ProxyData getProxyData(InetSocketAddress remoteAddress) { + ProxyDataFactory factory = getProxyDatabase(); + return factory == null ? null : factory.get(remoteAddress); + } + + private InetSocketAddress configureProxy(ProxyData proxyData, + InetSocketAddress remoteAddress) { + Proxy proxy = proxyData.getProxy(); + if (proxy.type() == Proxy.Type.DIRECT + || !(proxy.address() instanceof InetSocketAddress)) { + return remoteAddress; + } + InetSocketAddress address = (InetSocketAddress) proxy.address(); + switch (proxy.type()) { + case HTTP: + setClientProxyConnector( + new HttpClientConnector(address, remoteAddress, + proxyData.getUser(), proxyData.getPassword())); + return address; + case SOCKS: + setClientProxyConnector( + new Socks5ClientConnector(address, remoteAddress, + proxyData.getUser(), proxyData.getPassword())); + return address; + default: + log.warn(format(SshdText.get().unknownProxyProtocol, + proxy.type().name())); + return remoteAddress; + } + } + + private SshFutureListener<IoConnectFuture> createConnectCompletionListener( + ConnectFuture connectFuture, String username, + InetSocketAddress address, HostConfigEntry hostConfig) { + return new SshFutureListener<IoConnectFuture>() { + + @Override + public void operationComplete(IoConnectFuture future) { + if (future.isCanceled()) { + connectFuture.cancel(); + return; + } + Throwable t = future.getException(); + if (t != null) { + connectFuture.setException(t); + return; + } + IoSession ioSession = future.getSession(); + try { + JGitClientSession session = createSession(ioSession, + username, address, hostConfig); + connectFuture.setSession(session); + } catch (RuntimeException e) { + connectFuture.setException(e); + ioSession.close(true); + } + } + + @Override + public String toString() { + return "JGitSshClient$ConnectCompletionListener[" + username //$NON-NLS-1$ + + '@' + address + ']'; + } + }; + } + + private JGitClientSession createSession(IoSession ioSession, + String username, InetSocketAddress address, + HostConfigEntry hostConfig) { + AbstractSession rawSession = AbstractSession.getSession(ioSession); + if (!(rawSession instanceof JGitClientSession)) { + throw new IllegalStateException("Wrong session type: " //$NON-NLS-1$ + + rawSession.getClass().getCanonicalName()); + } + JGitClientSession session = (JGitClientSession) rawSession; + session.setUsername(username); + session.setConnectAddress(address); + session.setHostConfigEntry(hostConfig); + if (session.getCredentialsProvider() == null) { + session.setCredentialsProvider(getCredentialsProvider()); + } + int numberOfPasswordPrompts = getNumberOfPasswordPrompts(hostConfig); + session.getProperties().put(PASSWORD_PROMPTS, + Integer.valueOf(numberOfPasswordPrompts)); + FilePasswordProvider provider = getFilePasswordProvider(); + if (provider instanceof RepeatingFilePasswordProvider) { + ((RepeatingFilePasswordProvider) provider) + .setAttempts(numberOfPasswordPrompts); + } + FileKeyPairProvider ourConfiguredKeysProvider = null; + List<Path> identities = hostConfig.getIdentities().stream() + .map(s -> { + try { + return Paths.get(s); + } catch (InvalidPathException e) { + log.warn(format(SshdText.get().configInvalidPath, + SshConstants.IDENTITY_FILE, s), e); + return null; + } + }).filter(p -> p != null && Files.exists(p)) + .collect(Collectors.toList()); + ourConfiguredKeysProvider = new CachingKeyPairProvider(identities, + keyCache); + ourConfiguredKeysProvider.setPasswordFinder(getFilePasswordProvider()); + if (hostConfig.isIdentitiesOnly()) { + session.setKeyPairProvider(ourConfiguredKeysProvider); + } else { + KeyPairProvider defaultKeysProvider = getKeyPairProvider(); + if (defaultKeysProvider instanceof FileKeyPairProvider) { + ((FileKeyPairProvider) defaultKeysProvider) + .setPasswordFinder(getFilePasswordProvider()); + } + KeyPairProvider combinedProvider = new CombinedKeyPairProvider( + ourConfiguredKeysProvider, defaultKeysProvider); + session.setKeyPairProvider(combinedProvider); + } + return session; + } + + private int getNumberOfPasswordPrompts(HostConfigEntry hostConfig) { + String prompts = hostConfig + .getProperty(SshConstants.NUMBER_OF_PASSWORD_PROMPTS); + if (prompts != null) { + prompts = prompts.trim(); + int value = positive(prompts); + if (value > 0) { + return value; + } + log.warn(format(SshdText.get().configInvalidPositive, + SshConstants.NUMBER_OF_PASSWORD_PROMPTS, prompts)); + } + // Default for NumberOfPasswordPrompts according to + // https://man.openbsd.org/ssh_config + return 3; + } + + /** + * Set a cache for loaded keys. Newly discovered keys will be added when + * IdentityFile host entries from the ssh config file are used during + * session authentication. + * + * @param cache + * to use + */ + public void setKeyCache(KeyCache cache) { + keyCache = cache; + } + + /** + * Sets a {@link ProxyDataFactory} for connecting through proxies. + * + * @param factory + * to use, or {@code null} if proxying is not desired or + * supported + */ + public void setProxyDatabase(ProxyDataFactory factory) { + proxyDatabase = factory; + } + + /** + * Retrieves the {@link ProxyDataFactory}. + * + * @return the factory, or {@code null} if none is set + */ + protected ProxyDataFactory getProxyDatabase() { + return proxyDatabase; + } + + /** + * Sets the {@link CredentialsProvider} for this client. + * + * @param provider + * to set + */ + public void setCredentialsProvider(CredentialsProvider provider) { + credentialsProvider = provider; + } + + /** + * Retrieves the {@link CredentialsProvider} set for this client. + * + * @return the provider, or {@code null}Â if none is set. + */ + public CredentialsProvider getCredentialsProvider() { + return credentialsProvider; + } + + /** + * A {@link SessionFactory} to create our own specialized + * {@link JGitClientSession}s. + */ + private static class JGitSessionFactory extends SessionFactory { + + public JGitSessionFactory(JGitSshClient client) { + super(client); + } + + @Override + protected ClientSessionImpl doCreateSession(IoSession ioSession) + throws Exception { + return new JGitClientSession(getClient(), ioSession); + } + } + + /** + * A {@link KeyPairProvider} that iterates over the {@link Iterable}s + * returned by other {@link KeyPairProvider}s. + */ + private static class CombinedKeyPairProvider implements KeyPairProvider { + + private final List<KeyPairProvider> providers; + + public CombinedKeyPairProvider(KeyPairProvider... providers) { + this(Arrays.stream(providers).filter(Objects::nonNull) + .collect(Collectors.toList())); + } + + public CombinedKeyPairProvider(List<KeyPairProvider> providers) { + this.providers = providers; + } + + @Override + public Iterable<String> getKeyTypes() { + throw new UnsupportedOperationException( + "Should not have been called in a ssh client"); //$NON-NLS-1$ + } + + @Override + public KeyPair loadKey(String type) { + throw new UnsupportedOperationException( + "Should not have been called in a ssh client"); //$NON-NLS-1$ + } + + @Override + public Iterable<KeyPair> loadKeys() { + return () -> new Iterator<KeyPair>() { + + private Iterator<KeyPairProvider> factories = providers.iterator(); + private Iterator<KeyPair> current; + + private Boolean hasElement; + + @Override + public boolean hasNext() { + if (hasElement != null) { + return hasElement.booleanValue(); + } + while (current == null || !current.hasNext()) { + if (factories.hasNext()) { + current = factories.next().loadKeys().iterator(); + } else { + current = null; + hasElement = Boolean.FALSE; + return false; + } + } + hasElement = Boolean.TRUE; + return true; + } + + @Override + public KeyPair next() { + if (hasElement == null && !hasNext() + || !hasElement.booleanValue()) { + throw new NoSuchElementException(); + } + hasElement = null; + KeyPair result; + try { + result = current.next(); + } catch (NoSuchElementException e) { + result = null; + } + return result; + } + + }; + } + + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java new file mode 100644 index 0000000000..9eced0fa7f --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitSshConfig.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.flag; +import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.positive; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.sshd.client.config.hosts.HostConfigEntry; +import org.apache.sshd.client.config.hosts.HostConfigEntryResolver; +import org.apache.sshd.common.util.net.SshdSocketAddress; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile; +import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.HostEntry; +import org.eclipse.jgit.transport.SshConstants; + +/** + * A {@link HostConfigEntryResolver} adapted specifically for JGit. + * <p> + * We use our own config file parser and entry resolution since the default + * {@link org.apache.sshd.client.config.hosts.ConfigFileHostEntryResolver + * ConfigFileHostEntryResolver} has a number of problems: + * </p> + * <ul> + * <li>It does case-insensitive pattern matching. Matching in OpenSsh is + * case-sensitive! Compare also bug 531118.</li> + * <li>It only merges values from the global items (before the first "Host" + * line) into the host entries. Otherwise it selects the most specific match. + * OpenSsh processes <em>all</em> entries in the order they appear in the file + * and whenever one matches, it updates values as appropriate.</li> + * <li>We have to ensure that ~ replacement uses the same HOME directory as + * JGit. Compare bug bug 526175.</li> + * </ul> + * Therefore, this re-uses the parsing and caching from + * {@link OpenSshConfigFile}. + * + * @since 5.2 + */ +public class JGitSshConfig implements HostConfigEntryResolver { + + private OpenSshConfigFile configFile; + + /** + * Creates a new {@link OpenSshConfigFile} that will read the config from + * file {@code config} use the given file {@code home} as "home" directory. + * + * @param home + * user's home directory for the purpose of ~ replacement + * @param config + * file to load. + * @param localUserName + * user name of the current user on the local host OS + */ + public JGitSshConfig(@NonNull File home, @NonNull File config, + @NonNull String localUserName) { + configFile = new OpenSshConfigFile(home, config, localUserName); + } + + @Override + public HostConfigEntry resolveEffectiveHost(String host, int port, + String username) throws IOException { + HostEntry entry = configFile.lookup(host, port, username); + JGitHostConfigEntry config = new JGitHostConfigEntry(); + // Apache MINA conflates all keys, even multi-valued ones, in one map + // and puts multiple values separated by commas in one string. See + // the javadoc on HostConfigEntry. + Map<String, String> allOptions = new TreeMap<>( + String.CASE_INSENSITIVE_ORDER); + allOptions.putAll(entry.getOptions()); + // And what if a value contains a comma?? + entry.getMultiValuedOptions().entrySet().stream() + .forEach(e -> allOptions.put(e.getKey(), + String.join(",", e.getValue()))); //$NON-NLS-1$ + config.setProperties(allOptions); + // The following is an extension from JGitHostConfigEntry + config.setMultiValuedOptions(entry.getMultiValuedOptions()); + // Also make sure the underlying properties are set + String hostName = entry.getValue(SshConstants.HOST_NAME); + if (hostName == null || hostName.isEmpty()) { + hostName = host; + } + config.setHostName(hostName); + config.setProperty(SshConstants.HOST_NAME, hostName); + config.setHost(SshdSocketAddress.isIPv6Address(hostName) ? "" : hostName); //$NON-NLS-1$ + String user = username != null && !username.isEmpty() ? username + : entry.getValue(SshConstants.USER); + if (user == null || user.isEmpty()) { + user = configFile.getLocalUserName(); + } + config.setUsername(user); + config.setProperty(SshConstants.USER, user); + int p = port >= 0 ? port : positive(entry.getValue(SshConstants.PORT)); + config.setPort(p >= 0 ? p : SshConstants.SSH_DEFAULT_PORT); + config.setProperty(SshConstants.PORT, + Integer.toString(config.getPort())); + config.setIdentities(entry.getValues(SshConstants.IDENTITY_FILE)); + config.setIdentitiesOnly( + flag(entry.getValue(SshConstants.IDENTITIES_ONLY))); + return config; + } + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java new file mode 100644 index 0000000000..99288b7af1 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitUserInteraction.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.sshd.client.auth.keyboard.UserInteraction; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.session.SessionListener; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.SshConstants; +import org.eclipse.jgit.transport.URIish; + +/** + * A {@link UserInteraction} callback implementation based on a + * {@link CredentialsProvider}. + */ +public class JGitUserInteraction implements UserInteraction { + + private final CredentialsProvider provider; + + /** + * We need to reset the JGit credentials provider if we have repeated + * attempts. + */ + private final Map<Session, SessionListener> ongoing = new ConcurrentHashMap<>(); + + /** + * Creates a new {@link JGitUserInteraction} for interactive password input + * based on the given {@link CredentialsProvider}. + * + * @param provider + * to use + */ + public JGitUserInteraction(CredentialsProvider provider) { + this.provider = provider; + } + + @Override + public boolean isInteractionAllowed(ClientSession session) { + return provider != null && provider.isInteractive(); + } + + @Override + public String[] interactive(ClientSession session, String name, + String instruction, String lang, String[] prompt, boolean[] echo) { + // This is keyboard-interactive or password authentication + List<CredentialItem> items = new ArrayList<>(); + int numberOfHiddenInputs = 0; + for (int i = 0; i < prompt.length; i++) { + boolean hidden = i < echo.length && !echo[i]; + if (hidden) { + numberOfHiddenInputs++; + } + } + // RFC 4256 (SSH_MSG_USERAUTH_INFO_REQUEST) says: "The language tag is + // deprecated and SHOULD be the empty string." and "[If there are no + // prompts] the client SHOULD still display the name and instruction + // fields" and "[The] client SHOULD print the name and instruction (if + // non-empty)" + if (name != null && !name.isEmpty()) { + items.add(new CredentialItem.InformationalMessage(name)); + } + if (instruction != null && !instruction.isEmpty()) { + items.add(new CredentialItem.InformationalMessage(instruction)); + } + for (int i = 0; i < prompt.length; i++) { + boolean hidden = i < echo.length && !echo[i]; + if (hidden && numberOfHiddenInputs == 1) { + // We need to somehow trigger storing the password in the + // Eclipse secure storage in EGit. Currently, this is done only + // for password fields. + items.add(new CredentialItem.Password()); + // TODO Possibly change EGit to store all hidden strings + // (keyed by the URI and the prompt?) so that we don't have to + // use this kludge here. + } else { + items.add(new CredentialItem.StringType(prompt[i], hidden)); + } + } + if (items.isEmpty()) { + // Huh? No info, no prompts? + return prompt; // Is known to have length zero here + } + URIish uri = toURI(session.getUsername(), + (InetSocketAddress) session.getConnectAddress()); + // Reset the provider for this URI if it's not the first attempt and we + // have hidden inputs. Otherwise add a session listener that will remove + // itself once authenticated. + if (numberOfHiddenInputs > 0) { + SessionListener listener = ongoing.get(session); + if (listener != null) { + provider.reset(uri); + } else { + listener = new SessionAuthMarker(ongoing); + ongoing.put(session, listener); + session.addSessionListener(listener); + } + } + if (provider.get(uri, items)) { + return items.stream().map(i -> { + if (i instanceof CredentialItem.Password) { + return new String(((CredentialItem.Password) i).getValue()); + } else if (i instanceof CredentialItem.StringType) { + return ((CredentialItem.StringType) i).getValue(); + } + return null; + }).filter(s -> s != null).toArray(String[]::new); + } + // TODO What to throw to abort the connection/authentication process? + // In UserAuthKeyboardInteractive.getUserResponses() it's clear that + // returning null is valid and signifies "an error"; we'll try the + // next authentication method. But if the user explicitly canceled, + // then we don't want to try the next methods... + // + // Probably not a serious issue with the typical order of public-key, + // keyboard-interactive, password. + return null; + } + + @Override + public String getUpdatedPassword(ClientSession session, String prompt, + String lang) { + // TODO Implement password update in password authentication? + return null; + } + + /** + * Creates a {@link URIish} from the given remote address and user name. + * + * @param userName + * for the uri + * @param remote + * address of the remote host + * @return the uri, with {@link SshConstants#SSH_SCHEME} as scheme + */ + public static URIish toURI(String userName, InetSocketAddress remote) { + String host = remote.getHostString(); + int port = remote.getPort(); + return new URIish() // + .setScheme(SshConstants.SSH_SCHEME) // + .setHost(host) // + .setPort(port) // + .setUser(userName); + } + + /** + * A {@link SessionListener} that removes itself from the session when + * authentication is done or the session is closed. + */ + private static class SessionAuthMarker implements SessionListener { + + private final Map<Session, SessionListener> registered; + + public SessionAuthMarker(Map<Session, SessionListener> registered) { + this.registered = registered; + } + + @Override + public void sessionEvent(Session session, SessionListener.Event event) { + if (event == SessionListener.Event.Authenticated) { + session.removeSessionListener(this); + registered.remove(session, this); + } + } + + @Override + public void sessionClosed(Session session) { + session.removeSessionListener(this); + registered.remove(session, this); + } + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java new file mode 100644 index 0000000000..4db24a16b7 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static java.text.MessageFormat.format; +import static org.apache.sshd.client.config.hosts.HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM; +import static org.apache.sshd.client.config.hosts.HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.sshd.client.config.hosts.HostPatternValue; +import org.apache.sshd.client.config.hosts.HostPatternsHolder; +import org.apache.sshd.client.config.hosts.KnownHostEntry; +import org.apache.sshd.client.config.hosts.KnownHostHashValue; +import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Apache MINA sshd 2.0.0 KnownHostEntry cannot read a host entry line like + * "host:port ssh-rsa <key>"; it complains about an illegal character in the + * host name (correct would be "[host]:port"). The default known_hosts reader + * also aborts reading on the first error. + * <p> + * This reader is a bit more robust and tries to handle this case if there is + * only one colon (otherwise it might be an IPv6 address (without port)), and it + * skips and logs invalid entries, but still returns all other valid entries + * from the file. + * </p> + */ +public class KnownHostEntryReader { + + private static final Logger LOG = LoggerFactory + .getLogger(KnownHostEntryReader.class); + + private KnownHostEntryReader() { + // No instantiation + } + + /** + * Reads a known_hosts file and returns all valid entries. Invalid entries + * are skipped (and a message is logged). + * + * @param path + * of the file to read + * @return a {@link List} of all valid entries read from the file + * @throws IOException + * if the file cannot be read. + */ + public static List<KnownHostEntry> readFromFile(Path path) + throws IOException { + List<KnownHostEntry> result = new LinkedList<>(); + try (BufferedReader r = Files.newBufferedReader(path, + StandardCharsets.UTF_8)) { + r.lines().forEachOrdered(l -> { + if (l == null) { + return; + } + String line = clean(l); + if (line.isEmpty()) { + return; + } + try { + KnownHostEntry entry = parseHostEntry(line); + if (entry != null) { + result.add(entry); + } else { + LOG.warn(format(SshdText.get().knownHostsInvalidLine, + path, line)); + } + } catch (RuntimeException e) { + LOG.warn(format(SshdText.get().knownHostsInvalidLine, path, + line), e); + } + }); + } + return result; + } + + private static String clean(String line) { + int i = line.indexOf('#'); + return i < 0 ? line.trim() : line.substring(0, i).trim(); + } + + private static KnownHostEntry parseHostEntry(String line) { + KnownHostEntry entry = new KnownHostEntry(); + entry.setConfigLine(line); + String tmp = line; + int i = 0; + if (tmp.charAt(0) == KnownHostEntry.MARKER_INDICATOR) { + // A marker + i = tmp.indexOf(' ', 1); + if (i < 0) { + return null; + } + entry.setMarker(tmp.substring(1, i)); + tmp = tmp.substring(i + 1).trim(); + } + i = tmp.indexOf(' '); + if (i < 0) { + return null; + } + // Hash, or host patterns + if (tmp.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) { + // Hashed host entry + KnownHostHashValue hash = KnownHostHashValue + .parse(tmp.substring(0, i)); + if (hash == null) { + return null; + } + entry.setHashedEntry(hash); + entry.setPatterns(null); + } else { + Collection<HostPatternValue> patterns = parsePatterns( + tmp.substring(0, i)); + if (patterns == null || patterns.isEmpty()) { + return null; + } + entry.setHashedEntry(null); + entry.setPatterns(patterns); + } + tmp = tmp.substring(i + 1).trim(); + AuthorizedKeyEntry key = AuthorizedKeyEntry + .parseAuthorizedKeyEntry(tmp); + if (key == null) { + return null; + } + entry.setKeyEntry(key); + return entry; + } + + private static Collection<HostPatternValue> parsePatterns(String text) { + if (text.isEmpty()) { + return null; + } + List<String> items = Arrays.stream(text.split(",")) //$NON-NLS-1$ + .filter(item -> item != null && !item.isEmpty()).map(item -> { + if (NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM == item + .charAt(0)) { + return item; + } + int firstColon = item.indexOf(':'); + if (firstColon < 0) { + return item; + } + int secondColon = item.indexOf(':', firstColon + 1); + if (secondColon > 0) { + // Assume an IPv6 address (without port). + return item; + } + // We have "host:port", should be "[host]:port" + return NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM + + item.substring(0, firstColon) + + NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM + + item.substring(firstColon); + }).collect(Collectors.toList()); + return items.isEmpty() ? null : HostPatternsHolder.parsePatterns(items); + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java new file mode 100644 index 0000000000..540b586dda --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyVerifier.java @@ -0,0 +1,745 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import static java.text.MessageFormat.format; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +import org.apache.sshd.client.config.hosts.HostConfigEntry; +import org.apache.sshd.client.config.hosts.KnownHostEntry; +import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier; +import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair; +import org.apache.sshd.client.keyverifier.ServerKeyVerifier; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; +import org.apache.sshd.common.config.keys.KeyUtils; +import org.apache.sshd.common.config.keys.PublicKeyEntryResolver; +import org.apache.sshd.common.digest.BuiltinDigests; +import org.apache.sshd.common.util.io.ModifiableFileWatcher; +import org.apache.sshd.common.util.net.SshdSocketAddress; +import org.eclipse.jgit.internal.storage.file.LockFile; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.SshConstants; +import org.eclipse.jgit.transport.URIish; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A sever host key verifier that honors the {@code StrictHostKeyChecking} and + * {@code UserKnownHostsFile} values from the ssh configuration. + * <p> + * The verifier can be given default known_hosts files in the constructor, which + * will be used if the ssh config does not specify a {@code UserKnownHostsFile}. + * If the ssh config <em>does</em> set {@code UserKnownHostsFile}, the verifier + * uses the given files in the order given. Non-existing or unreadable files are + * ignored. + * <p> + * {@code StrictHostKeyChecking} accepts the following values: + * </p> + * <dl> + * <dt>ask</dt> + * <dd>Ask the user whether new or changed keys shall be accepted and be added + * to the known_hosts file.</dd> + * <dt>yes/true</dt> + * <dd>Accept only keys listed in the known_hosts file.</dd> + * <dt>no/false</dt> + * <dd>Silently accept all new or changed keys, add new keys to the known_hosts + * file.</dd> + * <dt>accept-new</dt> + * <dd>Silently accept keys for new hosts and add them to the known_hosts + * file.</dd> + * </dl> + * <p> + * If {@code StrictHostKeyChecking} is not set, or set to any other value, the + * default value <b>ask</b> is active. + * </p> + * <p> + * This implementation relies on the {@link ClientSession} being a + * {@link JGitClientSession}. By default Apache MINA sshd does not forward the + * config file host entry to the session, so it would be unknown here which + * entry it was and what setting of {@code StrictHostKeyChecking} should be + * used. If used with some other session type, the implementation assumes + * "<b>ask</b>". + * <p> + * <p> + * Asking the user is done via a {@link CredentialsProvider} obtained from the + * session. If none is set, the implementation falls back to strict host key + * checking ("<b>yes</b>"). + * </p> + * <p> + * Note that adding a key to the known hosts file may create the file. You can + * specify in the constructor whether the user shall be asked about that, too. + * If the the user declines updating the file, but the key was otherwise + * accepted (user confirmed for "<b>ask</b>", or "no" or "accept-new" are + * active), the key is accepted for this session only. + * </p> + * <p> + * If several known hosts files are specified, a new key is always added to the + * first file (even if it doesn't exist yet; see the note about file creation + * above). + * </p> + * + * @see <a href="http://man.openbsd.org/OpenBSD-current/man5/ssh_config.5">man + * ssh-config</a> + */ +public class OpenSshServerKeyVerifier + implements ServerKeyVerifier, ServerKeyLookup { + + // TODO: GlobalKnownHostsFile? May need some kind of LRU caching; these + // files may be large! + + private static final Logger LOG = LoggerFactory + .getLogger(OpenSshServerKeyVerifier.class); + + /** Can be used to mark revoked known host lines. */ + private static final String MARKER_REVOKED = "revoked"; //$NON-NLS-1$ + + private final boolean askAboutNewFile; + + private final Map<Path, HostKeyFile> knownHostsFiles = new ConcurrentHashMap<>(); + + private final List<HostKeyFile> defaultFiles = new ArrayList<>(); + + private enum ModifiedKeyHandling { + DENY, ALLOW, ALLOW_AND_STORE + } + + /** + * Creates a new {@link OpenSshServerKeyVerifier}. + * + * @param askAboutNewFile + * whether to ask the user, if possible, about creating a new + * non-existing known_hosts file + * @param defaultFiles + * typically ~/.ssh/known_hosts and ~/.ssh/known_hosts2. May be + * empty or {@code null}, in which case no default files are + * installed. The files need not exist. + */ + public OpenSshServerKeyVerifier(boolean askAboutNewFile, + List<Path> defaultFiles) { + if (defaultFiles != null) { + for (Path file : defaultFiles) { + HostKeyFile newFile = new HostKeyFile(file); + knownHostsFiles.put(file, newFile); + this.defaultFiles.add(newFile); + } + } + this.askAboutNewFile = askAboutNewFile; + } + + private List<HostKeyFile> getFilesToUse(ClientSession session) { + List<HostKeyFile> filesToUse = defaultFiles; + if (session instanceof JGitClientSession) { + HostConfigEntry entry = ((JGitClientSession) session) + .getHostConfigEntry(); + if (entry instanceof JGitHostConfigEntry) { + // Always true! + List<HostKeyFile> userFiles = addUserHostKeyFiles( + ((JGitHostConfigEntry) entry).getMultiValuedOptions() + .get(SshConstants.USER_KNOWN_HOSTS_FILE)); + if (!userFiles.isEmpty()) { + filesToUse = userFiles; + } + } + } + return filesToUse; + } + + @Override + public List<HostEntryPair> lookup(ClientSession session, + SocketAddress remote) { + List<HostKeyFile> filesToUse = getFilesToUse(session); + HostKeyHelper helper = new HostKeyHelper(); + List<HostEntryPair> result = new ArrayList<>(); + Collection<SshdSocketAddress> candidates = helper + .resolveHostNetworkIdentities(session, remote); + for (HostKeyFile file : filesToUse) { + for (HostEntryPair current : file.get()) { + KnownHostEntry entry = current.getHostEntry(); + for (SshdSocketAddress host : candidates) { + if (entry.isHostMatch(host.getHostName(), host.getPort())) { + result.add(current); + break; + } + } + } + } + return result; + } + + @Override + public boolean verifyServerKey(ClientSession clientSession, + SocketAddress remoteAddress, PublicKey serverKey) { + List<HostKeyFile> filesToUse = getFilesToUse(clientSession); + AskUser ask = new AskUser(); + HostEntryPair[] modified = { null }; + Path path = null; + HostKeyHelper helper = new HostKeyHelper(); + for (HostKeyFile file : filesToUse) { + try { + if (find(clientSession, remoteAddress, serverKey, file.get(), + modified, helper)) { + return true; + } + } catch (RevokedKeyException e) { + ask.revokedKey(clientSession, remoteAddress, serverKey, + file.getPath()); + return false; + } + if (path == null && modified[0] != null) { + // Remember the file in which we might need to update the + // entry + path = file.getPath(); + } + } + if (modified[0] != null) { + // We found an entry, but with a different key + ModifiedKeyHandling toDo = ask.acceptModifiedServerKey( + clientSession, remoteAddress, modified[0].getServerKey(), + serverKey, path); + if (toDo == ModifiedKeyHandling.ALLOW_AND_STORE) { + try { + updateModifiedServerKey(clientSession, remoteAddress, + serverKey, modified[0], path, helper); + knownHostsFiles.get(path).resetReloadAttributes(); + } catch (IOException e) { + LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate, + path)); + } + } + if (toDo == ModifiedKeyHandling.DENY) { + return false; + } + // TODO: OpenSsh disables password and keyboard-interactive + // authentication in this case. Also agent and local port forwarding + // are switched off. (Plus a few other things such as X11 forwarding + // that are of no interest to a git client.) + return true; + } else if (ask.acceptUnknownKey(clientSession, remoteAddress, + serverKey)) { + if (!filesToUse.isEmpty()) { + HostKeyFile toUpdate = filesToUse.get(0); + path = toUpdate.getPath(); + try { + updateKnownHostsFile(clientSession, remoteAddress, + serverKey, path, helper); + toUpdate.resetReloadAttributes(); + } catch (IOException e) { + LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate, + path)); + } + } + return true; + } + return false; + } + + private static class RevokedKeyException extends Exception { + private static final long serialVersionUID = 1L; + } + + private boolean find(ClientSession clientSession, + SocketAddress remoteAddress, PublicKey serverKey, + List<HostEntryPair> entries, HostEntryPair[] modified, + HostKeyHelper helper) throws RevokedKeyException { + Collection<SshdSocketAddress> candidates = helper + .resolveHostNetworkIdentities(clientSession, remoteAddress); + for (HostEntryPair current : entries) { + KnownHostEntry entry = current.getHostEntry(); + for (SshdSocketAddress host : candidates) { + if (entry.isHostMatch(host.getHostName(), host.getPort())) { + boolean isRevoked = MARKER_REVOKED + .equals(entry.getMarker()); + if (KeyUtils.compareKeys(serverKey, + current.getServerKey())) { + // Exact match + if (isRevoked) { + throw new RevokedKeyException(); + } + modified[0] = null; + return true; + } else if (!isRevoked) { + // Server sent a different key + modified[0] = current; + // Keep going -- maybe there's another entry for this + // host + } + } + } + } + return false; + } + + private List<HostKeyFile> addUserHostKeyFiles(List<String> fileNames) { + if (fileNames == null || fileNames.isEmpty()) { + return Collections.emptyList(); + } + List<HostKeyFile> userFiles = new ArrayList<>(); + for (String name : fileNames) { + try { + Path path = Paths.get(name); + HostKeyFile file = knownHostsFiles.computeIfAbsent(path, + p -> new HostKeyFile(path)); + userFiles.add(file); + } catch (InvalidPathException e) { + LOG.warn(format(SshdText.get().knownHostsInvalidPath, + name)); + } + } + return userFiles; + } + + private void updateKnownHostsFile(ClientSession clientSession, + SocketAddress remoteAddress, PublicKey serverKey, Path path, + HostKeyHelper updater) + throws IOException { + KnownHostEntry entry = updater.prepareKnownHostEntry(clientSession, + remoteAddress, serverKey); + if (entry == null) { + return; + } + if (!Files.exists(path)) { + if (askAboutNewFile) { + CredentialsProvider provider = getCredentialsProvider( + clientSession); + if (provider == null) { + // We can't ask, so don't create the file + return; + } + URIish uri = new URIish().setPath(path.toString()); + if (!askUser(provider, uri, // + format(SshdText.get().knownHostsUserAskCreationPrompt, + path), // + format(SshdText.get().knownHostsUserAskCreationMsg, + path))) { + return; + } + } + } + LockFile lock = new LockFile(path.toFile()); + if (lock.lockForAppend()) { + try { + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(lock.getOutputStream(), + StandardCharsets.UTF_8))) { + writer.newLine(); + writer.write(entry.getConfigLine()); + writer.newLine(); + } + lock.commit(); + } catch (IOException e) { + lock.unlock(); + throw e; + } + } else { + LOG.warn(format(SshdText.get().knownHostsFileLockedUpdate, + path)); + } + } + + private void updateModifiedServerKey(ClientSession clientSession, + SocketAddress remoteAddress, PublicKey serverKey, + HostEntryPair entry, Path path, HostKeyHelper helper) + throws IOException { + KnownHostEntry hostEntry = entry.getHostEntry(); + String oldLine = hostEntry.getConfigLine(); + String newLine = helper.prepareModifiedServerKeyLine(clientSession, + remoteAddress, hostEntry, oldLine, entry.getServerKey(), + serverKey); + if (newLine == null || newLine.isEmpty()) { + return; + } + if (oldLine == null || oldLine.isEmpty() || newLine.equals(oldLine)) { + // Shouldn't happen. + return; + } + LockFile lock = new LockFile(path.toFile()); + if (lock.lock()) { + try { + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(lock.getOutputStream(), + StandardCharsets.UTF_8)); + BufferedReader reader = Files.newBufferedReader(path, + StandardCharsets.UTF_8)) { + boolean done = false; + String line; + while ((line = reader.readLine()) != null) { + String toWrite = line; + if (!done) { + int pos = line.indexOf('#'); + String toTest = pos < 0 ? line + : line.substring(0, pos); + if (toTest.trim().equals(oldLine)) { + toWrite = newLine; + done = true; + } + } + writer.write(toWrite); + writer.newLine(); + } + } + lock.commit(); + } catch (IOException e) { + lock.unlock(); + throw e; + } + } else { + LOG.warn(format(SshdText.get().knownHostsFileLockedUpdate, + path)); + } + } + + private static CredentialsProvider getCredentialsProvider( + ClientSession session) { + if (session instanceof JGitClientSession) { + return ((JGitClientSession) session).getCredentialsProvider(); + } + return null; + } + + private static boolean askUser(CredentialsProvider provider, URIish uri, + String prompt, String... messages) { + List<CredentialItem> items = new ArrayList<>(messages.length + 1); + for (String message : messages) { + items.add(new CredentialItem.InformationalMessage(message)); + } + if (prompt != null) { + CredentialItem.YesNoType answer = new CredentialItem.YesNoType( + prompt); + items.add(answer); + return provider.get(uri, items) && answer.getValue(); + } else { + return provider.get(uri, items); + } + } + + private static class AskUser { + + private enum Check { + ASK, DENY, ALLOW; + } + + @SuppressWarnings("nls") + private Check checkMode(ClientSession session, + SocketAddress remoteAddress, boolean changed) { + if (!(remoteAddress instanceof InetSocketAddress)) { + return Check.DENY; + } + if (session instanceof JGitClientSession) { + HostConfigEntry entry = ((JGitClientSession) session) + .getHostConfigEntry(); + String value = entry.getProperty( + SshConstants.STRICT_HOST_KEY_CHECKING, "ask"); + switch (value.toLowerCase(Locale.ROOT)) { + case SshConstants.YES: + case SshConstants.ON: + return Check.DENY; + case SshConstants.NO: + case SshConstants.OFF: + return Check.ALLOW; + case "accept-new": + return changed ? Check.DENY : Check.ALLOW; + default: + break; + } + } + if (getCredentialsProvider(session) == null) { + // This is called only for new, unknown hosts. If we have no way + // to interact with the user, the fallback mode is to deny the + // key. + return Check.DENY; + } + return Check.ASK; + } + + public void revokedKey(ClientSession clientSession, + SocketAddress remoteAddress, PublicKey serverKey, Path path) { + CredentialsProvider provider = getCredentialsProvider( + clientSession); + if (provider == null) { + return; + } + InetSocketAddress remote = (InetSocketAddress) remoteAddress; + URIish uri = JGitUserInteraction.toURI(clientSession.getUsername(), + remote); + String sha256 = KeyUtils.getFingerPrint(BuiltinDigests.sha256, + serverKey); + String md5 = KeyUtils.getFingerPrint(BuiltinDigests.md5, serverKey); + String keyAlgorithm = serverKey.getAlgorithm(); + askUser(provider, uri, null, // + format(SshdText.get().knownHostsRevokedKeyMsg, + remote.getHostString(), path), + format(SshdText.get().knownHostsKeyFingerprints, + keyAlgorithm), + md5, sha256); + } + + public boolean acceptUnknownKey(ClientSession clientSession, + SocketAddress remoteAddress, PublicKey serverKey) { + Check check = checkMode(clientSession, remoteAddress, false); + if (check != Check.ASK) { + return check == Check.ALLOW; + } + CredentialsProvider provider = getCredentialsProvider( + clientSession); + InetSocketAddress remote = (InetSocketAddress) remoteAddress; + // Ask the user + String sha256 = KeyUtils.getFingerPrint(BuiltinDigests.sha256, + serverKey); + String md5 = KeyUtils.getFingerPrint(BuiltinDigests.md5, serverKey); + String keyAlgorithm = serverKey.getAlgorithm(); + String remoteHost = remote.getHostString(); + URIish uri = JGitUserInteraction.toURI(clientSession.getUsername(), + remote); + String prompt = SshdText.get().knownHostsUnknownKeyPrompt; + return askUser(provider, uri, prompt, // + format(SshdText.get().knownHostsUnknownKeyMsg, + remoteHost), + format(SshdText.get().knownHostsKeyFingerprints, + keyAlgorithm), + md5, sha256); + } + + public ModifiedKeyHandling acceptModifiedServerKey( + ClientSession clientSession, + SocketAddress remoteAddress, PublicKey expected, + PublicKey actual, Path path) { + Check check = checkMode(clientSession, remoteAddress, true); + if (check == Check.ALLOW) { + // Never auto-store on CHECK.ALLOW + return ModifiedKeyHandling.ALLOW; + } + InetSocketAddress remote = (InetSocketAddress) remoteAddress; + String keyAlgorithm = actual.getAlgorithm(); + String remoteHost = remote.getHostString(); + URIish uri = JGitUserInteraction.toURI(clientSession.getUsername(), + remote); + List<String> messages = new ArrayList<>(); + String warning = format( + SshdText.get().knownHostsModifiedKeyWarning, + keyAlgorithm, expected.getAlgorithm(), remoteHost, + KeyUtils.getFingerPrint(BuiltinDigests.md5, expected), + KeyUtils.getFingerPrint(BuiltinDigests.sha256, expected), + KeyUtils.getFingerPrint(BuiltinDigests.md5, actual), + KeyUtils.getFingerPrint(BuiltinDigests.sha256, actual)); + for (String line : warning.split("\n")) { //$NON-NLS-1$ + messages.add(line); + } + + CredentialsProvider provider = getCredentialsProvider( + clientSession); + if (check == Check.DENY) { + if (provider != null) { + messages.add(format( + SshdText.get().knownHostsModifiedKeyDenyMsg, path)); + askUser(provider, uri, null, + messages.toArray(new String[0])); + } + return ModifiedKeyHandling.DENY; + } + // ASK -- two questions: procceed? and store? + List<CredentialItem> items = new ArrayList<>(messages.size() + 2); + for (String message : messages) { + items.add(new CredentialItem.InformationalMessage(message)); + } + CredentialItem.YesNoType proceed = new CredentialItem.YesNoType( + SshdText.get().knownHostsModifiedKeyAcceptPrompt); + CredentialItem.YesNoType store = new CredentialItem.YesNoType( + SshdText.get().knownHostsModifiedKeyStorePrompt); + items.add(proceed); + items.add(store); + if (provider.get(uri, items) && proceed.getValue()) { + return store.getValue() ? ModifiedKeyHandling.ALLOW_AND_STORE + : ModifiedKeyHandling.ALLOW; + } + return ModifiedKeyHandling.DENY; + } + + } + + private static class HostKeyFile extends ModifiableFileWatcher + implements Supplier<List<HostEntryPair>> { + + private List<HostEntryPair> entries = Collections.emptyList(); + + public HostKeyFile(Path path) { + super(path); + } + + @Override + public List<HostEntryPair> get() { + Path path = getPath(); + try { + if (checkReloadRequired()) { + if (!Files.exists(path)) { + // Has disappeared. + resetReloadAttributes(); + return Collections.emptyList(); + } + LockFile lock = new LockFile(path.toFile()); + if (lock.lock()) { + try { + entries = reload(getPath()); + } finally { + lock.unlock(); + } + } else { + LOG.warn(format(SshdText.get().knownHostsFileLockedRead, + path)); + } + } + } catch (IOException e) { + LOG.warn(format(SshdText.get().knownHostsFileReadFailed, path)); + } + return Collections.unmodifiableList(entries); + } + + private List<HostEntryPair> reload(Path path) throws IOException { + try { + List<KnownHostEntry> rawEntries = KnownHostEntryReader + .readFromFile(path); + updateReloadAttributes(); + if (rawEntries == null || rawEntries.isEmpty()) { + return Collections.emptyList(); + } + List<HostEntryPair> newEntries = new LinkedList<>(); + for (KnownHostEntry entry : rawEntries) { + AuthorizedKeyEntry keyPart = entry.getKeyEntry(); + if (keyPart == null) { + continue; + } + try { + PublicKey serverKey = keyPart.resolvePublicKey( + PublicKeyEntryResolver.IGNORING); + if (serverKey == null) { + LOG.warn(format( + SshdText.get().knownHostsUnknownKeyType, + path, entry.getConfigLine())); + } else { + newEntries.add(new HostEntryPair(entry, serverKey)); + } + } catch (GeneralSecurityException e) { + LOG.warn(format(SshdText.get().knownHostsInvalidLine, + path, entry.getConfigLine())); + } + } + return newEntries; + } catch (FileNotFoundException e) { + resetReloadAttributes(); + return Collections.emptyList(); + } + } + } + + // The stuff below is just a hack to avoid having to copy a lot of code from + // KnownHostsServerKeyVerifier + + private static class HostKeyHelper extends KnownHostsServerKeyVerifier { + + public HostKeyHelper() { + // These two arguments will never be used in any way. + super((c, r, s) -> false, new File(".").toPath()); //$NON-NLS-1$ + } + + @Override + protected KnownHostEntry prepareKnownHostEntry( + ClientSession clientSession, SocketAddress remoteAddress, + PublicKey serverKey) throws IOException { + // Make this method accessible + try { + return super.prepareKnownHostEntry(clientSession, remoteAddress, + serverKey); + } catch (Exception e) { + throw new IOException(e.getMessage(), e); + } + } + + @Override + protected String prepareModifiedServerKeyLine( + ClientSession clientSession, SocketAddress remoteAddress, + KnownHostEntry entry, String curLine, PublicKey expected, + PublicKey actual) throws IOException { + // Make this method accessible + try { + return super.prepareModifiedServerKeyLine(clientSession, + remoteAddress, entry, curLine, expected, actual); + } catch (Exception e) { + throw new IOException(e.getMessage(), e); + } + } + + @Override + protected Collection<SshdSocketAddress> resolveHostNetworkIdentities( + ClientSession clientSession, SocketAddress remoteAddress) { + // Make this method accessible + return super.resolveHostNetworkIdentities(clientSession, + remoteAddress); + } + } + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java new file mode 100644 index 0000000000..93bd10285a --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.sshd.KeyPasswordProvider; + +/** + * A bridge from sshd's {@link RepeatingFilePasswordProvider} to our + * {@link KeyPasswordProvider} API. + */ +public class PasswordProviderWrapper implements RepeatingFilePasswordProvider { + + private final KeyPasswordProvider delegate; + + private Map<String, AtomicInteger> counts = new ConcurrentHashMap<>(); + + /** + * @param delegate + */ + public PasswordProviderWrapper(@NonNull KeyPasswordProvider delegate) { + this.delegate = delegate; + } + + @Override + public void setAttempts(int numberOfPasswordPrompts) { + delegate.setAttempts(numberOfPasswordPrompts); + } + + @Override + public int getAttempts() { + return delegate.getAttempts(); + } + + @Override + public String getPassword(String resourceKey) throws IOException { + int attempt = counts + .computeIfAbsent(resourceKey, k -> new AtomicInteger()).get(); + char[] passphrase = delegate.getPassphrase(toUri(resourceKey), attempt); + if (passphrase == null) { + return null; + } + try { + return new String(passphrase); + } finally { + Arrays.fill(passphrase, '\000'); + } + } + + @Override + public ResourceDecodeResult handleDecodeAttemptResult(String resourceKey, + String password, Exception err) + throws IOException, GeneralSecurityException { + AtomicInteger count = counts.get(resourceKey); + int numberOfAttempts = count == null ? 0 : count.incrementAndGet(); + ResourceDecodeResult result = null; + try { + if (delegate.keyLoaded(toUri(resourceKey), numberOfAttempts, err)) { + result = ResourceDecodeResult.RETRY; + } else { + result = ResourceDecodeResult.TERMINATE; + } + } finally { + if (result != ResourceDecodeResult.RETRY) { + counts.remove(resourceKey); + } + } + return result; + } + + /** + * Creates a {@link URIish} from a given string. The + * {@link CredentialsProvider} uses uris as resource identifications. + * + * @param resourceKey + * to convert + * @return the uri + */ + private URIish toUri(String resourceKey) { + try { + return new URIish(resourceKey); + } catch (URISyntaxException e) { + return new URIish().setPath(resourceKey); // Doesn't check!! + } + } + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java new file mode 100644 index 0000000000..5d58bd6d70 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/RepeatingFilePasswordProvider.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +import org.apache.sshd.common.config.keys.FilePasswordProvider; + +/** + * A {@link FilePasswordProvider} augmented to support repeatedly asking for + * passwords. + * + * @since 5.2 + */ +public interface RepeatingFilePasswordProvider extends FilePasswordProvider { + + /** + * Define the maximum number of attempts to get a password that should be + * attempted for one identity resource through this provider. + * + * @param numberOfPasswordPrompts + * number of times to ask for a password; + * {@link IllegalArgumentException} may be thrown if <= 0 + */ + void setAttempts(int numberOfPasswordPrompts); + + /** + * Gets the maximum number of attempts to get a password that should be + * attempted for one identity resource through this provider. + * + * @return the maximum number of attempts to try, always >= 1. + */ + default int getAttempts() { + return 1; + } + + // The following part of this interface is from the upstream resolution of + // SSHD-850. See https://github.com/apache/mina-sshd/commit/f19bd2e34 . + // TODO: remove this once we move to sshd > 2.1.0 + + /** + * Result value of + * {@link RepeatingFilePasswordProvider#handleDecodeAttemptResult(String, String, Exception)}. + */ + public enum ResourceDecodeResult { + /** Re-throw the decoding exception. */ + TERMINATE, + /** Retry the decoding process - including password prompt. */ + RETRY, + /** Skip attempt and see if we can proceed without the key. */ + IGNORE; + } + + /** + * Invoked to inform the password provider about the decoding result. + * <b>Note:</b> any exception thrown from this method (including if called + * to inform about success) will be propagated instead of the original (if + * any was reported) + * + * @param resourceKey + * The resource key representing the <U>private</U> file + * @param password + * The password that was attempted + * @param err + * The attempt result - {@code null} for success + * @return How to proceed in case of error - <u>ignored</u> if invoked in + * order to report success. <b>Note:</b> {@code null} is same as + * {@link ResourceDecodeResult#TERMINATE}. + * @throws IOException + * @throws GeneralSecurityException + */ + ResourceDecodeResult handleDecodeAttemptResult(String resourceKey, + String password, Exception err) + throws IOException, GeneralSecurityException; +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/ServerKeyLookup.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/ServerKeyLookup.java new file mode 100644 index 0000000000..4f5f497f7f --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/ServerKeyLookup.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd; + +import java.net.SocketAddress; +import java.util.List; + +import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair; +import org.apache.sshd.client.session.ClientSession; +import org.eclipse.jgit.annotations.NonNull; + +/** + * Offers operations to retrieve server keys from known_hosts files. + */ +public interface ServerKeyLookup { + + /** + * Retrieves all entries for a given remote address. + * + * @param session + * needed to determine the config files if specified in the ssh + * config + * @param remote + * to find entries for + * @return a possibly empty list of entries found, including revoked ones + */ + @NonNull + List<HostEntryPair> lookup(ClientSession session, SocketAddress remote); +} 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 new file mode 100644 index 0000000000..5c79f2d40e --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java @@ -0,0 +1,91 @@ +package org.eclipse.jgit.internal.transport.sshd; + +import org.eclipse.jgit.nls.NLS; +import org.eclipse.jgit.nls.TranslationBundle; + +/** + * Externalized text messages for localization. + */ +public final class SshdText extends TranslationBundle { + + /** + * Get an instance of this translation bundle. + * + * @return an instance of this translation bundle + */ + public static SshdText get() { + return NLS.getBundleFor(SshdText.class); + } + + // @formatter:off + /***/ public String authenticationCanceled; + /***/ public String closeListenerFailed; + /***/ public String configInvalidPath; + /***/ public String configInvalidPattern; + /***/ public String configInvalidPositive; + /***/ public String configNoKnownHostKeyAlgorithms; + /***/ public String configNoRemainingHostKeyAlgorithms; + /***/ public String ftpCloseFailed; + /***/ public String gssapiFailure; + /***/ public String gssapiInitFailure; + /***/ public String gssapiUnexpectedMechanism; + /***/ public String gssapiUnexpectedMessage; + /***/ public String identityFileCannotDecrypt; + /***/ public String identityFileNoKey; + /***/ public String identityFileMultipleKeys; + /***/ public String identityFileUnsupportedFormat; + /***/ public String kexServerKeyInvalid; + /***/ public String keyEncryptedMsg; + /***/ public String keyEncryptedPrompt; + /***/ public String keyEncryptedRetry; + /***/ public String keyLoadFailed; + /***/ public String knownHostsCouldNotUpdate; + /***/ public String knownHostsFileLockedRead; + /***/ public String knownHostsFileLockedUpdate; + /***/ public String knownHostsFileReadFailed; + /***/ public String knownHostsInvalidLine; + /***/ public String knownHostsInvalidPath; + /***/ public String knownHostsKeyFingerprints; + /***/ public String knownHostsModifiedKeyAcceptPrompt; + /***/ public String knownHostsModifiedKeyDenyMsg; + /***/ public String knownHostsModifiedKeyStorePrompt; + /***/ public String knownHostsModifiedKeyWarning; + /***/ public String knownHostsRevokedKeyMsg; + /***/ public String knownHostsUnknownKeyMsg; + /***/ public String knownHostsUnknownKeyPrompt; + /***/ public String knownHostsUnknownKeyType; + /***/ public String knownHostsUserAskCreationMsg; + /***/ public String knownHostsUserAskCreationPrompt; + /***/ public String passwordPrompt; + /***/ public String proxyCannotAuthenticate; + /***/ public String proxyHttpFailure; + /***/ public String proxyHttpInvalidUserName; + /***/ public String proxyHttpUnexpectedReply; + /***/ public String proxyHttpUnspecifiedFailureReason; + /***/ public String proxyPasswordPrompt; + /***/ public String proxySocksAuthenticationFailed; + /***/ public String proxySocksFailureForbidden; + /***/ public String proxySocksFailureGeneral; + /***/ public String proxySocksFailureHostUnreachable; + /***/ public String proxySocksFailureNetworkUnreachable; + /***/ public String proxySocksFailureRefused; + /***/ public String proxySocksFailureTTL; + /***/ public String proxySocksFailureUnspecified; + /***/ public String proxySocksFailureUnsupportedAddress; + /***/ public String proxySocksFailureUnsupportedCommand; + /***/ public String proxySocksGssApiFailure; + /***/ public String proxySocksGssApiMessageTooShort; + /***/ public String proxySocksGssApiUnknownMessage; + /***/ public String proxySocksGssApiVersionMismatch; + /***/ public String proxySocksNoRemoteHostName; + /***/ public String proxySocksPasswordTooLong; + /***/ public String proxySocksUnexpectedMessage; + /***/ public String proxySocksUnexpectedVersion; + /***/ public String proxySocksUsernameTooLong; + /***/ public String sessionCloseFailed; + /***/ public String sshClosingDown; + /***/ public String sshCommandTimeout; + /***/ public String sshProcessStillRunning; + /***/ public String unknownProxyProtocol; + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AbstractAuthenticationHandler.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AbstractAuthenticationHandler.java new file mode 100644 index 0000000000..6caa1b6aa1 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AbstractAuthenticationHandler.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.auth; + +import java.net.InetSocketAddress; + +/** + * Abstract base class for {@link AuthenticationHandler}s encapsulating basic + * common things. + * + * @param <ParameterType> + * defining the parameter type for the authentication + * @param <TokenType> + * defining the token type for the authentication + */ +public abstract class AbstractAuthenticationHandler<ParameterType, TokenType> + implements AuthenticationHandler<ParameterType, TokenType> { + + /** The {@link InetSocketAddress} or the proxy to connect to. */ + protected InetSocketAddress proxy; + + /** The last set parameters. */ + protected ParameterType params; + + /** A flag telling whether this authentication is done. */ + protected boolean done; + + /** + * Creates a new {@link AbstractAuthenticationHandler} to authenticate with + * the given {@code proxy}. + * + * @param proxy + * the {@link InetSocketAddress} of the proxy to connect to + */ + public AbstractAuthenticationHandler(InetSocketAddress proxy) { + this.proxy = proxy; + } + + @Override + public final void setParams(ParameterType input) { + params = input; + } + + @Override + public final boolean isDone() { + return done; + } + +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AuthenticationHandler.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AuthenticationHandler.java new file mode 100644 index 0000000000..0e98a2e17a --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/AuthenticationHandler.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.auth; + +import java.io.Closeable; + +/** + * An {@code AuthenticationHandler} encapsulates a possibly multi-step + * authentication protocol. Intended usage: + * + * <pre> + * setParams(something); + * start(); + * sendToken(getToken()); + * while (!isDone()) { + * setParams(receiveMessageAndExtractParams()); + * process(); + * Object t = getToken(); + * if (t != null) { + * sendToken(t); + * } + * } + * </pre> + * + * An {@code AuthenticationHandler} may be stateful and therefore is a + * {@link Closeable}. + * + * @param <ParameterType> + * defining the parameter type for {@link #setParams(Object)} + * @param <TokenType> + * defining the token type for {@link #getToken()} + */ +public interface AuthenticationHandler<ParameterType, TokenType> + extends Closeable { + + /** + * Produces the initial authentication token that can be then retrieved via + * {@link #getToken()}. + * + * @throws Exception + * if an error occurs + */ + void start() throws Exception; + + /** + * Produces the next authentication token, if any. + * + * @throws Exception + * if an error occurs + */ + void process() throws Exception; + + /** + * Sets the parameters for the next token generation via {@link #start()} or + * {@link #process()}. + * + * @param input + * to set, may be {@code null} + */ + void setParams(ParameterType input); + + /** + * Retrieves the last token generated. + * + * @return the token, or {@code null} if there is none + * @throws Exception + * if an error occurs + */ + TokenType getToken() throws Exception; + + /** + * Tells whether is authentication mechanism is done (successfully or + * unsuccessfully). + * + * @return whether this authentication is done + */ + boolean isDone(); + + @Override + void close(); +} 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 new file mode 100644 index 0000000000..efb1f55867 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.auth; + +import java.net.Authenticator; +import java.net.Authenticator.RequestorType; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.concurrent.CancellationException; + +import org.eclipse.jgit.internal.transport.sshd.SshdText; +import org.eclipse.jgit.transport.SshConstants; + +/** + * An abstract implementation of a username-password authentication. It can be + * given an initial known username-password pair; if so, this will be tried + * first. Subsequent rounds will then try to obtain a user name and password via + * the global {@link Authenticator}. + * + * @param <ParameterType> + * defining the parameter type for the authentication + * @param <TokenType> + * defining the token type for the authentication + */ +public abstract class BasicAuthentication<ParameterType, TokenType> + extends AbstractAuthenticationHandler<ParameterType, TokenType> { + + /** The current user name. */ + protected String user; + + /** The current password. */ + protected byte[] password; + + /** + * Creates a new {@link BasicAuthentication} to authenticate with the given + * {@code proxy}. + * + * @param proxy + * {@link InetSocketAddress} of the proxy to connect to + * @param initialUser + * initial user name to try; may be {@code null} + * @param initialPassword + * initial password to try, may be {@code null} + */ + public BasicAuthentication(InetSocketAddress proxy, String initialUser, + char[] initialPassword) { + super(proxy); + this.user = initialUser; + this.password = convert(initialPassword); + } + + private byte[] convert(char[] pass) { + if (pass == null) { + return new byte[0]; + } + ByteBuffer bytes = StandardCharsets.UTF_8.encode(CharBuffer.wrap(pass)); + byte[] pwd = new byte[bytes.remaining()]; + bytes.get(pwd); + if (bytes.hasArray()) { + Arrays.fill(bytes.array(), (byte) 0); + } + Arrays.fill(pass, '\000'); + return pwd; + } + + /** + * Clears the {@link #password}. + */ + protected void clearPassword() { + if (password != null) { + Arrays.fill(password, (byte) 0); + } + password = new byte[0]; + } + + @Override + public final void close() { + clearPassword(); + done = true; + } + + @Override + public final void start() throws Exception { + if (user != null && !user.isEmpty() + || password != null && password.length > 0) { + return; + } + askCredentials(); + } + + @Override + public void process() throws Exception { + askCredentials(); + } + + /** + * Asks for credentials via the global {@link Authenticator}. + */ + protected void askCredentials() { + clearPassword(); + PasswordAuthentication auth = AccessController + .doPrivileged(new PrivilegedAction<PasswordAuthentication>() { + + @Override + public PasswordAuthentication run() { + return Authenticator.requestPasswordAuthentication( + proxy.getHostString(), proxy.getAddress(), + proxy.getPort(), SshConstants.SSH_SCHEME, + SshdText.get().proxyPasswordPrompt, "Basic", //$NON-NLS-1$ + null, RequestorType.PROXY); + } + }); + if (auth == null) { + user = ""; //$NON-NLS-1$ + throw new CancellationException( + SshdText.get().authenticationCanceled); + } + user = auth.getUserName(); + password = convert(auth.getPassword()); + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/GssApiAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/GssApiAuthentication.java new file mode 100644 index 0000000000..63cc954479 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/GssApiAuthentication.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.auth; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import org.eclipse.jgit.internal.transport.sshd.GssApiMechanisms; +import org.eclipse.jgit.internal.transport.sshd.SshdText; +import org.ietf.jgss.GSSContext; + +/** + * An abstract implementation of a GSS-API multi-round authentication. + * + * @param <ParameterType> + * defining the parameter type for the authentication + * @param <TokenType> + * defining the token type for the authentication + */ +public abstract class GssApiAuthentication<ParameterType, TokenType> + extends AbstractAuthenticationHandler<ParameterType, TokenType> { + + private GSSContext context; + + /** The last token generated. */ + protected byte[] token; + + /** + * Creates a new {@link GssApiAuthentication} to authenticate with the given + * {@code proxy}. + * + * @param proxy + * the {@link InetSocketAddress} of the proxy to connect to + */ + public GssApiAuthentication(InetSocketAddress proxy) { + super(proxy); + } + + @Override + public void close() { + GssApiMechanisms.closeContextSilently(context); + context = null; + done = true; + } + + @Override + public final void start() throws Exception { + try { + context = createContext(); + context.requestMutualAuth(true); + context.requestConf(false); + context.requestInteg(false); + byte[] empty = new byte[0]; + token = context.initSecContext(empty, 0, 0); + } catch (Exception e) { + close(); + throw e; + } + } + + @Override + public final void process() throws Exception { + if (context == null) { + throw new IOException( + format(SshdText.get().proxyCannotAuthenticate, proxy)); + } + try { + byte[] received = extractToken(params); + token = context.initSecContext(received, 0, received.length); + checkDone(); + } catch (Exception e) { + close(); + throw e; + } + } + + private void checkDone() throws Exception { + done = context.isEstablished(); + if (done) { + context.dispose(); + context = null; + } + } + + /** + * Creates the {@link GSSContext} to use. + * + * @return a fresh {@link GSSContext} to use + * @throws Exception + * if the context cannot be created + */ + protected abstract GSSContext createContext() throws Exception; + + /** + * Extracts the token from the last set parameters. + * + * @param input + * to extract the token from + * @return the extracted token, or {@code null} if none + * @throws Exception + * if an error occurs + */ + protected abstract byte[] extractToken(ParameterType input) + throws Exception; +} 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 new file mode 100644 index 0000000000..2e87c57332 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AbstractClientProxyConnector.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.proxy; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.sshd.client.session.ClientSession; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.internal.transport.sshd.JGitClientSession; + +/** + * Basic common functionality for a {@link StatefulProxyConnector}. + */ +public abstract class AbstractClientProxyConnector + implements StatefulProxyConnector { + + private static final long DEFAULT_PROXY_TIMEOUT_MILLIS = TimeUnit.SECONDS + .toMillis(30L); + + /** Guards {@link #done} and {@link #bufferedCommands}. */ + private Object lock = new Object(); + + private boolean done; + + private List<Callable<Void>> bufferedCommands = new ArrayList<>(); + + private AtomicReference<Runnable> unregister = new AtomicReference<>(); + + private long remainingProxyProtocolTime = DEFAULT_PROXY_TIMEOUT_MILLIS; + + private long lastProxyOperationTime = 0L; + + /** The ultimate remote address to connect to. */ + protected final InetSocketAddress remoteAddress; + + /** The proxy address. */ + protected final InetSocketAddress proxyAddress; + + /** The user to authenticate at the proxy with. */ + protected String proxyUser; + + /** The password to use for authentication at the proxy. */ + protected char[] proxyPassword; + + /** + * Creates a new {@link AbstractClientProxyConnector}. + * + * @param proxyAddress + * of the proxy server we're connecting to + * @param remoteAddress + * of the target server to connect to + * @param proxyUser + * to authenticate at the proxy with; may be {@code null} + * @param proxyPassword + * to authenticate at the proxy with; may be {@code null} + */ + public AbstractClientProxyConnector(@NonNull InetSocketAddress proxyAddress, + @NonNull InetSocketAddress remoteAddress, String proxyUser, + char[] proxyPassword) { + this.proxyAddress = proxyAddress; + this.remoteAddress = remoteAddress; + this.proxyUser = proxyUser; + this.proxyPassword = proxyPassword == null ? new char[0] + : proxyPassword; + } + + /** + * Initializes this instance. Installs itself as proxy handler on the + * session. + * + * @param session + * to initialize for + */ + protected void init(ClientSession session) { + remainingProxyProtocolTime = session.getLongProperty( + StatefulProxyConnector.TIMEOUT_PROPERTY, + DEFAULT_PROXY_TIMEOUT_MILLIS); + if (remainingProxyProtocolTime <= 0L) { + remainingProxyProtocolTime = DEFAULT_PROXY_TIMEOUT_MILLIS; + } + if (session instanceof JGitClientSession) { + JGitClientSession s = (JGitClientSession) session; + unregister.set(() -> s.setProxyHandler(null)); + s.setProxyHandler(this); + } else { + // Internal error, no translation + throw new IllegalStateException( + "Not a JGit session: " + session.getClass().getName()); //$NON-NLS-1$ + } + } + + /** + * Obtains the timeout for the whole rest of the proxy connection protocol. + * + * @return the timeout in milliseconds, always > 0L + */ + protected long getTimeout() { + long last = lastProxyOperationTime; + long now = System.nanoTime(); + lastProxyOperationTime = now; + long remaining = remainingProxyProtocolTime; + if (last != 0L) { + long elapsed = now - last; + remaining -= elapsed; + if (remaining < 0L) { + remaining = 10L; // Give it grace period. + } + } + remainingProxyProtocolTime = remaining; + return remaining; + } + + /** + * Adjusts the timeout calculation to not account of elapsed time since the + * last time the timeout was gotten. Can be used for instance to ignore time + * spent in user dialogs be counted against the overall proxy connection + * protocol timeout. + */ + protected void adjustTimeout() { + lastProxyOperationTime = System.nanoTime(); + } + + /** + * Sets the "done" flag. + * + * @param success + * whether the connector terminated successfully. + * @throws Exception + * if starting ssh fails + */ + protected void setDone(boolean success) throws Exception { + List<Callable<Void>> buffered; + Runnable unset = unregister.getAndSet(null); + if (unset != null) { + unset.run(); + } + synchronized (lock) { + done = true; + buffered = bufferedCommands; + bufferedCommands = null; + } + if (success && buffered != null) { + for (Callable<Void> starter : buffered) { + starter.call(); + } + } + } + + @Override + public void runWhenDone(Callable<Void> starter) throws Exception { + synchronized (lock) { + if (!done) { + bufferedCommands.add(starter); + return; + } + } + starter.call(); + } + + /** + * Clears the proxy password. + */ + protected void clearPassword() { + Arrays.fill(proxyPassword, '\000'); + proxyPassword = new char[0]; + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AuthenticationChallenge.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AuthenticationChallenge.java new file mode 100644 index 0000000000..4a6572d45b --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/AuthenticationChallenge.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.proxy; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jgit.annotations.NonNull; + +/** + * A simple representation of an authentication challenge as sent in a + * "WWW-Authenticate" or "Proxy-Authenticate" header. Such challenges start with + * a mechanism name, followed either by one single token, or by a list of + * key=value pairs. + * + * @see <a href="https://tools.ietf.org/html/rfc7235#section-2.1">RFC 7235, sec. + * 2.1</a> + */ +public class AuthenticationChallenge { + + private final String mechanism; + + private String token; + + private Map<String, String> arguments; + + /** + * Create a new {@link AuthenticationChallenge} with the given mechanism. + * + * @param mechanism + * for the challenge + */ + public AuthenticationChallenge(String mechanism) { + this.mechanism = mechanism; + } + + /** + * Retrieves the authentication mechanism specified by this challenge, for + * instance "Basic". + * + * @return the mechanism name + */ + public String getMechanism() { + return mechanism; + } + + /** + * Retrieves the token of the challenge, if any. + * + * @return the token, or {@code null} if there is none. + */ + public String getToken() { + return token; + } + + /** + * Retrieves the arguments of the challenge. + * + * @return a possibly empty map of the key=value arguments of the challenge + */ + @NonNull + public Map<String, String> getArguments() { + return arguments == null ? Collections.emptyMap() : arguments; + } + + void addArgument(String key, String value) { + if (arguments == null) { + arguments = new LinkedHashMap<>(); + } + arguments.put(key, value); + } + + void setToken(String token) { + this.token = token; + } + + @Override + public String toString() { + return "AuthenticationChallenge[" + mechanism + ',' + token + ',' //$NON-NLS-1$ + + (arguments == null ? "<none>" : arguments.toString()) + ']'; //$NON-NLS-1$ + } +} 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 new file mode 100644 index 0000000000..46cdd52f5f --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpClientConnector.java @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.proxy; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.common.util.Readable; +import org.apache.sshd.common.util.buffer.Buffer; +import org.apache.sshd.common.util.buffer.ByteArrayBuffer; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.internal.transport.sshd.GssApiMechanisms; +import org.eclipse.jgit.internal.transport.sshd.SshdText; +import org.eclipse.jgit.internal.transport.sshd.auth.AuthenticationHandler; +import org.eclipse.jgit.internal.transport.sshd.auth.BasicAuthentication; +import org.eclipse.jgit.internal.transport.sshd.auth.GssApiAuthentication; +import org.eclipse.jgit.util.Base64; +import org.ietf.jgss.GSSContext; + +/** + * Simple HTTP proxy connector using Basic Authentication. + */ +public class HttpClientConnector extends AbstractClientProxyConnector { + + private static final String HTTP_HEADER_PROXY_AUTHENTICATION = "Proxy-Authentication:"; //$NON-NLS-1$ + + private static final String HTTP_HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization:"; //$NON-NLS-1$ + + private HttpAuthenticationHandler basic; + + private HttpAuthenticationHandler negotiate; + + private List<HttpAuthenticationHandler> availableAuthentications; + + private Iterator<HttpAuthenticationHandler> clientAuthentications; + + private HttpAuthenticationHandler authenticator; + + private boolean ongoing; + + /** + * Creates a new {@link HttpClientConnector}. The connector supports + * anonymous proxy connections as well as Basic and Negotiate + * authentication. + * + * @param proxyAddress + * of the proxy server we're connecting to + * @param remoteAddress + * of the target server to connect to + */ + public HttpClientConnector(@NonNull InetSocketAddress proxyAddress, + @NonNull InetSocketAddress remoteAddress) { + this(proxyAddress, remoteAddress, null, null); + } + + /** + * Creates a new {@link HttpClientConnector}. The connector supports + * anonymous proxy connections as well as Basic and Negotiate + * authentication. If a user name and password are given, the connector + * tries pre-emptive Basic authentication. + * + * @param proxyAddress + * of the proxy server we're connecting to + * @param remoteAddress + * of the target server to connect to + * @param proxyUser + * to authenticate at the proxy with + * @param proxyPassword + * to authenticate at the proxy with + */ + public HttpClientConnector(@NonNull InetSocketAddress proxyAddress, + @NonNull InetSocketAddress remoteAddress, String proxyUser, + char[] proxyPassword) { + super(proxyAddress, remoteAddress, proxyUser, proxyPassword); + basic = new HttpBasicAuthentication(); + negotiate = new NegotiateAuthentication(); + availableAuthentications = new ArrayList<>(2); + availableAuthentications.add(negotiate); + availableAuthentications.add(basic); + clientAuthentications = availableAuthentications.iterator(); + } + + private void close() { + HttpAuthenticationHandler current = authenticator; + authenticator = null; + if (current != null) { + current.close(); + } + } + + @Override + public void sendClientProxyMetadata(ClientSession sshSession) + throws Exception { + init(sshSession); + IoSession session = sshSession.getIoSession(); + session.addCloseFutureListener(f -> close()); + StringBuilder msg = connect(); + if (proxyUser != null && !proxyUser.isEmpty() + || proxyPassword != null && proxyPassword.length > 0) { + authenticator = basic; + basic.setParams(null); + basic.start(); + msg = authenticate(msg, basic.getToken()); + clearPassword(); + proxyUser = null; + } + ongoing = true; + try { + send(msg, session); + } catch (Exception e) { + ongoing = false; + throw e; + } + } + + private void send(StringBuilder msg, IoSession session) throws Exception { + byte[] data = eol(msg).toString().getBytes(StandardCharsets.US_ASCII); + Buffer buffer = new ByteArrayBuffer(data.length, false); + buffer.putRawBytes(data); + session.writePacket(buffer).verify(getTimeout()); + } + + private StringBuilder connect() { + StringBuilder msg = new StringBuilder(); + // Persistent connections are the default in HTTP 1.1 (see RFC 2616), + // but let's be explicit. + return msg.append(format( + "CONNECT {0}:{1} HTTP/1.1\r\nProxy-Connection: keep-alive\r\nConnection: keep-alive\r\nHost: {0}:{1}\r\n", //$NON-NLS-1$ + remoteAddress.getHostString(), + Integer.toString(remoteAddress.getPort()))); + } + + private StringBuilder authenticate(StringBuilder msg, String token) { + msg.append(HTTP_HEADER_PROXY_AUTHORIZATION).append(' ').append(token); + return eol(msg); + } + + private StringBuilder eol(StringBuilder msg) { + return msg.append('\r').append('\n'); + } + + @Override + public void messageReceived(IoSession session, Readable buffer) + throws Exception { + try { + int length = buffer.available(); + byte[] data = new byte[length]; + buffer.getRawBytes(data, 0, length); + String[] reply = new String(data, StandardCharsets.US_ASCII) + .split("\r\n"); //$NON-NLS-1$ + handleMessage(session, Arrays.asList(reply)); + } catch (Exception e) { + if (authenticator != null) { + authenticator.close(); + authenticator = null; + } + ongoing = false; + try { + setDone(false); + } catch (Exception inner) { + e.addSuppressed(inner); + } + throw e; + } + } + + private void handleMessage(IoSession session, List<String> reply) + throws Exception { + if (reply.isEmpty() || reply.get(0).isEmpty()) { + throw new IOException( + format(SshdText.get().proxyHttpUnexpectedReply, + proxyAddress, "<empty>")); //$NON-NLS-1$ + } + try { + StatusLine status = HttpParser.parseStatusLine(reply.get(0)); + if (!ongoing) { + throw new IOException(format( + SshdText.get().proxyHttpUnexpectedReply, proxyAddress, + Integer.toString(status.getResultCode()), + status.getReason())); + } + switch (status.getResultCode()) { + case HttpURLConnection.HTTP_OK: + if (authenticator != null) { + authenticator.close(); + } + authenticator = null; + ongoing = false; + setDone(true); + break; + case HttpURLConnection.HTTP_PROXY_AUTH: + List<AuthenticationChallenge> challenges = HttpParser + .getAuthenticationHeaders(reply, + HTTP_HEADER_PROXY_AUTHENTICATION); + authenticator = selectProtocol(challenges, authenticator); + if (authenticator == null) { + throw new IOException( + format(SshdText.get().proxyCannotAuthenticate, + proxyAddress)); + } + String token = authenticator.getToken(); + if (token == null) { + throw new IOException( + format(SshdText.get().proxyCannotAuthenticate, + proxyAddress)); + } + send(authenticate(connect(), token), session); + break; + default: + throw new IOException(format(SshdText.get().proxyHttpFailure, + proxyAddress, Integer.toString(status.getResultCode()), + status.getReason())); + } + } catch (HttpParser.ParseException e) { + throw new IOException( + format(SshdText.get().proxyHttpUnexpectedReply, + proxyAddress, reply.get(0))); + } + } + + private HttpAuthenticationHandler selectProtocol( + List<AuthenticationChallenge> challenges, + HttpAuthenticationHandler current) throws Exception { + if (current != null && !current.isDone()) { + AuthenticationChallenge challenge = getByName(challenges, + current.getName()); + if (challenge != null) { + current.setParams(challenge); + current.process(); + return current; + } + } + if (current != null) { + current.close(); + } + while (clientAuthentications.hasNext()) { + HttpAuthenticationHandler next = clientAuthentications.next(); + if (!next.isDone()) { + AuthenticationChallenge challenge = getByName(challenges, + next.getName()); + if (challenge != null) { + next.setParams(challenge); + next.start(); + return next; + } + } + } + return null; + } + + private AuthenticationChallenge getByName( + List<AuthenticationChallenge> challenges, + String name) { + return challenges.stream() + .filter(c -> c.getMechanism().equalsIgnoreCase(name)) + .findFirst().orElse(null); + } + + private interface HttpAuthenticationHandler + extends AuthenticationHandler<AuthenticationChallenge, String> { + + public String getName(); + } + + /** + * @see <a href="https://tools.ietf.org/html/rfc7617">RFC 7617</a> + */ + private class HttpBasicAuthentication + extends BasicAuthentication<AuthenticationChallenge, String> + implements HttpAuthenticationHandler { + + private boolean asked; + + public HttpBasicAuthentication() { + super(proxyAddress, proxyUser, proxyPassword); + } + + @Override + public String getName() { + return "Basic"; //$NON-NLS-1$ + } + + @Override + protected void askCredentials() { + // We ask only once. + if (asked) { + throw new IllegalStateException( + "Basic auth: already asked user for password"); //$NON-NLS-1$ + } + asked = true; + super.askCredentials(); + done = true; + } + + @Override + public String getToken() throws Exception { + if (user.indexOf(':') >= 0) { + throw new IOException(format( + SshdText.get().proxyHttpInvalidUserName, proxy, user)); + } + byte[] rawUser = user.getBytes(StandardCharsets.UTF_8); + byte[] toEncode = new byte[rawUser.length + 1 + password.length]; + System.arraycopy(rawUser, 0, toEncode, 0, rawUser.length); + toEncode[rawUser.length] = ':'; + System.arraycopy(password, 0, toEncode, rawUser.length + 1, + password.length); + Arrays.fill(password, (byte) 0); + String result = Base64.encodeBytes(toEncode); + Arrays.fill(toEncode, (byte) 0); + return getName() + ' ' + result; + } + + } + + /** + * @see <a href="https://tools.ietf.org/html/rfc4559">RFC 4559</a> + */ + private class NegotiateAuthentication + extends GssApiAuthentication<AuthenticationChallenge, String> + implements HttpAuthenticationHandler { + + public NegotiateAuthentication() { + super(proxyAddress); + } + + @Override + public String getName() { + return "Negotiate"; //$NON-NLS-1$ + } + + @Override + public String getToken() throws Exception { + return getName() + ' ' + Base64.encodeBytes(token); + } + + @Override + protected GSSContext createContext() throws Exception { + return GssApiMechanisms.createContext(GssApiMechanisms.SPNEGO, + GssApiMechanisms.getCanonicalName(proxyAddress)); + } + + @Override + protected byte[] extractToken(AuthenticationChallenge input) + throws Exception { + String received = input.getToken(); + if (received == null) { + return new byte[0]; + } + return Base64.decode(received); + } + + } +} 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 new file mode 100644 index 0000000000..b9b32b1300 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/HttpParser.java @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.proxy; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * A basic parser for HTTP response headers. Handles status lines and + * authentication headers (WWW-Authenticate, Proxy-Authenticate). + * + * @see <a href="https://tools.ietf.org/html/rfc7230">RFC 7230</a> + * @see <a href="https://tools.ietf.org/html/rfc7235">RFC 7235</a> + */ +public final class HttpParser { + + /** + * An exception indicating some problem parsing HTPP headers. + */ + public static class ParseException extends Exception { + + private static final long serialVersionUID = -1634090143702048640L; + + } + + private HttpParser() { + // No instantiation + } + + /** + * Parse a HTTP response status line. + * + * @param line + * to parse + * @return the {@link StatusLine} + * @throws ParseException + * if the line cannot be parsed or has the wrong HTTP version + */ + public static StatusLine parseStatusLine(String line) + throws ParseException { + // Format is HTTP/<version> Code Reason + int firstBlank = line.indexOf(' '); + if (firstBlank < 0) { + throw new ParseException(); + } + int secondBlank = line.indexOf(' ', firstBlank + 1); + if (secondBlank < 0) { + // Accept the line even if the (according to RFC 2616 mandatory) + // reason is missing. + secondBlank = line.length(); + } + int resultCode; + try { + resultCode = Integer.parseUnsignedInt( + line.substring(firstBlank + 1, secondBlank)); + } catch (NumberFormatException e) { + throw new ParseException(); + } + // Again, accept even if the reason is missing + String reason = ""; //$NON-NLS-1$ + if (secondBlank < line.length()) { + reason = line.substring(secondBlank + 1); + } + return new StatusLine(line.substring(0, firstBlank), resultCode, + reason); + } + + /** + * Extract the authentication headers from the header lines. It is assumed + * that the first element in {@code reply} is the raw status line as + * received from the server. It is skipped. Line processing stops on the + * first empty line thereafter. + * + * @param reply + * The complete (header) lines of the HTTP response + * @param authenticationHeader + * to look for (including the terminating ':'!) + * @return a list of {@link AuthenticationChallenge}s found. + */ + public static List<AuthenticationChallenge> getAuthenticationHeaders( + List<String> reply, String authenticationHeader) { + List<AuthenticationChallenge> challenges = new ArrayList<>(); + Iterator<String> lines = reply.iterator(); + // We know we have at least one line. Skip the response line. + lines.next(); + StringBuilder value = null; + while (lines.hasNext()) { + String line = lines.next(); + if (line.isEmpty()) { + break; + } + if (Character.isWhitespace(line.charAt(0))) { + // Continuation line. + if (value == null) { + // Skip if we have no current value + continue; + } + // Skip leading whitespace + int i = skipWhiteSpace(line, 1); + value.append(' ').append(line, i, line.length()); + continue; + } + if (value != null) { + parseChallenges(challenges, value.toString()); + value = null; + } + int firstColon = line.indexOf(':'); + if (firstColon > 0 && authenticationHeader + .equalsIgnoreCase(line.substring(0, firstColon + 1))) { + value = new StringBuilder(line.substring(firstColon + 1)); + } + } + if (value != null) { + parseChallenges(challenges, value.toString()); + } + return challenges; + } + + private static void parseChallenges( + List<AuthenticationChallenge> challenges, + String header) { + // Comma-separated list of challenges, each itself a scheme name + // followed optionally by either: a comma-separated list of key=value + // pairs, where the value may be a quoted string with backslash escapes, + // or a single token value, which itself may end in zero or more '=' + // characters. Ugh. + int length = header.length(); + for (int i = 0; i < length;) { + int start = skipWhiteSpace(header, i); + int end = scanToken(header, start); + if (end <= start) { + break; + } + AuthenticationChallenge challenge = new AuthenticationChallenge( + header.substring(start, end)); + challenges.add(challenge); + i = parseChallenge(challenge, header, end); + } + } + + private static int parseChallenge(AuthenticationChallenge challenge, + String header, int from) { + int length = header.length(); + boolean first = true; + for (int start = from; start <= length; first = false) { + // Now we have either a single token, which may end in zero or more + // equal signs, or a comma-separated list of key=value pairs (with + // optional legacy whitespace around the equals sign), where the + // value can be either a token or a quoted string. + start = skipWhiteSpace(header, start); + int end = scanToken(header, start); + if (end == start) { + // Nothing found. Either at end or on a comma. + if (start < header.length() && header.charAt(start) == ',') { + return start + 1; + } + return start; + } + int next = skipWhiteSpace(header, end); + // Comma, or equals sign, or end of string + if (next >= length || header.charAt(next) != '=') { + if (first) { + // It must be a token + challenge.setToken(header.substring(start, end)); + if (next < length && header.charAt(next) == ',') { + next++; + } + return next; + } else { + // This token must be the name of the next authentication + // scheme. + return start; + } + } + int nextStart = skipWhiteSpace(header, next + 1); + if (nextStart >= length) { + if (next == end) { + // '=' immediately after the key, no value: key must be the + // token, and the equals sign is part of the token + challenge.setToken(header.substring(start, end + 1)); + } else { + // Key without value... + challenge.addArgument(header.substring(start, end), null); + } + return nextStart; + } + if (nextStart == end + 1 && header.charAt(nextStart) == '=') { + // More than one equals sign: must be the single token. + end = nextStart + 1; + while (end < length && header.charAt(end) == '=') { + end++; + } + challenge.setToken(header.substring(start, end)); + end = skipWhiteSpace(header, end); + if (end < length && header.charAt(end) == ',') { + end++; + } + return end; + } + if (header.charAt(nextStart) == ',') { + if (next == end) { + // '=' immediately after the key, no value: key must be the + // token, and the equals sign is part of the token + challenge.setToken(header.substring(start, end + 1)); + return nextStart + 1; + } else { + // Key without value... + challenge.addArgument(header.substring(start, end), null); + start = nextStart + 1; + } + } else { + if (header.charAt(nextStart) == '"') { + int nextEnd[] = { nextStart + 1 }; + String value = scanQuotedString(header, nextStart + 1, + nextEnd); + challenge.addArgument(header.substring(start, end), value); + start = nextEnd[0]; + } else { + int nextEnd = scanToken(header, nextStart); + challenge.addArgument(header.substring(start, end), + header.substring(nextStart, nextEnd)); + start = nextEnd; + } + start = skipWhiteSpace(header, start); + if (start < length && header.charAt(start) == ',') { + start++; + } + } + } + return length; + } + + private static int skipWhiteSpace(String header, int i) { + int length = header.length(); + while (i < length && Character.isWhitespace(header.charAt(i))) { + i++; + } + return i; + } + + private static int scanToken(String header, int from) { + int length = header.length(); + int i = from; + while (i < length) { + char c = header.charAt(i); + switch (c) { + case '!': + case '#': + case '$': + case '%': + case '&': + case '\'': + case '*': + case '+': + case '-': + case '.': + case '^': + case '_': + case '`': + case '|': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i++; + break; + default: + if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') { + i++; + break; + } + return i; + } + } + return i; + } + + private static String scanQuotedString(String header, int from, int[] to) { + StringBuilder result = new StringBuilder(); + int length = header.length(); + boolean quoted = false; + int i = from; + while (i < length) { + char c = header.charAt(i++); + if (quoted) { + result.append(c); + quoted = false; + } else if (c == '\\') { + quoted = true; + } else if (c == '"') { + break; + } else { + result.append(c); + } + } + to[0] = i; + return result.toString(); + } +} 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 new file mode 100644 index 0000000000..1844fdc794 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/Socks5ClientConnector.java @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.proxy; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; + +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.common.util.Readable; +import org.apache.sshd.common.util.buffer.Buffer; +import org.apache.sshd.common.util.buffer.BufferUtils; +import org.apache.sshd.common.util.buffer.ByteArrayBuffer; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.internal.transport.sshd.GssApiMechanisms; +import org.eclipse.jgit.internal.transport.sshd.SshdText; +import org.eclipse.jgit.internal.transport.sshd.auth.AuthenticationHandler; +import org.eclipse.jgit.internal.transport.sshd.auth.BasicAuthentication; +import org.eclipse.jgit.internal.transport.sshd.auth.GssApiAuthentication; +import org.eclipse.jgit.transport.SshConstants; +import org.ietf.jgss.GSSContext; + +/** + * A {@link AbstractClientProxyConnector} to connect through a SOCKS5 proxy. + * + * @see <a href="https://tools.ietf.org/html/rfc1928">RFC 1928</a> + */ +public class Socks5ClientConnector extends AbstractClientProxyConnector { + + // private static final byte SOCKS_VERSION_4 = 4; + private static final byte SOCKS_VERSION_5 = 5; + + private static final byte SOCKS_CMD_CONNECT = 1; + // private static final byte SOCKS5_CMD_BIND = 2; + // private static final byte SOCKS5_CMD_UDP_ASSOCIATE = 3; + + // Address types + + private static final byte SOCKS_ADDRESS_IPv4 = 1; + + private static final byte SOCKS_ADDRESS_FQDN = 3; + + private static final byte SOCKS_ADDRESS_IPv6 = 4; + + // Reply codes + + private static final byte SOCKS_REPLY_SUCCESS = 0; + + private static final byte SOCKS_REPLY_FAILURE = 1; + + private static final byte SOCKS_REPLY_FORBIDDEN = 2; + + private static final byte SOCKS_REPLY_NETWORK_UNREACHABLE = 3; + + private static final byte SOCKS_REPLY_HOST_UNREACHABLE = 4; + + private static final byte SOCKS_REPLY_CONNECTION_REFUSED = 5; + + private static final byte SOCKS_REPLY_TTL_EXPIRED = 6; + + private static final byte SOCKS_REPLY_COMMAND_UNSUPPORTED = 7; + + private static final byte SOCKS_REPLY_ADDRESS_UNSUPPORTED = 8; + + /** + * Authentication methods for SOCKS5. + * + * @see <a href= + * "https://www.iana.org/assignments/socks-methods/socks-methods.xhtml">SOCKS + * Methods, IANA.org</a> + */ + private enum SocksAuthenticationMethod { + + ANONYMOUS(0), + GSSAPI(1), + PASSWORD(2), + // CHALLENGE_HANDSHAKE(3), + // CHALLENGE_RESPONSE(5), + // SSL(6), + // NDS(7), + // MULTI_AUTH(8), + // JSON(9), + NONE_ACCEPTABLE(0xFF); + + private byte value; + + SocksAuthenticationMethod(int value) { + this.value = (byte) value; + } + + public byte getValue() { + return value; + } + } + + private enum ProtocolState { + NONE, + + INIT { + @Override + public void handleMessage(Socks5ClientConnector connector, + IoSession session, Buffer data) throws Exception { + connector.versionCheck(data.getByte()); + SocksAuthenticationMethod authMethod = connector.getAuthMethod( + data.getByte()); + switch (authMethod) { + case ANONYMOUS: + connector.sendConnectInfo(session); + break; + case PASSWORD: + connector.doPasswordAuth(session); + break; + case GSSAPI: + connector.doGssApiAuth(session); + break; + default: + throw new IOException( + format(SshdText.get().proxyCannotAuthenticate, + connector.proxyAddress)); + } + } + }, + + AUTHENTICATING { + @Override + public void handleMessage(Socks5ClientConnector connector, + IoSession session, Buffer data) throws Exception { + connector.authStep(session, data); + } + }, + + CONNECTING { + @Override + public void handleMessage(Socks5ClientConnector connector, + IoSession session, Buffer data) throws Exception { + // Special case: when GSS-API authentication completes, the + // client moves into CONNECTING as soon as the GSS context is + // established and sends the connect request. This is per RFC + // 1961. But for the server, RFC 1961 says it _should_ send an + // empty token even if none generated when its server side + // context is established. That means we may actually get an + // empty token here. That message is 4 bytes long (and has + // content 0x01, 0x01, 0x00, 0x00). We simply skip this message + // if we get it here. If the server for whatever reason sends + // back a "GSS failed" message (it shouldn't, at this point) + // it will be two bytes 0x01 0xFF, which will fail the version + // check. + if (data.available() != 4) { + connector.versionCheck(data.getByte()); + connector.establishConnection(data); + } + } + }, + + CONNECTED, + + FAILED; + + public void handleMessage(Socks5ClientConnector connector, + @SuppressWarnings("unused") IoSession session, Buffer data) + throws Exception { + throw new IOException( + format(SshdText.get().proxySocksUnexpectedMessage, + connector.proxyAddress, this, + BufferUtils.toHex(data.array()))); + } + } + + private ProtocolState state; + + private AuthenticationHandler<Buffer, Buffer> authenticator; + + private GSSContext context; + + private byte[] authenticationProposals; + + /** + * Creates a new {@link Socks5ClientConnector}. The connector supports + * anonymous connections as well as username-password or Kerberos5 (GSS-API) + * authentication. + * + * @param proxyAddress + * of the proxy server we're connecting to + * @param remoteAddress + * of the target server to connect to + */ + public Socks5ClientConnector(@NonNull InetSocketAddress proxyAddress, + @NonNull InetSocketAddress remoteAddress) { + this(proxyAddress, remoteAddress, null, null); + } + + /** + * Creates a new {@link Socks5ClientConnector}. The connector supports + * anonymous connections as well as username-password or Kerberos5 (GSS-API) + * authentication. + * + * @param proxyAddress + * of the proxy server we're connecting to + * @param remoteAddress + * of the target server to connect to + * @param proxyUser + * to authenticate at the proxy with + * @param proxyPassword + * to authenticate at the proxy with + */ + public Socks5ClientConnector(@NonNull InetSocketAddress proxyAddress, + @NonNull InetSocketAddress remoteAddress, + String proxyUser, char[] proxyPassword) { + super(proxyAddress, remoteAddress, proxyUser, proxyPassword); + this.state = ProtocolState.NONE; + } + + @Override + public void sendClientProxyMetadata(ClientSession sshSession) + throws Exception { + init(sshSession); + IoSession session = sshSession.getIoSession(); + // Send the initial request + Buffer buffer = new ByteArrayBuffer(5, false); + buffer.putByte(SOCKS_VERSION_5); + context = getGSSContext(remoteAddress); + authenticationProposals = getAuthenticationProposals(); + buffer.putByte((byte) authenticationProposals.length); + buffer.putRawBytes(authenticationProposals); + state = ProtocolState.INIT; + session.writePacket(buffer).verify(getTimeout()); + } + + private byte[] getAuthenticationProposals() { + byte[] proposals = new byte[3]; + int i = 0; + proposals[i++] = SocksAuthenticationMethod.ANONYMOUS.getValue(); + proposals[i++] = SocksAuthenticationMethod.PASSWORD.getValue(); + if (context != null) { + proposals[i++] = SocksAuthenticationMethod.GSSAPI.getValue(); + } + if (i == proposals.length) { + return proposals; + } else { + byte[] result = new byte[i]; + System.arraycopy(proposals, 0, result, 0, i); + return result; + } + } + + private void sendConnectInfo(IoSession session) throws Exception { + GssApiMechanisms.closeContextSilently(context); + + byte[] rawAddress = getRawAddress(remoteAddress); + byte[] remoteName = null; + byte type; + int length = 0; + if (rawAddress == null) { + remoteName = remoteAddress.getHostString() + .getBytes(StandardCharsets.US_ASCII); + if (remoteName == null || remoteName.length == 0) { + throw new IOException( + format(SshdText.get().proxySocksNoRemoteHostName, + remoteAddress)); + } else if (remoteName.length > 255) { + // Should not occur; host names must not be longer than 255 + // US_ASCII characters. Internal error, no translation. + throw new IOException(format( + "Proxy host name too long for SOCKS (at most 255 characters): {0}", //$NON-NLS-1$ + remoteAddress.getHostString())); + } + type = SOCKS_ADDRESS_FQDN; + length = remoteName.length + 1; + } else { + length = rawAddress.length; + type = length == 4 ? SOCKS_ADDRESS_IPv4 : SOCKS_ADDRESS_IPv6; + } + Buffer buffer = new ByteArrayBuffer(4 + length + 2, false); + buffer.putByte(SOCKS_VERSION_5); + buffer.putByte(SOCKS_CMD_CONNECT); + buffer.putByte((byte) 0); // Reserved + buffer.putByte(type); + if (remoteName != null) { + buffer.putByte((byte) remoteName.length); + buffer.putRawBytes(remoteName); + } else { + buffer.putRawBytes(rawAddress); + } + int port = remoteAddress.getPort(); + if (port <= 0) { + port = SshConstants.SSH_DEFAULT_PORT; + } + buffer.putByte((byte) ((port >> 8) & 0xFF)); + buffer.putByte((byte) (port & 0xFF)); + state = ProtocolState.CONNECTING; + session.writePacket(buffer).verify(getTimeout()); + } + + private void doPasswordAuth(IoSession session) throws Exception { + GssApiMechanisms.closeContextSilently(context); + authenticator = new SocksBasicAuthentication(); + session.addCloseFutureListener(f -> close()); + startAuth(session); + } + + private void doGssApiAuth(IoSession session) throws Exception { + authenticator = new SocksGssApiAuthentication(); + session.addCloseFutureListener(f -> close()); + startAuth(session); + } + + private void close() { + AuthenticationHandler<?, ?> handler = authenticator; + authenticator = null; + if (handler != null) { + handler.close(); + } + } + + private void startAuth(IoSession session) throws Exception { + Buffer buffer = null; + try { + authenticator.setParams(null); + authenticator.start(); + buffer = authenticator.getToken(); + state = ProtocolState.AUTHENTICATING; + if (buffer == null) { + // Internal error; no translation + throw new IOException( + "No data for proxy authentication with " //$NON-NLS-1$ + + proxyAddress); + } + session.writePacket(buffer).verify(getTimeout()); + } finally { + if (buffer != null) { + buffer.clear(true); + } + } + } + + private void authStep(IoSession session, Buffer input) throws Exception { + Buffer buffer = null; + try { + authenticator.setParams(input); + authenticator.process(); + buffer = authenticator.getToken(); + if (buffer != null) { + session.writePacket(buffer).verify(getTimeout()); + } + } finally { + if (buffer != null) { + buffer.clear(true); + } + } + if (authenticator.isDone()) { + sendConnectInfo(session); + } + } + + private void establishConnection(Buffer data) throws Exception { + byte reply = data.getByte(); + switch (reply) { + case SOCKS_REPLY_SUCCESS: + state = ProtocolState.CONNECTED; + setDone(true); + return; + case SOCKS_REPLY_FAILURE: + throw new IOException(format( + SshdText.get().proxySocksFailureGeneral, proxyAddress)); + case SOCKS_REPLY_FORBIDDEN: + throw new IOException( + format(SshdText.get().proxySocksFailureForbidden, + proxyAddress, remoteAddress)); + case SOCKS_REPLY_NETWORK_UNREACHABLE: + throw new IOException( + format(SshdText.get().proxySocksFailureNetworkUnreachable, + proxyAddress, remoteAddress)); + case SOCKS_REPLY_HOST_UNREACHABLE: + throw new IOException( + format(SshdText.get().proxySocksFailureHostUnreachable, + proxyAddress, remoteAddress)); + case SOCKS_REPLY_CONNECTION_REFUSED: + throw new IOException( + format(SshdText.get().proxySocksFailureRefused, + proxyAddress, remoteAddress)); + case SOCKS_REPLY_TTL_EXPIRED: + throw new IOException( + format(SshdText.get().proxySocksFailureTTL, proxyAddress)); + case SOCKS_REPLY_COMMAND_UNSUPPORTED: + throw new IOException( + format(SshdText.get().proxySocksFailureUnsupportedCommand, + proxyAddress)); + case SOCKS_REPLY_ADDRESS_UNSUPPORTED: + throw new IOException( + format(SshdText.get().proxySocksFailureUnsupportedAddress, + proxyAddress)); + default: + throw new IOException(format( + SshdText.get().proxySocksFailureUnspecified, proxyAddress)); + } + } + + @Override + public void messageReceived(IoSession session, Readable buffer) + throws Exception { + try { + // Dispatch according to protocol state + ByteArrayBuffer data = new ByteArrayBuffer(buffer.available(), + false); + data.putBuffer(buffer); + data.compact(); + state.handleMessage(this, session, data); + } catch (Exception e) { + state = ProtocolState.FAILED; + if (authenticator != null) { + authenticator.close(); + authenticator = null; + } + try { + setDone(false); + } catch (Exception inner) { + e.addSuppressed(inner); + } + throw e; + } + } + + private void versionCheck(byte version) throws Exception { + if (version != SOCKS_VERSION_5) { + throw new IOException( + format(SshdText.get().proxySocksUnexpectedVersion, + Integer.toString(version & 0xFF))); + } + } + + private SocksAuthenticationMethod getAuthMethod(byte value) { + if (value != SocksAuthenticationMethod.NONE_ACCEPTABLE.getValue()) { + for (byte proposed : authenticationProposals) { + if (proposed == value) { + for (SocksAuthenticationMethod method : SocksAuthenticationMethod + .values()) { + if (method.getValue() == value) { + return method; + } + } + break; + } + } + } + return SocksAuthenticationMethod.NONE_ACCEPTABLE; + } + + private static byte[] getRawAddress(@NonNull InetSocketAddress address) { + InetAddress ipAddress = GssApiMechanisms.resolve(address); + return ipAddress == null ? null : ipAddress.getAddress(); + } + + private static GSSContext getGSSContext( + @NonNull InetSocketAddress address) { + if (!GssApiMechanisms.getSupportedMechanisms() + .contains(GssApiMechanisms.KERBEROS_5)) { + return null; + } + return GssApiMechanisms.createContext(GssApiMechanisms.KERBEROS_5, + GssApiMechanisms.getCanonicalName(address)); + } + + /** + * @see <a href="https://tools.ietf.org/html/rfc1929">RFC 1929</a> + */ + private class SocksBasicAuthentication + extends BasicAuthentication<Buffer, Buffer> { + + private static final byte SOCKS_BASIC_PROTOCOL_VERSION = 1; + + private static final byte SOCKS_BASIC_AUTH_SUCCESS = 0; + + public SocksBasicAuthentication() { + super(proxyAddress, proxyUser, proxyPassword); + } + + @Override + public void process() throws Exception { + // Retries impossible. RFC 1929 specifies that the server MUST + // close the connection if authentication is unsuccessful. + done = true; + if (params.getByte() != SOCKS_BASIC_PROTOCOL_VERSION + || params.getByte() != SOCKS_BASIC_AUTH_SUCCESS) { + throw new IOException(format( + SshdText.get().proxySocksAuthenticationFailed, proxy)); + } + } + + @Override + protected void askCredentials() { + super.askCredentials(); + adjustTimeout(); + } + + @Override + public Buffer getToken() throws IOException { + if (done) { + return null; + } + try { + byte[] rawUser = user.getBytes(StandardCharsets.UTF_8); + if (rawUser.length > 255) { + throw new IOException(format( + SshdText.get().proxySocksUsernameTooLong, proxy, + Integer.toString(rawUser.length), user)); + } + + if (password.length > 255) { + throw new IOException( + format(SshdText.get().proxySocksPasswordTooLong, + proxy, Integer.toString(password.length))); + } + ByteArrayBuffer buffer = new ByteArrayBuffer( + 3 + rawUser.length + password.length, false); + buffer.putByte(SOCKS_BASIC_PROTOCOL_VERSION); + buffer.putByte((byte) rawUser.length); + buffer.putRawBytes(rawUser); + buffer.putByte((byte) password.length); + buffer.putRawBytes(password); + return buffer; + } finally { + clearPassword(); + done = true; + } + } + } + + /** + * @see <a href="https://tools.ietf.org/html/rfc1961">RFC 1961</a> + */ + private class SocksGssApiAuthentication + extends GssApiAuthentication<Buffer, Buffer> { + + private static final byte SOCKS5_GSSAPI_VERSION = 1; + + private static final byte SOCKS5_GSSAPI_TOKEN = 1; + + private static final int SOCKS5_GSSAPI_FAILURE = 0xFF; + + public SocksGssApiAuthentication() { + super(proxyAddress); + } + + @Override + protected GSSContext createContext() throws Exception { + return context; + } + + @Override + public Buffer getToken() throws Exception { + if (token == null) { + return null; + } + Buffer buffer = new ByteArrayBuffer(4 + token.length, false); + buffer.putByte(SOCKS5_GSSAPI_VERSION); + buffer.putByte(SOCKS5_GSSAPI_TOKEN); + buffer.putByte((byte) ((token.length >> 8) & 0xFF)); + buffer.putByte((byte) (token.length & 0xFF)); + buffer.putRawBytes(token); + return buffer; + } + + @Override + protected byte[] extractToken(Buffer input) throws Exception { + if (context == null) { + return null; + } + int version = input.getUByte(); + if (version != SOCKS5_GSSAPI_VERSION) { + throw new IOException( + format(SshdText.get().proxySocksGssApiVersionMismatch, + remoteAddress, Integer.toString(version))); + } + int msgType = input.getUByte(); + if (msgType == SOCKS5_GSSAPI_FAILURE) { + throw new IOException(format( + SshdText.get().proxySocksGssApiFailure, remoteAddress)); + } else if (msgType != SOCKS5_GSSAPI_TOKEN) { + throw new IOException(format( + SshdText.get().proxySocksGssApiUnknownMessage, + remoteAddress, Integer.toHexString(msgType & 0xFF))); + } + if (input.available() >= 2) { + int length = (input.getUByte() << 8) + input.getUByte(); + if (input.available() >= length) { + byte[] value = new byte[length]; + if (length > 0) { + input.getRawBytes(value); + } + return value; + } + } + throw new IOException( + format(SshdText.get().proxySocksGssApiMessageTooShort, + remoteAddress)); + } + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatefulProxyConnector.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatefulProxyConnector.java new file mode 100644 index 0000000000..6bd836a587 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatefulProxyConnector.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.proxy; + +import java.util.concurrent.Callable; + +import org.apache.sshd.client.session.ClientProxyConnector; +import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.common.util.Readable; + +/** + * Some proxy connections are stateful and require the exchange of multiple + * request-reply messages. The default {@link ClientProxyConnector} has only + * support for sending a message; replies get routed through the Ssh session, + * and don't get back to this proxy connector. Augment the interface so that the + * session can know when to route messages received to the proxy connector, and + * when to start handling them itself. + */ +public interface StatefulProxyConnector extends ClientProxyConnector { + + /** + * A property key for a session property defining the timeout for setting up + * the proxy connection. + */ + static final String TIMEOUT_PROPERTY = StatefulProxyConnector.class + .getName() + "-timeout"; //$NON-NLS-1$ + + /** + * Handle a received message. + * + * @param session + * to use for writing data + * @param buffer + * received data + * @throws Exception + * if data cannot be read, or the connection attempt fails + */ + void messageReceived(IoSession session, Readable buffer) throws Exception; + + /** + * Runs {@code command} once the proxy connection is established. May be + * called multiple times; commands are run sequentially. If the proxy + * connection is already established, {@code command} is executed directly + * synchronously. + * + * @param command + * operation to run + * @throws Exception + * if the operation is run synchronously and throws an exception + */ + void runWhenDone(Callable<Void> command) throws Exception; +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatusLine.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatusLine.java new file mode 100644 index 0000000000..7ff0183b22 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/proxy/StatusLine.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.sshd.proxy; + +/** + * A very simple representation of a HTTP status line. + */ +public class StatusLine { + + private final String version; + + private final int resultCode; + + private final String reason; + + /** + * Create a new {@link StatusLine} with the given response code and reason + * string. + * + * @param version + * the version string (normally "HTTP/1.1" or "HTTP/1.0") + * @param resultCode + * the HTTP response code (200, 401, etc.) + * @param reason + * the reason phrase for the code + */ + public StatusLine(String version, int resultCode, String reason) { + this.version = version; + this.resultCode = resultCode; + this.reason = reason; + } + + /** + * Retrieves the version string. + * + * @return the version string + */ + public String getVersion() { + return version; + } + + /** + * Retrieves the HTTP response code. + * + * @return the code + */ + public int getResultCode() { + return resultCode; + } + + /** + * Retrieves the HTTP reason phrase. + * + * @return the reason + */ + public String getReason() { + return reason; + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/DefaultProxyDataFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/DefaultProxyDataFactory.java new file mode 100644 index 0000000000..97e0da0428 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/DefaultProxyDataFactory.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +/** + * A default implementation of a {@link ProxyDataFactory} based on the standard + * {@link java.net.ProxySelector}. + * + * @since 5.2 + */ +public class DefaultProxyDataFactory implements ProxyDataFactory { + + @Override + public ProxyData get(InetSocketAddress remoteAddress) { + try { + List<Proxy> proxies = ProxySelector.getDefault() + .select(new URI(Proxy.Type.SOCKS.name(), + "//" + remoteAddress.getHostString(), null)); //$NON-NLS-1$ + ProxyData data = getData(proxies, Proxy.Type.SOCKS); + if (data == null) { + proxies = ProxySelector.getDefault() + .select(new URI(Proxy.Type.HTTP.name(), + "//" + remoteAddress.getHostString(), //$NON-NLS-1$ + null)); + data = getData(proxies, Proxy.Type.HTTP); + } + return data; + } catch (URISyntaxException e) { + return null; + } + } + + private ProxyData getData(List<Proxy> proxies, Proxy.Type type) { + Proxy proxy = proxies.stream().filter(p -> type == p.type()).findFirst() + .orElse(null); + if (proxy == null) { + return null; + } + SocketAddress address = proxy.address(); + if (!(address instanceof InetSocketAddress)) { + return null; + } + switch (type) { + case HTTP: + return new ProxyData(proxy); + case SOCKS: + return new ProxyData(proxy); + default: + return null; + } + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java new file mode 100644 index 0000000000..2a5f2ff249 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/IdentityPasswordProvider.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CancellationException; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.internal.transport.sshd.SshdText; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.URIish; + +/** + * A {@link KeyPasswordProvider} based on a {@link CredentialsProvider}. + * + * @since 5.2 + */ +public class IdentityPasswordProvider implements KeyPasswordProvider { + + private CredentialsProvider provider; + + /** + * The number of times to ask successively for a password for a given + * identity resource. + */ + private int attempts = 1; + + /** + * A simple state object for repeated attempts to get a password for a + * resource. + */ + protected static class State { + + private int count = 0; + + private char[] password; + + /** + * Obtains the current count. The initial count is zero. + * + * @return the count + */ + public int getCount() { + return count; + } + + /** + * Increments the current count. Should be called for each new attempt + * to get a password. + * + * @return the incremented count. + */ + public int incCount() { + return ++count; + } + + /** + * Remembers the password. + * + * @param password + * the password + */ + public void setPassword(char[] password) { + if (this.password != null) { + Arrays.fill(this.password, '\000'); + } + if (password != null) { + this.password = password.clone(); + } else { + this.password = null; + } + } + + /** + * Retrieves the password from the current attempt. + * + * @return the password, or {@code null} if none was obtained + */ + public char[] getPassword() { + return password; + } + } + + /** + * Counts per resource key. + */ + private final Map<URIish, State> current = new HashMap<>(); + + /** + * Creates a new {@link IdentityPasswordProvider} to get the passphrase for + * an encrypted identity. + * + * @param provider + * to use + */ + public IdentityPasswordProvider(CredentialsProvider provider) { + this.provider = provider; + } + + @Override + public void setAttempts(int numberOfPasswordPrompts) { + if (numberOfPasswordPrompts <= 0) { + throw new IllegalArgumentException( + "Number of password prompts must be >= 1"); //$NON-NLS-1$ + } + attempts = numberOfPasswordPrompts; + } + + @Override + public int getAttempts() { + return Math.max(1, attempts); + } + + @Override + public char[] getPassphrase(URIish uri, int attempt) throws IOException { + return getPassword(uri, attempt, + current.computeIfAbsent(uri, r -> new State())); + } + + /** + * Retrieves a password to decrypt a private key. + * + * @param uri + * identifying the resource to obtain a password for + * @param attempt + * number of previous attempts to get a passphrase + * @param state + * encapsulating state information about attempts to get the + * password + * @return the password, or {@code null} or the empty string if none + * available. + * @throws IOException + * if an error occurs + */ + protected char[] getPassword(URIish uri, int attempt, @NonNull State state) + throws IOException { + state.setPassword(null); + state.incCount(); + String message = state.count == 1 ? SshdText.get().keyEncryptedMsg + : SshdText.get().keyEncryptedRetry; + char[] pass = getPassword(uri, message); + state.setPassword(pass); + return pass; + } + + private char[] getPassword(URIish uri, String message) { + if (provider == null) { + return null; + } + List<CredentialItem> items = new ArrayList<>(2); + items.add(new CredentialItem.InformationalMessage( + format(message, uri))); + CredentialItem.Password password = new CredentialItem.Password( + SshdText.get().keyEncryptedPrompt); + items.add(password); + try { + provider.get(uri, items); + char[] pass = password.getValue(); + if (pass == null) { + throw new CancellationException( + SshdText.get().authenticationCanceled); + } + return pass.clone(); + } finally { + password.clear(); + } + } + + /** + * Invoked to inform the password provider about the decoding result. + * + * @param uri + * identifying the key resource the key was attempted to be + * loaded from + * @param state + * associated with this key + * @param password + * the password that was attempted + * @param err + * the attempt result - {@code null} for success + * @return how to proceed in case of error + * @throws IOException + * @throws GeneralSecurityException + */ + protected boolean keyLoaded(URIish uri, + State state, char[] password, Exception err) + throws IOException, GeneralSecurityException { + if (err == null) { + return false; // Success, don't retry + } else if (err instanceof GeneralSecurityException) { + throw new InvalidKeyException( + format(SshdText.get().identityFileCannotDecrypt, uri), err); + } else { + // Unencrypted key (state == null && password == null), or exception + // before having asked for the password (state != null && password + // == null; might also be a user cancellation), or number of + // attempts exhausted. + if (state == null || password == null + || state.getCount() >= attempts) { + return false; + } + return true; + } + } + + @Override + public boolean keyLoaded(URIish uri, int attempt, Exception error) + throws IOException, GeneralSecurityException { + State state = null; + boolean retry = false; + try { + state = current.get(uri); + retry = keyLoaded(uri, state, + state == null ? null : state.getPassword(), error); + } finally { + if (state != null) { + state.setPassword(null); + } + if (!retry) { + current.remove(uri); + } + } + return retry; + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/JGitKeyCache.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/JGitKeyCache.java new file mode 100644 index 0000000000..52325c6780 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/JGitKeyCache.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +import javax.security.auth.DestroyFailedException; + +/** + * A simple {@link KeyCache}. JGit uses one such cache in its + * {@link SshdSessionFactory} to avoid loading keys multiple times. + * + * @since 5.2 + */ +public class JGitKeyCache implements KeyCache { + + private AtomicReference<Map<Path, KeyPair>> cache = new AtomicReference<>( + new ConcurrentHashMap<>()); + + @Override + public KeyPair get(Path path, + Function<? super Path, ? extends KeyPair> loader) { + return cache.get().computeIfAbsent(path, loader); + } + + @Override + public void close() { + Map<Path, KeyPair> map = cache.getAndSet(null); + if (map == null) { + return; + } + for (KeyPair k : map.values()) { + PrivateKey p = k.getPrivate(); + try { + p.destroy(); + } catch (DestroyFailedException e) { + // Ignore here. We did our best. + } + } + map.clear(); + } +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyCache.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyCache.java new file mode 100644 index 0000000000..d8c1d30426 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyCache.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import java.nio.file.Path; +import java.security.KeyPair; +import java.util.function.Function; + +/** + * A cache for {@link KeyPair}s. + * + * @since 5.2 + */ +public interface KeyCache { + + /** + * Obtains a {@link KeyPair} from the cache. Implementations must be + * thread-safe. + * + * @param path + * of the key + * @param loader + * to load the key if it isn't present in the cache yet + * @return the {@link KeyPair}, or {@code null} if not present and could not + * be loaded + */ + KeyPair get(Path path, Function<? super Path, ? extends KeyPair> loader); + + /** + * Removes all {@link KeyPair} from this cache and destroys their private + * keys. This cache instance must not be used anymore thereafter. + */ + void close(); +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java new file mode 100644 index 0000000000..0f315a4545 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProvider.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +import org.eclipse.jgit.transport.URIish; + +/** + * A {@code KeyPasswordProvider} provides passwords for encrypted private keys. + * + * @since 5.2 + */ +public interface KeyPasswordProvider { + + /** + * Obtains a passphrase to use to decrypt an ecrypted private key. Returning + * {@code null} or an empty array will skip this key. To cancel completely, + * the operation should raise + * {@link java.util.concurrent.CancellationException}. + * + * @param uri + * identifying the key resource that is being attempted to be + * loaded + * @param attempt + * the number of previous attempts to get a passphrase; >= 0 + * @return the passphrase + * @throws IOException + * if no password can be obtained + */ + char[] getPassphrase(URIish uri, int attempt) throws IOException; + + /** + * Define the maximum number of attempts to get a passphrase that should be + * attempted for one identity resource through this provider. + * + * @param maxNumberOfAttempts + * number of times to ask for a passphrase; + * {@link IllegalArgumentException} may be thrown if <= 0 + */ + void setAttempts(int maxNumberOfAttempts); + + /** + * Gets the maximum number of attempts to get a passphrase that should be + * attempted for one identity resource through this provider. The default + * return 1. + * + * @return the number of times to ask for a passphrase; should be >= 1. + */ + default int getAttempts() { + return 1; + } + + /** + * Invoked after a key has been loaded. If this raises an exception, the + * original {@code error} is lost unless it is attached to that exception. + * + * @param uri + * identifying the key resource the key was attempted to be + * loaded from + * @param attempt + * the number of times {@link #getPassphrase(URIish, int)} had + * been called; zero indicates that {@code uri} refers to a + * non-encrypted key + * @param error + * {@code null} if the key was loaded successfully; otherwise an + * exception indicating why the key could not be loaded + * @return {@code true} to re-try again; {@code false} to re-raise the + * {@code error} exception; Ignored if the key was loaded + * successfully, i.e., if {@code error == null}. + * @throws IOException + * @throws GeneralSecurityException + */ + boolean keyLoaded(URIish uri, int attempt, Exception error) + throws IOException, GeneralSecurityException; +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyData.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyData.java new file mode 100644 index 0000000000..39b1e02aec --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyData.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.util.Arrays; + +import org.eclipse.jgit.annotations.NonNull; + +/** + * A DTO encapsulating the data needed to connect through a proxy server. + * + * @since 5.2 + */ +public class ProxyData { + + private final @NonNull Proxy proxy; + + private final String proxyUser; + + private final char[] proxyPassword; + + /** + * Creates a new {@link ProxyData} instance without user name or password. + * + * @param proxy + * to connect to; must not be {@link java.net.Proxy.Type#DIRECT} + * and must have an {@link InetSocketAddress}. + */ + public ProxyData(@NonNull Proxy proxy) { + this(proxy, null, null); + } + + /** + * Creates a new {@link ProxyData} instance. + * + * @param proxy + * to connect to; must not be {@link java.net.Proxy.Type#DIRECT} + * and must have an {@link InetSocketAddress}. + * @param proxyUser + * to use for log-in to the proxy, may be {@code null} + * @param proxyPassword + * to use for log-in to the proxy, may be {@code null} + */ + public ProxyData(@NonNull Proxy proxy, String proxyUser, + char[] proxyPassword) { + this.proxy = proxy; + if (!(proxy.address() instanceof InetSocketAddress)) { + // Internal error not translated + throw new IllegalArgumentException( + "Proxy does not have an InetSocketAddress"); //$NON-NLS-1$ + } + this.proxyUser = proxyUser; + this.proxyPassword = proxyPassword == null ? null + : proxyPassword.clone(); + } + + /** + * Obtains the remote {@link InetSocketAddress} of the proxy to connect to. + * + * @return the remote address of the proxy + */ + @NonNull + public Proxy getProxy() { + return proxy; + } + + /** + * Obtains the user to log in at the proxy with. + * + * @return the user name, or {@code null} if none + */ + public String getUser() { + return proxyUser; + } + + /** + * Obtains a copy of the internally stored password. + * + * @return the password or {@code null} if none + */ + public char[] getPassword() { + return proxyPassword == null ? null : proxyPassword.clone(); + } + + /** + * Clears the stored password, if any. + */ + public void clearPassword() { + if (proxyPassword != null) { + Arrays.fill(proxyPassword, '\000'); + } + } + +}
\ No newline at end of file diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyDataFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyDataFactory.java new file mode 100644 index 0000000000..334fff4f64 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/ProxyDataFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import java.net.InetSocketAddress; + +/** + * Interface for obtaining {@link ProxyData} to connect through some proxy. + * + * @since 5.2 + */ +public interface ProxyDataFactory { + + /** + * Get the {@link ProxyData} to connect to a proxy. It should return a + * <em>new</em> {@link ProxyData} instance every time; if the returned + * {@link ProxyData} contains a password, the {@link SshdSession} will clear + * it once it is no longer needed. + * + * @param remoteAddress + * to connect to + * @return the {@link ProxyData} or {@code null} if a direct connection is + * to be made + */ + ProxyData get(InetSocketAddress remoteAddress); +} diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SessionCloseListener.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SessionCloseListener.java new file mode 100644 index 0000000000..31fc61f82c --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SessionCloseListener.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +/** + * A {@code SessionCloseListener} is invoked when a {@link SshdSession} is + * closed. + * + * @since 5.2 + */ +@FunctionalInterface +public interface SessionCloseListener { + + /** + * Invoked when a {@link SshdSession} has been closed. + * + * @param session + * that was closed. + */ + void sessionClosed(SshdSession session); +} 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 new file mode 100644 index 0000000000..7d0e686a28 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSession.java @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import static java.text.MessageFormat.format; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.channel.ChannelExec; +import org.apache.sshd.client.channel.ClientChannelEvent; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.client.subsystem.sftp.SftpClient; +import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle; +import org.apache.sshd.client.subsystem.sftp.SftpClient.CopyMode; +import org.apache.sshd.client.subsystem.sftp.SftpClientFactory; +import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.session.SessionListener; +import org.apache.sshd.common.subsystem.sftp.SftpException; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.internal.transport.sshd.SshdText; +import org.eclipse.jgit.transport.FtpChannel; +import org.eclipse.jgit.transport.RemoteSession; +import org.eclipse.jgit.transport.URIish; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An implementation of {@link RemoteSession}Â based on Apache MINA sshd. + * + * @since 5.2 + */ +public class SshdSession implements RemoteSession { + + private static final Logger LOG = LoggerFactory + .getLogger(SshdSession.class); + + private final CopyOnWriteArrayList<SessionCloseListener> listeners = new CopyOnWriteArrayList<>(); + + private final URIish uri; + + private SshClient client; + + private ClientSession session; + + SshdSession(URIish uri, Supplier<SshClient> clientFactory) { + this.uri = uri; + this.client = clientFactory.get(); + } + + void connect(Duration timeout) throws IOException { + if (!client.isStarted()) { + client.start(); + } + try { + String username = uri.getUser(); + String host = uri.getHost(); + int port = uri.getPort(); + long t = timeout.toMillis(); + if (t <= 0) { + session = client.connect(username, host, port).verify() + .getSession(); + } else { + session = client.connect(username, host, port) + .verify(timeout.toMillis()).getSession(); + } + session.addSessionListener(new SessionListener() { + + @Override + public void sessionClosed(Session s) { + notifyCloseListeners(); + } + }); + // Authentication timeout is by default 2 minutes. + session.auth().verify(session.getAuthTimeout()); + } catch (IOException e) { + disconnect(e); + throw e; + } + } + + /** + * Adds a {@link SessionCloseListener} to this session. Has no effect if the + * given {@code listener} is already registered with this session. + * + * @param listener + * to add + */ + public void addCloseListener(@NonNull SessionCloseListener listener) { + listeners.addIfAbsent(listener); + } + + /** + * Removes the given {@code listener}; has no effect if the listener is not + * currently registered with this session. + * + * @param listener + * to remove + */ + public void removeCloseListener(@NonNull SessionCloseListener listener) { + listeners.remove(listener); + } + + private void notifyCloseListeners() { + for (SessionCloseListener l : listeners) { + try { + l.sessionClosed(this); + } catch (RuntimeException e) { + LOG.warn(SshdText.get().closeListenerFailed, e); + } + } + } + + @Override + public Process exec(String commandName, int timeout) throws IOException { + @SuppressWarnings("resource") + ChannelExec exec = session.createExecChannel(commandName); + long timeoutMillis = TimeUnit.SECONDS.toMillis(timeout); + try { + if (timeout <= 0) { + exec.open().verify(); + } else { + long start = System.nanoTime(); + exec.open().verify(timeoutMillis); + timeoutMillis -= TimeUnit.NANOSECONDS + .toMillis(System.nanoTime() - start); + } + } catch (IOException e) { + exec.close(true); + throw e; + } catch (RuntimeException e) { + exec.close(true); + throw e; + } + if (timeout > 0 && timeoutMillis <= 0) { + // We have used up the whole timeout for opening the channel + exec.close(true); + throw new InterruptedIOException( + format(SshdText.get().sshCommandTimeout, commandName, + Integer.valueOf(timeout))); + } + return new SshdExecProcess(exec, commandName, timeoutMillis); + } + + /** + * Obtain an {@link FtpChannel} to perform SFTP operations in this + * {@link SshdSession}. + */ + @Override + @NonNull + public FtpChannel getFtpChannel() { + return new SshdFtpChannel(); + } + + @Override + public void disconnect() { + disconnect(null); + } + + private void disconnect(Throwable reason) { + try { + if (session != null) { + session.close(); + session = null; + } + } catch (IOException e) { + if (reason != null) { + reason.addSuppressed(e); + } else { + LOG.error(SshdText.get().sessionCloseFailed, e); + } + } finally { + client.stop(); + client = null; + } + } + + private static class SshdExecProcess extends Process { + + private final ChannelExec channel; + + private final long timeoutMillis; + + private final String commandName; + + public SshdExecProcess(ChannelExec channel, String commandName, + long timeoutMillis) { + this.channel = channel; + this.timeoutMillis = timeoutMillis > 0 ? timeoutMillis : -1L; + this.commandName = commandName; + } + + @Override + public OutputStream getOutputStream() { + return channel.getInvertedIn(); + } + + @Override + public InputStream getInputStream() { + return channel.getInvertedOut(); + } + + @Override + public InputStream getErrorStream() { + return channel.getInvertedErr(); + } + + @Override + public int waitFor() throws InterruptedException { + if (waitFor(timeoutMillis, TimeUnit.MILLISECONDS)) { + return exitValue(); + } + return -1; + } + + @Override + public boolean waitFor(long timeout, TimeUnit unit) + throws InterruptedException { + long millis = timeout >= 0 ? unit.toMillis(timeout) : -1L; + return channel + .waitFor(EnumSet.of(ClientChannelEvent.CLOSED), millis) + .contains(ClientChannelEvent.CLOSED); + } + + @Override + public int exitValue() { + Integer exitCode = channel.getExitStatus(); + if (exitCode == null) { + throw new IllegalThreadStateException( + format(SshdText.get().sshProcessStillRunning, + commandName)); + } + return exitCode.intValue(); + } + + @Override + public void destroy() { + if (channel.isOpen()) { + channel.close(true); + } + } + } + + /** + * Helper interface like {@link Supplier}, but possibly raising an + * {@link IOException}. + * + * @param <T> + * return type + */ + @FunctionalInterface + private interface FtpOperation<T> { + + T call() throws IOException; + + } + + private class SshdFtpChannel implements FtpChannel { + + private SftpClient ftp; + + /** Current working directory. */ + private String cwd = ""; //$NON-NLS-1$ + + @Override + public void connect(int timeout, TimeUnit unit) throws IOException { + if (timeout <= 0) { + session.getProperties().put( + SftpClient.SFTP_CHANNEL_OPEN_TIMEOUT, + Long.valueOf(Long.MAX_VALUE)); + } else { + session.getProperties().put( + SftpClient.SFTP_CHANNEL_OPEN_TIMEOUT, + Long.valueOf(unit.toMillis(timeout))); + } + ftp = SftpClientFactory.instance().createSftpClient(session); + try { + cd(cwd); + } catch (IOException e) { + ftp.close(); + } + } + + @Override + public void disconnect() { + try { + ftp.close(); + } catch (IOException e) { + LOG.error(SshdText.get().ftpCloseFailed, e); + } + } + + @Override + public boolean isConnected() { + return session.isAuthenticated() && ftp.isOpen(); + } + + private String absolute(String path) { + if (path.isEmpty()) { + return cwd; + } + // Note: there is no path injection vulnerability here. If + // path has too many ".." components, we rely on the server + // catching it and returning an error. + if (path.charAt(0) != '/') { + if (cwd.charAt(cwd.length() - 1) == '/') { + return cwd + path; + } else { + return cwd + '/' + path; + } + } + return path; + } + + private <T> T map(FtpOperation<T> op) throws IOException { + try { + return op.call(); + } catch (IOException e) { + if (e instanceof SftpException) { + throw new FtpChannel.FtpException(e.getLocalizedMessage(), + ((SftpException) e).getStatus(), e); + } + throw e; + } + } + + @Override + public void cd(String path) throws IOException { + cwd = map(() -> ftp.canonicalPath(absolute(path))); + if (cwd.isEmpty()) { + cwd += '/'; + } + } + + @Override + public String pwd() throws IOException { + return cwd; + } + + @Override + public Collection<DirEntry> ls(String path) throws IOException { + return map(() -> { + List<DirEntry> result = new ArrayList<>(); + try (CloseableHandle handle = ftp.openDir(absolute(path))) { + AtomicReference<Boolean> atEnd = new AtomicReference<>( + Boolean.FALSE); + while (!atEnd.get().booleanValue()) { + List<SftpClient.DirEntry> chunk = ftp.readDir(handle, + atEnd); + if (chunk == null) { + break; + } + 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(); + } + + }); + } + } + } + return result; + }); + } + + @Override + public void rmdir(String path) throws IOException { + map(() -> { + ftp.rmdir(absolute(path)); + return null; + }); + + } + + @Override + public void mkdir(String path) throws IOException { + map(() -> { + ftp.mkdir(absolute(path)); + return null; + }); + } + + @Override + public InputStream get(String path) throws IOException { + return map(() -> ftp.read(absolute(path))); + } + + @Override + public OutputStream put(String path) throws IOException { + return map(() -> ftp.write(absolute(path))); + } + + @Override + public void rm(String path) throws IOException { + map(() -> { + ftp.remove(absolute(path)); + return null; + }); + } + + @Override + public void rename(String from, String to) throws IOException { + map(() -> { + String src = absolute(from); + String dest = absolute(to); + try { + ftp.rename(src, dest, CopyMode.Atomic, CopyMode.Overwrite); + } catch (UnsupportedOperationException e) { + // Older server cannot do POSIX rename... + if (!src.equals(dest)) { + delete(dest); + ftp.rename(src, dest); + } + } + return null; + }); + } + } +} 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 new file mode 100644 index 0000000000..4ec6f22094 --- /dev/null +++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.sshd; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +import org.apache.sshd.client.ClientBuilder; +import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.auth.UserAuth; +import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory; +import org.apache.sshd.client.config.hosts.HostConfigEntryResolver; +import org.apache.sshd.client.keyverifier.ServerKeyVerifier; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.compression.BuiltinCompressions; +import org.apache.sshd.common.config.keys.FilePasswordProvider; +import org.apache.sshd.common.keyprovider.FileKeyPairProvider; +import org.apache.sshd.common.keyprovider.KeyPairProvider; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.internal.transport.sshd.CachingKeyPairProvider; +import org.eclipse.jgit.internal.transport.sshd.GssApiWithMicAuthFactory; +import org.eclipse.jgit.internal.transport.sshd.JGitPasswordAuthFactory; +import org.eclipse.jgit.internal.transport.sshd.JGitPublicKeyAuthFactory; +import org.eclipse.jgit.internal.transport.sshd.JGitSshClient; +import org.eclipse.jgit.internal.transport.sshd.JGitSshConfig; +import org.eclipse.jgit.internal.transport.sshd.JGitUserInteraction; +import org.eclipse.jgit.internal.transport.sshd.OpenSshServerKeyVerifier; +import org.eclipse.jgit.internal.transport.sshd.PasswordProviderWrapper; +import org.eclipse.jgit.internal.transport.sshd.SshdText; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.SshConstants; +import org.eclipse.jgit.transport.SshSessionFactory; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.util.FS; + +/** + * A {@link SshSessionFactory} that uses Apache MINA sshd. + * + * @since 5.2 + */ +public class SshdSessionFactory extends SshSessionFactory implements Closeable { + + private final AtomicBoolean closing = new AtomicBoolean(); + + private final Set<SshdSession> sessions = new HashSet<>(); + + private final Map<Tuple, HostConfigEntryResolver> defaultHostConfigEntryResolver = new ConcurrentHashMap<>(); + + private final Map<Tuple, ServerKeyVerifier> defaultServerKeyVerifier = new ConcurrentHashMap<>(); + + private final Map<Tuple, FileKeyPairProvider> defaultKeys = new ConcurrentHashMap<>(); + + private final KeyCache keyCache; + + private final ProxyDataFactory proxies; + + private File sshDirectory; + + private File homeDirectory; + + /** + * Creates a new {@link SshdSessionFactory} without key cache and a + * {@link DefaultProxyDataFactory}. + */ + public SshdSessionFactory() { + this(null, new DefaultProxyDataFactory()); + } + + /** + * 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}. + * <p> + * Caching ssh keys in memory for an extended period of time is generally + * considered bad practice, but there may be circumstances where using a + * {@link KeyCache} is still the right choice, for instance to avoid that a + * user gets prompted several times for the same password for the same key. + * In general, however, it is preferable <em>not</em> to use a key cache but + * 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. + * </p> + * <p> + * 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}. + * </p> + * + * @param keyCache + * {@link KeyCache} to use for caching ssh keys, or {@code null} + * to not use a key cache + * @param proxies + * {@link ProxyDataFactory} to use, or {@code null} to not use a + * proxy database (in which case connections through proxies will + * not be possible) + */ + public SshdSessionFactory(KeyCache keyCache, ProxyDataFactory proxies) { + super(); + this.keyCache = keyCache; + this.proxies = proxies; + } + + /** A simple general map key. */ + private static final class Tuple { + private Object[] objects; + + public Tuple(Object... objects) { + this.objects = objects; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj != null && obj.getClass() == Tuple.class) { + Tuple other = (Tuple) obj; + return Arrays.equals(objects, other.objects); + } + return false; + } + + @Override + public int hashCode() { + return Arrays.hashCode(objects); + } + } + + // We can't really use a single client. Clients need to be stopped + // properly, and we don't really know when to do that. Instead we use + // a dedicated SshClient instance per session. We need a bit of caching to + // avoid re-loading the ssh config and keys repeatedly. + + @Override + public SshdSession getSession(URIish uri, + CredentialsProvider credentialsProvider, FS fs, int tms) + throws TransportException { + SshdSession session = null; + try { + session = new SshdSession(uri, () -> { + File home = getHomeDirectory(); + if (home == null) { + // Always use the detected filesystem for the user home! + // It makes no sense to have different "user home" + // directories depending on what file system a repository + // is. + home = FS.DETECTED.userHome(); + } + File sshDir = getSshDirectory(); + if (sshDir == null) { + sshDir = new File(home, SshConstants.SSH_DIR); + } + HostConfigEntryResolver configFile = getHostConfigEntryResolver( + home, sshDir); + KeyPairProvider defaultKeysProvider = getDefaultKeysProvider( + sshDir); + KeyPasswordProvider passphrases = createKeyPasswordProvider( + credentialsProvider); + SshClient client = ClientBuilder.builder() + .factory(JGitSshClient::new) + .filePasswordProvider( + createFilePasswordProvider(passphrases)) + .hostConfigEntryResolver(configFile) + .serverKeyVerifier(getServerKeyVerifier(home, sshDir)) + .compressionFactories( + new ArrayList<>(BuiltinCompressions.VALUES)) + .build(); + client.setUserInteraction( + new JGitUserInteraction(credentialsProvider)); + client.setUserAuthFactories(getUserAuthFactories()); + client.setKeyPairProvider(defaultKeysProvider); + // JGit-specific things: + JGitSshClient jgitClient = (JGitSshClient) client; + jgitClient.setKeyCache(getKeyCache()); + jgitClient.setCredentialsProvider(credentialsProvider); + jgitClient.setProxyDatabase(proxies); + String defaultAuths = getDefaultPreferredAuthentications(); + if (defaultAuths != null) { + jgitClient.setAttribute( + JGitSshClient.PREFERRED_AUTHENTICATIONS, + defaultAuths); + } + // Other things? + return client; + }); + session.addCloseListener(s -> unregister(s)); + register(session); + session.connect(Duration.ofMillis(tms)); + return session; + } catch (Exception e) { + unregister(session); + throw new TransportException(uri, e.getMessage(), e); + } + } + + @Override + public void close() { + closing.set(true); + boolean cleanKeys = false; + synchronized (this) { + cleanKeys = sessions.isEmpty(); + } + if (cleanKeys) { + KeyCache cache = getKeyCache(); + if (cache != null) { + cache.close(); + } + } + } + + private void register(SshdSession newSession) throws IOException { + if (newSession == null) { + return; + } + if (closing.get()) { + throw new IOException(SshdText.get().sshClosingDown); + } + synchronized (this) { + sessions.add(newSession); + } + } + + private void unregister(SshdSession oldSession) { + boolean cleanKeys = false; + synchronized (this) { + sessions.remove(oldSession); + cleanKeys = closing.get() && sessions.isEmpty(); + } + if (cleanKeys) { + KeyCache cache = getKeyCache(); + if (cache != null) { + cache.close(); + } + } + } + + /** + * Set a global directory to use as the user's home directory + * + * @param homeDir + * to use + */ + public void setHomeDirectory(@NonNull File homeDir) { + if (homeDir.isAbsolute()) { + homeDirectory = homeDir; + } else { + homeDirectory = homeDir.getAbsoluteFile(); + } + } + + /** + * Retrieves the global user home directory + * + * @return the directory, or {@code null} if not set + */ + public File getHomeDirectory() { + return homeDirectory; + } + + /** + * Set a global directory to use as the .ssh directory + * + * @param sshDir + * to use + */ + public void setSshDirectory(@NonNull File sshDir) { + if (sshDir.isAbsolute()) { + sshDirectory = sshDir; + } else { + sshDirectory = sshDir.getAbsoluteFile(); + } + } + + /** + * Retrieves the global .ssh directory + * + * @return the directory, or {@code null} if not set + */ + public File getSshDirectory() { + return sshDirectory; + } + + /** + * Obtain a {@link HostConfigEntryResolver} to read the ssh config file and + * to determine host entries for connections. + * + * @param homeDir + * home directory to use for ~ replacement + * @param sshDir + * to use for looking for the config file + * @return the resolver + */ + @NonNull + private HostConfigEntryResolver getHostConfigEntryResolver( + @NonNull File homeDir, @NonNull File sshDir) { + return defaultHostConfigEntryResolver.computeIfAbsent( + new Tuple(homeDir, sshDir), + t -> new JGitSshConfig(homeDir, + new File(sshDir, SshConstants.CONFIG), + getLocalUserName())); + } + + /** + * Obtain a {@link ServerKeyVerifier} to read known_hosts files and to + * verify server host keys. The default implementation returns a + * {@link ServerKeyVerifier} that recognizes the two openssh standard files + * {@code ~/.ssh/known_hosts} and {@code ~/.ssh/known_hosts2} as well as any + * files configured via the {@code UserKnownHostsFile} option in the ssh + * config file. + * + * @param homeDir + * home directory to use for ~ replacement + * @param sshDir + * representing ~/.ssh/ + * @return the resolver + */ + @NonNull + private ServerKeyVerifier getServerKeyVerifier(@NonNull File homeDir, + @NonNull File sshDir) { + return defaultServerKeyVerifier.computeIfAbsent( + new Tuple(homeDir, sshDir), + t -> new OpenSshServerKeyVerifier(true, + getDefaultKnownHostsFiles(sshDir))); + } + + /** + * Gets the list of default user known hosts files. The default returns + * ~/.ssh/known_hosts and ~/.ssh/known_hosts2. The ssh config + * {@code UserKnownHostsFile} overrides this default. + * + * @param sshDir + * @return the possibly empty list of default known host file paths. + */ + @NonNull + protected List<Path> getDefaultKnownHostsFiles(@NonNull File sshDir) { + return Arrays.asList(sshDir.toPath().resolve(SshConstants.KNOWN_HOSTS), + sshDir.toPath().resolve(SshConstants.KNOWN_HOSTS + '2')); + } + + /** + * Determines a {@link KeyPairProvider} to use to load the default keys. + * + * @param sshDir + * to look in for keys + * @return the {@link KeyPairProvider} + */ + @NonNull + private KeyPairProvider getDefaultKeysProvider(@NonNull File sshDir) { + return defaultKeys.computeIfAbsent(new Tuple(sshDir), + t -> new CachingKeyPairProvider(getDefaultIdentities(sshDir), + getKeyCache())); + } + + /** + * Gets a list of default identities, i.e., private key files that shall + * always be tried for public key authentication. Typically those are + * ~/.ssh/id_dsa, ~/.ssh/id_rsa, and so on. The default implementation + * returns the files defined in {@link SshConstants#DEFAULT_IDENTITIES}. + * + * @param sshDir + * the directory that represents ~/.ssh/ + * @return a possibly empty list of paths containing default identities + * (private keys) + */ + @NonNull + protected List<Path> getDefaultIdentities(@NonNull File sshDir) { + return Arrays + .asList(SshConstants.DEFAULT_IDENTITIES).stream() + .map(s -> new File(sshDir, s).toPath()).filter(Files::exists) + .collect(Collectors.toList()); + } + + /** + * Obtains the {@link KeyCache} to use to cache loaded keys. + * + * @return the {@link KeyCache}, or {@code null} if none. + */ + protected final KeyCache getKeyCache() { + return keyCache; + } + + /** + * Creates a {@link KeyPasswordProvider} for a new session. + * + * @param provider + * the {@link CredentialsProvider} to delegate to for user + * interactions + * @return a new {@link KeyPasswordProvider} + */ + @NonNull + protected KeyPasswordProvider createKeyPasswordProvider( + CredentialsProvider provider) { + return new IdentityPasswordProvider(provider); + } + + /** + * Creates a {@link FilePasswordProvider} for a new session. + * + * @param provider + * the {@link KeyPasswordProvider} to delegate to + * @return a new {@link FilePasswordProvider} + */ + @NonNull + private FilePasswordProvider createFilePasswordProvider( + KeyPasswordProvider provider) { + return new PasswordProviderWrapper(provider); + } + + /** + * Gets the user authentication mechanisms (or rather, factories for them). + * By default this returns gssapi-with-mic, public-key, password, and + * keyboard-interactive, in that order. The order is only significant if the + * ssh config does <em>not</em> set {@code PreferredAuthentications}; if it + * is set, the order defined there will be taken. + * + * @return the non-empty list of factories. + */ + @NonNull + private List<NamedFactory<UserAuth>> getUserAuthFactories() { + // About the order of password and keyboard-interactive, see upstream + // bug https://issues.apache.org/jira/projects/SSHD/issues/SSHD-866 . + // Password auth doesn't have this problem. + return Collections.unmodifiableList( + Arrays.asList(GssApiWithMicAuthFactory.INSTANCE, + JGitPublicKeyAuthFactory.INSTANCE, + JGitPasswordAuthFactory.INSTANCE, + UserAuthKeyboardInteractiveFactory.INSTANCE)); + } + + /** + * Gets the list of default preferred authentication mechanisms. If + * {@code null} is returned the openssh default list will be in effect. If + * the ssh config defines {@code PreferredAuthentications} the value from + * the ssh config takes precedence. + * + * @return a comma-separated list of algorithm names, or {@code null} if + * none + */ + protected String getDefaultPreferredAuthentications() { + return null; + } +} diff --git a/org.eclipse.jgit.test/.classpath b/org.eclipse.jgit.test/.classpath index 84b5052a6c..7cc18cca34 100644 --- a/org.eclipse.jgit.test/.classpath +++ b/org.eclipse.jgit.test/.classpath @@ -1,9 +1,22 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry excluding="**/*.idx|**/*.pack" kind="src" path="tst"/> + <classpathentry excluding="**/*.idx|**/*.pack" kind="src" path="tst" output="bin-tst"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> <classpathentry kind="src" path="src"/> - <classpathentry kind="src" path="tst-rsrc"/> - <classpathentry kind="src" path="exttst"/> + <classpathentry kind="src" path="tst-rsrc" output="bin-tst"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="src" path="exttst" output="bin-tst"> + <attributes> + <attribute name="test" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="src" path="resources"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="output" path="bin"/> diff --git a/org.eclipse.jgit.test/.gitignore b/org.eclipse.jgit.test/.gitignore index 934e0e06ff..561cf84a1b 100644 --- a/org.eclipse.jgit.test/.gitignore +++ b/org.eclipse.jgit.test/.gitignore @@ -1,2 +1,3 @@ /bin /target +/bin-tst/ diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs index 794592dee1..2ca78ff2d0 100644 --- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.test/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.test/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD index bbda838f00..0b18e5ef7d 100644 --- a/org.eclipse.jgit.test/BUILD +++ b/org.eclipse.jgit.test/BUILD @@ -6,7 +6,10 @@ load( PKG = "tst/org/eclipse/jgit/" -HELPERS = glob(["src/**/*.java"]) + [PKG + c for c in [ +HELPERS = glob( + ["src/**/*.java"], + exclude = ["src/org/eclipse/jgit/transport/ssh/*.java"], +) + [PKG + c for c in [ "api/AbstractRemoteCommandTest.java", "diff/AbstractDiffTestCase.java", "internal/storage/file/GcTestCase.java", @@ -18,6 +21,7 @@ HELPERS = glob(["src/**/*.java"]) + [PKG + c for c in [ "nls/NonTranslatedBundle.java", "revwalk/RevQueueTestCase.java", "revwalk/RevWalkTestCase.java", + "transport/ObjectIdMatcher.java", "transport/SpiTransport.java", "treewalk/FileTreeIteratorWithTimeControl.java", "treewalk/filter/AlwaysCloneTreeFilter.java", @@ -31,6 +35,8 @@ DATA = [ PKG + "lib/sorttest.gitindex.dat", ] +RESOURCES = glob(["resources/**"]) + tests(glob( ["tst/**/*.java"], exclude = HELPERS + DATA, @@ -42,9 +48,31 @@ java_library( srcs = HELPERS, resources = DATA, deps = [ + "//lib:jsch", + "//lib:junit", + "//lib:mockito", + "//org.eclipse.jgit:jgit", + "//org.eclipse.jgit.junit:junit", + ], +) + +java_library( + name = "sshd-helpers", + testonly = 1, + srcs = glob(["src/org/eclipse/jgit/transport/ssh/*.java"]), + resource_strip_prefix = "org.eclipse.jgit.test/resources", + resources = RESOURCES, + visibility = [ + "//org.eclipse.jgit.ssh.apache.test:__pkg__", + ], + deps = [ + "//lib:jsch", "//lib:junit", + "//lib:sshd-core", + "//lib:sshd-sftp", "//org.eclipse.jgit:jgit", "//org.eclipse.jgit.junit:junit", + "//org.eclipse.jgit.junit.ssh:junit-ssh", ], ) diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index 69ea99b4f1..55ccecf469 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -3,63 +3,73 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.test Bundle-SymbolicName: org.eclipse.jgit.test -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", com.jcraft.jsch;version="[0.1.54,0.2.0)", - org.eclipse.jgit.api;version="[5.1.9,5.2.0)", - org.eclipse.jgit.api.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.attributes;version="[5.1.9,5.2.0)", - org.eclipse.jgit.awtui;version="[5.1.9,5.2.0)", - org.eclipse.jgit.blame;version="[5.1.9,5.2.0)", - org.eclipse.jgit.diff;version="[5.1.9,5.2.0)", - org.eclipse.jgit.dircache;version="[5.1.9,5.2.0)", - org.eclipse.jgit.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.events;version="[5.1.9,5.2.0)", - org.eclipse.jgit.fnmatch;version="[5.1.9,5.2.0)", - org.eclipse.jgit.gitrepo;version="[5.1.9,5.2.0)", - org.eclipse.jgit.hooks;version="[5.1.9,5.2.0)", - org.eclipse.jgit.ignore;version="[5.1.9,5.2.0)", - org.eclipse.jgit.ignore.internal;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.fsck;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.io;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.pack;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.reftable;version="[5.1.9,5.2.0)", - org.eclipse.jgit.internal.storage.reftree;version="[5.1.9,5.2.0)", - org.eclipse.jgit.junit;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lfs;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.merge;version="[5.1.9,5.2.0)", - org.eclipse.jgit.nls;version="[5.1.9,5.2.0)", - org.eclipse.jgit.notes;version="[5.1.9,5.2.0)", - org.eclipse.jgit.patch;version="[5.1.9,5.2.0)", - org.eclipse.jgit.pgm;version="[5.1.9,5.2.0)", - org.eclipse.jgit.pgm.internal;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revplot;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk.filter;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.file;version="[5.1.9,5.2.0)", - org.eclipse.jgit.storage.pack;version="[5.1.9,5.2.0)", - org.eclipse.jgit.submodule;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.http;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport.resolver;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.treewalk.filter;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util.io;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util.sha1;version="[5.1.9,5.2.0)", + net.bytebuddy.dynamic.loading;version="[1.7.0,2.0.0)", + org.eclipse.jgit.annotations;version="[5.2.3,5.3.0)", + org.eclipse.jgit.api;version="[5.2.3,5.3.0)", + org.eclipse.jgit.api.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.attributes;version="[5.2.3,5.3.0)", + org.eclipse.jgit.awtui;version="[5.2.3,5.3.0)", + org.eclipse.jgit.blame;version="[5.2.3,5.3.0)", + org.eclipse.jgit.diff;version="[5.2.3,5.3.0)", + org.eclipse.jgit.dircache;version="[5.2.3,5.3.0)", + org.eclipse.jgit.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.events;version="[5.2.3,5.3.0)", + org.eclipse.jgit.fnmatch;version="[5.2.3,5.3.0)", + org.eclipse.jgit.gitrepo;version="[5.2.3,5.3.0)", + org.eclipse.jgit.hooks;version="[5.2.3,5.3.0)", + org.eclipse.jgit.ignore;version="[5.2.3,5.3.0)", + org.eclipse.jgit.ignore.internal;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.fsck;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.dfs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.io;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.pack;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.reftable;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.storage.reftree;version="[5.2.3,5.3.0)", + org.eclipse.jgit.internal.transport.parser;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit;version="[5.2.3,5.3.0)", + org.eclipse.jgit.junit.ssh;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lfs;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.merge;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.notes;version="[5.2.3,5.3.0)", + org.eclipse.jgit.patch;version="[5.2.3,5.3.0)", + org.eclipse.jgit.pgm;version="[5.2.3,5.3.0)", + org.eclipse.jgit.pgm.internal;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revplot;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk.filter;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.file;version="[5.2.3,5.3.0)", + org.eclipse.jgit.storage.pack;version="[5.2.3,5.3.0)", + org.eclipse.jgit.submodule;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.http;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport.resolver;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.treewalk.filter;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util.io;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util.sha1;version="[5.2.3,5.3.0)", org.junit;version="[4.12,5.0.0)", org.junit.experimental.theories;version="[4.12,5.0.0)", org.junit.rules;version="[4.12,5.0.0)", org.junit.runner;version="[4.12,5.0.0)", org.junit.runners;version="[4.12,5.0.0)", + org.mockito;version="[2.13.0,3.0.0)", + org.mockito.invocation;version="[2.13.0,3.0.0)", + org.mockito.junit;version="[2.13.0,3.0.0)", + org.mockito.stubbing;version="[2.13.0,3.0.0)", + org.objenesis;version="[2.6.0,3.0.0)", org.slf4j;version="[1.7.0,2.0.0)" Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)", org.hamcrest.library;bundle-version="[1.1.0,2.0.0)" +Export-Package: org.eclipse.jgit.transport.ssh;version="5.2.3";x-friends:="org.eclipse.jgit.ssh.apache.test" diff --git a/org.eclipse.jgit.test/build.properties b/org.eclipse.jgit.test/build.properties index e7b3b9978e..78c8f55f3a 100644 --- a/org.eclipse.jgit.test/build.properties +++ b/org.eclipse.jgit.test/build.properties @@ -1,9 +1,12 @@ source.. = tst/,\ tst-rsrc/,\ exttst/,\ - src/ + src/,\ + resources/ bin.includes = META-INF/,\ .,\ - plugin.properties + plugin.properties,\ + bin-tst/,\ + bin/ additional.bundles = org.apache.log4j,\ org.slf4j.impl.log4j12 diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml index 7a4d58017d..6b19eb368c 100644 --- a/org.eclipse.jgit.test/pom.xml +++ b/org.eclipse.jgit.test/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.test</artifactId> @@ -73,12 +73,10 @@ </dependency> <!-- Optional security provider for encryption tests. --> - <!-- See https://dev.eclipse.org/ipzilla/show_bug.cgi?id=9554 --> - <!-- See https://bugs.eclipse.org/bugs/show_bug.cgi?id=467064 --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> - <version>1.52</version> + <version>1.59</version> <scope>test</scope> </dependency> @@ -90,6 +88,12 @@ </dependency> <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>2.13.0</version> + </dependency> + + <dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit</artifactId> <version>${project.version}</version> @@ -103,6 +107,12 @@ <dependency> <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.junit.ssh</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit.ui</artifactId> <version>${project.version}</version> </dependency> @@ -142,6 +152,18 @@ </testResource> </testResources> + <resources> + <resource> + <directory>.</directory> + <includes> + <include>plugin.properties</include> + </includes> + </resource> + <resource> + <directory>resources/</directory> + </resource> + </resources> + <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> @@ -157,7 +179,7 @@ <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>@{argLine} -Xmx768m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine> + <argLine>-Xmx1024m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine> <includes> <include>**/*Test.java</include> <include>**/*Tests.java</include> diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa new file mode 100644 index 0000000000..f097516df2 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQC+mJEX/XBloWhNM+BEuoh5z+EAuZfVyJ8cHNKlQmC1sWrENKGh +P8ZhzWeHW0A7JnvTQgMqW6yD4mDzCpbR1wEz5KeXAphEjCGPnRik7Q4RjpZTd6Nq +nNF/CYYGYuwR7ZGUPITTpKJWgX6NkEk+a4tvTWP7xfxOq5iKIspFEhEOlQIVAIBi +TdAR8M2twrXZdspBjdJprjDXAoGAOrRYdXRHhpsOewIi9GQah0lde7AVrmZawK9Z +BwhDUagL58gS8PvcsNNVhS2dKEX45pqZmgayt2UEE/5bke3+CdZtStDsezBYMu8P +I/0qjOULhl7xLJT5ayCIN2ZuvcH8vtqH89fXgZkIz0c68AzY1ZFjJPc+TdE0puI9 +3mMVRaoCgYEAslyMZiOwYA3oiFMQTJEphKdgejWsjqQ9LoKppfZ3d4Jj1V3tgI1s +/wHfoneUUrUwM+sMHZKXbBDLWWQUOSIxDYcXKDkbZ1FlmhvJR+45D2LyLKjEnjVD +lQCwYly4P26zXqciZS7k3H/DjiHtAPUeoHm9IYb1A03K8Bd/xW0guMcCFFeUfQeX +3mFPCfKJ5uXMjkPUqIo/ +-----END DSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa.pub new file mode 100644 index 0000000000..676685332e --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAL6YkRf9cGWhaE0z4ES6iHnP4QC5l9XInxwc0qVCYLWxasQ0oaE/xmHNZ4dbQDsme9NCAypbrIPiYPMKltHXATPkp5cCmESMIY+dGKTtDhGOllN3o2qc0X8JhgZi7BHtkZQ8hNOkolaBfo2QST5ri29NY/vF/E6rmIoiykUSEQ6VAAAAFQCAYk3QEfDNrcK12XbKQY3Saa4w1wAAAIA6tFh1dEeGmw57AiL0ZBqHSV17sBWuZlrAr1kHCENRqAvnyBLw+9yw01WFLZ0oRfjmmpmaBrK3ZQQT/luR7f4J1m1K0Ox7MFgy7w8j/SqM5QuGXvEslPlrIIg3Zm69wfy+2ofz19eBmQjPRzrwDNjVkWMk9z5N0TSm4j3eYxVFqgAAAIEAslyMZiOwYA3oiFMQTJEphKdgejWsjqQ9LoKppfZ3d4Jj1V3tgI1s/wHfoneUUrUwM+sMHZKXbBDLWWQUOSIxDYcXKDkbZ1FlmhvJR+45D2LyLKjEnjVDlQCwYly4P26zXqciZS7k3H/DjiHtAPUeoHm9IYb1A03K8Bd/xW0guMc= testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass new file mode 100644 index 0000000000..375d38fe43 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass @@ -0,0 +1,15 @@ +-----BEGIN DSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,EBB6ACAA4F1FC558865344E3C2B91A5F + +CWMAq20YBO8ueHnmQ7IaKa7ISEvNbwbzqoBIxor6TZYSU3JvlIf5AL2UvGpMJDk1 +fyROdCjdVAeWKQC0peU54D3YnD3am4gZlrclPMjMRnjBmqO+vnU7bTudIt/8y6vg +gmHZki0/aceQ6QvGwGrxBezBPaK4Bc926lePujHHE/PbtuQgkBw7rhIBGKVuy0qN +sFbC4AGnYl5tudy5RLvCcpQvpDCjnYAfGQVimRYSOsaOwTEBvsnQFUH1pqQAYLC4 +Capo1yj6Q0smzwsGoyFSvmPkyzLbMTT42m+M48gc5nuaOkbU5absqOb8cQgRVmWB +W1HnpufqGtyF6vBK+qlzg157bhQDYMwZuubX+IrTRL67djBiSIpiRDZduJavT3zq +iSrRGSnjnkhp4NxtJJjprDQe4VAZEccN5GWPjClbogjpsG+fmTJiNDMI88L11DrV +Vjeaxsql31iur/xGwvmBYd+/V+Nu4v7kA4XViO/3ZIpqi8qvQ3si5hbALSX0OPnm +9q0eMp9qfmzPvbmysq2BEenBaZDwEWYTYpcF23pjwc1EvmfP8EAYT+xH95ZhxVmc +Sujq0VyGeIhy7+gRHZo2Fg== +-----END DSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass.pub new file mode 100644 index 0000000000..676685332e --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_dsa_testpass.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAL6YkRf9cGWhaE0z4ES6iHnP4QC5l9XInxwc0qVCYLWxasQ0oaE/xmHNZ4dbQDsme9NCAypbrIPiYPMKltHXATPkp5cCmESMIY+dGKTtDhGOllN3o2qc0X8JhgZi7BHtkZQ8hNOkolaBfo2QST5ri29NY/vF/E6rmIoiykUSEQ6VAAAAFQCAYk3QEfDNrcK12XbKQY3Saa4w1wAAAIA6tFh1dEeGmw57AiL0ZBqHSV17sBWuZlrAr1kHCENRqAvnyBLw+9yw01WFLZ0oRfjmmpmaBrK3ZQQT/luR7f4J1m1K0Ox7MFgy7w8j/SqM5QuGXvEslPlrIIg3Zm69wfy+2ofz19eBmQjPRzrwDNjVkWMk9z5N0TSm4j3eYxVFqgAAAIEAslyMZiOwYA3oiFMQTJEphKdgejWsjqQ9LoKppfZ3d4Jj1V3tgI1s/wHfoneUUrUwM+sMHZKXbBDLWWQUOSIxDYcXKDkbZ1FlmhvJR+45D2LyLKjEnjVDlQCwYly4P26zXqciZS7k3H/DjiHtAPUeoHm9IYb1A03K8Bd/xW0guMc= testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256 new file mode 100644 index 0000000000..8a4c864afa --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256 @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAqXVKoLNr7/wNluxmGZnZmJCD/5h06ptAICRk+8FIjfoAoGCCqGSM49 +AwEHoUQDQgAEoQHTUWwu3nJnCHeSv3YE59UxfuGNjAXLzK0MjDwoXt6/qePjjKAQ +ehHdAIYQHr9zYJu5SA5b86HL5glqjcy+Pg== +-----END EC PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256.pub new file mode 100644 index 0000000000..43540ec0ec --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKEB01FsLt5yZwh3kr92BOfVMX7hjYwFy8ytDIw8KF7ev6nj44ygEHoR3QCGEB6/c2CbuUgOW/Ohy+YJao3Mvj4= testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass new file mode 100644 index 0000000000..b767c8e99a --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,86940587F5C93441B585F469FF31AC06 + +LaIyzOCeBPJA6OkFOFnFfVorYO+Rm1g5QpvqEcFZ+FCuEvhMZN00NMZ5hHKvwQLt +XSK5Se8MUD+e6qFH/ZcoYTixUqYjYJlOkxJzKaXg5nM82wQHa1LqQqcL4IDrJmzv +qJbCLtl6XOfkQQUA6gezqhtiNYWLDZIPfZ0dsaIB/fU= +-----END EC PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass.pub new file mode 100644 index 0000000000..43540ec0ec --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_256_testpass.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKEB01FsLt5yZwh3kr92BOfVMX7hjYwFy8ytDIw8KF7ev6nj44ygEHoR3QCGEB6/c2CbuUgOW/Ohy+YJao3Mvj4= testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384 new file mode 100644 index 0000000000..dc2ac86508 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384 @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDAgAgPcgkPaitxOrphrrLe+am0eUhYi346UUTnb5WZL3164MEjFByd9 +Egv6KwB4hCqgBwYFK4EEACKhZANiAAQhJrJ+vJLbkbd9C1he+4XuxaOyZ1IqYJqz +PZCXcKkIlgy+0I07RAxRUd75GHKc4ViyUnLq5odV25H6FNzHJHO7ifE4H6jrEpA/ +UL6LkfZReYZ4sNmeQI7MBXm2IXQsIZ4= +-----END EC PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384.pub new file mode 100644 index 0000000000..3e813a5e48 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBCEmsn68ktuRt30LWF77he7Fo7JnUipgmrM9kJdwqQiWDL7QjTtEDFFR3vkYcpzhWLJScurmh1XbkfoU3Mckc7uJ8TgfqOsSkD9QvouR9lF5hniw2Z5AjswFebYhdCwhng== testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass new file mode 100644 index 0000000000..06032d049f --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass @@ -0,0 +1,9 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,80B1C4D6D9B45690A07B9886050C63A7 + +WxS7EGs77p1aPZuxXW0G/yTFKAh4M30AaeGQBPjDR/HTAmPJe3irDH56fdmGhY4+ +zBT+6X1VppB+UqB0nJ/qHq7FeA37eJPXJnuskPh2BzLlBaVhmEnzZylEW33gzAuH +XzC/Z2OjdWRjn+rBXM5fwo9IIC0WzTNpBokdeMo8tpnPzGTlsTFeyVgMZJ3wOlCO +4ItX9ddY5P+MrLzWP672IyZZqAQGfLec4YoJ286wpHY= +-----END EC PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass.pub new file mode 100644 index 0000000000..3e813a5e48 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_384_testpass.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBCEmsn68ktuRt30LWF77he7Fo7JnUipgmrM9kJdwqQiWDL7QjTtEDFFR3vkYcpzhWLJScurmh1XbkfoU3Mckc7uJ8TgfqOsSkD9QvouR9lF5hniw2Z5AjswFebYhdCwhng== testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521 new file mode 100644 index 0000000000..c28151e5ab --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521 @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIB4zI/MCFSfJ0wfyLwZPxG1vP2o3fF7fEuOTpK+fxbDHKYz6r4bNv3 +HkPQEVTIAqDl7r5Ebcx0BMeYr9oe69tPZIigBwYFK4EEACOhgYkDgYYABAChltEM +zT8dXwIhQD2iuy7QbaBkhWMhpFaxztvzSQqoTZvBgBsOmSr9frFA93lSQoHD1Bge +wuwBkNGm9lRcw0tEgABqifONkj07Qj2847MKS1iiVu1sHh7Ys3YimyfJc+nZRNi+ +W03nkcdvWd6PP8y/VENoV7+BtIO9txj8Dt5LYOtFgw== +-----END EC PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521.pub new file mode 100644 index 0000000000..9bac1e80f0 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAChltEMzT8dXwIhQD2iuy7QbaBkhWMhpFaxztvzSQqoTZvBgBsOmSr9frFA93lSQoHD1BgewuwBkNGm9lRcw0tEgABqifONkj07Qj2847MKS1iiVu1sHh7Ys3YimyfJc+nZRNi+W03nkcdvWd6PP8y/VENoV7+BtIO9txj8Dt5LYOtFgw== testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass new file mode 100644 index 0000000000..c1c1bbaced --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass @@ -0,0 +1,10 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,7070032284B3C310353B8C352AB2D8CE + +UBgXTwobcLX1VFtQaLNiwwVzdN1+TlmhSRCnU+kv2EpunXxfvyOVS1mZTam9NyhE +O0Mc7REi5hDHp8UYM7MP+wrwK+QM3D2Vm2/Rh0+acd4Gu2XGACJHWXGIyKwNsU0R +ZddusHIi+979sHw3vSUFCvuDwc9YZBoujpzls7NYEWXiAVv6wd1RCtAynkBk/uvc +1F7iHLuRttejBPvrb/a2AxY0pFpCuCVmGjuiS5bfVWBj7xLEplqdU6/95rd9pRwx +e2uRlU0AFiQGNPStfhjgfCWnmf+aX3vAgVqkLMYKYQE= +-----END EC PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass.pub new file mode 100644 index 0000000000..9bac1e80f0 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ecdsa_521_testpass.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAChltEMzT8dXwIhQD2iuy7QbaBkhWMhpFaxztvzSQqoTZvBgBsOmSr9frFA93lSQoHD1BgewuwBkNGm9lRcw0tEgABqifONkj07Qj2847MKS1iiVu1sHh7Ys3YimyfJc+nZRNi+W03nkcdvWd6PP8y/VENoV7+BtIO9txj8Dt5LYOtFgw== testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519 new file mode 100644 index 0000000000..02afa54788 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBIJlrW8XB46iAVY0XqbjYKG8wJ95iILxOb5ONQhFBvPQAAAJC8jORLvIzk +SwAAAAtzc2gtZWQyNTUxOQAAACBIJlrW8XB46iAVY0XqbjYKG8wJ95iILxOb5ONQhFBvPQ +AAAECjklggj+glO2K60Ptg+aXYGBdvXtk9TQnKINhrEIxW9UgmWtbxcHjqIBVjRepuNgob +zAn3mIgvE5vk41CEUG89AAAACHRlc3R1c2VyAQIDBAU= +-----END OPENSSH PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519.pub new file mode 100644 index 0000000000..7857db5602 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgmWtbxcHjqIBVjRepuNgobzAn3mIgvE5vk41CEUG89 testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass new file mode 100644 index 0000000000..7ad4a77056 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABA4hLhtuV +MNBBC+j45F4KFcAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEgmWtbxcHjqIBVj +RepuNgobzAn3mIgvE5vk41CEUG89AAAAkPH343T+NbHb05J/6CHnF9h7C11LJDHe2x9+HC +dNB50fP9M+KJ/cC5cqIeHm8y0fg+wX2WLlJPjNVoSd5MciWCfUWO0k32ciVpoyrGCz5Gh6 +axKVVY42QjdgO0S2QxWClnAuMdkVdl2ke/PcGp4yqTTIruAAB0m3d0jZdKNT1Vziww0rQB ++DOo7xQ9Tx99U+rA== +-----END OPENSSH PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass.pub new file mode 100644 index 0000000000..7857db5602 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_ed25519_testpass.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgmWtbxcHjqIBVjRepuNgobzAn3mIgvE5vk41CEUG89 testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024 new file mode 100644 index 0000000000..0b403674fa --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDGfj0Jmqj+CUb+WdFrlkRV49TJtNzvvMb/nX20zqgGm50cOIYr +MzfFpSQN630pXeAidIgiV/PWAsipntQfSWPRG+RpB/wMKHVUNPJCJkjjRFEa56Yx +gAhgNwF511K13x4p2tEN0r6wsfw1nos9VoO8XDBAu3lellAgBdufyCt8vwIDAQAB +AoGBAKU+bNP1BGDQGmEfJv+5DlSuofP19MREVSpx0zfVnv45SFc5G0EVl4Wb0GMi +O4VXmIM2nipxLBZrJOBI0HDnaQcx1zQR6tpvBO7BbAU0sflOvUDldUStTnz3TTQW +2ECm2y8bsArNqkeLndqis3ICmYL1budhDdUYYcqv10IlbjPJAkEA6yE0zduCE2wM +Ob7lcqiQCOiXeZ0KijHTmSZV4Fn4HRbp+XuxUpjSWFaoDTO0bncGNE+JYjywe64V +XvEORb1hTQJBANgcjEoCrUFY7VYWx3f1tpN0Q6jwwcj67Sd+ysaZNgghTPU32GTa +auGQFv+tifUQMyyVrhAfZ6s8myKOH5SWUDsCQGVvqOkaRq58UXXkDfZ+E81UEm0L +u81Mm52ZdTjZd3mNNhlELIaWmUA0+kDfynpRbOLKYVl5FyX0PxH7ao3Zmo0CQFpL ++1YFLk0KkggRdoCp+wI7ZvXUurN2HNcOxD0c0RWujFA9aD4jgNsEcIeeA/GQNkGf +vN3hsVg793oFti5Ia/cCQAubCMvRqFTyXUBervPVC0kibO3OwYt2xN/7lQXAVSfm +nRwV/46trioV3rMF84hpOk/46Qe5hqbWyQnL+dZljpY= +-----END RSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024.pub new file mode 100644 index 0000000000..4aded97ab6 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDGfj0Jmqj+CUb+WdFrlkRV49TJtNzvvMb/nX20zqgGm50cOIYrMzfFpSQN630pXeAidIgiV/PWAsipntQfSWPRG+RpB/wMKHVUNPJCJkjjRFEa56YxgAhgNwF511K13x4p2tEN0r6wsfw1nos9VoO8XDBAu3lellAgBdufyCt8vw== testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass new file mode 100644 index 0000000000..0b66dc0aa9 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,4B8025AB7456E0A2B48408407C6E3FF4 + +B9gztX+5QQPqMR/79eJHxjNdo9baoKjfWY+Ye7t1h7ucOPMCEXRSP8FwPwBfbzQh +6W1AHOfiDCHTzArDRG9SXrFfRlU+8o5ffs/TStTNqde/AXJeNuM3pwbmqKV1m9oY +oWelabmGtNUvGMAHMFm/2uk4BgS9Kjv71KnJg0cQQfIiPKTPBncJe/R5mf6O12rB +ByOrrlDmjtgveZZsgggEZbU9Y9DYiHZp6yT0JepxIWNImQ/A9EeUPTQheVB2ECT6 +DLUOwRfyFhdvsfD2eXLK+u7T47keFny3rIfm1e8HC1y3X+T/nFxKGoShecx1NmEL +HMgOKyFSwGSZh5jxE66dSQoc+rRZhCWSyPJEb9cjwp8JLON8oH3Yg+PIXYJhMFK+ +nghAIVXp3/H+cYXMN27j21cRGC7ePuF3YX242Gr+LSj42Wf4qCMTyvWur8WrSe6U +iyrWJ8+w2J7O7rRHGM8v+GYGaiX1qIXFheM/774vsDmjuueOhkjiqs254gaap8xk +LcJUuqJU2AL21+eW+R+EG3Rl/AbMIaQ4GFDpHfgEmmvVVoOvJunNQkDIP9JzKczO +g7cN/EYLUC2TcdmNaiunB8RhXMiaTqw4kYJEzy4lsxk/xjubC7vlQKTvtnWCpob+ +WpHX/2FBdPPULt38AIk4HQq7vKvKw9TmvGeOvQmCUun7eCFFhxKrwNKO5YCXAHvs +fv7JNGfrST4jwbqCvamuk+XTf0GkgJN83G7DT04EIzee6wwai/NRDybgYptJsj9G +6wBpKH15BtkktuUzM1MCt5+T6Ccsg+d6xE6eStimwDxkXCjvgz/KlS+sPKe7uS4h +-----END RSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass.pub new file mode 100644 index 0000000000..4aded97ab6 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_1024_testpass.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDGfj0Jmqj+CUb+WdFrlkRV49TJtNzvvMb/nX20zqgGm50cOIYrMzfFpSQN630pXeAidIgiV/PWAsipntQfSWPRG+RpB/wMKHVUNPJCJkjjRFEa56YxgAhgNwF511K13x4p2tEN0r6wsfw1nos9VoO8XDBAu3lellAgBdufyCt8vw== testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048 new file mode 100644 index 0000000000..a2d7d62310 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA1sFEWiIp8SVO8/sDhKJ67O7tQdtDwsqWi9Fm238tAuy26OH+ +ylireX/qVcndU8Yr0qYShcwloEUaeNe77VgffZa2ZIUee75u8u+WGCYjea4RQ1bZ +tDcioWkxl+xYfVuuKaA8CQn47XUdyoA/5P3DpDhaJl8KevaYupJNHOo9Lt2E5dVT +93OksZBOQ6E3nNlsefP/hnFByiczlde2GIXP2sWLoxsiVsbI+CLeGtxQxPubX9yu +vWrl/nv/yERR5ZBOEVY5N2+1BdT7DvOIMg2q60FpJv6zZpQi7Ov1iONMVafytIRW +Ma2rPkpS83Ebxh5c92T3rgLUf5DcZjKvBgxtpwIDAQABAoIBAHeDZv6iNKU3FhFB +iFuv8Kka7n7P/43QIKf/CTbuN6aBBenkm18QqZ0cStUjWkDc8FZyhaxgSDBBRNIr +fTJA8IV78lVOoABNooEgRG98ChIVhRXsp4tbg7JAUJEzvqtE8k/IFKETI61CmCmx +5d0SPGaP1du02KhFxAlQkgmdch85st+tRFv5GZXqiKbR6QlNaJgIXIoOlykVvnz6 +rnl6Q1SDutBOKGC8xFrDzFI8KxLFe3RFQxtHtsLRPcrrpukNSHICTMO4jtGXSZ12 +9Zh29ZtkouCDk+b176dGrJKfIBbxXtBGVXtkuo7rj8EWVWrJiiYbL2hcWD+Pw1VL +0GWkaEECgYEA+RrE4nVkfdJ0Zgx+sACQqs4uKi/JuFHU69JnO7RB2lDwzQbIPKl7 +nn0ExJ4V9m035/3mqKReBIyMIjIhwXgLFiakNO/+GAWa4ycRMB3pV8WaVFCnWZEF +oLRg1ukoLs01TfOszcux831n8zmPlz/NLTTkC26O3WXsVmnCSlPXd1MCgYEA3LMW +B8ONEDFYACB8xKA5zn3jrKq/yVFfiQzEO87zSkgG1mQbsb5T8jggWiIHVyZKQUSk +8ZkrwBKW+LwyRik1lVwawALmcvvN3VyCW5BukniErAUu8jb3+R2aFdrjzpiNqzMF +M18BPDElirTXMjJusC7z/0I7+gyAu9ttYJY1id0CgYBqjIiqVIwnRV2ESNPndFZs +uMQGR2qA7H+mXtjJMND6EKTvDXeYeuXlZJQlhXjfbtf64x9GAwgz6eoGtmq51h7n +2p9iBUUqATu+7Xbsnd6xLFRWvCjYpq9BjeXeBtypKB0kupWvcPEstPdBkd1ZVHDu +ZTElsqRpDq+IRrRUFoiTAQKBgQCxTGmRWSa08H8asv6o03M9EONbrlyedXHDXu8y +gQHwFcbwasHY2+cCetZ6skWlXIxgvK2prXx5NDX2ovHcbXSvhauzv2C01NdAUvYi +avh5ULp8mzlouoIhrgdAMXW7XdDJzRYLe/I5Ed5v/PG4UM2dWksIMISQT4UH5bKL +2oAuPQKBgCQaJ2oc5qE/f6MiL0XfGSdY26gOZcVrm9L1XKXtyHkfj4xWYQ58DSYa +vNZH3fGyfR+q7g1WgUmLib5etOjUjbVYRjIEov8xLA41UZZLNGRLc4VzgeCT73CW +YvbxeN93fL0tgvKeyNVzIsWRazHMo+aQodlXvpPckHXYxYHS93W+ +-----END RSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048.pub new file mode 100644 index 0000000000..b787e36483 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDWwURaIinxJU7z+wOEonrs7u1B20PCypaL0Wbbfy0C7Lbo4f7KWKt5f+pVyd1TxivSphKFzCWgRRp417vtWB99lrZkhR57vm7y75YYJiN5rhFDVtm0NyKhaTGX7Fh9W64poDwJCfjtdR3KgD/k/cOkOFomXwp69pi6kk0c6j0u3YTl1VP3c6SxkE5DoTec2Wx58/+GcUHKJzOV17YYhc/axYujGyJWxsj4It4a3FDE+5tf3K69auX+e//IRFHlkE4RVjk3b7UF1PsO84gyDarrQWkm/rNmlCLs6/WI40xVp/K0hFYxras+SlLzcRvGHlz3ZPeuAtR/kNxmMq8GDG2n testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass new file mode 100644 index 0000000000..7b3a3f4e58 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,DED10D02EF74A02F24F46AB44A84F4B3 + +DL0M2dNu7cXNLE6KGPqEt3pSKiQw6ajlxYaaXyyAwvpZB0Pv44HUjXfhMONs5FH5 +rDjz1RSYDRdMF/h6FtltdEEareXwMtRTvP2wb0gsQKiYS5M9WeebM3TY55JmwS1U +hhrPrEaP6hs6WEy9xp9DVxJN2y1MA5iss7M+fQ4/C6QeSp9On6bgCEvNPwdMTS4A +3sLp+yzRvrefQmSi2SWbJoYlChitOMdc84iDJXDo951QQLX75GqMm41fFLHHrcTO +7v/k/D7p/KLlNf43Ru+2yPNE7qyEK0pDSjvPnjPykIa6SWq3Qx2DnVdtZ7bWF8LA +B349QmuE1r/YYHNvWnp0/5SztivJk3NMeTT29PIiZoHioo53Vtru6RcXYMOvHbh1 +maioVkgRl5gkhLC86o4V+3hiJQNrVCWMuT+lxLY2Tt6bFXulbf3WH69AEAFW4S4a +e7zH4fwvkSwz+bFxg9B+Yynv42ke1a+tvDI+aDvsMmv9JUCy6G4Te+isXYxLdtT0 +nyqJ+wwP53AWS8gOvoUXzxxsEchTDtQnBQMWuSHEdFrk3OLGykNN6vZaxUROxpJf +vcPl7JniWGhzDzUdHh0AQbLxXoZlv4YU1uO/+1OnrvIkuO5DCDg8v2sTFRW6sgiU +JXm3QPJiU/bu3/FJ4XCU75cTcunZMXsL7TY9mURq7Y5FxcByuvSL2nlA7KfROTVq +I6w+Ej+r99C1u0G63sk5b99Pm4cb2+V/sr7pslqlU9Yw1Z5hw55ibih04CiWZAhJ +Az7s8ho4dY9E1n/XJSe26p14RPYU+w7WZuN6Xb04t3+BhF4Ubbsdn6F3lAVOrrWH +6xNoncmIEYdfdcI089UPpV4/bIpdakXRIbaLmpshyU6aIRUXqYkzwduXcHUrxgq3 +1QCZHNvq1+9i5Wqj8JP8cZrq9YVldOeXdIIsm1SSepbDQ7820d5T4Dk6cj85BXYC +6t12UNZ5mhzTvIAqbR3Who53jQ8cY0MSVXR6Jd6vPih2OhAnccnuJmRCNNJkL4mg +pVcsSgYjoUx+w6Ou1muCIkkGpdEhLLwEnKFc0HUmPBToRqgiB1Aec+7oMv62XhXe +yA26/dpT6N6SWYKN7MyDWUe2ilkmjXI+JrPCH+/w4FXh+GKafOn8XlcBnRWHVBEX +ZQfYLckd1j9B6p7By7ed2H+8FxZLz3gthcSxRG89IP/EQImY/e9A3aoLrFX6C/W0 +Gd6JrIvzC2bZCvrq+VTYs3101j1xe6ZDJnq68HokjpG8P9DlFYDOpRetCjR7TuqN +I5s606KAsGkt/jfbSNUMIEtuM0AC75m3TTJeWdfYh/PVYevUC+pUoreJ0ZsttQ2i +D550sAAzU7PCzZQsDF1i2jv/YZ0wXz7+C7YFiGNmb3HmXH0Lb2HISJR5UL+x+hHY +RArXtVubqjFz179pawzI0n03Z1OXiHolwer7C+Twmarv7SPe8rMU3HcHP25JeTAW +mo0PxNGG3yQPlRZWpPz8LEWGo+fDqfA4kbqy4+Pvo7B8YFIQyE9QG+oBv+/7uqMU +UOs1ZqsmvEUmvWMeQnWsjETmHKucbmTBm8ktsesb3sCKfY/pf8hAHbO6+9J3ebYf +-----END RSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass.pub new file mode 100644 index 0000000000..b787e36483 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_2048_testpass.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDWwURaIinxJU7z+wOEonrs7u1B20PCypaL0Wbbfy0C7Lbo4f7KWKt5f+pVyd1TxivSphKFzCWgRRp417vtWB99lrZkhR57vm7y75YYJiN5rhFDVtm0NyKhaTGX7Fh9W64poDwJCfjtdR3KgD/k/cOkOFomXwp69pi6kk0c6j0u3YTl1VP3c6SxkE5DoTec2Wx58/+GcUHKJzOV17YYhc/axYujGyJWxsj4It4a3FDE+5tf3K69auX+e//IRFHlkE4RVjk3b7UF1PsO84gyDarrQWkm/rNmlCLs6/WI40xVp/K0hFYxras+SlLzcRvGHlz3ZPeuAtR/kNxmMq8GDG2n testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072 new file mode 100644 index 0000000000..10d622c905 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072 @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG5AIBAAKCAYEA1M84bePnK6cR9Ei/H0S36QhdfUl0qUIZXrHNvS9i/npTZdN1 +mCzWxeNHm0YJQWpn9AqPZGG/dPGt3CQEL52TKXawY/0ks+4p0JJ9260oqVBFJrXE +5latVQCdIZ1GR2iJL3kZLXHXSkURygEL9aBfOEUSmC4SkNY0LOGuwMZ2TyXiFWHL +Y9le1DU2UMbfk65+6LgzU+FKzO4sg/zZD3oB9A+n+ozSZv/YEMuPvUAboMUJru/u +c6D5UxhwJ6GSKNkSt3xJUKnsohkCbRAq/ansvVJqEsgZc+oKVFidLPPz9rLjoEl3 +w+cUlM0TbbXaqtFXCoE8S6CAJG/G5Rkfrw7bUUkjGbYrVqjR1W32dg3txzZMVojI +zolB5LWtsbZY620b+hHk7Vh+F3Vw2yinGNrPDVnVMwB+pRCsPkWSvlLvpR4C7xqq +iEucB1eFqwWSVhfDgzkvtTiaMJ7M7YunJ5pFWjNd0yLZNgIa5SESzrn564wwjcwB +bMVifimMp3pvFBbrAgMBAAECggGBAIGcT8cGFiaNE69Pmy/FH6nLUX1b/rSTsHXv +HtpJgSZyhFaxKp7rOEe//D3CsyJnVzbYM6s0qXHlPDmmqfICK74GLrpHVFJODKOe +hQ8FcI1meSdxb6HGSr1JqWnuqv4U2fDS9ZWrDy+Jz6LTbmBEM5pG32NWNDKIc7Ce +J1v7w9TCwua48DI5Ert2SUV7SnJcxaihf4ln3rHfobcliWIWshfebTV5DTB0RDk+ +caYW5HzPZO1p7jX4ZcHJUY2hpy4/vjwHLNLhXBV75bkZwXZJGaITD+uDAbQIAb8g +T401x/+YZlNWP1kK1zu5Mo9cCl5o4o4oK3FyLKUoCXyYrahTfmbHgVc/toiJ0F91 +BMUWkunpz+B9GcKPUkBmu0xGTominFmz1ZW/etpufJcDt8B42kcoDoOsQMl2B2CT +zW7Bo+R3hFD80I4tIAtlFiKuKwKlRBF+E405yTxjlUwefczshWSSeppPZfxFwSQc +3Q07RB0MepIZ0w2RqVsG1rkq/GPaqQKBwQD/wAapoc02U034Id2ny3HALiV1u/io +Ve9r7oq02oltrRa9bUydLF9jknInl2p64R3x19JwIgtBK/AwDb6KKnrVFfJhw80W +TlyVvls0b0jmohhYcn/5EY/ROg3ex4eySJIvJYZDLDWNAToMWLr6STBFXQdaYHIf +BleOsyO/ARoiKZdtJB5Foes/GFwdIo5tgJgfZXw3mBrcF+UVIhyd9VRotg2ltIHX +UvbF0vanm+nN77g0dPAYz+p7IYQbguZANqUCgcEA1QRz/6KErIfjly00Dx3hMf+T +YCTe2Z0IyDex9b15tsF1C/sFJ7P3HUel198Fon86Wmc6OCxhfAHyhoTYdpaCwgGp +2rVRd6flkABW+4koi8Kr0qXOnjAe54LcXlrZgo7/iDUjEMjCXKOkirXW8L1Uqk2X +nuXJq3Vp7iexBaZCRe7Q2kPcV03QXA3r5sph67SsjJWrEVSll7/XaL1RBiTDRXHZ +1aQpnf0DQnvdHnnqrwewbrUxcEBPVq7faoWPizJPAoHBALdNbnkOWwLg2jVKMJAf +JLxVVsv3mdUtIpj9M7VEHNPbBz1lpU/RidzYDbGKuOqxhsDbqxxrih1/3HrUnwhw +QfGP9VVU/R1LtNguwzflux5yd3iNOGPPzoBrV52g7QU/NmdMQdrLSOZzRqOqxPi2 +lD5i2u5Pyfuqk/7XLnur0otBvCKhjIDj+LQURZEsP2EElgOKvWkrP7UX+z0WYeRk +/ca/FTD7G0S1VeGbvuWKvhy4ABK47Y0bGDiAYStGurizcQKBwBTj0ehg7Lfqv6QE +t9U/reT0VmSYWQ5oOwM/iwE5aqVEhZD+Nfw1xuclLptj8K6F4ZgaBXiayZiarEkK +4BuJGRujhB/BplKgsX+UuPMD+WjzV1xaDFAxEebMS4YpTKlkEqUt6NlthroFBk7g +FEsZliL5ZwQbLtLUueW0GMUgD+HB0NOG0iXxqJxOdTL15/Jwjnde+h7B+VdPZfWM +k1SR6GB4EM/FwJsQw/ASK5YgiKZPj7rbpBSJCf7LOXe9z1zsOwKBwFZ3GdC9arW+ +AFvXk0TuF5xjq0WuTDmEJn8PI5HPAajyeNoAnp9xwUpMnklfT6uk5ZWKQUJszbtm +IFaNUDXwOlE/S7Zf8FXQsoUz7koCs/IGKBBdRwK+Hh4e89Qme3nOU8I66DWxeohF +t0zuJJaVCUdJdEW6HbOdS9/J/zzIPeL2kQU+lvD7FfmN0ynFcGi9M8O6dEl/2L/0 +FmI9bz1F+bExm39yFXnY4lsK/gTVdkjyeEK7T6Fg9PFCqxhqh0lyww== +-----END RSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072.pub new file mode 100644 index 0000000000..686d3b06b1 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUzzht4+crpxH0SL8fRLfpCF19SXSpQhlesc29L2L+elNl03WYLNbF40ebRglBamf0Co9kYb908a3cJAQvnZMpdrBj/SSz7inQkn3brSipUEUmtcTmVq1VAJ0hnUZHaIkveRktcddKRRHKAQv1oF84RRKYLhKQ1jQs4a7AxnZPJeIVYctj2V7UNTZQxt+Trn7ouDNT4UrM7iyD/NkPegH0D6f6jNJm/9gQy4+9QBugxQmu7+5zoPlTGHAnoZIo2RK3fElQqeyiGQJtECr9qey9UmoSyBlz6gpUWJ0s8/P2suOgSXfD5xSUzRNttdqq0VcKgTxLoIAkb8blGR+vDttRSSMZtitWqNHVbfZ2De3HNkxWiMjOiUHkta2xtljrbRv6EeTtWH4XdXDbKKcY2s8NWdUzAH6lEKw+RZK+Uu+lHgLvGqqIS5wHV4WrBZJWF8ODOS+1OJownszti6cnmkVaM13TItk2AhrlIRLOufnrjDCNzAFsxWJ+KYynem8UFus= testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass new file mode 100644 index 0000000000..353a24c206 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass @@ -0,0 +1,42 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,040847DD0487D72145EB88CB09486EB0 + +2vC15lwRqJvaSU+yYCmqerJft8dqlrx9EK3gW4WtMW6C4ebqlj5DkIthSvJLgF6O +wufFV0hgmEeOhLEIrdJc/FTeD6VsSBYHTttoMeQ0Yb0eETnLhSbFO+9NRvPBpT4/ +EsBozu1m/fnv14qbXtgiX9d3zRR5Il9Q/TP9/MO25QO0/7SLHn8ar255piZobBQ4 +xqW26UywI9pUMjcfgroE4PYZTqTPY8xGFBeOIXBGuw3m4geKcOMbiPehB2o7gZDJ +iC2conFycbi0xUBYytnRO4BboB1PhFnh4CXFqAkJycWj20Q2iFVliEXEey+Qyd4m +vu9Hr1sp+35kByS5uQ7UfDgBcoo25JKz3HIcqFrSzJ3cwRuRrj27eydojR12o4FI +Cd06GTMq6khN3lovVUaQWlE1MLUpT9zT0rLzJylZ7fgHi3cTZ9n5Nr70vX8+pvFA +mzQ/53nvXQkiKfyUWV1aVypNsl0kYEM9+6uLyknyUPmLDOGxwAz5bS2xp6J7BKku +PojN6NHChyqndHArpR6EUx8RYCQV7PL0EPCSVlyiscetNBfTe9+BzCbPisorukQT +EweviRMUmW/pdr4zPuMwfZQSzRGYZ+19sIfV/VsRvgYvTUqUZ4mvWQbyiGpeLoM9 +W/bAJrqJBgfMISw4n+j3oVd0HJULWxktZGD8grLsmeh3Yjk5TCXcv6dH5OGx66nR +ATMjEinVcwop+z5RdlaP48Lw7/FfaWTiOln9O9DMT1pjbyO01qXHCKvo+TnSYryK +SqqaomMm7vMQMytxPPZGuiSCKpaIWwfMLIzreFw2LdvzGEF3wX/SBW+8g30hwyfq +YKrP3ZXe1g56oRqU8S2dB69rkap4nljj4HSXvIr/7XNQpkKlJX8yOAncGUcXfBaB +kIytyAfX7Xfibk8uPnDFxL7JEmCMR78LP3jYLX1Icl7lLdbUFUfxb2WM6Fng5qyX +Ffggcd7gucydjFNKR/KYlJVCIfxJTt9D1tGz9MT0sk7hSEIlIPieG2VkKEYKHbUj +UHEwbPbeFxm9INyccBAdnCvqfJ5ppQKB9TrZliPeLclx52NlX+3gtBErneycyzOk +oQmFtV+Bqg2hgH8TDLenGmG2xJsviuNTXeAjyZFLXkE1kFAPEKmz3Bys/tSJ+NTw +mAQxRnZ14BmO11o+/3xrrA3FkxiZq6hVUOyUZ/rejkbMTXUb81kyJe5o0kgLnQ8p +EJGi6tQl2z9YPQC9wWXO5ssu+Q+5MJ+H8YlvV6oc0nXUcLq9mgxPDPRBu33n79zq +mKymh4jO5qTExqnC6lLOsw7YVsss91opBLPGO8nXtcRvtqiRGwI+2D9kUVHH4J9R +dHXQaVXgUGxmhJFUxHEEckrT6NN923uY13R9Uw5Ifmh3XHob2hFQlbBP3GeiwfTI +DlNxIEguXxuZddJD2Fg/vLn5KNzkCOlYcrvoa+eH2jzcLN94tLNjliOgX69eERdt +qjz3x8Xoyh/bWcrdw7LZC7PtjwfLlkoubUVtOv++ZN4iR1XEjmEuyzibOUTQ8Ydz +ZwUXchQKupTxEGgIJ6tl7NGXSjA/TT1KYQUgVil9Uv5zZbOZecFClFF+1Vcmuzgd +hLzWG1DhZzvEAI3whQafUZf2BuyfYdnS2aKjVYR+k9dCTKAIz0MWOl29BC7/v1L8 +d7uqonqiVhwfHOjnUH0cD+QRM1i63+Luyo4c2WyCnQ7DFOfs5l+SwnQL1Lxu67F9 +7lGr2g0l721hBTaUKMETrTjNSz/OBURebumgMtr+45K5JCj8hJ2NFQUbmqkqhyf8 +f3niFJymhtywyUPafsodRbQhKMVg4TYVzQsRnpdsQ1IOFt3vcZnRNVuv0Y4bTXH0 +TjdwxAxtxtulvE6K7esXTQdElW+yH2Fkq2edHsxquf7PoMhBLV/myMPq+4inrLU0 +rr+Er/yYLZLdolld849WTtYdDB1GwcPQ6PmuBTpt5ccoFQDvK20U4uG2EswpVkoY +YCWf9sUnGwZh9YE0h6Ag0IY13CeQL3dsiua0+xsVEOiAZ3Y6Mawb7W0VZPHo35Kh +ettpfjDQUF3FA/J7hW0qa4soapbymbtlkOjdQMe3tOV28ElWe2ve/TmDvUtVVB8j +y0vjRJtwkcONM3CUuOiJPHKFvKwUBAC+7VyvRy2lRPKYVZibIr98fyd6BXsP4tD1 +R9e+Me6Cq2UsC7ywii9DmkBqpSP8XBOMNdBzbDN9gPnQzGx8oXo2w3mZZlfJe9uK +v09UMglCxrYBDw30MEfoF913crEofxrHRSzp17tFEB74M/r7OmeegSCD8Ud7twH1 +mpnZRlGanu2DQrEmhVpfJxjn7pHPmolJsQirFfVY6wCz5UQ7iXRV3LILnruVjpIZ +-----END RSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass.pub new file mode 100644 index 0000000000..686d3b06b1 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_3072_testpass.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUzzht4+crpxH0SL8fRLfpCF19SXSpQhlesc29L2L+elNl03WYLNbF40ebRglBamf0Co9kYb908a3cJAQvnZMpdrBj/SSz7inQkn3brSipUEUmtcTmVq1VAJ0hnUZHaIkveRktcddKRRHKAQv1oF84RRKYLhKQ1jQs4a7AxnZPJeIVYctj2V7UNTZQxt+Trn7ouDNT4UrM7iyD/NkPegH0D6f6jNJm/9gQy4+9QBugxQmu7+5zoPlTGHAnoZIo2RK3fElQqeyiGQJtECr9qey9UmoSyBlz6gpUWJ0s8/P2suOgSXfD5xSUzRNttdqq0VcKgTxLoIAkb8blGR+vDttRSSMZtitWqNHVbfZ2De3HNkxWiMjOiUHkta2xtljrbRv6EeTtWH4XdXDbKKcY2s8NWdUzAH6lEKw+RZK+Uu+lHgLvGqqIS5wHV4WrBZJWF8ODOS+1OJownszti6cnmkVaM13TItk2AhrlIRLOufnrjDCNzAFsxWJ+KYynem8UFus= testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096 b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096 new file mode 100644 index 0000000000..1a10b388e6 --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096 @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKQIBAAKCAgEAwzbSXgR8dM/EU36T2lAKUoRlojKspPhKVfDt7N3prGAc2L6A +P0y3G1HLLgKPK29S0Cydcqyl694ST+uu9qYzDLQlFQHbxIG76POmHXj92bF47lJU +RNxi78hoEDnZWtDG0rsUCBD1I4z+tXjWV81pv3BqVg5ilR6uqNgv3RzXj2jL6Q+3 +zwXxeMw7jJ3Tuukhf50hlxblH4bBIOLuZyb4t8R4EyXmrAPupHaUZSiwbxaDrV+s +gdu/7G8dnyB0dVL3AUNUEp7Wrh2PewnjgUcNQQmyJuB98wEP3+GrTsktixjIEmCd +e/gfDsl5JxBzzbUFtlQ8JVOnn9JCQ2U37cRggsW3yojFxGCU+bJaXz0zSgPmfR/r +T7oXgDKR76JZ2VSTGuAFrPcdRyErPg4PC6FtW0mNxn2S6RK28s6xpTDywEDu8ETh +lKIXGnN9XDX26gYw56ZlmAaJQ6KQP/F0Akf8nRARzkPJtIa21iBrUHRXLF7YKnBw +LyCUgTA3WSDgNdP9Ga7+6JC5gGPW5KGIKoK7SZY9LxNoV66iglp0nGEM27ZU1raw +llwcJAzkbSaViD/vvrIiuz04s4+5K+rAhe8CU4UTBWUJgUvtTSV7d/SBfFLsQJI/ +W11n9+SCIbBCx9nON+xkMkMQVyrMPWoD+oYRx/wXGIO2qkkPeegGyb8oKYsCAwEA +AQKCAgA7qPx/yUUz+e9ZSRzsonuVHmtlN7F1tYAaZciBFIz+pl0KjKXrMonfao76 +38NbleksQAZabpNC05qrHC9bqA1/+2o90lSU6MVB+3ywEzMZndiElVq1tNjzyT6s +ftGDpLyu2IfVs0EH/WY2ldiD+v4viK6m4DyWsErWxUNTgyYJ6RAwiSI2ve0/asNk +RTPZMriPJLmIUHHzwZ4ya8hKdCmdGAlOaM3nkkgTsT3G8LmDKdFSYiP5h+xO2OKn +qCaPWKyukSIXkr2vds9L3gjOkKVnVAxDP2aepptwY6qUKH2nvgofO7HFml37ie1h +1/BcVM+LGpFLIxbejEa+DCgcnWCU7VbWRSvU3TeV0uTdrGBhKSHLBMigyqtt4OTw +QcWLd9zygDO02Jm9vlMO2D6WmI0medbgXPT+vwFBXvt6/Z2sNf2zW55qXn7yeFlu +7/GiZFIlpH4jOw6U8uG6YV7YueXSaKmbeI9hSB4d8hrRqud0Ny7fu6m5+/GB8Q6q +2cZ7mETvrNmISe4waD9xk4CP7NchM0LSU2RWP5VtZAHEM2iIYin27aI0GjdhEm8Q +oc5fU+kGJdLiMZ7IaCp2tZZ16PLjtWXqdbCgqjmdp8jwtwLuMil9XAFHm22jbrnP +/bFCnlNLcknH/csS0jVxZI+nunS9UgMZVCudvJ8lzY9LDlFUcQKCAQEA+b5tSOfC +EVdVY5+9zvx2glvQRxqN/5fonMTZXK1qqVNcbxb5tQ9I5uBQCykg7HJ30ukgK00+ +qbGCc64l1XNu2dFFXKJbSOV/8Ts5vzfmgdwZoC+W4IwojRQmfyKCwfIsP8IwrBSp +IlcO7LMkHCnlmRPPMSPeQ1NB/N3mnilz0I5KfihahziKccCTGBvpESD945qWqCrL +ynHmuEyb1zvwU8Z4psrfiP/RosFjItVJpsQzeVS3CGrTFe0b4PzrIQo12wPXhUX+ +um2WMQYoBVZzcrRSIH31RY9PJ3avbPJC8RqGBAZov0Zv5KvpZcL1EeDfBn8leld/ +eCpqIheDiOdewwKCAQEAyBq0DF6Qhz5Rl7CJ7BxmaN/CbW4aHw9m9dfpNVqqi36Z +ODfpb0sl40QnRLeWByfDj6BdhTBc3XXcIDVBjsstnnX1IAc3PZgzaONrmDaoIUfi +GIROql5l86tMSjuW53eGze713z86GhvUv19r748asaKTepXgssaY7ppXCZ42dKt7 +0euXYyJSirMmO+A98wOtqamKf0X2FK/ZB7CyfhLFskHEVO2noojvZiJwAyz8zvm/ +GpOArbRTjEfg2Sqxk27CATVIVjVc4LBzsZc9mzLKVb+Cs/sZa72gy+gLmIM4ItID ++FPW8NbeZmVngiARJcIL4alxXXy+p/uXBILxhuLtmQKCAQEAzzlF3seGzPLFRGuo +iBYNk27xa/5JsrnuZh4kKXUvWp5zxS2wNp8fI4sef5Q54Fe+uv97FNL8WruSfcAT +XoBwi0XMoueIjPz440X8TYDpv/jMPpEeROWnRCBjLPyKuLjkJGdSEYb3LCpGlPqz +zLaq7xBzy9dyNjTgPRw2nifRFEzs3K9JJogwv+BFbSzDf9X7NJ7xwUn5XNqT0Xqn +mLkAWdMGC4esYTW7UavbQWzutvR3rYYwdUiGK9xZVJ8nznt1YmxWqRwCF9iUVctA +6+Tm2FdtCc7Z9ETMLfeZ6fE+wGX8q1xSD9w3PeuzNx/ET3hiNjbL9y6g8ylmdTFD +kBZDFwKCAQA2by0zgDYI1GcVwKyEUmV5egVGB4GLmYEEt6t1HCjwsYu0w2D5KZQw +8sVL6DUj1SlZ1OIb7UAV7o3nJRWkZpkOVkBMaioY02KI0fTe/19VTlyvFq7fobZS +RvMF7pfqd5VwR+USyfxgRdnmBWszS9aTJArCeisZ9vR7U/kBYMyniE6ymEgia5/Q +o1NvTl0L0qBXWwuV+84pany7ntGvgiPNjh5+i/fiOyYEvrGB66cKFt5puF504m0n +6BW+feK4nJSiB4CaEwIlDVsroFzd7z8jfGlt1IzhxkALuCAPaQLIViFGWGhMM+dk +K4mw2FBR2SuqQ5HXQKwMvmAilgxmCS1hAoIBAQCykRU4k5qTxoNWfkYz9oYxsLUt +FnyBoLxAzGrzM7F3fImVjetXoCow2xRxHnsD4dns7OdE3VbuJrbUDFdvzkEHBT/i +MFJpaF/zrdnKA4hlQ3omccq+y0n1wLcG5LoHMoKoQQNHPO6G+Wf4uA4M9+p0ImH7 +ajEf/Rs+PC3cqKuvJdoFpSOseFNwAo5Vbc6N9nVgFfuaZ95puKgq9BzdCJnpK0Ss +J1K4VmpE98jBMYiEAAVPBdLA01nBiAY+Nwdkh4VjAJ46E++5pofTm4xvYljxIoMl +v7FbW0X6S4azOtIrGJ6EC2mziz07PA2Ad1zf7yPWilMfxC8mNIbS1pAmcVoy +-----END RSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096.pub new file mode 100644 index 0000000000..3c3c16feeb --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDNtJeBHx0z8RTfpPaUApShGWiMqyk+EpV8O3s3emsYBzYvoA/TLcbUcsuAo8rb1LQLJ1yrKXr3hJP6672pjMMtCUVAdvEgbvo86YdeP3ZsXjuUlRE3GLvyGgQOdla0MbSuxQIEPUjjP61eNZXzWm/cGpWDmKVHq6o2C/dHNePaMvpD7fPBfF4zDuMndO66SF/nSGXFuUfhsEg4u5nJvi3xHgTJeasA+6kdpRlKLBvFoOtX6yB27/sbx2fIHR1UvcBQ1QSntauHY97CeOBRw1BCbIm4H3zAQ/f4atOyS2LGMgSYJ17+B8OyXknEHPNtQW2VDwlU6ef0kJDZTftxGCCxbfKiMXEYJT5slpfPTNKA+Z9H+tPuheAMpHvolnZVJMa4AWs9x1HISs+Dg8LoW1bSY3GfZLpErbyzrGlMPLAQO7wROGUohcac31cNfbqBjDnpmWYBolDopA/8XQCR/ydEBHOQ8m0hrbWIGtQdFcsXtgqcHAvIJSBMDdZIOA10/0Zrv7okLmAY9bkoYgqgrtJlj0vE2hXrqKCWnScYQzbtlTWtrCWXBwkDORtJpWIP+++siK7PTizj7kr6sCF7wJThRMFZQmBS+1NJXt39IF8UuxAkj9bXWf35IIhsELH2c437GQyQxBXKsw9agP6hhHH/BcYg7aqSQ956AbJvygpiw== testuser diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass new file mode 100644 index 0000000000..96e29fc15c --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass @@ -0,0 +1,54 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,1EFAFB79DD5E78C98C5A2204D6747AF8 + +p+WHiqnR+5M7mTVZH2xYA6TcpD5824tU0qCgcU0VdUx9Ikb4Mq7X9Y2by2jTXpDP +9TN/XcUoaiEm/lAG+RESwFIFjMDe3kbWqv6IFw2GAsvwzeQ3HTjqke1MSpmcoRwA +vUgHXMl1wK/SQaJIrr0P7aiSt02Zu/hWCZg19rZLLYREC27oLFhgpVsB1HsNzmvt +au3RaPAkiZ78RpTz5ynSWawTUEqXuL0ctaivvmCnIoThy72gw5RQqw0GmkGEv/lT +uWZHxqXj+dZggeOvq8G3xNS+eoub/OFrH5t4+5zJB9P8f28vwlsGCYe25dH0oH+K +2Mhhnp4RNjsJ+YaqkTVjpJrMddz0WUgFWFzmD3b59DIDxWigmKIH6sCjlkMkCvVC +djS6B+D5HE7dtWm12u38hZ6I1dgz6W+dtlpqZvt7j/opHNYeyAlaY1yEL2HiEoF9 +hI4FdxxXC332FdOP/FS/q+nuTj4wqvO6QsVG6V2nEhIKe7tLEiKmlBf9rAVqTEZp +rWURoDDfUZPwGe38AloFpMr3k+NR1k0CmG9j9L6aw5bugS1Yqb/6oX3e/d5AQkJK +XmhfsGUTShNEF5WthotgPGoBKF2astUAF0p50GB9lfuzlBZVvt6hVIecQDUO6/G7 +MT68JbRk2kHw2U0K9+3T2y8PpvHurE8jcH1kkSy0bKW+h0CTK17869keLSLH4+2D +3gk6xrEEUFb+qLGTTfIbWCLxbCUJP5FGZHsQiTmecGECP4qYNlaedAiI76wxJGG3 +UrMi8kkae5PeujFDVo1CsRXAQoeBAzVkuVU93acCPm62hm8Z3wBJafEIWwEQmXRQ +Zuk443OkjT4eB3U1RJSoglDaFBvj3eq9CthXZBDZPWFD21gXa4r3MW84aRBX3FPc +FVrLqAbEcoULomvQz/lKJ4Q2i6jHloHioz/X4OgyrkkYXqst5UuXB8hE7jI48i4e +mlOxQ0ORyXEwhXS6CnT0zGYlyrevipqI0ch0QSW4391dDVG+ud6PTaft9kc3zDpK +CDONQYlN2GNQ91KxUDYKcPtH5wDjsSUPYYfsPBL10+yhhJLQ3S9lKsnNOnRvtTa5 +EORCFcDkDi18pR4rXz2qQhdrv5slWiWrB76d/1bhUo3hFnbSHDbl1jOO/e/OJ+wP +cb/bfIH6iua4X3EVrVK0hm22SaoarhXi4XLdPiIUTVrEiSqDKF3XOE5uq+kGzfWc +YaToLAOTFuwBYjIfgnhu/CrrrPganMFQrKOxjnR5q12xYmkneRc8xc5XYab9jVG2 +vdYh3yNl9/bwbguPmYZkwh3POrSiUfMnhTr/s6umNMjvnacab1c35hJUGssYZ7kV +20a1jjTvYzH+RFhzPZpRUwiCcYKTQneta54h4eVCOOE1wdhWxeBv8MwtXijvf8Mq +0+wpbCuW46/jO0F+oHEunTppXGgFKiwiKlElcMqrCpgVaFGgmyHDrE0Kgi+up9hv +a5UG//0uRAvBgZAsffX9KbbkJLrZsv/YXqvlN5xhFolUNjtUndxLgRrEe6Z4r5EL +FAjkH0ex1/Yvb3WromGbfAQRRzLqDKGqdAO6OgYeIW5q13QO1UwrPPPFHdXTDx64 +/8t5YC2ctJ/PAS6QMPFpHl3CrybkO7mvugQYaEG0vxV1whXb1uFe1OGILDUsGR/E +XCz0D9xTNojphOK1zRof0Qg4FPIZGI90SZLGJTNZnwN52b/ig839B4MIlT6nwUCr +42yBCbI/k1QYm4Gb3zxDxBZwlOkQjU9LSv5lsmW/ObRsPmnK2pAjmT8n5O7wyXnR +I3LuIWB2ssxySbvqzRAx2WC6fo4PBXpAKRgM5ZTH9NwFACyR84AC0ijw5UAGztXe +WUAxx4l2aUYRasKQsQ9IS1wDmUE+q9zhCiv/toyDMwTENW3iFMoWFnaZVGWNAnlA +YTjrix/SPwA9ybYIxRbh+FpP/aEWyp7OGDk9hQQvDLUkzwNJnfAycV8jq5OETid3 +3l+xzpGe414S5xAMMr3KDZnwVNbIkoYDAmtjIrfemnB0NuT1lDZ0eRZZXpFQPUAv +U9y3p/5VRU7Ihe7TWjOrs9WGF2yBt5pcC8WbNDu8WMs3wtA8e+DBZHIJnHa/UsSu +HTIKAXrrB4fmchumVwQT3Fdd8ZgJVvlgAcGNmko6fPVbM+CgwJ1iVwzsNKinF9xT +J53twma7cpAYpwqSLMENZle9Wc2RPzv/mb4brud38csgrwQ28xfkcntjcT+Jykgw +2ae5zlaP/R1a2sYbbT/ta0PncdfBuYuRbGZSNBQKKbe2+0BDqSvFSJGNB0beQ/xE +daxg7Q6nZdeWksmIUZB5BHC1WDmfmk9N6M+pl0+7YbH1pUMqg61JE2QTCMzfQAoC +v4jQ4o703KdvMRcnjQQCqab/Ihoeq5HUmXRCy3za6Vpxxp6mJpIK/6OWGn2UU/6w +saujG7F2ewBBBGReg8pgUZODayAX+TBu8+5JCKeAD+u707KABaeBEyGa8bp3AZZu +onzQ2tMmylusmmC/GiJO5UnousOovogl8HtsANdP16A/U6222kuQ5aahAnGTHLpF +2EwMWDo6SWN5bBDlUQq0IA9WIMGvGFaID1rDwNKw4ZOLdVhGlXhZxq8FoVZVozrX +khyiiK1UAp9/BpeXTzqJm+aUQNJu3J28LcRMNgmrdWTjzA9X7s/7mFQfYauLehz+ +Jf/RwSca4EXTFkvmhauhzwnPhdBqCSncCJmNi2I0OeJRFsOerajicxvHW6AUDkiD +7SCDSTvOBEl20cZwdk/WJ7n+ID5QwWDxV+KzB6dXoMPFZXggat5qA+e0JMEbWxJD +be0HwuiHyK3lLpBMc7Vv7KzZOtH1JscVT9n1Yd184CphTyi0gexcdwa5T0WmVpyi +ze1zT8pbTOOHWCvJkdqmxKfHp9GutAtviEloNoK4YbRUJUM4uCF81p7vOYNK6vot +bGvqXtQ7QvTkyKA/Ue4uSQCG1dLaedZQPSIVGFrqMrAFoUxDWFN8NcPiMkETrGFE +l9psgia1ktvdFdUOgMjpy7xNBodRedSHMcsyVHjfhGxdxPGW9sG8N1DWxTeArGpX +nkXs+RaZmMWijknT1dZdNt3XZ7+cGm04NG6JfjxY/kvWcuDKAnhCWmNUnQzLEbZF +-----END RSA PRIVATE KEY----- diff --git a/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass.pub b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass.pub new file mode 100644 index 0000000000..3c3c16feeb --- /dev/null +++ b/org.eclipse.jgit.test/resources/org/eclipse/jgit/transport/ssh/id_rsa_4096_testpass.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDNtJeBHx0z8RTfpPaUApShGWiMqyk+EpV8O3s3emsYBzYvoA/TLcbUcsuAo8rb1LQLJ1yrKXr3hJP6672pjMMtCUVAdvEgbvo86YdeP3ZsXjuUlRE3GLvyGgQOdla0MbSuxQIEPUjjP61eNZXzWm/cGpWDmKVHq6o2C/dHNePaMvpD7fPBfF4zDuMndO66SF/nSGXFuUfhsEg4u5nJvi3xHgTJeasA+6kdpRlKLBvFoOtX6yB27/sbx2fIHR1UvcBQ1QSntauHY97CeOBRw1BCbIm4H3zAQ/f4atOyS2LGMgSYJ17+B8OyXknEHPNtQW2VDwlU6ef0kJDZTftxGCCxbfKiMXEYJT5slpfPTNKA+Z9H+tPuheAMpHvolnZVJMa4AWs9x1HISs+Dg8LoW1bSY3GfZLpErbyzrGlMPLAQO7wROGUohcac31cNfbqBjDnpmWYBolDopA/8XQCR/ydEBHOQ8m0hrbWIGtQdFcsXtgqcHAvIJSBMDdZIOA10/0Zrv7okLmAY9bkoYgqgrtJlj0vE2hXrqKCWnScYQzbtlTWtrCWXBwkDORtJpWIP+++siK7PTizj7kr6sCF7wJThRMFZQmBS+1NJXt39IF8UuxAkj9bXWf35IIhsELH2c437GQyQxBXKsw9agP6hhHH/BcYg7aqSQ956AbJvygpiw== testuser diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java new file mode 100644 index 0000000000..dde55b6d79 --- /dev/null +++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java @@ -0,0 +1,844 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.ssh; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; +import java.util.Locale; + +import org.eclipse.jgit.api.errors.TransportException; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.JschConfigSessionFactory; +import org.junit.Test; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theory; + +/** + * The ssh tests. Concrete subclasses can re-use these tests by implementing the + * abstract operations from {@link SshTestHarness}. This gives a way to test + * different ssh clients against a unified test suite. + */ +public abstract class SshTestBase extends SshTestHarness { + + @DataPoints + public static String[] KEY_RESOURCES = { // + "id_dsa", // + "id_rsa_1024", // + "id_rsa_2048", // + "id_rsa_3072", // + "id_rsa_4096", // + "id_ecdsa_256", // + "id_ecdsa_384", // + "id_ecdsa_521", // + "id_ed25519", // + // And now encrypted. Passphrase is "testpass". + "id_dsa_testpass", // + "id_rsa_1024_testpass", // + "id_rsa_2048_testpass", // + "id_rsa_3072_testpass", // + "id_rsa_4096_testpass", // + "id_ecdsa_256_testpass", // + "id_ecdsa_384_testpass", // + "id_ecdsa_521_testpass" }; + + protected File defaultCloneDir; + + @Override + public void setUp() throws Exception { + super.setUp(); + defaultCloneDir = new File(getTemporaryDirectory(), "cloned"); + } + + @Test(expected = TransportException.class) + public void testSshWithoutConfig() throws Exception { + cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort + + "/doesntmatter", defaultCloneDir, null); + } + + @Test + public void testSshWithGlobalIdentity() throws Exception { + cloneWith( + "ssh://" + TEST_USER + "@localhost:" + testPort + + "/doesntmatter", + defaultCloneDir, null, + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test + public void testSshWithDefaultIdentity() throws Exception { + File idRsa = new File(privateKey1.getParentFile(), "id_rsa"); + Files.copy(privateKey1.toPath(), idRsa.toPath()); + // We expect the session factory to pick up these keys... + cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort + + "/doesntmatter", defaultCloneDir, null); + } + + @Test + public void testSshWithConfig() throws Exception { + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test + public void testSshWithConfigEncryptedUnusedKey() throws Exception { + // Copy the encrypted test key from the bundle. + File encryptedKey = new File(sshDir, "id_dsa"); + copyTestResource("id_dsa_testpass", encryptedKey); + TestCredentialsProvider provider = new TestCredentialsProvider( + "testpass"); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + assertEquals("CredentialsProvider should not have been called", 0, + provider.getLog().size()); + } + + @Test + public void testSshWithConfigEncryptedUnusedKeyInConfigLast() + throws Exception { + // Copy the encrypted test key from the bundle. + File encryptedKey = new File(sshDir, "id_dsa_test_key"); + copyTestResource("id_dsa_testpass", encryptedKey); + TestCredentialsProvider provider = new TestCredentialsProvider( + "testpass"); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), + "IdentityFile " + encryptedKey.getAbsolutePath()); + // This test passes with JSch per chance because JSch completely ignores + // the second IdentityFile + assertEquals("CredentialsProvider should not have been called", 0, + provider.getLog().size()); + } + + @Test + public void testSshWithConfigEncryptedUnusedKeyInConfigFirst() + throws Exception { + // Test cannot pass with JSch; it handles only one IdentityFile. + // assumeTrue(!(getSessionFactory() instanceof + // JschConfigSessionFactory)); gives in bazel a failure with "Never + // found parameters that satisfied method assumptions." + // In maven it's fine!? + if (getSessionFactory() instanceof JschConfigSessionFactory) { + return; + } + // Copy the encrypted test key from the bundle. + File encryptedKey = new File(sshDir, "id_dsa_test_key"); + copyTestResource("id_dsa_testpass", encryptedKey); + TestCredentialsProvider provider = new TestCredentialsProvider( + "testpass"); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + encryptedKey.getAbsolutePath(), + "IdentityFile " + privateKey1.getAbsolutePath()); + assertEquals("CredentialsProvider should have been called once", 1, + provider.getLog().size()); + } + + @Test + public void testSshEncryptedUsedKeyCached() throws Exception { + // Make sure we are asked for the password only once if we do several + // operations with an encrypted key. + File encryptedKey = new File(sshDir, "id_dsa_test_key"); + copyTestResource("id_dsa_testpass", encryptedKey); + File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub"); + copyTestResource("id_dsa_testpass.pub", encryptedPublicKey); + server.setTestUserPublicKey(encryptedPublicKey.toPath()); + TestCredentialsProvider provider = new TestCredentialsProvider( + "testpass"); + pushTo(provider, + cloneWith("ssh://localhost/doesntmatter", // + defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + encryptedKey.getAbsolutePath())); + assertEquals("CredentialsProvider should have been called once", 1, + provider.getLog().size()); + } + + @Test(expected = TransportException.class) + public void testSshEncryptedUsedKeyWrongPassword() throws Exception { + File encryptedKey = new File(sshDir, "id_dsa_test_key"); + copyTestResource("id_dsa_testpass", encryptedKey); + File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub"); + copyTestResource("id_dsa_testpass.pub", encryptedPublicKey); + server.setTestUserPublicKey(encryptedPublicKey.toPath()); + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass"); + cloneWith("ssh://localhost/doesntmatter", // + defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "NumberOfPasswordPrompts 1", // + "IdentityFile " + encryptedKey.getAbsolutePath()); + } + + @Test + public void testSshEncryptedUsedKeySeveralPassword() throws Exception { + File encryptedKey = new File(sshDir, "id_dsa_test_key"); + copyTestResource("id_dsa_testpass", encryptedKey); + File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub"); + copyTestResource("id_dsa_testpass.pub", encryptedPublicKey); + server.setTestUserPublicKey(encryptedPublicKey.toPath()); + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass", "wrongpass2", "testpass"); + cloneWith("ssh://localhost/doesntmatter", // + defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + encryptedKey.getAbsolutePath()); + assertEquals("CredentialsProvider should have been called 3 times", 3, + provider.getLog().size()); + } + + @Test(expected = TransportException.class) + public void testSshWithoutKnownHosts() throws Exception { + assertTrue("Could not delete known_hosts", knownHosts.delete()); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test + public void testSshWithoutKnownHostsWithProviderAsk() + throws Exception { + File copiedHosts = new File(knownHosts.getParentFile(), + "copiedKnownHosts"); + assertTrue("Failed to rename known_hosts", + knownHosts.renameTo(copiedHosts)); + // The provider will answer "yes" to all questions, so we should be able + // to connect and end up with a new known_hosts file with the host key. + TestCredentialsProvider provider = new TestCredentialsProvider(); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + List<LogEntry> messages = provider.getLog(); + assertFalse("Expected user interaction", messages.isEmpty()); + if (getSessionFactory() instanceof JschConfigSessionFactory) { + // JSch doesn't create a non-existing file. + assertEquals("Expected to be asked about the key", 1, + messages.size()); + return; + } + assertEquals( + "Expected to be asked about the key, and the file creation", + 2, messages.size()); + assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists()); + // Instead of checking the file contents, let's just clone again + // without provider. If it works, the server host key was written + // correctly. + File clonedAgain = new File(getTemporaryDirectory(), "cloned2"); + cloneWith("ssh://localhost/doesntmatter", clonedAgain, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test + public void testSshWithoutKnownHostsWithProviderAcceptNew() + throws Exception { + File copiedHosts = new File(knownHosts.getParentFile(), + "copiedKnownHosts"); + assertTrue("Failed to rename known_hosts", + knownHosts.renameTo(copiedHosts)); + TestCredentialsProvider provider = new TestCredentialsProvider(); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "StrictHostKeyChecking accept-new", // + "IdentityFile " + privateKey1.getAbsolutePath()); + if (getSessionFactory() instanceof JschConfigSessionFactory) { + // JSch doesn't create new files. + assertTrue("CredentialsProvider not called", + provider.getLog().isEmpty()); + return; + } + assertEquals("Expected to be asked about the file creation", 1, + provider.getLog().size()); + assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists()); + // Instead of checking the file contents, let's just clone again + // without provider. If it works, the server host key was written + // correctly. + File clonedAgain = new File(getTemporaryDirectory(), "cloned2"); + cloneWith("ssh://localhost/doesntmatter", clonedAgain, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test(expected = TransportException.class) + public void testSshWithoutKnownHostsDeny() throws Exception { + File copiedHosts = new File(knownHosts.getParentFile(), + "copiedKnownHosts"); + assertTrue("Failed to rename known_hosts", + knownHosts.renameTo(copiedHosts)); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "StrictHostKeyChecking yes", // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test(expected = TransportException.class) + public void testSshModifiedHostKeyDeny() + throws Exception { + File copiedHosts = new File(knownHosts.getParentFile(), + "copiedKnownHosts"); + assertTrue("Failed to rename known_hosts", + knownHosts.renameTo(copiedHosts)); + // Now produce a new known_hosts file containing some other key. + createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "StrictHostKeyChecking yes", // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test(expected = TransportException.class) + public void testSshModifiedHostKeyWithProviderDeny() throws Exception { + File copiedHosts = new File(knownHosts.getParentFile(), + "copiedKnownHosts"); + assertTrue("Failed to rename known_hosts", + knownHosts.renameTo(copiedHosts)); + // Now produce a new known_hosts file containing some other key. + createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1); + TestCredentialsProvider provider = new TestCredentialsProvider(); + try { + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "StrictHostKeyChecking yes", // + "IdentityFile " + privateKey1.getAbsolutePath()); + } catch (Exception e) { + assertEquals("Expected to be told about the modified key", 1, + provider.getLog().size()); + assertTrue("Only messages expected", provider.getLog().stream() + .flatMap(l -> l.getItems().stream()).allMatch( + c -> c instanceof CredentialItem.InformationalMessage)); + throw e; + } + } + + private void checkKnownHostsModifiedHostKey(File backup, File newFile, + String wrongKey) throws IOException { + List<String> oldLines = Files.readAllLines(backup.toPath(), + StandardCharsets.UTF_8); + // Find the original entry. We should have that again in known_hosts. + String oldKeyPart = null; + for (String oldLine : oldLines) { + if (oldLine.contains("[localhost]:")) { + String[] parts = oldLine.split("\\s+"); + if (parts.length > 2) { + oldKeyPart = parts[parts.length - 2] + ' ' + + parts[parts.length - 1]; + break; + } + } + } + assertNotNull("Old key not found", oldKeyPart); + List<String> newLines = Files.readAllLines(newFile.toPath(), + StandardCharsets.UTF_8); + assertFalse("Old host key still found in known_hosts file" + newFile, + hasHostKey("localhost", testPort, wrongKey, newLines)); + assertTrue("New host key not found in known_hosts file" + newFile, + hasHostKey("localhost", testPort, oldKeyPart, newLines)); + + } + + @Test + public void testSshModifiedHostKeyAllow() throws Exception { + assertTrue("Failed to delete known_hosts", knownHosts.delete()); + createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1); + File backup = new File(getTemporaryDirectory(), "backupKnownHosts"); + Files.copy(knownHosts.toPath(), backup.toPath()); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "StrictHostKeyChecking no", // + "IdentityFile " + privateKey1.getAbsolutePath()); + // File should not have been updated! + String[] oldLines = Files + .readAllLines(backup.toPath(), StandardCharsets.UTF_8) + .toArray(new String[0]); + String[] newLines = Files + .readAllLines(knownHosts.toPath(), StandardCharsets.UTF_8) + .toArray(new String[0]); + assertArrayEquals("Known hosts file should not be modified", oldLines, + newLines); + } + + @Test + public void testSshModifiedHostKeyAsk() throws Exception { + File copiedHosts = new File(knownHosts.getParentFile(), + "copiedKnownHosts"); + assertTrue("Failed to rename known_hosts", + knownHosts.renameTo(copiedHosts)); + String wrongKeyPart = createKnownHostsFile(knownHosts, "localhost", + testPort, publicKey1); + TestCredentialsProvider provider = new TestCredentialsProvider(); + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + checkKnownHostsModifiedHostKey(copiedHosts, knownHosts, wrongKeyPart); + assertEquals("Expected to be asked about the modified key", 1, + provider.getLog().size()); + } + + @Test + public void testSshCloneWithConfigAndPush() throws Exception { + pushTo(cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath())); + } + + @Test + public void testSftpWithConfig() throws Exception { + cloneWith("sftp://localhost/.git", defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test + public void testSftpCloneWithConfigAndPush() throws Exception { + pushTo(cloneWith("sftp://localhost/.git", defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath())); + } + + @Test(expected = TransportException.class) + public void testSshWithConfigWrongKey() throws Exception { + cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey2.getAbsolutePath()); + } + + @Test + public void testSshWithWrongUserNameInConfig() throws Exception { + // Bug 526778 + cloneWith( + "ssh://" + TEST_USER + "@localhost:" + testPort + + "/doesntmatter", + defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "User sombody_else", // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test + public void testSshWithWrongPortInConfig() throws Exception { + // Bug 526778 + cloneWith( + "ssh://" + TEST_USER + "@localhost:" + testPort + + "/doesntmatter", + defaultCloneDir, null, // + "Host localhost", // + "HostName localhost", // + "Port 22", // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test + public void testSshWithAliasInConfig() throws Exception { + // Bug 531118 + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), "", // + "Host localhost", // + "HostName localhost", // + "Port 22", // + "User someone_else", // + "IdentityFile " + privateKey2.getAbsolutePath()); + } + + @Test + public void testSshWithUnknownCiphersInConfig() throws Exception { + // Bug 535672 + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), // + "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr"); + } + + @Test + public void testSshWithUnknownHostKeyAlgorithmsInConfig() + throws Exception { + // Bug 535672 + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), // + "HostKeyAlgorithms foobar,ssh-rsa,ssh-dss"); + } + + @Test + public void testSshWithUnknownKexAlgorithmsInConfig() + throws Exception { + // Bug 535672 + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), // + "KexAlgorithms foobar,diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521"); + } + + @Test + public void testSshWithMinimalHostKeyAlgorithmsInConfig() + throws Exception { + // Bug 537790 + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), // + "HostKeyAlgorithms ssh-rsa,ssh-dss"); + } + + @Test + public void testSshWithUnknownAuthInConfig() throws Exception { + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), // + "PreferredAuthentications gssapi-with-mic,hostbased,publickey,keyboard-interactive,password"); + } + + @Test(expected = TransportException.class) + public void testSshWithNoMatchingAuthInConfig() throws Exception { + // Server doesn't do password, and anyway we set no password. + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath(), // + "PreferredAuthentications password"); + } + + @Test + public void testRsaHostKeySecond() throws Exception { + // See https://git.eclipse.org/r/#/c/130402/ : server has EcDSA + // (preferred), RSA, we have RSA in known_hosts: client and server + // should agree on RSA. + File newHostKey = new File(getTemporaryDirectory(), "newhostkey"); + copyTestResource("id_ecdsa_256", newHostKey); + server.addHostKey(newHostKey.toPath(), true); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test + public void testEcDsaHostKey() throws Exception { + // See https://git.eclipse.org/r/#/c/130402/ : server has RSA + // (preferred), EcDSA, we have EcDSA in known_hosts: client and server + // should agree on EcDSA. + File newHostKey = new File(getTemporaryDirectory(), "newhostkey"); + copyTestResource("id_ecdsa_256", newHostKey); + server.addHostKey(newHostKey.toPath(), false); + File newHostKeyPub = new File(getTemporaryDirectory(), + "newhostkey.pub"); + copyTestResource("id_ecdsa_256.pub", newHostKeyPub); + createKnownHostsFile(knownHosts, "localhost", testPort, newHostKeyPub); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey1.getAbsolutePath()); + } + + @Test + public void testPasswordAuth() throws Exception { + server.enablePasswordAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider( + TEST_USER.toUpperCase(Locale.ROOT)); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications password"); + } + + @Test + public void testPasswordAuthSeveralTimes() throws Exception { + server.enablePasswordAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT)); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications password"); + } + + @Test(expected = TransportException.class) + public void testPasswordAuthWrongPassword() throws Exception { + server.enablePasswordAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass"); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications password"); + } + + @Test(expected = TransportException.class) + public void testPasswordAuthNoPassword() throws Exception { + server.enablePasswordAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider(); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications password"); + } + + @Test(expected = TransportException.class) + public void testPasswordAuthCorrectPasswordTooLate() throws Exception { + server.enablePasswordAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass", "wrongpass", "wrongpass", + TEST_USER.toUpperCase(Locale.ROOT)); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications password"); + } + + @Test + public void testKeyboardInteractiveAuth() throws Exception { + server.enableKeyboardInteractiveAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider( + TEST_USER.toUpperCase(Locale.ROOT)); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications keyboard-interactive"); + } + + @Test + public void testKeyboardInteractiveAuthSeveralTimes() throws Exception { + server.enableKeyboardInteractiveAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT)); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications keyboard-interactive"); + } + + @Test(expected = TransportException.class) + public void testKeyboardInteractiveAuthWrongPassword() throws Exception { + server.enableKeyboardInteractiveAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass"); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications keyboard-interactive"); + } + + @Test(expected = TransportException.class) + public void testKeyboardInteractiveAuthNoPassword() throws Exception { + server.enableKeyboardInteractiveAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider(); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications keyboard-interactive"); + } + + @Test(expected = TransportException.class) + public void testKeyboardInteractiveAuthCorrectPasswordTooLate() + throws Exception { + server.enableKeyboardInteractiveAuthentication(); + TestCredentialsProvider provider = new TestCredentialsProvider( + "wrongpass", "wrongpass", "wrongpass", + TEST_USER.toUpperCase(Locale.ROOT)); + cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // + "Host git", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "PreferredAuthentications keyboard-interactive"); + } + + @Theory + public void testSshKeys(String keyName) throws Exception { + // JSch fails on ECDSA 384/521 keys. Compare + // https://sourceforge.net/p/jsch/patches/10/ + assumeTrue(!(getSessionFactory() instanceof JschConfigSessionFactory + && (keyName.contains("ed25519") + || keyName.startsWith("id_ecdsa_384") + || keyName.startsWith("id_ecdsa_521")))); + File cloned = new File(getTemporaryDirectory(), "cloned"); + String keyFileName = keyName + "_key"; + File privateKey = new File(sshDir, keyFileName); + copyTestResource(keyName, privateKey); + File publicKey = new File(sshDir, keyFileName + ".pub"); + copyTestResource(keyName + ".pub", publicKey); + server.setTestUserPublicKey(publicKey.toPath()); + TestCredentialsProvider provider = new TestCredentialsProvider( + "testpass"); + pushTo(provider, + cloneWith("ssh://localhost/doesntmatter", // + cloned, provider, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey.getAbsolutePath())); + int expectedCalls = keyName.endsWith("testpass") ? 1 : 0; + assertEquals("Unexpected calls to CredentialsProvider", expectedCalls, + provider.getLog().size()); + // Should now also work without credentials provider, even if the key + // was encrypted. + cloned = new File(getTemporaryDirectory(), "cloned2"); + pushTo(null, + cloneWith("ssh://localhost/doesntmatter", // + cloned, null, // + "Host localhost", // + "HostName localhost", // + "Port " + testPort, // + "User " + TEST_USER, // + "IdentityFile " + privateKey.getAbsolutePath())); + } +} diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java new file mode 100644 index 0000000000..59925a5a16 --- /dev/null +++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.ssh; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jgit.api.CloneCommand; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.PushCommand; +import org.eclipse.jgit.api.ResetCommand.ResetType; +import org.eclipse.jgit.errors.UnsupportedCredentialItem; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.junit.ssh.SshTestGitServer; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.PushResult; +import org.eclipse.jgit.transport.RemoteRefUpdate; +import org.eclipse.jgit.transport.SshSessionFactory; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.util.FS; +import org.junit.After; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.KeyPair; + +/** + * Root class for ssh tests. Sets up the ssh test server. A set of pre-computed + * keys for testing is provided in the bundle and can be used in test cases via + * {@link #copyTestResource(String, File)}. These test key files names have four + * components, separated by a single underscore: "id", the algorithm, the bits + * (if variable), and the password if the private key is encrypted. For instance + * "{@code id_ecdsa_384_testpass}" is an encrypted ECDSA-384 key. The passphrase + * to decrypt is "testpass". The key "{@code id_ecdsa_384}" is the same but + * unencrypted. All keys were generated and encrypted via ssh-keygen. Note that + * DSA and ec25519 have no "bits" component. Available keys are listed in + * {@link SshTestBase#KEY_RESOURCES}. + */ +public abstract class SshTestHarness extends RepositoryTestCase { + + protected static final String TEST_USER = "testuser"; + + protected File sshDir; + + protected File privateKey1; + + protected File privateKey2; + + protected File publicKey1; + + protected SshTestGitServer server; + + private SshSessionFactory factory; + + protected int testPort; + + protected File knownHosts; + + private File homeDir; + + @Override + public void setUp() throws Exception { + super.setUp(); + writeTrashFile("file.txt", "something"); + try (Git git = new Git(db)) { + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("Initial commit").call(); + } + mockSystemReader.setProperty("user.home", + getTemporaryDirectory().getAbsolutePath()); + mockSystemReader.setProperty("HOME", + getTemporaryDirectory().getAbsolutePath()); + homeDir = FS.DETECTED.userHome(); + FS.DETECTED.setUserHome(getTemporaryDirectory().getAbsoluteFile()); + sshDir = new File(getTemporaryDirectory(), ".ssh"); + assertTrue(sshDir.mkdir()); + File serverDir = new File(getTemporaryDirectory(), "srv"); + assertTrue(serverDir.mkdir()); + // Create two key pairs. Let's not call them "id_rsa". + privateKey1 = new File(sshDir, "first_key"); + privateKey2 = new File(sshDir, "second_key"); + publicKey1 = createKeyPair(privateKey1); + createKeyPair(privateKey2); + ByteArrayOutputStream publicHostKey = new ByteArrayOutputStream(); + // Start a server with our test user and the first key. + server = new SshTestGitServer(TEST_USER, publicKey1.toPath(), db, + createHostKey(publicHostKey)); + testPort = server.start(); + assertTrue(testPort > 0); + knownHosts = new File(sshDir, "known_hosts"); + Files.write(knownHosts.toPath(), Collections.singleton("[localhost]:" + + testPort + ' ' + + publicHostKey.toString(StandardCharsets.US_ASCII.name()))); + factory = createSessionFactory(); + SshSessionFactory.setInstance(factory); + } + + private static File createKeyPair(File privateKeyFile) throws Exception { + // Found no way to do this with MINA sshd except rolling it all + // ourselves... + JSch jsch = new JSch(); + KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048); + try (OutputStream out = new FileOutputStream(privateKeyFile)) { + pair.writePrivateKey(out); + } + File publicKeyFile = new File(privateKeyFile.getParentFile(), + privateKeyFile.getName() + ".pub"); + try (OutputStream out = new FileOutputStream(publicKeyFile)) { + pair.writePublicKey(out, TEST_USER); + } + return publicKeyFile; + } + + private static byte[] createHostKey(OutputStream publicKey) + throws Exception { + JSch jsch = new JSch(); + KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048); + pair.writePublicKey(publicKey, ""); + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + pair.writePrivateKey(out); + out.flush(); + return out.toByteArray(); + } + } + + /** + * Creates a new known_hosts file with one entry for the given host and port + * taken from the given public key file. + * + * @param file + * to write the known_hosts file to + * @param host + * for the entry + * @param port + * for the entry + * @param publicKey + * to use + * @return the public-key part of the line + * @throws IOException + */ + protected static String createKnownHostsFile(File file, String host, + int port, File publicKey) throws IOException { + List<String> lines = Files.readAllLines(publicKey.toPath(), + StandardCharsets.UTF_8); + assertEquals("Public key has too many lines", 1, lines.size()); + String pubKey = lines.get(0); + // Strip off the comment. + String[] parts = pubKey.split("\\s+"); + assertTrue("Unexpected key content", + parts.length == 2 || parts.length == 3); + String keyPart = parts[0] + ' ' + parts[1]; + String line = '[' + host + "]:" + port + ' ' + keyPart; + Files.write(file.toPath(), Collections.singletonList(line)); + return keyPart; + } + + /** + * Checks whether there is a line for the given host and port that also + * matches the given key part in the list of lines. + * + * @param host + * to look for + * @param port + * to look for + * @param keyPart + * to look for + * @param lines + * to look in + * @return {@code true} if found, {@code false} otherwise + */ + protected boolean hasHostKey(String host, int port, String keyPart, + List<String> lines) { + String h = '[' + host + "]:" + port; + return lines.stream() + .anyMatch(l -> l.contains(h) && l.contains(keyPart)); + } + + @After + public void shutdownServer() throws Exception { + if (server != null) { + server.stop(); + server = null; + } + FS.DETECTED.setUserHome(homeDir); + SshSessionFactory.setInstance(null); + factory = null; + } + + protected abstract SshSessionFactory createSessionFactory(); + + protected SshSessionFactory getSessionFactory() { + return factory; + } + + protected abstract void installConfig(String... config); + + /** + * Copies a test data file contained in the test bundle to the given file. + * Equivalent to {@link #copyTestResource(Class, String, File)} with + * {@code SshTestHarness.class} as first parameter. + * + * @param resourceName + * of the test resource to copy + * @param to + * file to copy the resource to + * @throws IOException + * if the resource cannot be copied + */ + protected void copyTestResource(String resourceName, File to) + throws IOException { + copyTestResource(SshTestHarness.class, resourceName, to); + } + + /** + * Copies a test data file contained in the test bundle to the given file, + * using {@link Class#getResourceAsStream(String)} to get the test resource. + * + * @param loader + * {@link Class} to use to load the resource + * @param resourceName + * of the test resource to copy + * @param to + * file to copy the resource to + * @throws IOException + * if the resource cannot be copied + */ + protected void copyTestResource(Class<?> loader, String resourceName, + File to) throws IOException { + try (InputStream in = loader.getResourceAsStream(resourceName)) { + Files.copy(in, to.toPath()); + } + } + + protected File cloneWith(String uri, File to, CredentialsProvider provider, + String... config) throws Exception { + installConfig(config); + CloneCommand clone = Git.cloneRepository().setCloneAllBranches(true) + .setDirectory(to).setURI(uri); + if (provider != null) { + clone.setCredentialsProvider(provider); + } + try (Git git = clone.call()) { + Repository repo = git.getRepository(); + assertNotNull(repo.resolve("master")); + assertNotEquals(db.getWorkTree(), + git.getRepository().getWorkTree()); + assertTrue(new File(git.getRepository().getWorkTree(), "file.txt") + .exists()); + return repo.getWorkTree(); + } + } + + protected void pushTo(File localClone) throws Exception { + pushTo(null, localClone); + } + + protected void pushTo(CredentialsProvider provider, File localClone) + throws Exception { + RevCommit commit; + File newFile = null; + try (Git git = Git.open(localClone)) { + // Write a new file and modify a file. + Repository local = git.getRepository(); + newFile = File.createTempFile("new", "sshtest", + local.getWorkTree()); + write(newFile, "something new"); + File existingFile = new File(local.getWorkTree(), "file.txt"); + write(existingFile, "something else"); + git.add().addFilepattern("file.txt") + .addFilepattern(newFile.getName()) + .call(); + commit = git.commit().setMessage("Local commit").call(); + // Push + PushCommand push = git.push().setPushAll(); + if (provider != null) { + push.setCredentialsProvider(provider); + } + Iterable<PushResult> results = push.call(); + for (PushResult result : results) { + for (RemoteRefUpdate u : result.getRemoteUpdates()) { + assertEquals( + "Could not update " + u.getRemoteName() + ' ' + + u.getMessage(), + RemoteRefUpdate.Status.OK, u.getStatus()); + } + } + } + // Now check "master" in the remote repo directly: + assertEquals("Unexpected remote commit", commit, db.resolve("master")); + assertEquals("Unexpected remote commit", commit, + db.resolve(Constants.HEAD)); + File remoteFile = new File(db.getWorkTree(), newFile.getName()); + assertFalse("File should not exist on remote", remoteFile.exists()); + try (Git git = new Git(db)) { + git.reset().setMode(ResetType.HARD).setRef(Constants.HEAD).call(); + } + assertTrue("File does not exist on remote", remoteFile.exists()); + checkFile(remoteFile, "something new"); + } + + protected static class TestCredentialsProvider extends CredentialsProvider { + + private final List<String> stringStore; + + private final Iterator<String> strings; + + public TestCredentialsProvider(String... strings) { + if (strings == null || strings.length == 0) { + stringStore = Collections.emptyList(); + } else { + stringStore = Arrays.asList(strings); + } + this.strings = stringStore.iterator(); + } + + @Override + public boolean isInteractive() { + return true; + } + + @Override + public boolean supports(CredentialItem... items) { + return true; + } + + @Override + public boolean get(URIish uri, CredentialItem... items) + throws UnsupportedCredentialItem { + System.out.println("URI: " + uri); + for (CredentialItem item : items) { + System.out.println(item.getClass().getSimpleName() + ' ' + + item.getPromptText()); + } + logItems(uri, items); + for (CredentialItem item : items) { + if (item instanceof CredentialItem.InformationalMessage) { + continue; + } + if (item instanceof CredentialItem.YesNoType) { + ((CredentialItem.YesNoType) item).setValue(true); + } else if (item instanceof CredentialItem.CharArrayType) { + if (strings.hasNext()) { + ((CredentialItem.CharArrayType) item) + .setValue(strings.next().toCharArray()); + } else { + return false; + } + } else if (item instanceof CredentialItem.StringType) { + if (strings.hasNext()) { + ((CredentialItem.StringType) item) + .setValue(strings.next()); + } else { + return false; + } + } else { + return false; + } + } + return true; + } + + private List<LogEntry> log = new ArrayList<>(); + + private void logItems(URIish uri, CredentialItem... items) { + log.add(new LogEntry(uri, Arrays.asList(items))); + } + + public List<LogEntry> getLog() { + return log; + } + } + + protected static class LogEntry { + + private URIish uri; + + private List<CredentialItem> items; + + public LogEntry(URIish uri, List<CredentialItem> items) { + this.uri = uri; + this.items = items; + } + + public URIish getURIish() { + return uri; + } + + public List<CredentialItem> getItems() { + return items; + } + } +} diff --git a/org.eclipse.jgit.test/tests.bzl b/org.eclipse.jgit.test/tests.bzl index bc06e3ef40..dc2102964f 100644 --- a/org.eclipse.jgit.test/tests.bzl +++ b/org.eclipse.jgit.test/tests.bzl @@ -41,7 +41,18 @@ def tests(tests): additional_deps = [ "//lib:jsch", ] - + if src.endswith("JSchSshTest.java"): + additional_deps = [ + "//lib:jsch", + "//lib:jzlib", + "//lib:sshd-core", + "//lib:sshd-sftp", + ":sshd-helpers", + ] + if src.endswith("JDKHttpConnectionTest.java"): + additional_deps = [ + "//lib:mockito", + ] heap_size = "-Xmx256m" if src.endswith("HugeCommitMessageTest.java"): heap_size = "-Xmx512m" diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java index 911f659149..1a5793ce31 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java @@ -43,6 +43,7 @@ */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.util.FileUtils.RECURSIVE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -118,7 +119,7 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddExistingSingleFile() throws IOException, GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } @@ -489,7 +490,7 @@ public class AddCommandTest extends RepositoryTestCase { GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("row1\r\nrow2"); } @@ -519,7 +520,7 @@ public class AddCommandTest extends RepositoryTestCase { data.append("row1\r\nrow2"); } String crData = data.toString(); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print(crData); } String lfData = data.toString().replaceAll("\r", ""); @@ -544,7 +545,7 @@ public class AddCommandTest extends RepositoryTestCase { GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("row1\r\nrow2\u0000"); } @@ -570,7 +571,7 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } @@ -588,7 +589,7 @@ public class AddCommandTest extends RepositoryTestCase { GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } @@ -597,7 +598,7 @@ public class AddCommandTest extends RepositoryTestCase { dc.getEntry(0).getObjectId(); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("other content"); } @@ -613,7 +614,7 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddExistingSingleFileTwiceWithCommit() throws Exception { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } @@ -624,7 +625,7 @@ public class AddCommandTest extends RepositoryTestCase { git.commit().setMessage("commit a.txt").call(); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("other content"); } @@ -640,7 +641,7 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddRemovedFile() throws Exception { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } @@ -663,7 +664,7 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddRemovedCommittedFile() throws Exception { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } @@ -690,13 +691,13 @@ public class AddCommandTest extends RepositoryTestCase { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } File file2 = new File(db.getWorkTree(), "b.txt"); FileUtils.createNewFile(file2); - try (PrintWriter writer = new PrintWriter(file2)) { + try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) { writer.print("content b"); } @@ -707,12 +708,12 @@ public class AddCommandTest extends RepositoryTestCase { addEntryToBuilder("b.txt", file2, newObjectInserter, builder, 0); addEntryToBuilder("a.txt", file, newObjectInserter, builder, 1); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("other content"); } addEntryToBuilder("a.txt", file, newObjectInserter, builder, 3); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("our content"); } addEntryToBuilder("a.txt", file, newObjectInserter, builder, 2) @@ -743,13 +744,13 @@ public class AddCommandTest extends RepositoryTestCase { public void testAddTwoFiles() throws Exception { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } File file2 = new File(db.getWorkTree(), "b.txt"); FileUtils.createNewFile(file2); - try (PrintWriter writer = new PrintWriter(file2)) { + try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) { writer.print("content b"); } @@ -767,13 +768,13 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - try (PrintWriter writer = new PrintWriter(file2)) { + try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) { writer.print("content b"); } @@ -791,19 +792,19 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } File ignoreFile = new File(db.getWorkTree(), ".gitignore"); FileUtils.createNewFile(ignoreFile); - try (PrintWriter writer = new PrintWriter(ignoreFile)) { + try (PrintWriter writer = new PrintWriter(ignoreFile, UTF_8.name())) { writer.print("sub/b.txt"); } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - try (PrintWriter writer = new PrintWriter(file2)) { + try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) { writer.print("content b"); } @@ -821,13 +822,13 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - try (PrintWriter writer = new PrintWriter(file2)) { + try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) { writer.print("content b"); } @@ -849,13 +850,13 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - try (PrintWriter writer = new PrintWriter(file2)) { + try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) { writer.print("content b"); } @@ -872,12 +873,12 @@ public class AddCommandTest extends RepositoryTestCase { // new unstaged file sub/c.txt File file3 = new File(db.getWorkTree(), "sub/c.txt"); FileUtils.createNewFile(file3); - try (PrintWriter writer = new PrintWriter(file3)) { + try (PrintWriter writer = new PrintWriter(file3, UTF_8.name())) { writer.print("content c"); } // file sub/a.txt is modified - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("modified content"); } @@ -904,13 +905,13 @@ public class AddCommandTest extends RepositoryTestCase { FileUtils.mkdir(new File(db.getWorkTree(), "sub")); File file = new File(db.getWorkTree(), "sub/a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } File file2 = new File(db.getWorkTree(), "sub/b.txt"); FileUtils.createNewFile(file2); - try (PrintWriter writer = new PrintWriter(file2)) { + try (PrintWriter writer = new PrintWriter(file2, UTF_8.name())) { writer.print("content b"); } @@ -927,12 +928,12 @@ public class AddCommandTest extends RepositoryTestCase { // new unstaged file sub/c.txt File file3 = new File(db.getWorkTree(), "sub/c.txt"); FileUtils.createNewFile(file3); - try (PrintWriter writer = new PrintWriter(file3)) { + try (PrintWriter writer = new PrintWriter(file3, UTF_8.name())) { writer.print("content c"); } // file sub/a.txt is modified - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("modified content"); } @@ -1244,7 +1245,8 @@ public class AddCommandTest extends RepositoryTestCase { ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true); config.save(); - assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks()); + assertTrue( + db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks()); try (Git git = new Git(db)) { git.add().addFilepattern("nested-repo").call(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java index 1300f98d8a..1c41018161 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -190,7 +191,8 @@ public class ArchiveCommandTest extends RepositoryTestCase { } } - private class MockFormat implements ArchiveCommand.Format<MockOutputStream> { + private static class MockFormat + implements ArchiveCommand.Format<MockOutputStream> { private Map<String, String> entries = new HashMap<>(); @@ -230,7 +232,9 @@ public class ArchiveCommandTest extends RepositoryTestCase { @Override public void putEntry(MockOutputStream out, ObjectId tree, String path, FileMode mode, ObjectLoader loader) { - String content = mode != FileMode.TREE ? new String(loader.getBytes()) : null; + String content = mode != FileMode.TREE + ? new String(loader.getBytes(), UTF_8) + : null; entries.put(path, content); } @@ -240,7 +244,7 @@ public class ArchiveCommandTest extends RepositoryTestCase { } } - public class MockOutputStream extends OutputStream { + public static class MockOutputStream extends OutputStream { private int foo; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java index 7a1d222ca0..7e73084e8e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java @@ -44,6 +44,7 @@ package org.eclipse.jgit.api; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import java.io.File; @@ -489,4 +490,73 @@ public class BlameCommandTest extends RepositoryTestCase { assertEquals(side, lines.getSourceCommit(2)); } } + + @Test + public void testBlameWithNulByteInHistory() throws Exception { + try (Git git = new Git(db)) { + String[] content1 = { "First line", "Another line" }; + writeTrashFile("file.txt", join(content1)); + git.add().addFilepattern("file.txt").call(); + RevCommit c1 = git.commit().setMessage("create file").call(); + + String[] content2 = { "First line", "Second line with NUL >\000<", + "Another line" }; + assertTrue("Content should contain a NUL byte", + content2[1].indexOf(0) > 0); + writeTrashFile("file.txt", join(content2)); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("add line with NUL").call(); + + String[] content3 = { "First line", "Second line with NUL >\000<", + "Third line" }; + writeTrashFile("file.txt", join(content3)); + git.add().addFilepattern("file.txt").call(); + RevCommit c3 = git.commit().setMessage("change third line").call(); + + String[] content4 = { "First line", "Second line with NUL >\\000<", + "Third line" }; + assertTrue("Content should not contain a NUL byte", + content4[1].indexOf(0) < 0); + writeTrashFile("file.txt", join(content4)); + git.add().addFilepattern("file.txt").call(); + RevCommit c4 = git.commit().setMessage("fix NUL line").call(); + + BlameResult lines = git.blame().setFilePath("file.txt").call(); + assertEquals(3, lines.getResultContents().size()); + assertEquals(c1, lines.getSourceCommit(0)); + assertEquals(c4, lines.getSourceCommit(1)); + assertEquals(c3, lines.getSourceCommit(2)); + } + } + + @Test + public void testBlameWithNulByteInTopRevision() throws Exception { + try (Git git = new Git(db)) { + String[] content1 = { "First line", "Another line" }; + writeTrashFile("file.txt", join(content1)); + git.add().addFilepattern("file.txt").call(); + RevCommit c1 = git.commit().setMessage("create file").call(); + + String[] content2 = { "First line", "Second line with NUL >\000<", + "Another line" }; + assertTrue("Content should contain a NUL byte", + content2[1].indexOf(0) > 0); + writeTrashFile("file.txt", join(content2)); + git.add().addFilepattern("file.txt").call(); + RevCommit c2 = git.commit().setMessage("add line with NUL").call(); + + String[] content3 = { "First line", "Second line with NUL >\000<", + "Third line" }; + writeTrashFile("file.txt", join(content3)); + git.add().addFilepattern("file.txt").call(); + RevCommit c3 = git.commit().setMessage("change third line").call(); + + BlameResult lines = git.blame().setFilePath("file.txt").call(); + assertEquals(3, lines.getResultContents().size()); + assertEquals(c1, lines.getSourceCommit(0)); + assertEquals(c2, lines.getSourceCommit(1)); + assertEquals(c3, lines.getSourceCommit(2)); + } + } + } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java index 71df59e1ff..498005deda 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java @@ -154,7 +154,7 @@ public class CheckoutCommandTest extends RepositoryTestCase { } @Test - public void testCheckoutWithConflict() { + public void testCheckoutWithConflict() throws Exception { CheckoutCommand co = git.checkout(); try { writeTrashFile("Test.txt", "Another change"); @@ -165,6 +165,8 @@ public class CheckoutCommandTest extends RepositoryTestCase { assertEquals(Status.CONFLICTS, co.getResult().getStatus()); assertTrue(co.getResult().getConflictList().contains("Test.txt")); } + git.checkout().setName("master").setForce(true).call(); + assertThat(read("Test.txt"), is("Hello world")); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java index 065b5b4c3e..139f199f7a 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java @@ -234,6 +234,27 @@ public class CleanCommandTest extends RepositoryTestCase { } @Test + public void testCleanDirsWithPrefixFolder() throws Exception { + String path = "sub/foo.txt"; + writeTrashFile(path, "sub is a prefix of sub-noclean"); + git.add().addFilepattern(path).call(); + Status beforeCleanStatus = git.status().call(); + assertTrue(beforeCleanStatus.getAdded().contains(path)); + + Set<String> cleanedFiles = git.clean().setCleanDirectories(true).call(); + + // The "sub" directory should not be cleaned. + assertTrue(!cleanedFiles.contains(path + "/")); + + assertTrue(cleanedFiles.contains("File2.txt")); + assertTrue(cleanedFiles.contains("File3.txt")); + assertTrue(!cleanedFiles.contains("sub-noclean/File1.txt")); + assertTrue(cleanedFiles.contains("sub-noclean/File2.txt")); + assertTrue(cleanedFiles.contains("sub-clean/")); + assertTrue(cleanedFiles.size() == 4); + } + + @Test public void testCleanDirsWithSubmodule() throws Exception { SubmoduleAddCommand command = new SubmoduleAddCommand(db); String path = "sub"; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java index 0d7009dca4..6b5fe502b7 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java @@ -370,8 +370,7 @@ public class CloneCommandTest extends RepositoryTestCase { } @Test - public void testCloneRepositoryOnlyOneBranch() throws IOException, - JGitInternalException, GitAPIException { + public void testCloneRepositoryOnlyOneBranch() throws Exception { File directory = createTempDirectory("testCloneRepositoryWithBranch"); CloneCommand command = Git.cloneRepository(); command.setBranch("refs/heads/master"); @@ -382,25 +381,47 @@ public class CloneCommandTest extends RepositoryTestCase { Git git2 = command.call(); addRepoToClose(git2.getRepository()); assertNotNull(git2); + assertNull(git2.getRepository().resolve("tag-for-blob")); + assertNotNull(git2.getRepository().resolve("tag-initial")); assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master"); assertEquals("refs/remotes/origin/master", allRefNames(git2 .branchList().setListMode(ListMode.REMOTE).call())); + RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(), + Constants.DEFAULT_REMOTE_NAME); + List<RefSpec> specs = cfg.getFetchRefSpecs(); + assertEquals(1, specs.size()); + assertEquals( + new RefSpec("+refs/heads/master:refs/remotes/origin/master"), + specs.get(0)); + } + @Test + public void testBareCloneRepositoryOnlyOneBranch() throws Exception { // Same thing, but now test with bare repo - directory = createTempDirectory("testCloneRepositoryWithBranch_bare"); - command = Git.cloneRepository(); + File directory = createTempDirectory( + "testCloneRepositoryWithBranch_bare"); + CloneCommand command = Git.cloneRepository(); command.setBranch("refs/heads/master"); command.setBranchesToClone(Collections .singletonList("refs/heads/master")); command.setDirectory(directory); command.setURI(fileUri()); command.setBare(true); - git2 = command.call(); + Git git2 = command.call(); addRepoToClose(git2.getRepository()); assertNotNull(git2); + assertNull(git2.getRepository().resolve("tag-for-blob")); + assertNotNull(git2.getRepository().resolve("tag-initial")); assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master"); assertEquals("refs/heads/master", allRefNames(git2.branchList() .setListMode(ListMode.ALL).call())); + RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(), + Constants.DEFAULT_REMOTE_NAME); + List<RefSpec> specs = cfg.getFetchRefSpecs(); + assertEquals(1, specs.size()); + assertEquals( + new RefSpec("+refs/heads/master:refs/heads/master"), + specs.get(0)); } public static String allRefNames(List<Ref> refs) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java index ca0630ea35..c028ca300c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -120,7 +121,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { // create first file File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content1"); } @@ -131,7 +132,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { // create second file file = new File(db.getWorkTree(), "b.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content2"); } @@ -231,7 +232,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { JGitInternalException, GitAPIException { File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content"); } @@ -242,7 +243,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea", tw.getObjectId(0).getName()); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content2"); } commit = git.commit().setMessage("second commit").call(); @@ -265,7 +266,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { // create file File file = new File(db.getWorkTree(), "a.txt"); FileUtils.createNewFile(file); - try (PrintWriter writer = new PrintWriter(file)) { + try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) { writer.print("content1"); } @@ -358,7 +359,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { messageHeader + messageFooter) .setInsertChangeId(true).call(); // we should find a real change id (at the end of the file) - byte[] chars = commit.getFullMessage().getBytes(); + byte[] chars = commit.getFullMessage().getBytes(UTF_8); int lastLineBegin = RawParseUtils.prevLF(chars, chars.length - 2); String lastLine = RawParseUtils.decode(chars, lastLineBegin + 1, chars.length); @@ -371,7 +372,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { .setInsertChangeId(true).call(); // we should find a real change id (in the line as dictated by the // template) - chars = commit.getFullMessage().getBytes(); + chars = commit.getFullMessage().getBytes(UTF_8); int lineStart = 0; int lineEnd = 0; for (int i = 0; i < 4; i++) { @@ -389,7 +390,7 @@ public class CommitAndLogCommandTest extends RepositoryTestCase { messageHeader + changeIdTemplate + messageFooter) .setInsertChangeId(false).call(); // we should find the untouched template - chars = commit.getFullMessage().getBytes(); + chars = commit.getFullMessage().getBytes(UTF_8); lineStart = 0; lineEnd = 0; for (int i = 0; i < 4; i++) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitOnlyTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitOnlyTest.java index bbd6ec0bde..83181eea23 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitOnlyTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitOnlyTest.java @@ -43,6 +43,7 @@ */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -1298,7 +1299,7 @@ public class CommitOnlyTest extends RepositoryTestCase { final TreeWalk tw = TreeWalk.forPath(repo, path, rw.parseTree(headId)); return new String(tw.getObjectReader().open(tw.getObjectId(0)) - .getBytes()); + .getBytes(), UTF_8); } } catch (Exception e) { return ""; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java new file mode 100644 index 0000000000..c72612850a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.api; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jgit.api.ResetCommand.ResetType; +import org.eclipse.jgit.junit.MockSystemReader; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.CoreConfig.EolStreamType; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; +import org.eclipse.jgit.util.SystemReader; +import org.junit.Test; + +public class CrLfNativeTest extends RepositoryTestCase { + + @Test + public void checkoutWithCrLfNativeUnix() throws Exception { + verifyNativeCheckout(new MockSystemReader() { + { + setUnix(); + } + }); + } + + @Test + public void checkoutWithCrLfNativeWindows() throws Exception { + verifyNativeCheckout(new MockSystemReader() { + { + setWindows(); + } + }); + } + + private void verifyNativeCheckout(SystemReader systemReader) + throws Exception { + SystemReader.setInstance(systemReader); + Git git = Git.wrap(db); + FileBasedConfig config = db.getConfig(); + config.setString("core", null, "autocrlf", "false"); + config.setString("core", null, "eol", "native"); + config.save(); + // core.eol is active only if text is set, or if text=auto + writeTrashFile(".gitattributes", "*.txt text\n"); + File file = writeTrashFile("file.txt", "line 1\nline 2\n"); + git.add().addFilepattern("file.txt").addFilepattern(".gitattributes") + .call(); + git.commit().setMessage("Initial").call(); + // Check-in with core.eol=native normalization + assertEquals( + "[.gitattributes, mode:100644, content:*.txt text\n]" + + "[file.txt, mode:100644, content:line 1\nline 2\n]", + indexState(CONTENT)); + writeTrashFile("file.txt", "something else"); + git.add().addFilepattern("file.txt").call(); + git.commit().setMessage("New commit").call(); + git.reset().setMode(ResetType.HARD).setRef("HEAD~").call(); + // Check-out should convert to the native line separator + checkFile(file, systemReader.isWindows() ? "line 1\r\nline 2\r\n" + : "line 1\nline 2\n"); + Status status = git.status().call(); + assertTrue("git status should be clean", status.isClean()); + } + + /** + * Verifies the handling of the crlf attribute: crlf == text, -crlf == + * -text, crlf=input == eol=lf + * + * @throws Exception + */ + @Test + public void testCrLfAttribute() throws Exception { + FileBasedConfig config = db.getConfig(); + config.setString("core", null, "autocrlf", "false"); + config.setString("core", null, "eol", "crlf"); + config.save(); + writeTrashFile(".gitattributes", + "*.txt text\n*.crlf crlf\n*.bin -text\n*.nocrlf -crlf\n*.input crlf=input\n*.eol eol=lf"); + writeTrashFile("foo.txt", ""); + writeTrashFile("foo.crlf", ""); + writeTrashFile("foo.bin", ""); + writeTrashFile("foo.nocrlf", ""); + writeTrashFile("foo.input", ""); + writeTrashFile("foo.eol", ""); + Map<String, EolStreamType> inTypes = new HashMap<>(); + Map<String, EolStreamType> outTypes = new HashMap<>(); + try (TreeWalk walk = new TreeWalk(db)) { + walk.addTree(new FileTreeIterator(db)); + while (walk.next()) { + String path = walk.getPathString(); + if (".gitattributes".equals(path)) { + continue; + } + EolStreamType in = walk + .getEolStreamType(OperationType.CHECKIN_OP); + EolStreamType out = walk + .getEolStreamType(OperationType.CHECKOUT_OP); + inTypes.put(path, in); + outTypes.put(path, out); + } + } + assertEquals("", checkTypes("check-in", inTypes)); + assertEquals("", checkTypes("check-out", outTypes)); + } + + private String checkTypes(String prefix, Map<String, EolStreamType> types) { + StringBuilder result = new StringBuilder(); + EolStreamType a = types.get("foo.crlf"); + EolStreamType b = types.get("foo.txt"); + report(result, prefix, "crlf != text", a, b); + a = types.get("foo.nocrlf"); + b = types.get("foo.bin"); + report(result, prefix, "-crlf != -text", a, b); + a = types.get("foo.input"); + b = types.get("foo.eol"); + report(result, prefix, "crlf=input != eol=lf", a, b); + return result.toString(); + } + + private void report(StringBuilder result, String prefix, String label, + EolStreamType a, + EolStreamType b) { + if (a == null || b == null || !a.equals(b)) { + result.append(prefix).append(' ').append(label).append(": ") + .append(toString(a)).append(" != ").append(toString(b)) + .append('\n'); + } + } + + private String toString(EolStreamType type) { + return type == null ? "null" : type.name(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java index f2093e3940..807079eb23 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java @@ -42,14 +42,16 @@ */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; import java.util.Arrays; import java.util.Collection; @@ -427,7 +429,7 @@ public class DescribeCommandTest extends RepositoryTestCase { } private static void touch(File f, String contents) throws Exception { - try (FileWriter w = new FileWriter(f)) { + try (BufferedWriter w = Files.newBufferedWriter(f.toPath(), UTF_8)) { w.write(contents); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java index 48d373344e..47806cb99d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java @@ -38,6 +38,7 @@ */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -722,10 +723,10 @@ public class EolRepositoryTest extends RepositoryTestCase { } e.attrs = e.attrs.trim(); e.file = new String( - IO.readFully(new File(db.getWorkTree(), pathName))); + IO.readFully(new File(db.getWorkTree(), pathName)), UTF_8); DirCacheEntry dce = dirCache.getEntry(pathName); ObjectLoader open = walk.getObjectReader().open(dce.getObjectId()); - e.index = new String(open.getBytes()); + e.index = new String(open.getBytes(), UTF_8); e.indexContentLength = dce.getLength(); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java index ca86d81301..98dfcc083e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java @@ -148,7 +148,7 @@ public class PushCommandTest extends RepositoryTestCase { git1.push().setRemote("test").setRefSpecs(spec).call(); assertEquals("1:test, 2:" + uri + ", 3:\n" + "refs/heads/master " + commit.getName() + " refs/heads/x " - + ObjectId.zeroId().name(), read(hookOutput)); + + ObjectId.zeroId().name() + "\n", read(hookOutput)); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java index 2b97b307bf..9b12011aab 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java @@ -82,6 +82,8 @@ public class ResetCommandTest extends RepositoryTestCase { private File indexFile; + private File indexNestedFile; + private File untrackedFile; private DirCacheEntry prestage; @@ -97,7 +99,7 @@ public class ResetCommandTest extends RepositoryTestCase { indexFile = writeTrashFile("a.txt", "content"); // create nested file - writeTrashFile("dir/b.txt", "content"); + indexNestedFile = writeTrashFile("dir/b.txt", "content"); // add files and commit them git.add().addFilepattern("a.txt").addFilepattern("dir/b.txt").call(); @@ -119,13 +121,16 @@ public class ResetCommandTest extends RepositoryTestCase { AmbiguousObjectException, IOException, GitAPIException { setupRepository(); ObjectId prevHead = db.resolve(Constants.HEAD); - assertSameAsHead(git.reset().setMode(ResetType.HARD) + ResetCommand reset = git.reset(); + assertSameAsHead(reset.setMode(ResetType.HARD) .setRef(initialCommit.getName()).call()); + assertFalse("reflog should be enabled", reset.isReflogDisabled()); // check if HEAD points to initial commit now ObjectId head = db.resolve(Constants.HEAD); assertEquals(initialCommit, head); // check if files were removed assertFalse(indexFile.exists()); + assertFalse(indexNestedFile.exists()); assertTrue(untrackedFile.exists()); // fileInIndex must no longer be in HEAD and in the index String fileInIndexPath = indexFile.getAbsolutePath(); @@ -148,6 +153,7 @@ public class ResetCommandTest extends RepositoryTestCase { assertEquals(initialCommit, head); // check if files were removed assertFalse(indexFile.exists()); + assertFalse(indexNestedFile.exists()); assertTrue(untrackedFile.exists()); // fileInIndex must no longer be in HEAD and in the index String fileInIndexPath = indexFile.getAbsolutePath(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java index 196c4f7d9c..08553e1ae4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java @@ -415,6 +415,14 @@ public class AttributesMatcherTest { } } + @Test + public void testFileNameWithLineTerminator() { + assertMatched("a?", "a\r"); + assertMatched("a?", "dir/a\r"); + assertMatched("*a", "\ra"); + assertMatched("dir/*a*", "dir/\ra\r"); + } + /** * Check for a match. If target ends with "/", match will assume that the * target is meant to be a directory. diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java index f0d3c3690f..f4ccf0506b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.attributes; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.attributes.Attribute.State.SET; import static org.eclipse.jgit.attributes.Attribute.State.UNSET; import static org.junit.Assert.assertEquals; @@ -88,7 +89,7 @@ public class AttributesNodeTest { String attributeFileContent = "*.type1 A -B C=value\n" + "*.type2 -A B C=value2"; - is = new ByteArrayInputStream(attributeFileContent.getBytes()); + is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8)); AttributesNode node = new AttributesNode(); node.parse(is); assertAttribute("file.type1", node, @@ -102,7 +103,7 @@ public class AttributesNodeTest { String attributeFileContent = "!*.type1 A -B C=value\n" + "!*.type2 -A B C=value2"; - is = new ByteArrayInputStream(attributeFileContent.getBytes()); + is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8)); AttributesNode node = new AttributesNode(); node.parse(is); assertAttribute("file.type1", node, new Attributes()); @@ -113,7 +114,7 @@ public class AttributesNodeTest { public void testEmptyNegativeAttributeKey() throws IOException { String attributeFileContent = "*.type1 - \n" // + "*.type2 - -A"; - is = new ByteArrayInputStream(attributeFileContent.getBytes()); + is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8)); AttributesNode node = new AttributesNode(); node.parse(is); assertAttribute("file.type1", node, new Attributes()); @@ -125,7 +126,7 @@ public class AttributesNodeTest { String attributeFileContent = "*.type1 = \n" // + "*.type2 =value\n"// + "*.type3 attr=\n"; - is = new ByteArrayInputStream(attributeFileContent.getBytes()); + is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8)); AttributesNode node = new AttributesNode(); node.parse(is); assertAttribute("file.type1", node, new Attributes()); @@ -140,7 +141,7 @@ public class AttributesNodeTest { + " \n" // + "*.type2 -A B C=value2"; - is = new ByteArrayInputStream(attributeFileContent.getBytes()); + is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8)); AttributesNode node = new AttributesNode(); node.parse(is); assertAttribute("file.type1", node, @@ -156,7 +157,7 @@ public class AttributesNodeTest { + "*.type3 \t\t B\n" // + "*.type3\t-A";// - is = new ByteArrayInputStream(attributeFileContent.getBytes()); + is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8)); AttributesNode node = new AttributesNode(); node.parse(is); assertAttribute("file.type1", node, @@ -170,7 +171,7 @@ public class AttributesNodeTest { public void testDoubleAsteriskAtEnd() throws IOException { String attributeFileContent = "dir/** \tA -B\tC=value"; - is = new ByteArrayInputStream(attributeFileContent.getBytes()); + is = new ByteArrayInputStream(attributeFileContent.getBytes(UTF_8)); AttributesNode node = new AttributesNode(); node.parse(is); assertAttribute("dir", node, diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java index 73c230ac68..de768118bf 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java @@ -59,9 +59,12 @@ import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; +import org.eclipse.jgit.internal.storage.file.FileRepository; import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.treewalk.EmptyTreeIterator; import org.eclipse.jgit.treewalk.FileTreeIterator; @@ -417,4 +420,64 @@ public class DiffEntryTest extends RepositoryTestCase { assertEquals(FileMode.REGULAR_FILE, diff.getOldMode()); } } + + @Test + public void shouldReportSubmoduleReplacedByFileMove() throws Exception { + // Create a submodule + FileRepository submoduleStandalone = createWorkRepository(); + JGitTestUtil.writeTrashFile(submoduleStandalone, "fileInSubmodule", + "submodule"); + Git submoduleStandaloneGit = Git.wrap(submoduleStandalone); + submoduleStandaloneGit.add().addFilepattern("fileInSubmodule").call(); + submoduleStandaloneGit.commit().setMessage("add file to submodule") + .call(); + + Repository submodule_db = Git.wrap(db).submoduleAdd() + .setPath("modules/submodule") + .setURI(submoduleStandalone.getDirectory().toURI().toString()) + .call(); + File submodule_trash = submodule_db.getWorkTree(); + addRepoToClose(submodule_db); + writeTrashFile("fileInRoot", "root"); + Git rootGit = Git.wrap(db); + rootGit.add().addFilepattern("fileInRoot").call(); + rootGit.commit().setMessage("add submodule and root file").call(); + // Dummy change on fileInRoot + writeTrashFile("fileInRoot", "changed"); + rootGit.add().addFilepattern("fileInRoot").call(); + RevCommit firstCommit = rootGit.commit().setMessage("change root file") + .call(); + // Remove the submodule again and move fileInRoot into that subfolder + rootGit.rm().setCached(true).addFilepattern("modules/submodule").call(); + recursiveDelete(submodule_trash); + JGitTestUtil.deleteTrashFile(db, "fileInRoot"); + // Move the fileInRoot file + writeTrashFile("modules/submodule/fileInRoot", "changed"); + rootGit.rm().addFilepattern("fileInRoot").addFilepattern("modules/") + .call(); + rootGit.add().addFilepattern("modules/").call(); + RevCommit secondCommit = rootGit.commit() + .setMessage("remove submodule and move root file") + .call(); + // Diff should report submodule having been deleted and file moved + // (deleted and added) + try (TreeWalk walk = new TreeWalk(db)) { + walk.addTree(firstCommit.getTree()); + walk.addTree(secondCommit.getTree()); + walk.setRecursive(true); + List<DiffEntry> diffs = DiffEntry.scan(walk); + assertEquals(3, diffs.size()); + DiffEntry e = diffs.get(0); + assertEquals(DiffEntry.ChangeType.DELETE, e.getChangeType()); + assertEquals("fileInRoot", e.getOldPath()); + e = diffs.get(1); + assertEquals(DiffEntry.ChangeType.DELETE, e.getChangeType()); + assertEquals("modules/submodule", e.getOldPath()); + assertEquals(FileMode.GITLINK, e.getOldMode()); + e = diffs.get(2); + assertEquals(DiffEntry.ChangeType.ADD, e.getChangeType()); + assertEquals("modules/submodule/fileInRoot", e.getNewPath()); + } + + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java index 5885d9b7e6..178d62072d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java @@ -66,13 +66,16 @@ public class RawTextTest { } @Test - public void testBinary() { + public void testNul() { String input = "foo-a\nf\0o-b\n"; byte[] data = Constants.encodeASCII(input); final RawText a = new RawText(data); assertArrayEquals(a.content, data); - assertEquals(a.size(), 1); - assertEquals(a.getString(0, 1, false), input); + assertEquals(2, a.size()); + assertEquals("foo-a\n", a.getString(0, 1, false)); + assertEquals("f\0o-b\n", a.getString(1, 2, false)); + assertEquals("foo-a", a.getString(0, 1, true)); + assertEquals("f\0o-b", a.getString(1, 2, true)); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java index d12f302dae..b6291bfca4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java @@ -89,6 +89,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase { assertEquals(0, e.getRawMode()); try { b.add(e); + fail("did not reject unset file mode"); } catch (IllegalArgumentException err) { assertEquals("FileMode not set for path a", err.getMessage()); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java index 92f0cf6196..1f6861b356 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java @@ -52,16 +52,20 @@ import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileReader; import java.io.IOException; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.InvalidRefNameException; import org.eclipse.jgit.api.errors.InvalidRemoteException; -import org.eclipse.jgit.api.errors.RefNotFoundException; +import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile; +import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.BlobBasedConfig; @@ -73,6 +77,7 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.util.FS; import org.junit.Test; @@ -139,8 +144,9 @@ public class RepoCommandTest extends RepositoryTestCase { resolveRelativeUris(); } - class IndexedRepos implements RepoCommand.RemoteReader { + static class IndexedRepos implements RepoCommand.RemoteReader { Map<String, Repository> uriRepoMap; + IndexedRepos() { uriRepoMap = new HashMap<>(); } @@ -169,19 +175,21 @@ public class RepoCommandTest extends RepositoryTestCase { } @Override - public byte[] readFile(String uri, String refName, String path) - throws GitAPIException, IOException { + public RemoteFile readFileWithMode(String uri, String ref, String path) + throws GitAPIException, IOException { Repository repo = uriRepoMap.get(uri); - - String idStr = refName + ":" + path; - ObjectId id = repo.resolve(idStr); - if (id == null) { - throw new RefNotFoundException( - String.format("repo %s does not have %s", repo.toString(), idStr)); - } - try (ObjectReader reader = repo.newObjectReader()) { - return reader.open(id).getCachedBytes(Integer.MAX_VALUE); + ObjectId refCommitId = sha1(uri, ref); + if (refCommitId == null) { + throw new InvalidRefNameException(MessageFormat + .format(JGitText.get().refNotResolved, ref)); } + RevCommit commit = repo.parseCommit(refCommitId); + TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree()); + + // TODO(ifrade): Cope better with big files (e.g. using InputStream + // instead of byte[]) + return new RemoteFile(tw.getObjectReader().open(tw.getObjectId(0)) + .getCachedBytes(Integer.MAX_VALUE), tw.getFileMode(0)); } } @@ -199,6 +207,15 @@ public class RepoCommandTest extends RepositoryTestCase { return r; } + private static void assertContents(Path path, String expected) + throws IOException { + try (BufferedReader reader = Files.newBufferedReader(path, UTF_8)) { + String content = reader.readLine(); + assertEquals("Unexpected content in " + path.getFileName(), + expected, content); + } + } + @Test public void runTwiceIsNOP() throws Exception { try (Repository child = cloneRepository(groupADb, true); @@ -474,12 +491,7 @@ public class RepoCommandTest extends RepositoryTestCase { .call(); File hello = new File(db.getWorkTree(), "foo/hello.txt"); assertTrue("submodule should be checked out", hello.exists()); - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { - String content = reader.readLine(); - assertEquals("submodule content should be as expected", - "master world", content); - } + assertContents(hello.toPath(), "master world"); } @Test @@ -565,20 +577,66 @@ public class RepoCommandTest extends RepositoryTestCase { // The original file should exist File hello = new File(localDb.getWorkTree(), "foo/hello.txt"); assertTrue("The original file should exist", hello.exists()); - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { + assertFalse("The original file should not be executable", + hello.canExecute()); + assertContents(hello.toPath(), "master world"); + // The dest file should also exist + hello = new File(localDb.getWorkTree(), "Hello"); + assertTrue("The destination file should exist", hello.exists()); + assertFalse("The destination file should not be executable", + hello.canExecute()); + assertContents(hello.toPath(), "master world"); + } + + @Test + public void testRepoManifestCopyFile_executable() throws Exception { + try (Git git = new Git(defaultDb)) { + git.checkout().setName("master").call(); + File f = JGitTestUtil.writeTrashFile(defaultDb, "hello.sh", + "content of the executable file"); + f.setExecutable(true); + git.add().addFilepattern("hello.sh").call(); + git.commit().setMessage("Add binary file").call(); + } + + Repository localDb = createWorkRepository(); + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") + .append("<manifest>") + .append("<remote name=\"remote1\" fetch=\".\" />") + .append("<default revision=\"master\" remote=\"remote1\" />") + .append("<project path=\"foo\" name=\"").append(defaultUri) + .append("\">") + .append("<copyfile src=\"hello.sh\" dest=\"copy-hello.sh\" />") + .append("</project>").append("</manifest>"); + JGitTestUtil.writeTrashFile(localDb, "manifest.xml", + xmlContent.toString()); + RepoCommand command = new RepoCommand(localDb); + command.setPath( + localDb.getWorkTree().getAbsolutePath() + "/manifest.xml") + .setURI(rootUri).call(); + + // The original file should exist and be an executable + File hello = new File(localDb.getWorkTree(), "foo/hello.sh"); + assertTrue("The original file should exist", hello.exists()); + assertTrue("The original file must be executable", hello.canExecute()); + try (BufferedReader reader = Files.newBufferedReader(hello.toPath(), + UTF_8)) { String content = reader.readLine(); assertEquals("The original file should have expected content", - "master world", content); + "content of the executable file", content); } - // The dest file should also exist - hello = new File(localDb.getWorkTree(), "Hello"); + + // The destination file should also exist and be an executable + hello = new File(localDb.getWorkTree(), "copy-hello.sh"); assertTrue("The destination file should exist", hello.exists()); - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { + assertTrue("The destination file must be executable", + hello.canExecute()); + try (BufferedReader reader = Files.newBufferedReader(hello.toPath(), + UTF_8)) { String content = reader.readLine(); assertEquals("The destination file should have expected content", - "master world", content); + "content of the executable file", content); } } @@ -610,8 +668,8 @@ public class RepoCommandTest extends RepositoryTestCase { assertTrue("The .gitmodules file should exist", gitmodules.exists()); // The first line of .gitmodules file should be expected - try (BufferedReader reader = new BufferedReader( - new FileReader(gitmodules))) { + try (BufferedReader reader = Files + .newBufferedReader(gitmodules.toPath(), UTF_8)) { String content = reader.readLine(); assertEquals( "The first line of .gitmodules file should be as expected", @@ -644,8 +702,8 @@ public class RepoCommandTest extends RepositoryTestCase { .setURI(rootUri) .call(); File hello = new File(db.getWorkTree(), "foo/hello.txt"); - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { + try (BufferedReader reader = Files.newBufferedReader(hello.toPath(), + UTF_8)) { String content = reader.readLine(); assertEquals("submodule content should be as expected", "branch world", content); @@ -671,12 +729,7 @@ public class RepoCommandTest extends RepositoryTestCase { .setURI(rootUri) .call(); File hello = new File(db.getWorkTree(), "foo/hello.txt"); - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { - String content = reader.readLine(); - assertEquals("submodule content should be as expected", - "branch world", content); - } + assertContents(hello.toPath(), "branch world"); } @Test @@ -698,12 +751,7 @@ public class RepoCommandTest extends RepositoryTestCase { .setURI(rootUri) .call(); File hello = new File(db.getWorkTree(), "foo/hello.txt"); - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { - String content = reader.readLine(); - assertEquals("submodule content should be as expected", - "branch world", content); - } + assertContents(hello.toPath(), "branch world"); } @Test @@ -771,12 +819,69 @@ public class RepoCommandTest extends RepositoryTestCase { assertFalse("The foo/Hello file should be skipped", foohello.exists()); // The content of Hello file should be expected - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { + assertContents(hello.toPath(), "branch world"); + } + } + + @Test + public void testCopyFileBare_executable() throws Exception { + try (Git git = new Git(defaultDb)) { + git.checkout().setName(BRANCH).call(); + File f = JGitTestUtil.writeTrashFile(defaultDb, "hello.sh", + "content of the executable file"); + f.setExecutable(true); + git.add().addFilepattern("hello.sh").call(); + git.commit().setMessage("Add binary file").call(); + } + + Repository remoteDb = createBareRepository(); + Repository tempDb = createWorkRepository(); + + StringBuilder xmlContent = new StringBuilder(); + xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") + .append("<manifest>") + .append("<remote name=\"remote1\" fetch=\".\" />") + .append("<default revision=\"master\" remote=\"remote1\" />") + .append("<project path=\"foo\" name=\"").append(defaultUri) + .append("\" revision=\"").append(BRANCH) + .append("\" >") + .append("<copyfile src=\"hello.txt\" dest=\"Hello\" />") + .append("<copyfile src=\"hello.txt\" dest=\"foo/Hello\" />") + .append("<copyfile src=\"hello.sh\" dest=\"copy-hello.sh\" />") + .append("</project>").append("</manifest>"); + JGitTestUtil.writeTrashFile(tempDb, "manifest.xml", + xmlContent.toString()); + RepoCommand command = new RepoCommand(remoteDb); + command.setPath( + tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml") + .setURI(rootUri).call(); + // Clone it + File directory = createTempDirectory("testCopyFileBare"); + try (Repository localDb = Git.cloneRepository().setDirectory(directory) + .setURI(remoteDb.getDirectory().toURI().toString()).call() + .getRepository()) { + // The Hello file should exist + File hello = new File(localDb.getWorkTree(), "Hello"); + assertTrue("The Hello file should exist", hello.exists()); + // The foo/Hello file should be skipped. + File foohello = new File(localDb.getWorkTree(), "foo/Hello"); + assertFalse("The foo/Hello file should be skipped", + foohello.exists()); + // The content of Hello file should be expected + try (BufferedReader reader = Files.newBufferedReader(hello.toPath(), + UTF_8)) { String content = reader.readLine(); assertEquals("The Hello file should have expected content", "branch world", content); } + + // The executable file must be there and preserve the executable bit + File helloSh = new File(localDb.getWorkTree(), "copy-hello.sh"); + assertTrue("Destination file should exist", helloSh.exists()); + assertContents(helloSh.toPath(), "content of the executable file"); + assertTrue("Destination file should be executable", + helloSh.canExecute()); + } } @@ -829,8 +934,8 @@ public class RepoCommandTest extends RepositoryTestCase { // The .gitmodules file should have 'submodule "bar"' and shouldn't // have // 'submodule "foo"' lines. - try (BufferedReader reader = new BufferedReader( - new FileReader(dotmodules))) { + try (BufferedReader reader = Files + .newBufferedReader(dotmodules.toPath(), UTF_8)) { boolean foo = false; boolean bar = false; while (true) { @@ -879,8 +984,8 @@ public class RepoCommandTest extends RepositoryTestCase { } // Check .gitmodules file - try (BufferedReader reader = new BufferedReader( - new FileReader(dotmodules))) { + try (BufferedReader reader = Files + .newBufferedReader(dotmodules.toPath(), UTF_8)) { boolean foo = false; boolean foobar = false; boolean a = false; @@ -935,8 +1040,8 @@ public class RepoCommandTest extends RepositoryTestCase { .call(); File hello = new File(localDb.getWorkTree(), "foo/hello.txt"); assertTrue("submodule should be checked out", hello.exists()); - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { + try (BufferedReader reader = Files.newBufferedReader(hello.toPath(), + UTF_8)) { String content = reader.readLine(); assertEquals("submodule content should be as expected", "master world", content); @@ -1074,8 +1179,9 @@ public class RepoCommandTest extends RepositoryTestCase { ".gitattributes"); assertTrue("The .gitattributes file should exist", gitattributes.exists()); - try (BufferedReader reader = new BufferedReader( - new FileReader(gitattributes));) { + try (BufferedReader reader = Files + .newBufferedReader(gitattributes.toPath(), + UTF_8)) { String content = reader.readLine(); assertEquals(".gitattributes content should be as expected", "/test a1 a2", content); @@ -1142,12 +1248,7 @@ public class RepoCommandTest extends RepositoryTestCase { .setURI(rootUri) .call(); File hello = new File(db.getWorkTree(), "foo/hello.txt"); - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { - String content = reader.readLine(); - assertEquals("submodule content should be as expected", - "branch world", content); - } + assertContents(hello.toPath(), "branch world"); } @Test @@ -1169,8 +1270,8 @@ public class RepoCommandTest extends RepositoryTestCase { .setURI(rootUri) .call(); File hello = new File(db.getWorkTree(), "foo/hello.txt"); - try (BufferedReader reader = new BufferedReader( - new FileReader(hello))) { + try (BufferedReader reader = Files.newBufferedReader(hello.toPath(), + UTF_8)) { String content = reader.readLine(); assertEquals("submodule content should be as expected", "branch world", content); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java index 2a1721e66c..4bd1dab3e8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java @@ -512,6 +512,15 @@ public class FastIgnoreRuleTest { assertMatched("x/**/", "x/y/a/"); } + @Test + public void testFileNameWithLineTerminator() { + assertMatched("a?", "a\r"); + assertMatched("a?", "dir/a\r"); + assertMatched("a?", "a\r/file"); + assertMatched("*a", "\ra"); + assertMatched("dir/*a*", "dir/\ra\r"); + } + private void assertMatched(String pattern, String path) { boolean match = match(pattern, path); String result = path + " is " + (match ? "ignored" : "not ignored") diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsFsckTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsFsckTest.java index 804d744ae2..c1811251c6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsFsckTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsFsckTest.java @@ -266,4 +266,59 @@ public class DfsFsckTest { "refs/heads/master"); } + private ObjectId insertGitModules(String contents) throws IOException { + ObjectId blobId = ins.insert(Constants.OBJ_BLOB, + Constants.encode(contents)); + + byte[] blobIdBytes = new byte[OBJECT_ID_LENGTH]; + blobId.copyRawTo(blobIdBytes, 0); + byte[] data = concat(encodeASCII("100644 .gitmodules\0"), blobIdBytes); + ins.insert(Constants.OBJ_TREE, data); + ins.flush(); + + return blobId; + } + + @Test + public void testInvalidGitModules() throws Exception { + String fakeGitmodules = new StringBuilder() + .append("[submodule \"test\"]\n") + .append(" path = xlib\n") + .append(" url = https://example.com/repo/xlib.git\n\n") + .append("[submodule \"test2\"]\n") + .append(" path = zlib\n") + .append(" url = -upayload.sh\n") + .toString(); + + ObjectId blobId = insertGitModules(fakeGitmodules); + + DfsFsck fsck = new DfsFsck(repo); + FsckError errors = fsck.check(null); + assertEquals(errors.getCorruptObjects().size(), 1); + + CorruptObject error = errors.getCorruptObjects().iterator().next(); + assertEquals(error.getId(), blobId); + assertEquals(error.getType(), Constants.OBJ_BLOB); + assertEquals(error.getErrorType(), ErrorType.GITMODULES_URL); + } + + + @Test + public void testValidGitModules() throws Exception { + String fakeGitmodules = new StringBuilder() + .append("[submodule \"test\"]\n") + .append(" path = xlib\n") + .append(" url = https://example.com/repo/xlib.git\n\n") + .append("[submodule \"test2\"]\n") + .append(" path = zlib\n") + .append(" url = ok/path\n") + .toString(); + + insertGitModules(fakeGitmodules); + + DfsFsck fsck = new DfsFsck(repo); + FsckError errors = fsck.check(null); + assertEquals(errors.getCorruptObjects().size(), 0); + } + } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java index deffa04b54..f6cb55870b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java @@ -43,14 +43,16 @@ package org.eclipse.jgit.internal.storage.file; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; import org.eclipse.jgit.lib.ConfigConstants; @@ -120,18 +122,19 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase { Repository repo1 = createWorkRepository(); File dir = createTempDirectory("dir"); File dotGit = new File(dir, Constants.DOT_GIT); - try (FileWriter writer = new FileWriter(dotGit)) { - writer.append("gitdir: " + repo1.getDirectory().getAbsolutePath()).close(); - FileRepositoryBuilder builder = new FileRepositoryBuilder(); + try (BufferedWriter writer = Files.newBufferedWriter(dotGit.toPath(), + UTF_8)) { + writer.append("gitdir: " + repo1.getDirectory().getAbsolutePath()); + } + FileRepositoryBuilder builder = new FileRepositoryBuilder(); - builder.setWorkTree(dir); - builder.setMustExist(true); - Repository repo2 = builder.build(); + builder.setWorkTree(dir); + builder.setMustExist(true); + Repository repo2 = builder.build(); - assertEquals(repo1.getDirectory().getAbsolutePath(), repo2 - .getDirectory().getAbsolutePath()); - assertEquals(dir, repo2.getWorkTree()); - } + assertEquals(repo1.getDirectory().getAbsolutePath(), + repo2.getDirectory().getAbsolutePath()); + assertEquals(dir, repo2.getWorkTree()); } @Test @@ -140,20 +143,20 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase { File dir = new File(repo1.getWorkTree(), "dir"); assertTrue(dir.mkdir()); File dotGit = new File(dir, Constants.DOT_GIT); - try (FileWriter writer = new FileWriter(dotGit)) { - writer.append("gitdir: ../" + Constants.DOT_GIT).close(); - - FileRepositoryBuilder builder = new FileRepositoryBuilder(); - builder.setWorkTree(dir); - builder.setMustExist(true); - Repository repo2 = builder.build(); - - // The tmp directory may be a symlink so the actual path - // may not - assertEquals(repo1.getDirectory().getCanonicalPath(), repo2 - .getDirectory().getCanonicalPath()); - assertEquals(dir, repo2.getWorkTree()); + try (BufferedWriter writer = Files.newBufferedWriter(dotGit.toPath(), + UTF_8)) { + writer.append("gitdir: ../" + Constants.DOT_GIT); } + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + builder.setWorkTree(dir); + builder.setMustExist(true); + Repository repo2 = builder.build(); + + // The tmp directory may be a symlink so the actual path + // may not + assertEquals(repo1.getDirectory().getCanonicalPath(), + repo2.getDirectory().getCanonicalPath()); + assertEquals(dir, repo2.getWorkTree()); } @Test @@ -161,22 +164,23 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase { Repository repo1 = createWorkRepository(); File dir = createTempDirectory("dir"); File dotGit = new File(dir, Constants.DOT_GIT); - try (FileWriter writer = new FileWriter(dotGit)) { + try (BufferedWriter writer = Files.newBufferedWriter(dotGit.toPath(), + UTF_8)) { writer.append( - "gitdir: " + repo1.getDirectory().getAbsolutePath()).close(); - FileRepositoryBuilder builder = new FileRepositoryBuilder(); - - builder.setWorkTree(dir); - builder.findGitDir(dir); - assertEquals(repo1.getDirectory().getAbsolutePath(), builder - .getGitDir().getAbsolutePath()); - builder.setMustExist(true); - Repository repo2 = builder.build(); - - // The tmp directory may be a symlink - assertEquals(repo1.getDirectory().getCanonicalPath(), repo2 - .getDirectory().getCanonicalPath()); - assertEquals(dir, repo2.getWorkTree()); + "gitdir: " + repo1.getDirectory().getAbsolutePath()); } + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + + builder.setWorkTree(dir); + builder.findGitDir(dir); + assertEquals(repo1.getDirectory().getAbsolutePath(), + builder.getGitDir().getAbsolutePath()); + builder.setMustExist(true); + Repository repo2 = builder.build(); + + // The tmp directory may be a symlink + assertEquals(repo1.getDirectory().getCanonicalPath(), + repo2.getDirectory().getCanonicalPath()); + assertEquals(dir, repo2.getWorkTree()); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java index cbb73bb08e..3ca689ac01 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java @@ -42,6 +42,7 @@ package org.eclipse.jgit.internal.storage.file; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -189,7 +190,8 @@ public class ObjectDirectoryTest extends RepositoryTestCase { String commit = "d3148f9410b071edd4a4c85d2a43d1fa2574b0d2"; try (PrintWriter writer = new PrintWriter( - new File(repository.getDirectory(), Constants.SHALLOW))) { + new File(repository.getDirectory(), Constants.SHALLOW), + UTF_8.name())) { writer.println(commit); } Set<ObjectId> shallowCommits = dir.getShallowCommits(); @@ -205,7 +207,8 @@ public class ObjectDirectoryTest extends RepositoryTestCase { String commit = "X3148f9410b071edd4a4c85d2a43d1fa2574b0d2"; try (PrintWriter writer = new PrintWriter( - new File(repository.getDirectory(), Constants.SHALLOW))) { + new File(repository.getDirectory(), Constants.SHALLOW), + UTF_8.name())) { writer.println(commit); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java index dc05eeabe1..acdaf3aa3c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java @@ -44,6 +44,7 @@ package org.eclipse.jgit.internal.storage.file; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -67,31 +68,31 @@ import org.junit.Test; public class ReflogReaderTest extends SampleDataRepositoryTestCase { static byte[] oneLine = "da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c A O Thor Too <authortoo@wri.tr> 1243028200 +0200\tcommit: Add a toString for debugging to RemoteRefUpdate\n" - .getBytes(); + .getBytes(UTF_8); static byte[] twoLine = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n" + "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n") - .getBytes(); + .getBytes(UTF_8); static byte[] twoLineWithAppendInProgress = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n" + "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n" + "54794942a18a237c57a80719afed44bb78172b10 ") - .getBytes(); + .getBytes(UTF_8); static byte[] aLine = "1111111111111111111111111111111111111111 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\tbranch: change to a\n" - .getBytes(); + .getBytes(UTF_8); static byte[] masterLine = "2222222222222222222222222222222222222222 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\tbranch: change to master\n" - .getBytes(); + .getBytes(UTF_8); static byte[] headLine = "3333333333333333333333333333333333333333 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\tbranch: change to HEAD\n" - .getBytes(); + .getBytes(UTF_8); static byte[] oneLineWithoutComment = "da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c A O Thor Too <authortoo@wri.tr> 1243028200 +0200\n" - .getBytes(); + .getBytes(UTF_8); static byte[] switchBranch = "0d43a6890a19fd657faad1c4cfbe3cb1b47851c3 4809df9c0d8bce5b00955563f77c5a9f25aa0d12 A O Thor Too <authortoo@wri.tr> 1315088009 +0200\tcheckout: moving from new/work to master\n" - .getBytes(); + .getBytes(UTF_8); @Test public void testReadOneLine() throws Exception { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java index 1d188c3148..a84be7e9f0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java @@ -42,6 +42,7 @@ *******************************************************************************/ package org.eclipse.jgit.internal.storage.file; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.File; @@ -73,9 +74,9 @@ public class ReflogWriterTest extends SampleDataRepositoryTestCase { writer.log("refs/heads/master", oldId, newId, ident, "stash: Add\nmessage\r\nwith line feeds"); - byte[] buffer = new byte[oneLine.getBytes().length]; + byte[] buffer = new byte[oneLine.getBytes(UTF_8).length]; readReflog(buffer); - assertEquals(oneLine, new String(buffer)); + assertEquals(oneLine, new String(buffer, UTF_8)); } private void readReflog(byte[] buffer) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java index 02073226d0..96caa01a9e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java @@ -333,9 +333,9 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase { public void test002_CreateBadTree() throws Exception { // We won't create a tree entry with an empty filename // + final TreeFormatter formatter = new TreeFormatter(); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage(JGitText.get().invalidTreeZeroLengthName); - final TreeFormatter formatter = new TreeFormatter(); formatter.append("", FileMode.TREE, ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java index 01426eeb8c..9063b65189 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java @@ -141,7 +141,7 @@ public class WindowCacheGetTest extends SampleDataRepositoryTestCase { } } - private class TestObject { + private static class TestObject { ObjectId id; int type; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/parser/FirstWantTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/parser/FirstWantTest.java new file mode 100644 index 0000000000..b877c598ef --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/transport/parser/FirstWantTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2018, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.parser; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jgit.errors.PackProtocolException; +import org.junit.Test; + +public class FirstWantTest { + + @Test + public void testFirstWantWithOptions() throws PackProtocolException { + String line = "want b9d4d1eb2f93058814480eae9e1b67550f46ee38 " + + "no-progress include-tag ofs-delta agent=JGit/unknown"; + + FirstWant r = FirstWant.fromLine(line); + assertEquals("want b9d4d1eb2f93058814480eae9e1b67550f46ee38", + r.getLine()); + Set<String> capabilities = r.getCapabilities(); + Set<String> expectedCapabilities = new HashSet<>( + Arrays.asList("no-progress", "include-tag", "ofs-delta")); + assertEquals(expectedCapabilities, capabilities); + assertEquals("JGit/unknown", r.getAgent()); + } + + @Test + public void testFirstWantWithoutOptions() throws PackProtocolException { + String line = "want b9d4d1eb2f93058814480eae9e1b67550f46ee38"; + + FirstWant r = FirstWant.fromLine(line); + assertEquals("want b9d4d1eb2f93058814480eae9e1b67550f46ee38", + r.getLine()); + assertTrue(r.getCapabilities().isEmpty()); + assertNull(r.getAgent()); + } + + private String makeFirstWantLine(String capability) { + return String.format("want b9d4d1eb2f93058814480eae9e1b67550f46ee38 %s", capability); + } + + @Test + public void testFirstWantNoWhitespace() { + try { + FirstWant.fromLine( + "want b9d4d1eb2f93058814480eae9e1b67550f400000capability"); + fail("Accepting first want line without SP between oid and first capability"); + } catch (PackProtocolException e) { + // pass + } + } + + @Test + public void testFirstWantOnlyWhitespace() throws PackProtocolException { + FirstWant r = FirstWant + .fromLine("want b9d4d1eb2f93058814480eae9e1b67550f46ee38 "); + assertEquals("want b9d4d1eb2f93058814480eae9e1b67550f46ee38", + r.getLine()); + } + + @Test + public void testFirstWantValidCapabilityNames() + throws PackProtocolException { + List<String> validNames = Arrays.asList( + "c", "cap", "C", "CAP", "1", "1cap", "cap-64k_test", + "-", "-cap", + "_", "_cap"); + + for (String capability: validNames) { + FirstWant r = FirstWant.fromLine(makeFirstWantLine(capability)); + assertEquals(r.getCapabilities().size(), 1); + assertTrue(r.getCapabilities().contains(capability)); + } + } + + @Test + public void testFirstWantValidAgentName() throws PackProtocolException { + FirstWant r = FirstWant.fromLine(makeFirstWantLine("agent=pack.age/Version")); + assertEquals(r.getCapabilities().size(), 0); + assertEquals("pack.age/Version", r.getAgent()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java index c4c4da8165..21d8d66adf 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java @@ -48,6 +48,7 @@ package org.eclipse.jgit.lib; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.HOURS; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -66,11 +67,15 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import org.eclipse.jgit.api.MergeCommand.FastForwardMode; import org.eclipse.jgit.errors.ConfigInvalidException; @@ -78,6 +83,7 @@ import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.junit.MockSystemReader; import org.eclipse.jgit.merge.MergeConfig; import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.SystemReader; import org.junit.After; @@ -94,6 +100,12 @@ public class ConfigTest { // A non-ASCII whitespace character: U+2002 EN QUAD. private static final char WS = '\u2002'; + private static final String REFS_ORIGIN = "+refs/heads/*:refs/remotes/origin/*"; + + private static final String REFS_UPSTREAM = "+refs/heads/*:refs/remotes/upstream/*"; + + private static final String REFS_BACKUP = "+refs/heads/*:refs/remotes/backup/*"; + @Rule public ExpectedException expectedEx = ExpectedException.none(); @@ -690,11 +702,7 @@ public class ConfigTest { assertEquals("", c.getString("a", null, "y")); assertArrayEquals(new String[]{""}, c.getStringList("a", null, "y")); - try { - c.getInt("a", null, "y", 1); - } catch (IllegalArgumentException e) { - assertEquals("Invalid integer value: a.y=", e.getMessage()); - } + assertEquals(1, c.getInt("a", null, "y", 1)); assertNull(c.getString("a", null, "z")); assertArrayEquals(new String[]{}, c.getStringList("a", null, "z")); @@ -711,11 +719,7 @@ public class ConfigTest { assertNull(c.getString("a", null, "y")); assertArrayEquals(new String[]{null}, c.getStringList("a", null, "y")); - try { - c.getInt("a", null, "y", 1); - } catch (IllegalArgumentException e) { - assertEquals("Invalid integer value: a.y=", e.getMessage()); - } + assertEquals(1, c.getInt("a", null, "y", 1)); assertNull(c.getString("a", null, "z")); assertArrayEquals(new String[]{}, c.getStringList("a", null, "z")); @@ -801,11 +805,9 @@ public class ConfigTest { public void testIncludeTooManyRecursions() throws IOException { File config = tmp.newFile("config"); String include = "[include]\npath=" + pathToString(config) + "\n"; - Files.write(config.toPath(), include.getBytes()); - FileBasedConfig fbConfig = new FileBasedConfig(null, config, - FS.DETECTED); + Files.write(config.toPath(), include.getBytes(UTF_8)); try { - fbConfig.load(); + loadConfig(config); fail(); } catch (ConfigInvalidException cie) { for (Throwable t = cie; t != null; t = t.getCause()) { @@ -824,7 +826,7 @@ public class ConfigTest { File config = tmp.newFile("config"); String fooBar = "[foo]\nbar=true\n"; - Files.write(config.toPath(), fooBar.getBytes()); + Files.write(config.toPath(), fooBar.getBytes(UTF_8)); Config parsed = parse("[include]\npath=" + pathToString(config) + "\n"); assertFalse(parsed.getBoolean("foo", "bar", false)); @@ -835,15 +837,13 @@ public class ConfigTest { throws IOException, ConfigInvalidException { File included = tmp.newFile("included"); String content = "[foo]\nbar=true\n"; - Files.write(included.toPath(), content.getBytes()); + Files.write(included.toPath(), content.getBytes(UTF_8)); File config = tmp.newFile("config"); content = "[Include]\npath=" + pathToString(included) + "\n"; - Files.write(config.toPath(), content.getBytes()); + Files.write(config.toPath(), content.getBytes(UTF_8)); - FileBasedConfig fbConfig = new FileBasedConfig(null, config, - FS.DETECTED); - fbConfig.load(); + FileBasedConfig fbConfig = loadConfig(config); assertTrue(fbConfig.getBoolean("foo", "bar", false)); } @@ -852,15 +852,13 @@ public class ConfigTest { throws IOException, ConfigInvalidException { File included = tmp.newFile("included"); String content = "[foo]\nbar=true\n"; - Files.write(included.toPath(), content.getBytes()); + Files.write(included.toPath(), content.getBytes(UTF_8)); File config = tmp.newFile("config"); content = "[include]\nPath=" + pathToString(included) + "\n"; - Files.write(config.toPath(), content.getBytes()); + Files.write(config.toPath(), content.getBytes(UTF_8)); - FileBasedConfig fbConfig = new FileBasedConfig(null, config, - FS.DETECTED); - fbConfig.load(); + FileBasedConfig fbConfig = loadConfig(config); assertTrue(fbConfig.getBoolean("foo", "bar", false)); } @@ -881,15 +879,13 @@ public class ConfigTest { File included = tmp.newFile("included"); String includedPath = pathToString(included); String content = "[include]\npath=\n"; - Files.write(included.toPath(), content.getBytes()); + Files.write(included.toPath(), content.getBytes(UTF_8)); File config = tmp.newFile("config"); String include = "[include]\npath=" + includedPath + "\n"; - Files.write(config.toPath(), include.getBytes()); - FileBasedConfig fbConfig = new FileBasedConfig(null, config, - FS.DETECTED); + Files.write(config.toPath(), include.getBytes(UTF_8)); try { - fbConfig.load(); + loadConfig(config); fail("Expected ConfigInvalidException"); } catch (ConfigInvalidException e) { // Check that there is some exception in the chain that contains @@ -904,6 +900,306 @@ public class ConfigTest { } } + @Test + public void testIncludeSetValueMustNotTouchIncludedLines1() + throws IOException, ConfigInvalidException { + File includedFile = createAllTypesIncludedContent(); + + File configFile = tmp.newFile("config"); + String content = createAllTypesSampleContent("Alice Parker", false, 11, + 21, 31, CoreConfig.AutoCRLF.FALSE, + "+refs/heads/*:refs/remotes/origin/*") + "\n[include]\npath=" + + pathToString(includedFile); + Files.write(configFile.toPath(), content.getBytes(UTF_8)); + + FileBasedConfig fbConfig = loadConfig(configFile); + assertValuesAsIncluded(fbConfig, REFS_ORIGIN, REFS_UPSTREAM); + assertSections(fbConfig, "user", "core", "remote", "include"); + + setAllValuesNew(fbConfig); + assertValuesAsIsSaveLoad(fbConfig, config -> { + assertValuesAsIncluded(config, REFS_BACKUP, REFS_UPSTREAM); + assertSections(fbConfig, "user", "core", "remote", "include"); + }); + } + + @Test + public void testIncludeSetValueMustNotTouchIncludedLines2() + throws IOException, ConfigInvalidException { + File includedFile = createAllTypesIncludedContent(); + + File configFile = tmp.newFile("config"); + String content = "[include]\npath=" + pathToString(includedFile) + "\n" + + createAllTypesSampleContent("Alice Parker", false, 11, 21, 31, + CoreConfig.AutoCRLF.FALSE, + "+refs/heads/*:refs/remotes/origin/*"); + Files.write(configFile.toPath(), content.getBytes(UTF_8)); + + FileBasedConfig fbConfig = loadConfig(configFile); + assertValuesAsConfig(fbConfig, REFS_UPSTREAM, REFS_ORIGIN); + assertSections(fbConfig, "include", "user", "core", "remote"); + + setAllValuesNew(fbConfig); + assertValuesAsIsSaveLoad(fbConfig, config -> { + assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP); + assertSections(fbConfig, "include", "user", "core", "remote"); + }); + } + + @Test + public void testIncludeSetValueOnFileWithJustContainsInclude() + throws IOException, ConfigInvalidException { + File includedFile = createAllTypesIncludedContent(); + + File configFile = tmp.newFile("config"); + String content = "[include]\npath=" + pathToString(includedFile); + Files.write(configFile.toPath(), content.getBytes(UTF_8)); + + FileBasedConfig fbConfig = loadConfig(configFile); + assertValuesAsIncluded(fbConfig, REFS_UPSTREAM); + assertSections(fbConfig, "include", "user", "core", "remote"); + + setAllValuesNew(fbConfig); + assertValuesAsIsSaveLoad(fbConfig, config -> { + assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP); + assertSections(fbConfig, "include", "user", "core", "remote"); + }); + } + + @Test + public void testIncludeSetValueOnFileWithJustEmptySection1() + throws IOException, ConfigInvalidException { + File includedFile = createAllTypesIncludedContent(); + + File configFile = tmp.newFile("config"); + String content = "[user]\n[include]\npath=" + + pathToString(includedFile); + Files.write(configFile.toPath(), content.getBytes(UTF_8)); + + FileBasedConfig fbConfig = loadConfig(configFile); + assertValuesAsIncluded(fbConfig, REFS_UPSTREAM); + assertSections(fbConfig, "user", "include", "core", "remote"); + + setAllValuesNew(fbConfig); + assertValuesAsIsSaveLoad(fbConfig, config -> { + assertValuesAsNewWithName(config, "Alice Muller", REFS_UPSTREAM, + REFS_BACKUP); + assertSections(fbConfig, "user", "include", "core", "remote"); + }); + } + + @Test + public void testIncludeSetValueOnFileWithJustEmptySection2() + throws IOException, ConfigInvalidException { + File includedFile = createAllTypesIncludedContent(); + + File configFile = tmp.newFile("config"); + String content = "[include]\npath=" + pathToString(includedFile) + + "\n[user]"; + Files.write(configFile.toPath(), content.getBytes(UTF_8)); + + FileBasedConfig fbConfig = loadConfig(configFile); + assertValuesAsIncluded(fbConfig, REFS_UPSTREAM); + assertSections(fbConfig, "include", "user", "core", "remote"); + + setAllValuesNew(fbConfig); + assertValuesAsIsSaveLoad(fbConfig, config -> { + assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP); + assertSections(fbConfig, "include", "user", "core", "remote"); + }); + } + + @Test + public void testIncludeSetValueOnFileWithJustExistingSection1() + throws IOException, ConfigInvalidException { + File includedFile = createAllTypesIncludedContent(); + + File configFile = tmp.newFile("config"); + String content = "[user]\nemail=alice@home\n[include]\npath=" + + pathToString(includedFile); + Files.write(configFile.toPath(), content.getBytes(UTF_8)); + + FileBasedConfig fbConfig = loadConfig(configFile); + assertValuesAsIncluded(fbConfig, REFS_UPSTREAM); + assertSections(fbConfig, "user", "include", "core", "remote"); + + setAllValuesNew(fbConfig); + assertValuesAsIsSaveLoad(fbConfig, config -> { + assertValuesAsNewWithName(config, "Alice Muller", REFS_UPSTREAM, + REFS_BACKUP); + assertSections(fbConfig, "user", "include", "core", "remote"); + }); + } + + @Test + public void testIncludeSetValueOnFileWithJustExistingSection2() + throws IOException, ConfigInvalidException { + File includedFile = createAllTypesIncludedContent(); + + File configFile = tmp.newFile("config"); + String content = "[include]\npath=" + pathToString(includedFile) + + "\n[user]\nemail=alice@home\n"; + Files.write(configFile.toPath(), content.getBytes(UTF_8)); + + FileBasedConfig fbConfig = loadConfig(configFile); + assertValuesAsIncluded(fbConfig, REFS_UPSTREAM); + assertSections(fbConfig, "include", "user", "core", "remote"); + + setAllValuesNew(fbConfig); + assertValuesAsIsSaveLoad(fbConfig, config -> { + assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP); + assertSections(fbConfig, "include", "user", "core", "remote"); + }); + } + + @Test + public void testIncludeUnsetSectionMustNotTouchIncludedLines() + throws IOException, ConfigInvalidException { + File includedFile = tmp.newFile("included"); + RefSpec includedRefSpec = new RefSpec(REFS_UPSTREAM); + String includedContent = "[remote \"origin\"]\n" + "fetch=" + + includedRefSpec; + Files.write(includedFile.toPath(), includedContent.getBytes(UTF_8)); + + File configFile = tmp.newFile("config"); + RefSpec refSpec = new RefSpec(REFS_ORIGIN); + String content = "[include]\npath=" + pathToString(includedFile) + "\n" + + "[remote \"origin\"]\n" + "fetch=" + refSpec; + Files.write(configFile.toPath(), content.getBytes(UTF_8)); + + FileBasedConfig fbConfig = loadConfig(configFile); + + Consumer<FileBasedConfig> assertion = config -> { + assertEquals(Arrays.asList(includedRefSpec, refSpec), + config.getRefSpecs("remote", "origin", "fetch")); + }; + assertion.accept(fbConfig); + + fbConfig.unsetSection("remote", "origin"); + assertValuesAsIsSaveLoad(fbConfig, config -> { + assertEquals(Collections.singletonList(includedRefSpec), + config.getRefSpecs("remote", "origin", "fetch")); + }); + } + + private File createAllTypesIncludedContent() throws IOException { + File includedFile = tmp.newFile("included"); + String includedContent = createAllTypesSampleContent("Alice Muller", + true, 10, 20, 30, CoreConfig.AutoCRLF.TRUE, + "+refs/heads/*:refs/remotes/upstream/*"); + Files.write(includedFile.toPath(), includedContent.getBytes(UTF_8)); + return includedFile; + } + + private static void assertValuesAsIsSaveLoad(FileBasedConfig fbConfig, + Consumer<FileBasedConfig> assertion) + throws IOException, ConfigInvalidException { + assertion.accept(fbConfig); + + fbConfig.save(); + assertion.accept(fbConfig); + + fbConfig = loadConfig(fbConfig.getFile()); + assertion.accept(fbConfig); + } + + private static void setAllValuesNew(Config config) { + config.setString("user", null, "name", "Alice Bauer"); + config.setBoolean("core", null, "fileMode", false); + config.setInt("core", null, "deltaBaseCacheLimit", 12); + config.setLong("core", null, "packedGitLimit", 22); + config.setLong("core", null, "repositoryCacheExpireAfter", 32); + config.setEnum("core", null, "autocrlf", CoreConfig.AutoCRLF.FALSE); + config.setString("remote", "origin", "fetch", + "+refs/heads/*:refs/remotes/backup/*"); + } + + private static void assertValuesAsIncluded(Config config, String... refs) { + assertAllTypesSampleContent("Alice Muller", true, 10, 20, 30, + CoreConfig.AutoCRLF.TRUE, config, refs); + } + + private static void assertValuesAsConfig(Config config, String... refs) { + assertAllTypesSampleContent("Alice Parker", false, 11, 21, 31, + CoreConfig.AutoCRLF.FALSE, config, refs); + } + + private static void assertValuesAsNew(Config config, String... refs) { + assertValuesAsNewWithName(config, "Alice Bauer", refs); + } + + private static void assertValuesAsNewWithName(Config config, String name, + String... refs) { + assertAllTypesSampleContent(name, false, 12, 22, 32, + CoreConfig.AutoCRLF.FALSE, config, refs); + } + + private static void assertSections(Config config, String... sections) { + assertEquals(Arrays.asList(sections), + new ArrayList<>(config.getSections())); + } + + private static String createAllTypesSampleContent(String name, + boolean fileMode, int deltaBaseCacheLimit, long packedGitLimit, + long repositoryCacheExpireAfter, CoreConfig.AutoCRLF autoCRLF, + String fetchRefSpec) { + final StringBuilder builder = new StringBuilder(); + builder.append("[user]\n"); + builder.append("name="); + builder.append(name); + builder.append("\n"); + + builder.append("[core]\n"); + builder.append("fileMode="); + builder.append(fileMode); + builder.append("\n"); + + builder.append("deltaBaseCacheLimit="); + builder.append(deltaBaseCacheLimit); + builder.append("\n"); + + builder.append("packedGitLimit="); + builder.append(packedGitLimit); + builder.append("\n"); + + builder.append("repositoryCacheExpireAfter="); + builder.append(repositoryCacheExpireAfter); + builder.append("\n"); + + builder.append("autocrlf="); + builder.append(autoCRLF.name()); + builder.append("\n"); + + builder.append("[remote \"origin\"]\n"); + builder.append("fetch="); + builder.append(fetchRefSpec); + builder.append("\n"); + return builder.toString(); + } + + private static void assertAllTypesSampleContent(String name, + boolean fileMode, int deltaBaseCacheLimit, long packedGitLimit, + long repositoryCacheExpireAfter, CoreConfig.AutoCRLF autoCRLF, + Config config, String... fetchRefSpecs) { + assertEquals(name, config.getString("user", null, "name")); + assertEquals(fileMode, + config.getBoolean("core", "fileMode", !fileMode)); + assertEquals(deltaBaseCacheLimit, + config.getInt("core", "deltaBaseCacheLimit", -1)); + assertEquals(packedGitLimit, + config.getLong("core", "packedGitLimit", -1)); + assertEquals(repositoryCacheExpireAfter, config.getTimeUnit("core", + null, "repositoryCacheExpireAfter", -1, MILLISECONDS)); + assertEquals(autoCRLF, config.getEnum("core", null, "autocrlf", + CoreConfig.AutoCRLF.INPUT)); + final List<RefSpec> refspecs = new ArrayList<>(); + for (String fetchRefSpec : fetchRefSpecs) { + refspecs.add(new RefSpec(fetchRefSpec)); + } + + assertEquals(refspecs, config.getRefSpecs("remote", "origin", "fetch")); + } + private static void assertReadLong(long exp) throws ConfigInvalidException { assertReadLong(exp, String.valueOf(exp)); } @@ -1217,4 +1513,12 @@ public class ConfigTest { assertEquals(expectedMessage, e.getMessage()); } } + + private static FileBasedConfig loadConfig(File file) + throws IOException, ConfigInvalidException { + final FileBasedConfig config = new FileBasedConfig(null, file, + FS.DETECTED); + config.load(); + return config; + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java index 32a1ec96a5..057e0c881b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java @@ -37,6 +37,7 @@ */ package org.eclipse.jgit.lib; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -343,7 +344,7 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase { ObjectInserter newObjectInserter; newObjectInserter = git.getRepository().newObjectInserter(); ObjectId blobId = newObjectInserter.insert(Constants.OBJ_BLOB, - "data".getBytes()); + "data".getBytes(UTF_8)); newObjectInserter = git.getRepository().newObjectInserter(); FileMode mode = FileMode.REGULAR_FILE; ObjectId insertId = blobId; @@ -366,8 +367,8 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase { insertId = blobId; for (int i = path.length - 1; i >= 0; --i) { TreeFormatter treeFormatter = new TreeFormatter(); - treeFormatter.append(path[i].getBytes(), 0, - path[i].getBytes().length, + treeFormatter.append(path[i].getBytes(UTF_8), 0, + path[i].getBytes(UTF_8).length, mode, insertId, true); insertId = newObjectInserter.insert(treeFormatter); mode = FileMode.TREE; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java index eb87827805..534b323fe6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java @@ -40,6 +40,7 @@ */ package org.eclipse.jgit.lib; +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.assertFalse; @@ -310,7 +311,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { assertTrue("unexpected content for path " + path + " in index. Expected: <" + expectedValue + ">", Arrays.equals(db.open(read.getEntry(j).getObjectId()) - .getCachedBytes(), i.get(path).getBytes())); + .getCachedBytes(), i.get(path).getBytes(UTF_8))); } } @@ -405,7 +406,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { ObjectId genSha1(String data) { try (ObjectInserter w = db.newObjectInserter()) { - ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes()); + ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes(UTF_8)); w.flush(); return id; } catch (IOException e) { @@ -928,6 +929,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { "e/g3")); try { checkout(); + fail("did not throw CheckoutConflictException"); } catch (CheckoutConflictException e) { assertWorkDir(mkmap("a", "a", "b/c", "b/c", "d", "d", "e/f", "e/f", "e/g", "e/g3")); @@ -2048,7 +2050,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { assertArrayEquals( "unexpected content for path " + path + " in workDir. ", - buffer, i.get(path).getBytes()); + buffer, i.get(path).getBytes(UTF_8)); } nrFiles++; } else if (file.isDirectory()) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java new file mode 100644 index 0000000000..2098b17f41 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2018, Salesforce. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.lib; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.junit.Test; + +public class GpgConfigTest { + + private static Config parse(String content) throws ConfigInvalidException { + final Config c = new Config(null); + c.fromText(content); + return c; + } + + @Test + public void isSignCommits_defaultIsFalse() throws Exception { + Config c = parse(""); + + assertFalse(new GpgConfig(c).isSignCommits()); + } + + @Test + public void isSignCommits_false() throws Exception { + Config c = parse("" // + + "[gpg]\n" // + + " format = x509\n" // + + "[commit]\n" // + + " gpgSign = false\n" // + ); + + assertFalse(new GpgConfig(c).isSignCommits()); + } + + @Test + public void isSignCommits_true() throws Exception { + Config c = parse("" // + + "[commit]\n" // + + " gpgSign = true\n" // + ); + + assertTrue(new GpgConfig(c).isSignCommits()); + } + + @Test + public void testGetKeyFormat_defaultsToOpenpgp() throws Exception { + Config c = parse(""); + + assertEquals(GpgConfig.GpgFormat.OPENPGP, + new GpgConfig(c).getKeyFormat()); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetKeyFormat_failsForInvalidValue() throws Exception { + Config c = parse("" // + + "[gpg]\n" // + + " format = invalid\n" // + ); + + new GpgConfig(c).getKeyFormat(); + fail("Call should not have succeeded!"); + } + + @Test + public void testGetKeyFormat_openpgp() throws Exception { + Config c = parse("" // + + "[gpg]\n" // + + " format = openpgp\n" // + ); + + assertEquals(GpgConfig.GpgFormat.OPENPGP, + new GpgConfig(c).getKeyFormat()); + } + + @Test + public void testGetKeyFormat_x509() throws Exception { + Config c = parse("" // + + "[gpg]\n" // + + " format = x509\n" // + ); + + assertEquals(GpgConfig.GpgFormat.X509, new GpgConfig(c).getKeyFormat()); + } + + @Test + public void testGetSigningKey() throws Exception { + Config c = parse("" // + + "[user]\n" // + + " signingKey = 0x2345\n" // + ); + + assertEquals("0x2345", new GpgConfig(c).getSigningKey()); + } + + @Test + public void testGetSigningKey_defaultToNull() throws Exception { + Config c = parse(""); + + assertNull(new GpgConfig(c).getSigningKey()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java index d89aabe75f..fa7f5ab522 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java @@ -43,14 +43,17 @@ package org.eclipse.jgit.lib; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Set; +import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.errors.NoWorkTreeException; @@ -109,6 +112,59 @@ public class IndexDiffSubmoduleTest extends RepositoryTestCase { assertFalse(indexDiff.diff()); } + private Repository cloneWithoutCloningSubmodule() throws Exception { + File directory = createTempDirectory( + "testCloneWithoutCloningSubmodules"); + CloneCommand clone = Git.cloneRepository(); + clone.setDirectory(directory); + clone.setCloneSubmodules(false); + clone.setURI(db.getDirectory().toURI().toString()); + Git git2 = clone.call(); + addRepoToClose(git2.getRepository()); + return git2.getRepository(); + } + + @Theory + public void testCleanAfterClone(IgnoreSubmoduleMode mode) throws Exception { + Repository db2 = cloneWithoutCloningSubmodule(); + IndexDiff indexDiff = new IndexDiff(db2, Constants.HEAD, + new FileTreeIterator(db2)); + indexDiff.setIgnoreSubmoduleMode(mode); + boolean changed = indexDiff.diff(); + assertFalse(changed); + } + + @Theory + public void testMissingIfDirectoryGone(IgnoreSubmoduleMode mode) + throws Exception { + recursiveDelete(submodule_trash); + IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD, + new FileTreeIterator(db)); + indexDiff.setIgnoreSubmoduleMode(mode); + boolean hasChanges = indexDiff.diff(); + if (mode != IgnoreSubmoduleMode.ALL) { + assertTrue(hasChanges); + assertEquals("[modules/submodule]", + indexDiff.getMissing().toString()); + } else { + assertFalse(hasChanges); + } + } + + @Theory + public void testSubmoduleReplacedByFile(IgnoreSubmoduleMode mode) + throws Exception { + recursiveDelete(submodule_trash); + writeTrashFile("modules/submodule", "nonsense"); + IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD, + new FileTreeIterator(db)); + indexDiff.setIgnoreSubmoduleMode(mode); + assertTrue(indexDiff.diff()); + assertEquals("[]", indexDiff.getMissing().toString()); + assertEquals("[]", indexDiff.getUntracked().toString()); + assertEquals("[modules/submodule]", indexDiff.getModified().toString()); + } + @Theory public void testDirtyRootWorktree(IgnoreSubmoduleMode mode) throws IOException { @@ -210,4 +266,33 @@ public class IndexDiffSubmoduleTest extends RepositoryTestCase { assertDiff(indexDiff, mode, IgnoreSubmoduleMode.ALL, IgnoreSubmoduleMode.DIRTY, IgnoreSubmoduleMode.UNTRACKED); } + + @Theory + public void testSubmoduleReplacedByMovedFile(IgnoreSubmoduleMode mode) + throws Exception { + Git git = Git.wrap(db); + git.rm().setCached(true).addFilepattern("modules/submodule").call(); + recursiveDelete(submodule_trash); + JGitTestUtil.deleteTrashFile(db, "fileInRoot"); + // Move the fileInRoot file + writeTrashFile("modules/submodule/fileInRoot", "root"); + git.rm().addFilepattern("fileInRoot").addFilepattern("modules/").call(); + git.add().addFilepattern("modules/").call(); + IndexDiff indexDiff = new IndexDiff(db, Constants.HEAD, + new FileTreeIterator(db)); + indexDiff.setIgnoreSubmoduleMode(mode); + assertTrue(indexDiff.diff()); + String[] removed = indexDiff.getRemoved().toArray(new String[0]); + Arrays.sort(removed); + if (IgnoreSubmoduleMode.ALL.equals(mode)) { + assertArrayEquals(new String[] { "fileInRoot" }, removed); + } else { + assertArrayEquals( + new String[] { "fileInRoot", "modules/submodule" }, + removed); + } + assertEquals("[modules/submodule/fileInRoot]", + indexDiff.getAdded().toString()); + } + } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java index 580b08b42f..ba5aaf1b18 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java @@ -119,6 +119,28 @@ public class IndexDiffTest extends RepositoryTestCase { } @Test + public void testMissing() throws Exception { + File file2 = writeTrashFile("file2", "file2"); + File file3 = writeTrashFile("dir/file3", "dir/file3"); + Git git = Git.wrap(db); + git.add().addFilepattern("file2").addFilepattern("dir/file3").call(); + git.commit().setMessage("commit").call(); + assertTrue(file2.delete()); + assertTrue(file3.delete()); + IndexDiff diff = new IndexDiff(db, Constants.HEAD, + new FileTreeIterator(db)); + diff.diff(); + assertEquals(2, diff.getMissing().size()); + assertTrue(diff.getMissing().contains("file2")); + assertTrue(diff.getMissing().contains("dir/file3")); + assertEquals(0, diff.getChanged().size()); + assertEquals(0, diff.getModified().size()); + assertEquals(0, diff.getAdded().size()); + assertEquals(0, diff.getRemoved().size()); + assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders()); + } + + @Test public void testRemoved() throws IOException { writeTrashFile("file2", "file2"); writeTrashFile("dir/file3", "dir/file3"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java index 347883f842..abf7d56819 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MergeHeadMsgTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.lib; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -72,7 +73,9 @@ public class MergeHeadMsgTest extends RepositoryTestCase { // same test again, this time with lower-level io try (FileOutputStream fos = new FileOutputStream( new File(db.getDirectory(), "MERGE_HEAD"));) { - fos.write("0000000000000000000000000000000000000000\n1c6db447abdbb291b25f07be38ea0b1bf94947c5\n".getBytes(Constants.CHARACTER_ENCODING)); + fos.write( + "0000000000000000000000000000000000000000\n1c6db447abdbb291b25f07be38ea0b1bf94947c5\n" + .getBytes(UTF_8)); } assertEquals(db.readMergeHeads().size(), 2); assertEquals(db.readMergeHeads().get(0), ObjectId.zeroId()); @@ -82,7 +85,7 @@ public class MergeHeadMsgTest extends RepositoryTestCase { assertEquals(db.readMergeHeads(), null); try (FileOutputStream fos = new FileOutputStream( new File(db.getDirectory(), "MERGE_HEAD"))) { - fos.write(sampleId.getBytes(Constants.CHARACTER_ENCODING)); + fos.write(sampleId.getBytes(UTF_8)); } assertEquals(db.readMergeHeads().size(), 1); assertEquals(db.readMergeHeads().get(0), ObjectId.fromString(sampleId)); @@ -100,7 +103,7 @@ public class MergeHeadMsgTest extends RepositoryTestCase { assertFalse(new File(db.getDirectory(), "MERGE_MSG").exists()); try (FileOutputStream fos = new FileOutputStream( new File(db.getDirectory(), Constants.MERGE_MSG))) { - fos.write(mergeMsg.getBytes(Constants.CHARACTER_ENCODING)); + fos.write(mergeMsg.getBytes(UTF_8)); } assertEquals(db.readMergeCommitMsg(), mergeMsg); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectLoaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectLoaderTest.java index 83e61d9ab4..055e66ed81 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectLoaderTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectLoaderTest.java @@ -260,6 +260,12 @@ public class ObjectLoaderTest { fail("never should have reached read"); return -1; } + + @Override + public int read(byte b[], int off, int len) { + fail("never should have reached read"); + return -1; + } }; } }; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java index 3542dfad2d..bb24994ee8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.lib; import static java.lang.Long.valueOf; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; @@ -181,7 +182,7 @@ public class RacyGitTests extends RepositoryTestCase { private File addToWorkDir(String path, String content) throws IOException { File f = new File(db.getWorkTree(), path); try (FileOutputStream fos = new FileOutputStream(f)) { - fos.write(content.getBytes(Constants.CHARACTER_ENCODING)); + fos.write(content.getBytes(UTF_8)); return f; } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java index a42027b584..7d2c4a2784 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.lib; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -261,7 +262,7 @@ public class RefTest extends SampleDataRepositoryTestCase { assertEquals(Storage.PACKED, ref.getStorage()); try (FileOutputStream os = new FileOutputStream( new File(db.getDirectory(), "refs/heads/master"))) { - os.write(ref.getObjectId().name().getBytes()); + os.write(ref.getObjectId().name().getBytes(UTF_8)); os.write('\n'); } @@ -333,4 +334,17 @@ public class RefTest extends SampleDataRepositoryTestCase { assertEquals(1, refs.size()); checkContainsRef(refs, db.exactRef("refs/heads/prefix/a")); } + + @Test + public void testGetRefsByPrefixes() throws IOException { + List<Ref> refs = db.getRefDatabase().getRefsByPrefix(); + assertEquals(0, refs.size()); + + refs = db.getRefDatabase().getRefsByPrefix("refs/heads/p", + "refs/tags/A"); + assertEquals(3, refs.size()); + checkContainsRef(refs, db.exactRef("refs/heads/pa")); + checkContainsRef(refs, db.exactRef("refs/heads/prefix/a")); + checkContainsRef(refs, db.exactRef("refs/tags/A")); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java index 203c00e28a..f58ab06074 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SquashCommitMsgTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.lib; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -68,7 +69,7 @@ public class SquashCommitMsgTest extends RepositoryTestCase { assertFalse(new File(db.getDirectory(), Constants.SQUASH_MSG).exists()); try (FileOutputStream fos = new FileOutputStream( new File(db.getDirectory(), Constants.SQUASH_MSG))) { - fos.write(squashMsg.getBytes(Constants.CHARACTER_ENCODING)); + fos.write(squashMsg.getBytes(UTF_8)); } assertEquals(db.readSquashCommitMsg(), squashMsg); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java index 87e901fcfe..df5079ae16 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java @@ -270,8 +270,8 @@ public class ValidRefNameTest { @Test public void testNormalizeBranchName() { - assertEquals(true, Repository.normalizeBranchName(null) == ""); - assertEquals(true, Repository.normalizeBranchName("").equals("")); + assertEquals("", Repository.normalizeBranchName(null)); + assertEquals("", Repository.normalizeBranchName("")); assertNormalized("Bug 12345::::Hello World", "Bug_12345-Hello_World"); assertNormalized("Bug 12345 :::: Hello World", "Bug_12345_Hello_World"); assertNormalized("Bug 12345 :::: Hello::: World", diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java index 5af62b6704..3da779b4ec 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.merge; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.ByteArrayOutputStream; @@ -301,8 +302,8 @@ public class MergeAlgorithmTest { MergeResult r = new MergeAlgorithm().merge(RawTextComparator.DEFAULT, T(commonBase), T(ours), T(theirs)); ByteArrayOutputStream bo=new ByteArrayOutputStream(50); - fmt.formatMerge(bo, r, "B", "O", "T", Constants.CHARACTER_ENCODING); - return new String(bo.toByteArray(), Constants.CHARACTER_ENCODING); + fmt.formatMerge(bo, r, "B", "O", "T", UTF_8); + return new String(bo.toByteArray(), UTF_8); } public String t(String text) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java index 8f12dd7b27..8ca5d453fb 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java @@ -839,7 +839,7 @@ public class MergerTest extends RepositoryTestCase { /** * Throws an exception if reading beyond limit. */ - class BigReadForbiddenStream extends ObjectStream.Filter { + static class BigReadForbiddenStream extends ObjectStream.Filter { int limit; BigReadForbiddenStream(ObjectStream orig, int limit) { @@ -878,7 +878,7 @@ public class MergerTest extends RepositoryTestCase { } } - class BigReadForbiddenReader extends ObjectReader.Filter { + static class BigReadForbiddenReader extends ObjectReader.Filter { ObjectReader delegate; int limit; @@ -972,7 +972,7 @@ public class MergerTest extends RepositoryTestCase { merger.getMergeResults().get("file"); try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { fmt.formatMerge(out, merger.getMergeResults().get("file"), - "BASE", "OURS", "THEIRS", UTF_8.name()); + "BASE", "OURS", "THEIRS", UTF_8); String expected = "<<<<<<< OURS\n" + "1master\n" + "=======\n" diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java index 9a6043f321..7297de3646 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java @@ -56,7 +56,7 @@ import org.junit.Test; public class PlotCommitListTest extends RevWalkTestCase { - class CommitListAssert { + static class CommitListAssert { private PlotCommitList<PlotLane> pcl; private PlotCommit<PlotLane> current; private int nextIndex = 0; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java index 7f0d60295c..b401d2bea2 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/FileBasedConfigTest.java @@ -52,6 +52,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.util.StringTokenizer; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.util.FS; @@ -67,16 +68,23 @@ public class FileBasedConfigTest { private static final String NAME = "name"; + private static final String EMAIL = "email"; + private static final String ALICE = "Alice"; private static final String BOB = "Bob"; + private static final String ALICE_EMAIL = "alice@home"; + private static final String CONTENT1 = "[" + USER + "]\n\t" + NAME + " = " + ALICE + "\n"; private static final String CONTENT2 = "[" + USER + "]\n\t" + NAME + " = " + BOB + "\n"; + private static final String CONTENT3 = "[" + USER + "]\n\t" + NAME + " = " + + ALICE + "\n" + "[" + USER + "]\n\t" + EMAIL + " = " + ALICE_EMAIL; + private File trash; @Before @@ -93,14 +101,14 @@ public class FileBasedConfigTest { @Test public void testSystemEncoding() throws IOException, ConfigInvalidException { - final File file = createFile(CONTENT1.getBytes()); + final File file = createFile(CONTENT1.getBytes(UTF_8)); final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED); config.load(); assertEquals(ALICE, config.getString(USER, null, NAME)); config.setString(USER, null, NAME, BOB); config.save(); - assertArrayEquals(CONTENT2.getBytes(), IO.readFully(file)); + assertArrayEquals(CONTENT2.getBytes(UTF_8), IO.readFully(file)); } @Test @@ -112,7 +120,7 @@ public class FileBasedConfigTest { config.setString(USER, null, NAME, BOB); config.save(); - assertArrayEquals(CONTENT2.getBytes(), IO.readFully(file)); + assertArrayEquals(CONTENT2.getBytes(UTF_8), IO.readFully(file)); } @Test @@ -142,8 +150,8 @@ public class FileBasedConfigTest { @Test public void testLeadingWhitespaces() throws IOException, ConfigInvalidException { final ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); - bos1.write(" \n\t".getBytes()); - bos1.write(CONTENT1.getBytes()); + bos1.write(" \n\t".getBytes(UTF_8)); + bos1.write(CONTENT1.getBytes(UTF_8)); final File file = createFile(bos1.toByteArray()); final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED); @@ -154,18 +162,18 @@ public class FileBasedConfigTest { config.save(); final ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); - bos2.write(" \n\t".getBytes()); - bos2.write(CONTENT2.getBytes()); + bos2.write(" \n\t".getBytes(UTF_8)); + bos2.write(CONTENT2.getBytes(UTF_8)); assertArrayEquals(bos2.toByteArray(), IO.readFully(file)); } @Test public void testIncludeAbsolute() throws IOException, ConfigInvalidException { - final File includedFile = createFile(CONTENT1.getBytes()); + final File includedFile = createFile(CONTENT1.getBytes(UTF_8)); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bos.write("[include]\npath=".getBytes()); - bos.write(pathToString(includedFile).getBytes()); + bos.write("[include]\npath=".getBytes(UTF_8)); + bos.write(pathToString(includedFile).getBytes(UTF_8)); final File file = createFile(bos.toByteArray()); final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED); @@ -176,10 +184,10 @@ public class FileBasedConfigTest { @Test public void testIncludeRelativeDot() throws IOException, ConfigInvalidException { - final File includedFile = createFile(CONTENT1.getBytes(), "dir1"); + final File includedFile = createFile(CONTENT1.getBytes(UTF_8), "dir1"); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bos.write("[include]\npath=".getBytes()); - bos.write(("./" + includedFile.getName()).getBytes()); + bos.write("[include]\npath=".getBytes(UTF_8)); + bos.write(("./" + includedFile.getName()).getBytes(UTF_8)); final File file = createFile(bos.toByteArray(), "dir1"); final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED); @@ -190,11 +198,11 @@ public class FileBasedConfigTest { @Test public void testIncludeRelativeDotDot() throws IOException, ConfigInvalidException { - final File includedFile = createFile(CONTENT1.getBytes(), "dir1"); + final File includedFile = createFile(CONTENT1.getBytes(UTF_8), "dir1"); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bos.write("[include]\npath=".getBytes()); + bos.write("[include]\npath=".getBytes(UTF_8)); bos.write(("../" + includedFile.getParentFile().getName() + "/" - + includedFile.getName()).getBytes()); + + includedFile.getName()).getBytes(UTF_8)); final File file = createFile(bos.toByteArray(), "dir2"); final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED); @@ -205,10 +213,10 @@ public class FileBasedConfigTest { @Test public void testIncludeRelativeDotDotNotFound() throws IOException, ConfigInvalidException { - final File includedFile = createFile(CONTENT1.getBytes()); + final File includedFile = createFile(CONTENT1.getBytes(UTF_8)); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bos.write("[include]\npath=".getBytes()); - bos.write(("../" + includedFile.getName()).getBytes()); + bos.write("[include]\npath=".getBytes(UTF_8)); + bos.write(("../" + includedFile.getName()).getBytes(UTF_8)); final File file = createFile(bos.toByteArray()); final FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED); @@ -219,10 +227,10 @@ public class FileBasedConfigTest { @Test public void testIncludeWithTilde() throws IOException, ConfigInvalidException { - final File includedFile = createFile(CONTENT1.getBytes(), "home"); + final File includedFile = createFile(CONTENT1.getBytes(UTF_8), "home"); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bos.write("[include]\npath=".getBytes()); - bos.write(("~/" + includedFile.getName()).getBytes()); + bos.write("[include]\npath=".getBytes(UTF_8)); + bos.write(("~/" + includedFile.getName()).getBytes(UTF_8)); final File file = createFile(bos.toByteArray(), "repo"); final FS fs = FS.DETECTED.newInstance(); @@ -233,6 +241,50 @@ public class FileBasedConfigTest { assertEquals(ALICE, config.getString(USER, null, NAME)); } + @Test + public void testIncludeDontInlineIncludedLinesOnSave() + throws IOException, ConfigInvalidException { + // use a content with multiple sections and multiple key/value pairs + // because code for first line works different than for subsequent lines + final File includedFile = createFile(CONTENT3.getBytes(UTF_8), "dir1"); + + final File file = createFile(new byte[0], "dir2"); + FileBasedConfig config = new FileBasedConfig(file, FS.DETECTED); + config.setString("include", null, "path", + ("../" + includedFile.getParentFile().getName() + "/" + + includedFile.getName())); + + // just by setting the include.path, it won't be included + assertEquals(null, config.getString(USER, null, NAME)); + assertEquals(null, config.getString(USER, null, EMAIL)); + config.save(); + + // and it won't be included after saving + assertEquals(null, config.getString(USER, null, NAME)); + assertEquals(null, config.getString(USER, null, EMAIL)); + + final String expectedText = config.toText(); + assertEquals(2, + new StringTokenizer(expectedText, "\n", false).countTokens()); + + config = new FileBasedConfig(file, FS.DETECTED); + config.load(); + + String actualText = config.toText(); + assertEquals(expectedText, actualText); + // but it will be included after (re)loading + assertEquals(ALICE, config.getString(USER, null, NAME)); + assertEquals(ALICE_EMAIL, config.getString(USER, null, EMAIL)); + + config.save(); + + actualText = config.toText(); + assertEquals(expectedText, actualText); + // and of course preserved after saving + assertEquals(ALICE, config.getString(USER, null, NAME)); + assertEquals(ALICE_EMAIL, config.getString(USER, null, EMAIL)); + } + private File createFile(byte[] content) throws IOException { return createFile(content, null); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java index fed22c0262..a0cd37ee5f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.submodule; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_URL; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SUBMODULE_SECTION; @@ -52,9 +53,10 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Status; @@ -155,10 +157,12 @@ public class SubmoduleWalkTest extends RepositoryTestCase { if (!dotGit.getParentFile().exists()) dotGit.getParentFile().mkdirs(); - File modulesGitDir = new File(db.getDirectory(), "modules" - + File.separatorChar + path); - new FileWriter(dotGit).append( - "gitdir: " + modulesGitDir.getAbsolutePath()).close(); + File modulesGitDir = new File(db.getDirectory(), + "modules" + File.separatorChar + path); + try (BufferedWriter fw = Files.newBufferedWriter(dotGit.toPath(), + UTF_8)) { + fw.append("gitdir: " + modulesGitDir.getAbsolutePath()); + } FileRepositoryBuilder builder = new FileRepositoryBuilder(); builder.setWorkTree(new File(db.getWorkTree(), path)); builder.build().create(); @@ -209,9 +213,11 @@ public class SubmoduleWalkTest extends RepositoryTestCase { File modulesGitDir = new File(db.getDirectory(), "modules" + File.separatorChar + path); - new FileWriter(dotGit).append( - "gitdir: " + "../" + Constants.DOT_GIT + "/modules/" + path) - .close(); + try (BufferedWriter fw = Files.newBufferedWriter(dotGit.toPath(), + UTF_8)) { + fw.append("gitdir: " + "../" + Constants.DOT_GIT + "/modules/" + + path); + } FileRepositoryBuilder builder = new FileRepositoryBuilder(); builder.setWorkTree(new File(db.getWorkTree(), path)); builder.build().create(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java new file mode 100644 index 0000000000..8ff70c4e97 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.util.Arrays; + +import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.transport.OpenSshConfig.Host; +import org.eclipse.jgit.transport.ssh.SshTestBase; +import org.eclipse.jgit.util.FS; +import org.junit.experimental.theories.Theories; +import org.junit.runner.RunWith; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +@RunWith(Theories.class) +public class JSchSshTest extends SshTestBase { + + private class TestSshSessionFactory extends JschConfigSessionFactory { + + @Override + protected void configure(Host hc, Session session) { + // Nothing + } + + @Override + public synchronized RemoteSession getSession(URIish uri, + CredentialsProvider credentialsProvider, FS fs, int tms) + throws TransportException { + return super.getSession(uri, credentialsProvider, fs, tms); + } + + @Override + protected JSch createDefaultJSch(FS fs) throws JSchException { + JSch defaultJSch = super.createDefaultJSch(fs); + if (knownHosts.exists()) { + defaultJSch.setKnownHosts(knownHosts.getAbsolutePath()); + } + return defaultJSch; + } + } + + @Override + protected SshSessionFactory createSessionFactory() { + return new TestSshSessionFactory(); + } + + @Override + protected void installConfig(String... config) { + SshSessionFactory factory = getSessionFactory(); + assertTrue(factory instanceof JschConfigSessionFactory); + JschConfigSessionFactory j = (JschConfigSessionFactory) factory; + try { + j.setConfig(createConfig(config)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private OpenSshConfig createConfig(String... content) throws IOException { + File configFile = new File(sshDir, Constants.CONFIG); + if (content != null) { + Files.write(configFile.toPath(), Arrays.asList(content)); + } + return new OpenSshConfig(getTemporaryDirectory(), configFile); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ObjectIdMatcher.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ObjectIdMatcher.java new file mode 100644 index 0000000000..4c6e0f0add --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ObjectIdMatcher.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Sets; +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +/** + * Multiple tests check that a collection of ObjectIds contain certain SHA1 + * (written as strings). This matcher hides the ObjectId to string conversion to + * make the assertion more readable: + * + * assertThat(req.getWantsIds(), hasOnlyObjectIds("123123", "234234")); + */ +class ObjectIdMatcher extends TypeSafeMatcher<Collection<ObjectId>> { + + private final Set<ObjectId> expectedOids; + + private ObjectIdMatcher(Set<String> oids) { + this.expectedOids = oids.stream().map(ObjectId::fromString) + .collect(Collectors.toSet()); + } + + @Override + public void describeTo(Description desc) { + desc.appendText("Object ids:"); + desc.appendValueList("<", ",", ">", expectedOids); + } + + @Override + protected boolean matchesSafely(Collection<ObjectId> resultOids) { + return resultOids.containsAll(expectedOids) + && expectedOids.containsAll(resultOids); + } + + /** + * Assert that all and only the received {@link ObjectId object ids} are in + * the expected set. + * <p> + * ObjectIds are compared by SHA1. + * + * @param oids + * Object ids to examine. + * @return true if examined and specified sets contains exactly the same + * elements. + */ + @Factory + static Matcher<Collection<ObjectId>> hasOnlyObjectIds( + String... oids) { + return new ObjectIdMatcher(Sets.of(oids)); + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java index 19fcbfd7a2..1a22e10f4c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java @@ -50,7 +50,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.File; @@ -67,6 +66,7 @@ import org.junit.Before; import org.junit.Test; import com.jcraft.jsch.ConfigRepository; +import com.jcraft.jsch.ConfigRepository.Config; public class OpenSshConfigTest extends RepositoryTestCase { private File home; @@ -164,6 +164,20 @@ public class OpenSshConfigTest extends RepositoryTestCase { } @Test + public void testCaseInsensitiveKeyLookup() throws Exception { + config("Host orcz\n" + "Port 29418\n" + + "\tHostName repo.or.cz\nStrictHostKeyChecking yes\n"); + final Host h = osc.lookup("orcz"); + Config c = h.getConfig(); + 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 Host h = osc.lookup("repo.or.cz"); @@ -334,21 +348,6 @@ public class OpenSshConfigTest extends RepositoryTestCase { } @Test - public void testRepeatedLookups() throws Exception { - config("Host orcz\n" + "\tConnectionAttempts 5\n"); - final Host h1 = osc.lookup("orcz"); - final Host h2 = osc.lookup("orcz"); - assertNotNull(h1); - assertSame(h1, h2); - assertEquals(5, h1.getConnectionAttempts()); - assertEquals(h1.getConnectionAttempts(), h2.getConnectionAttempts()); - final ConfigRepository.Config c = osc.getConfig("orcz"); - assertNotNull(c); - assertSame(c, h1.getConfig()); - assertSame(c, h2.getConfig()); - } - - @Test public void testRepeatedLookupsWithModification() throws Exception { config("Host orcz\n" + "\tConnectionAttempts -1\n"); final Host h1 = osc.lookup("orcz"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java index 391a701b8e..ad8ae42536 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.transport; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -50,7 +51,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import org.eclipse.jgit.lib.Constants; import org.junit.Before; import org.junit.Test; @@ -173,8 +173,8 @@ public class PacketLineOutTest { assertEquals(1, flushCnt[0]); } - private void assertBuffer(String exp) throws IOException { + private void assertBuffer(String exp) { assertEquals(exp, new String(rawOut.toByteArray(), - Constants.CHARACTER_ENCODING)); + UTF_8)); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java new file mode 100644 index 0000000000..2c98c84ae5 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2018, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport; + +import static org.eclipse.jgit.transport.ObjectIdMatcher.hasOnlyObjectIds; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.eclipse.jgit.errors.PackProtocolException; +import org.eclipse.jgit.lib.Config; +import org.junit.Test; + +public class ProtocolV0ParserTest { + /* + * Convert the input lines to the PacketLine that the parser reads. + */ + private static PacketLineIn formatAsPacketLine(String... inputLines) + throws IOException { + ByteArrayOutputStream send = new ByteArrayOutputStream(); + PacketLineOut pckOut = new PacketLineOut(send); + for (String line : inputLines) { + if (line == PacketLineIn.END) { + pckOut.end(); + } else if (line == PacketLineIn.DELIM) { + pckOut.writeDelim(); + } else { + pckOut.writeString(line); + } + } + + return new PacketLineIn(new ByteArrayInputStream(send.toByteArray())); + } + + private static TransferConfig defaultConfig() { + Config rc = new Config(); + rc.setBoolean("uploadpack", null, "allowfilter", true); + return new TransferConfig(rc); + } + + @Test + public void testRecvWantsWithCapabilities() + throws PackProtocolException, IOException { + PacketLineIn pckIn = formatAsPacketLine( + String.join(" ", "want", + "4624442d68ee402a94364191085b77137618633e", "thin-pack", + "no-progress", "include-tag", "ofs-delta", "\n"), + "want f900c8326a43303685c46b279b9f70411bff1a4b\n", + PacketLineIn.END); + ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); + FetchV0Request request = parser.recvWants(pckIn); + assertTrue(request.getClientCapabilities() + .contains(GitProtocolConstants.OPTION_THIN_PACK)); + assertTrue(request.getClientCapabilities() + .contains(GitProtocolConstants.OPTION_NO_PROGRESS)); + assertTrue(request.getClientCapabilities() + .contains(GitProtocolConstants.OPTION_INCLUDE_TAG)); + assertTrue(request.getClientCapabilities() + .contains(GitProtocolConstants.CAPABILITY_OFS_DELTA)); + assertThat(request.getWantIds(), + hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", + "f900c8326a43303685c46b279b9f70411bff1a4b")); + } + + @Test + public void testRecvWantsWithAgent() + throws PackProtocolException, IOException { + PacketLineIn pckIn = formatAsPacketLine( + String.join(" ", "want", + "4624442d68ee402a94364191085b77137618633e", "thin-pack", + "agent=JGit.test/0.0.1", "\n"), + "want f900c8326a43303685c46b279b9f70411bff1a4b\n", + PacketLineIn.END); + ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); + FetchV0Request request = parser.recvWants(pckIn); + assertTrue(request.getClientCapabilities() + .contains(GitProtocolConstants.OPTION_THIN_PACK)); + assertEquals(1, request.getClientCapabilities().size()); + assertEquals("JGit.test/0.0.1", request.getAgent()); + assertThat(request.getWantIds(), + hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", + "f900c8326a43303685c46b279b9f70411bff1a4b")); + } + + /* + * First round of protocol v0 negotiation. Client send wants, no + * capabilities. + */ + @Test + public void testRecvWantsWithoutCapabilities() + throws PackProtocolException, IOException { + PacketLineIn pckIn = formatAsPacketLine( + "want 4624442d68ee402a94364191085b77137618633e\n", + "want f900c8326a43303685c46b279b9f70411bff1a4b\n", + PacketLineIn.END); + ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); + FetchV0Request request = parser.recvWants(pckIn); + assertTrue(request.getClientCapabilities().isEmpty()); + assertThat(request.getWantIds(), + hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", + "f900c8326a43303685c46b279b9f70411bff1a4b")); + } + + @Test + public void testRecvWantsDeepen() + throws PackProtocolException, IOException { + PacketLineIn pckIn = formatAsPacketLine( + "want 4624442d68ee402a94364191085b77137618633e\n", + "want f900c8326a43303685c46b279b9f70411bff1a4b\n", "deepen 3\n", + PacketLineIn.END); + ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); + FetchV0Request request = parser.recvWants(pckIn); + assertTrue(request.getClientCapabilities().isEmpty()); + assertEquals(3, request.getDepth()); + assertThat(request.getWantIds(), + hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", + "f900c8326a43303685c46b279b9f70411bff1a4b")); + } + + @Test + public void testRecvWantsShallow() + throws PackProtocolException, IOException { + PacketLineIn pckIn = formatAsPacketLine( + "want 4624442d68ee402a94364191085b77137618633e\n", + "want f900c8326a43303685c46b279b9f70411bff1a4b\n", + "shallow 4b643d0ef739a1b494e7d6926d8d8ed80d35edf4\n", + PacketLineIn.END); + ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); + FetchV0Request request = parser.recvWants(pckIn); + assertTrue(request.getClientCapabilities().isEmpty()); + assertThat(request.getWantIds(), + hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", + "f900c8326a43303685c46b279b9f70411bff1a4b")); + assertThat(request.getClientShallowCommits(), + hasOnlyObjectIds("4b643d0ef739a1b494e7d6926d8d8ed80d35edf4")); + } + + @Test + public void testRecvWantsFilter() + throws PackProtocolException, IOException { + PacketLineIn pckIn = formatAsPacketLine( + "want 4624442d68ee402a94364191085b77137618633e\n", + "want f900c8326a43303685c46b279b9f70411bff1a4b\n", + "filter blob:limit=13000\n", + PacketLineIn.END); + ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); + FetchV0Request request = parser.recvWants(pckIn); + assertTrue(request.getClientCapabilities().isEmpty()); + assertThat(request.getWantIds(), + hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", + "f900c8326a43303685c46b279b9f70411bff1a4b")); + assertEquals(13000, request.getFilterBlobLimit()); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java index bf67d46d51..dafa81ecd0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java @@ -44,22 +44,20 @@ package org.eclipse.jgit.transport; import static org.hamcrest.Matchers.hasItems; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.eclipse.jgit.transport.ObjectIdMatcher.hasOnlyObjectIds; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Before; import org.junit.Rule; @@ -137,12 +135,6 @@ public class ProtocolV2ParserTest { return new PacketLineIn(new ByteArrayInputStream(send.toByteArray())); } - private static List<String> objIdsAsStrings(Collection<ObjectId> objIds) { - // TODO(ifrade) Translate this to a matcher, so it would read as - // assertThat(req.wantsIds(), hasObjectIds("...", "...")) - return objIds.stream().map(ObjectId::name).collect(Collectors.toList()); - } - /* * Succesful fetch with the basic core commands of the protocol. */ @@ -160,19 +152,19 @@ public class ProtocolV2ParserTest { ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); FetchV2Request request = parser.parseFetchRequest(pckIn); - assertTrue(request.getOptions() + assertTrue(request.getClientCapabilities() .contains(GitProtocolConstants.OPTION_THIN_PACK)); - assertTrue(request.getOptions() + assertTrue(request.getClientCapabilities() .contains(GitProtocolConstants.OPTION_NO_PROGRESS)); - assertTrue(request.getOptions() + assertTrue(request.getClientCapabilities() .contains(GitProtocolConstants.OPTION_INCLUDE_TAG)); - assertTrue(request.getOptions() + assertTrue(request.getClientCapabilities() .contains(GitProtocolConstants.CAPABILITY_OFS_DELTA)); - assertThat(objIdsAsStrings(request.getWantsIds()), - hasItems("4624442d68ee402a94364191085b77137618633e", + assertThat(request.getWantIds(), + hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", "f900c8326a43303685c46b279b9f70411bff1a4b")); - assertThat(objIdsAsStrings(request.getPeerHas()), - hasItems("554f6e41067b9e3e565b6988a8294fac1cb78f4b", + assertThat(request.getPeerHas(), + hasOnlyObjectIds("554f6e41067b9e3e565b6988a8294fac1cb78f4b", "abc760ab9ad72f08209943251b36cb886a578f87")); assertTrue(request.getWantedRefs().isEmpty()); assertTrue(request.wasDoneReceived()); @@ -190,12 +182,12 @@ public class ProtocolV2ParserTest { ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); FetchV2Request request = parser.parseFetchRequest(pckIn); - assertThat(objIdsAsStrings(request.getClientShallowCommits()), - hasItems("28274d02c489f4c7e68153056e9061a46f62d7a0", + assertThat(request.getClientShallowCommits(), + hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0", "145e683b229dcab9d0e2ccb01b386f9ecc17d29d")); assertTrue(request.getDeepenNotRefs().isEmpty()); assertEquals(15, request.getDepth()); - assertTrue(request.getOptions() + assertTrue(request.getClientCapabilities() .contains(GitProtocolConstants.OPTION_DEEPEN_RELATIVE)); } @@ -209,8 +201,8 @@ public class ProtocolV2ParserTest { ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); FetchV2Request request = parser.parseFetchRequest(pckIn); - assertThat(objIdsAsStrings(request.getClientShallowCommits()), - hasItems("28274d02c489f4c7e68153056e9061a46f62d7a0", + assertThat(request.getClientShallowCommits(), + hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0", "145e683b229dcab9d0e2ccb01b386f9ecc17d29d")); assertThat(request.getDeepenNotRefs(), hasItems("a08595f76159b09d57553e37a5123f1091bb13e7")); @@ -226,8 +218,8 @@ public class ProtocolV2ParserTest { ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); FetchV2Request request = parser.parseFetchRequest(pckIn); - assertThat(objIdsAsStrings(request.getClientShallowCommits()), - hasItems("28274d02c489f4c7e68153056e9061a46f62d7a0", + assertThat(request.getClientShallowCommits(), + hasOnlyObjectIds("28274d02c489f4c7e68153056e9061a46f62d7a0", "145e683b229dcab9d0e2ccb01b386f9ecc17d29d")); assertEquals(123123123, request.getDeepenSince()); } @@ -256,24 +248,25 @@ public class ProtocolV2ParserTest { @Test public void testFetchMustNotHaveMultipleFilters() throws IOException { - thrown.expect(PackProtocolException.class); PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, "filter blob:none", "filter blob:limit=12", PacketLineIn.END); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.start().allowFilter().done()); - FetchV2Request request = parser.parseFetchRequest(pckIn); - assertEquals(0, request.getFilterBlobLimit()); + + thrown.expect(PackProtocolException.class); + parser.parseFetchRequest(pckIn); } @Test public void testFetchFilterWithoutAllowFilter() throws IOException { - thrown.expect(PackProtocolException.class); PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, "filter blob:limit=12", PacketLineIn.END); ProtocolV2Parser parser = new ProtocolV2Parser( ConfigBuilder.getDefault()); + + thrown.expect(PackProtocolException.class); parser.parseFetchRequest(pckIn); } @@ -293,10 +286,11 @@ public class ProtocolV2ParserTest { FetchV2Request request = parser.parseFetchRequest(pckIn); assertEquals(1, request.getWantedRefs().size()); - assertThat(request.getWantedRefs(), hasItems("refs/heads/branchA")); - assertEquals(1, request.getWantsIds().size()); - assertThat(objIdsAsStrings(request.getWantsIds()), - hasItems("e4980cdc48cfa1301493ca94eb70523f6788b819")); + assertThat(request.getWantedRefs(), + hasItems("refs/heads/branchA")); + assertEquals(1, request.getWantIds().size()); + assertThat(request.getWantIds(), hasOnlyObjectIds( + "e4980cdc48cfa1301493ca94eb70523f6788b819")); } @Test @@ -318,4 +312,60 @@ public class ProtocolV2ParserTest { assertThat(request.getWantedRefs(), hasItems("refs/heads/branchC")); } + @Test + public void testLsRefsMinimalReq() throws IOException { + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + PacketLineIn.END); + + ProtocolV2Parser parser = new ProtocolV2Parser( + ConfigBuilder.getDefault()); + LsRefsV2Request req = parser.parseLsRefsRequest(pckIn); + assertFalse(req.getPeel()); + assertFalse(req.getSymrefs()); + assertEquals(0, req.getRefPrefixes().size()); + } + + @Test + public void testLsRefsSymrefs() throws IOException { + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, "symrefs", + PacketLineIn.END); + + ProtocolV2Parser parser = new ProtocolV2Parser( + ConfigBuilder.getDefault()); + LsRefsV2Request req = parser.parseLsRefsRequest(pckIn); + assertFalse(req.getPeel()); + assertTrue(req.getSymrefs()); + assertEquals(0, req.getRefPrefixes().size()); + + } + + @Test + public void testLsRefsPeel() throws IOException { + PacketLineIn pckIn = formatAsPacketLine( + PacketLineIn.DELIM, + "peel", + PacketLineIn.END); + + ProtocolV2Parser parser = new ProtocolV2Parser( + ConfigBuilder.getDefault()); + LsRefsV2Request req = parser.parseLsRefsRequest(pckIn); + assertTrue(req.getPeel()); + assertFalse(req.getSymrefs()); + assertEquals(0, req.getRefPrefixes().size()); + } + + @Test + public void testLsRefsRefPrefixes() throws IOException { + PacketLineIn pckIn = formatAsPacketLine(PacketLineIn.DELIM, + "ref-prefix refs/for", "ref-prefix refs/heads", + PacketLineIn.END); + + ProtocolV2Parser parser = new ProtocolV2Parser( + ConfigBuilder.getDefault()); + LsRefsV2Request req = parser.parseLsRefsRequest(pckIn); + assertFalse(req.getPeel()); + assertFalse(req.getSymrefs()); + assertEquals(2, req.getRefPrefixes().size()); + assertThat(req.getRefPrefixes(), hasItems("refs/for", "refs/heads")); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java index 0647167eab..4bf26b6288 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java @@ -42,6 +42,7 @@ package org.eclipse.jgit.transport; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -334,7 +335,7 @@ public class PushCertificateParserTest { assertFalse(input.contains(PushCertificateParser.END_CERT)); input += input; Reader reader = new InputStreamReader( - new ByteArrayInputStream(Constants.encode(input))); + new ByteArrayInputStream(Constants.encode(input)), UTF_8); assertNotNull(PushCertificateParser.fromReader(reader)); assertNotNull(PushCertificateParser.fromReader(reader)); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java index 68e0129525..fa4fd65069 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.transport; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.lib.ObjectId.zeroId; import static org.eclipse.jgit.lib.RefUpdate.Result.FAST_FORWARD; import static org.eclipse.jgit.lib.RefUpdate.Result.LOCK_FAILURE; @@ -96,7 +97,9 @@ public class PushCertificateStoreTest { + "-----END PGP SIGNATURE-----\n"); try { return PushCertificateParser.fromReader(new InputStreamReader( - new ByteArrayInputStream(Constants.encode(cert.toString())))); + new ByteArrayInputStream( + Constants.encode(cert.toString())), + UTF_8)); } catch (IOException e) { throw new IllegalArgumentException(e); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java index c959f6c497..dfa50b6bb6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java @@ -492,9 +492,8 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas assertSame(PacketLineIn.END, r.readString()); String errorLine = r.readString(); - System.out.println(errorLine); - assertTrue(errorLine.startsWith( - "unpack error Invalid submodule URL '-")); + assertTrue(errorLine.startsWith("unpack error")); + assertTrue(errorLine.contains("Invalid submodule URL '-")); assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString()); assertSame(PacketLineIn.END, r.readString()); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java index 4d3e162240..b6cf3564c1 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java @@ -44,6 +44,7 @@ package org.eclipse.jgit.transport; import static java.lang.Integer.valueOf; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA; import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR; import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS; @@ -59,7 +60,6 @@ import java.io.OutputStream; import java.text.MessageFormat; import org.eclipse.jgit.internal.JGitText; -import org.eclipse.jgit.lib.Constants; import org.junit.Before; import org.junit.Test; @@ -259,8 +259,7 @@ public class SideBandOutputStreamTest { } } - private void assertBuffer(String exp) throws IOException { - assertEquals(exp, new String(rawOut.toByteArray(), - Constants.CHARACTER_ENCODING)); + private void assertBuffer(String exp) { + assertEquals(exp, new String(rawOut.toByteArray(), UTF_8)); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java index 953c9fc30a..1c4d0cfe24 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java @@ -46,6 +46,7 @@ package org.eclipse.jgit.transport; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.List; @@ -238,6 +239,7 @@ public class TestProtocolTest { .setRemote(user1Uri.toString()) .setRefSpecs(MASTER) .call(); + fail("accepted not permitted fetch"); } catch (InvalidRemoteException expected) { // Expected. } @@ -282,6 +284,7 @@ public class TestProtocolTest { .setRemote(user1Uri.toString()) .setRefSpecs(HEADS) .call(); + fail("accepted not permitted push"); } catch (TransportException expected) { assertTrue(expected.getMessage().contains( JGitText.get().pushNotPermitted)); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java index 317ac32e6d..8acbcce36d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java @@ -7,6 +7,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.theInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -27,6 +28,7 @@ import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; @@ -427,17 +429,7 @@ public class UploadPackTest { RefFilter refFilter, ProtocolV2Hook hook, String... inputLines) throws Exception { - ByteArrayOutputStream send = new ByteArrayOutputStream(); - PacketLineOut pckOut = new PacketLineOut(send); - for (String line : inputLines) { - if (line == PacketLineIn.END) { - pckOut.end(); - } else if (line == PacketLineIn.DELIM) { - pckOut.writeDelim(); - } else { - pckOut.writeString(line); - } - } + ByteArrayInputStream send = linesAsInputStream(inputLines); server.getConfig().setString("protocol", null, "version", "2"); UploadPack up = new UploadPack(server); @@ -451,11 +443,28 @@ public class UploadPackTest { } ByteArrayOutputStream recv = new ByteArrayOutputStream(); - up.upload(new ByteArrayInputStream(send.toByteArray()), recv, null); + up.upload(send, recv, null); return new ByteArrayInputStream(recv.toByteArray()); } + private static ByteArrayInputStream linesAsInputStream(String... inputLines) + throws IOException { + try (ByteArrayOutputStream send = new ByteArrayOutputStream()) { + PacketLineOut pckOut = new PacketLineOut(send); + for (String line : inputLines) { + if (line == PacketLineIn.END) { + pckOut.end(); + } else if (line == PacketLineIn.DELIM) { + pckOut.writeDelim(); + } else { + pckOut.writeString(line); + } + } + return new ByteArrayInputStream(send.toByteArray()); + } + } + /* * Invokes UploadPack with protocol v2 and sends it the given lines. * Returns UploadPack's output stream, not including the capability @@ -484,6 +493,8 @@ public class UploadPackTest { private LsRefsV2Request lsRefsRequest; + private FetchV2Request fetchRequest; + @Override public void onCapabilities(CapabilitiesV2Request req) { capabilitiesRequest = req; @@ -493,6 +504,11 @@ public class UploadPackTest { public void onLsRefs(LsRefsV2Request req) { lsRefsRequest = req; } + + @Override + public void onFetch(FetchV2Request req) { + fetchRequest = req; + } } @Test @@ -501,18 +517,18 @@ public class UploadPackTest { ByteArrayInputStream recvStream = uploadPackV2Setup(null, null, hook, PacketLineIn.END); PacketLineIn pckIn = new PacketLineIn(recvStream); - assertThat(hook.capabilitiesRequest, notNullValue()); assertThat(pckIn.readString(), is("version 2")); assertThat( - Arrays.asList(pckIn.readString(), pckIn.readString()), - // TODO(jonathantanmy) This check is written this way - // to make it simple to see that we expect this list of - // capabilities, but probably should be loosened to - // allow additional commands to be added to the list, - // and additional capabilities to be added to existing - // commands without requiring test changes. - hasItems("ls-refs", "fetch=shallow")); + Arrays.asList(pckIn.readString(), pckIn.readString(), + pckIn.readString()), + // TODO(jonathantanmy) This check is written this way + // to make it simple to see that we expect this list of + // capabilities, but probably should be loosened to + // allow additional commands to be added to the list, + // and additional capabilities to be added to existing + // commands without requiring test changes. + hasItems("ls-refs", "fetch=shallow", "server-option")); assertTrue(pckIn.readString() == PacketLineIn.END); } @@ -525,10 +541,11 @@ public class UploadPackTest { assertThat(pckIn.readString(), is("version 2")); assertThat( - Arrays.asList(pckIn.readString(), pckIn.readString()), - // TODO(jonathantanmy) This check overspecifies the - // order of the capabilities of "fetch". - hasItems("ls-refs", "fetch=filter shallow")); + Arrays.asList(pckIn.readString(), pckIn.readString(), + pckIn.readString()), + // TODO(jonathantanmy) This check overspecifies the + // order of the capabilities of "fetch". + hasItems("ls-refs", "fetch=filter shallow", "server-option")); assertTrue(pckIn.readString() == PacketLineIn.END); } @@ -541,10 +558,12 @@ public class UploadPackTest { assertThat(pckIn.readString(), is("version 2")); assertThat( - Arrays.asList(pckIn.readString(), pckIn.readString()), - // TODO(jonathantanmy) This check overspecifies the - // order of the capabilities of "fetch". - hasItems("ls-refs", "fetch=ref-in-want shallow")); + Arrays.asList(pckIn.readString(), pckIn.readString(), + pckIn.readString()), + // TODO(jonathantanmy) This check overspecifies the + // order of the capabilities of "fetch". + hasItems("ls-refs", "fetch=ref-in-want shallow", + "server-option")); assertTrue(pckIn.readString() == PacketLineIn.END); } @@ -557,8 +576,9 @@ public class UploadPackTest { assertThat(pckIn.readString(), is("version 2")); assertThat( - Arrays.asList(pckIn.readString(), pckIn.readString()), - hasItems("ls-refs", "fetch=shallow")); + Arrays.asList(pckIn.readString(), pckIn.readString(), + pckIn.readString()), + hasItems("ls-refs", "fetch=shallow", "server-option")); assertTrue(pckIn.readString() == PacketLineIn.END); } @@ -572,8 +592,9 @@ public class UploadPackTest { assertThat(pckIn.readString(), is("version 2")); assertThat( - Arrays.asList(pckIn.readString(), pckIn.readString()), - hasItems("ls-refs", "fetch=shallow")); + Arrays.asList(pckIn.readString(), pckIn.readString(), + pckIn.readString()), + hasItems("ls-refs", "fetch=shallow", "server-option")); assertTrue(pckIn.readString() == PacketLineIn.END); } @@ -718,6 +739,21 @@ public class UploadPackTest { PacketLineIn.END); } + @Test + public void testV2LsRefsServerOptions() throws Exception { + String[] lines = { "command=ls-refs\n", + "server-option=one\n", "server-option=two\n", + PacketLineIn.DELIM, + PacketLineIn.END }; + + TestV2Hook testHook = new TestV2Hook(); + uploadPackV2Setup(null, null, testHook, lines); + + LsRefsV2Request req = testHook.lsRefsRequest; + assertEquals(2, req.getServerOptions().size()); + assertThat(req.getServerOptions(), hasItems("one", "two")); + } + /* * Parse multiplexed packfile output from upload-pack using protocol V2 * into the client repository. @@ -1191,6 +1227,270 @@ public class UploadPackTest { } @Test + public void testV2FetchShallowSince() throws Exception { + PersonIdent person = new PersonIdent(remote.getRepository()); + + RevCommit beyondBoundary = remote.commit() + .committer(new PersonIdent(person, 1510000000, 0)).create(); + RevCommit boundary = remote.commit().parent(beyondBoundary) + .committer(new PersonIdent(person, 1520000000, 0)).create(); + RevCommit tooOld = remote.commit() + .committer(new PersonIdent(person, 1500000000, 0)).create(); + RevCommit merge = remote.commit().parent(boundary).parent(tooOld) + .committer(new PersonIdent(person, 1530000000, 0)).create(); + + remote.update("branch1", merge); + + // Report that we only have "boundary" as a shallow boundary. + ByteArrayInputStream recvStream = uploadPackV2( + "command=fetch\n", + PacketLineIn.DELIM, + "shallow " + boundary.toObjectId().getName() + "\n", + "deepen-since 1510000\n", + "want " + merge.toObjectId().getName() + "\n", + "have " + boundary.toObjectId().getName() + "\n", + "done\n", + PacketLineIn.END); + PacketLineIn pckIn = new PacketLineIn(recvStream); + assertThat(pckIn.readString(), is("shallow-info")); + + // "merge" is shallow because one of its parents is committed + // earlier than the given deepen-since time. + assertThat(pckIn.readString(), is("shallow " + merge.toObjectId().getName())); + + // "boundary" is unshallow because its parent committed at or + // later than the given deepen-since time. + assertThat(pckIn.readString(), is("unshallow " + boundary.toObjectId().getName())); + + assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertThat(pckIn.readString(), is("packfile")); + parsePack(recvStream); + + // The server does not send this because it is committed + // earlier than the given deepen-since time. + assertFalse(client.hasObject(tooOld.toObjectId())); + + // The server does not send this because the client claims to + // have it. + assertFalse(client.hasObject(boundary.toObjectId())); + + // The server sends both these commits. + assertTrue(client.hasObject(beyondBoundary.toObjectId())); + assertTrue(client.hasObject(merge.toObjectId())); + } + + @Test + public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws Exception { + PersonIdent person = new PersonIdent(remote.getRepository()); + + RevCommit base = remote.commit() + .committer(new PersonIdent(person, 1500000000, 0)).create(); + RevCommit child1 = remote.commit().parent(base) + .committer(new PersonIdent(person, 1510000000, 0)).create(); + RevCommit child2 = remote.commit().parent(base) + .committer(new PersonIdent(person, 1520000000, 0)).create(); + + remote.update("branch1", child1); + remote.update("branch2", child2); + + ByteArrayInputStream recvStream = uploadPackV2( + "command=fetch\n", + PacketLineIn.DELIM, + "deepen-since 1510000\n", + "want " + child1.toObjectId().getName() + "\n", + "want " + child2.toObjectId().getName() + "\n", + "done\n", + PacketLineIn.END); + PacketLineIn pckIn = new PacketLineIn(recvStream); + assertThat(pckIn.readString(), is("shallow-info")); + + // "base" is excluded, so its children are shallow. + assertThat( + Arrays.asList(pckIn.readString(), pckIn.readString()), + hasItems( + "shallow " + child1.toObjectId().getName(), + "shallow " + child2.toObjectId().getName())); + + assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertThat(pckIn.readString(), is("packfile")); + parsePack(recvStream); + + // Only the children are sent. + assertFalse(client.hasObject(base.toObjectId())); + assertTrue(client.hasObject(child1.toObjectId())); + assertTrue(client.hasObject(child2.toObjectId())); + } + + @Test + public void testV2FetchShallowSince_noCommitsSelected() throws Exception { + PersonIdent person = new PersonIdent(remote.getRepository()); + + RevCommit tooOld = remote.commit() + .committer(new PersonIdent(person, 1500000000, 0)).create(); + + remote.update("branch1", tooOld); + + thrown.expect(PackProtocolException.class); + thrown.expectMessage("No commits selected for shallow request"); + uploadPackV2( + "command=fetch\n", + PacketLineIn.DELIM, + "deepen-since 1510000\n", + "want " + tooOld.toObjectId().getName() + "\n", + "done\n", + PacketLineIn.END); + } + + @Test + public void testV2FetchDeepenNot() throws Exception { + RevCommit one = remote.commit().message("one").create(); + RevCommit two = remote.commit().message("two").parent(one).create(); + RevCommit three = remote.commit().message("three").parent(two).create(); + RevCommit side = remote.commit().message("side").parent(one).create(); + RevCommit merge = remote.commit().message("merge") + .parent(three).parent(side).create(); + + remote.update("branch1", merge); + remote.update("side", side); + + // The client is a shallow clone that only has "three", and + // wants "merge" while excluding "side". + ByteArrayInputStream recvStream = uploadPackV2( + "command=fetch\n", + PacketLineIn.DELIM, + "shallow " + three.toObjectId().getName() + "\n", + "deepen-not side\n", + "want " + merge.toObjectId().getName() + "\n", + "have " + three.toObjectId().getName() + "\n", + "done\n", + PacketLineIn.END); + PacketLineIn pckIn = new PacketLineIn(recvStream); + assertThat(pckIn.readString(), is("shallow-info")); + + // "merge" is shallow because "side" is excluded by deepen-not. + // "two" is shallow because "one" (as parent of "side") is excluded by deepen-not. + assertThat( + Arrays.asList(pckIn.readString(), pckIn.readString()), + hasItems( + "shallow " + merge.toObjectId().getName(), + "shallow " + two.toObjectId().getName())); + + // "three" is unshallow because its parent "two" is now available. + assertThat(pckIn.readString(), is("unshallow " + three.toObjectId().getName())); + + assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertThat(pckIn.readString(), is("packfile")); + parsePack(recvStream); + + // The server does not send these because they are excluded by + // deepen-not. + assertFalse(client.hasObject(side.toObjectId())); + assertFalse(client.hasObject(one.toObjectId())); + + // The server does not send this because the client claims to + // have it. + assertFalse(client.hasObject(three.toObjectId())); + + // The server sends both these commits. + assertTrue(client.hasObject(merge.toObjectId())); + assertTrue(client.hasObject(two.toObjectId())); + } + + @Test + public void testV2FetchDeepenNot_excludeDescendantOfWant() throws Exception { + RevCommit one = remote.commit().message("one").create(); + RevCommit two = remote.commit().message("two").parent(one).create(); + RevCommit three = remote.commit().message("three").parent(two).create(); + RevCommit four = remote.commit().message("four").parent(three).create(); + + remote.update("two", two); + remote.update("four", four); + + thrown.expect(PackProtocolException.class); + thrown.expectMessage("No commits selected for shallow request"); + uploadPackV2( + "command=fetch\n", + PacketLineIn.DELIM, + "deepen-not four\n", + "want " + two.toObjectId().getName() + "\n", + "done\n", + PacketLineIn.END); + } + + @Test + public void testV2FetchDeepenNot_supportAnnotatedTags() throws Exception { + RevCommit one = remote.commit().message("one").create(); + RevCommit two = remote.commit().message("two").parent(one).create(); + RevCommit three = remote.commit().message("three").parent(two).create(); + RevCommit four = remote.commit().message("four").parent(three).create(); + RevTag twoTag = remote.tag("twotag", two); + + remote.update("refs/tags/twotag", twoTag); + remote.update("four", four); + + ByteArrayInputStream recvStream = uploadPackV2( + "command=fetch\n", + PacketLineIn.DELIM, + "deepen-not twotag\n", + "want " + four.toObjectId().getName() + "\n", + "done\n", + PacketLineIn.END); + PacketLineIn pckIn = new PacketLineIn(recvStream); + assertThat(pckIn.readString(), is("shallow-info")); + assertThat(pckIn.readString(), is("shallow " + three.toObjectId().getName())); + assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertThat(pckIn.readString(), is("packfile")); + parsePack(recvStream); + assertFalse(client.hasObject(one.toObjectId())); + assertFalse(client.hasObject(two.toObjectId())); + assertTrue(client.hasObject(three.toObjectId())); + assertTrue(client.hasObject(four.toObjectId())); + } + + @Test + public void testV2FetchDeepenNot_excludedParentWithMultipleChildren() throws Exception { + PersonIdent person = new PersonIdent(remote.getRepository()); + + RevCommit base = remote.commit() + .committer(new PersonIdent(person, 1500000000, 0)).create(); + RevCommit child1 = remote.commit().parent(base) + .committer(new PersonIdent(person, 1510000000, 0)).create(); + RevCommit child2 = remote.commit().parent(base) + .committer(new PersonIdent(person, 1520000000, 0)).create(); + + remote.update("base", base); + remote.update("branch1", child1); + remote.update("branch2", child2); + + ByteArrayInputStream recvStream = uploadPackV2( + "command=fetch\n", + PacketLineIn.DELIM, + "deepen-not base\n", + "want " + child1.toObjectId().getName() + "\n", + "want " + child2.toObjectId().getName() + "\n", + "done\n", + PacketLineIn.END); + PacketLineIn pckIn = new PacketLineIn(recvStream); + assertThat(pckIn.readString(), is("shallow-info")); + + // "base" is excluded, so its children are shallow. + assertThat( + Arrays.asList(pckIn.readString(), pckIn.readString()), + hasItems( + "shallow " + child1.toObjectId().getName(), + "shallow " + child2.toObjectId().getName())); + + assertThat(pckIn.readString(), theInstance(PacketLineIn.DELIM)); + assertThat(pckIn.readString(), is("packfile")); + parsePack(recvStream); + + // Only the children are sent. + assertFalse(client.hasObject(base.toObjectId())); + assertTrue(client.hasObject(child1.toObjectId())); + assertTrue(client.hasObject(child2.toObjectId())); + } + + @Test public void testV2FetchUnrecognizedArgument() throws Exception { thrown.expect(PackProtocolException.class); thrown.expectMessage("unexpected invalid-argument"); @@ -1202,6 +1502,21 @@ public class UploadPackTest { } @Test + public void testV2FetchServerOptions() throws Exception { + String[] lines = { "command=fetch\n", "server-option=one\n", + "server-option=two\n", PacketLineIn.DELIM, + PacketLineIn.END }; + + TestV2Hook testHook = new TestV2Hook(); + uploadPackV2Setup(null, null, testHook, lines); + + FetchV2Request req = testHook.fetchRequest; + assertNotNull(req); + assertEquals(2, req.getServerOptions().size()); + assertThat(req.getServerOptions(), hasItems("one", "two")); + } + + @Test public void testV2FetchFilter() throws Exception { RevBlob big = remote.blob("foobar"); RevBlob small = remote.blob("fooba"); @@ -1455,6 +1770,45 @@ public class UploadPackTest { assertTrue(client.hasObject(three.toObjectId())); } + @Test + public void testGetPeerAgentProtocolV0() throws Exception { + RevCommit one = remote.commit().message("1").create(); + remote.update("one", one); + + UploadPack up = new UploadPack(server); + ByteArrayInputStream send = linesAsInputStream( + "want " + one.getName() + " agent=JGit-test/1.2.3\n", + PacketLineIn.END, + "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n"); + + ByteArrayOutputStream recv = new ByteArrayOutputStream(); + up.upload(send, recv, null); + + assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.3"); + } + + @Test + public void testGetPeerAgentProtocolV2() throws Exception { + server.getConfig().setString("protocol", null, "version", "2"); + + RevCommit one = remote.commit().message("1").create(); + remote.update("one", one); + + UploadPack up = new UploadPack(server); + up.setExtraParameters(Sets.of("version=2")); + + ByteArrayInputStream send = linesAsInputStream( + "command=fetch\n", "agent=JGit-test/1.2.4\n", + PacketLineIn.DELIM, "want " + one.getName() + "\n", + "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n", + PacketLineIn.END); + + ByteArrayOutputStream recv = new ByteArrayOutputStream(); + up.upload(send, recv, null); + + assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.4"); + } + private static class RejectAllRefFilter implements RefFilter { @Override public Map<String, Ref> filter(Map<String, Ref> refs) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java index f2fb0224ef..4750d15b3d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java @@ -651,7 +651,8 @@ public class WalkEncryptionTest { Properties props = Props.discover(); props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS); props.put(AmazonS3.Keys.CRYPTO_ALG, algorithm); - try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE)) { + try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE, + UTF_8.name())) { props.store(writer, "JGIT S3 connection configuration file."); } } @@ -665,7 +666,8 @@ public class WalkEncryptionTest { static void configCreate(Properties source) throws Exception { Properties target = Props.discover(); target.putAll(source); - try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE)) { + try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE, + UTF_8.name())) { target.store(writer, "JGIT S3 connection configuration file."); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/http/JDKHttpConnectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/http/JDKHttpConnectionTest.java new file mode 100644 index 0000000000..10ee829199 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/http/JDKHttpConnectionTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2018 Matthias Sohn <matthias.sohn@sap.com> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport.http; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.HttpURLConnection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +public class JDKHttpConnectionTest { + + private Map<String, List<String>> headers = new HashMap<>(); + + private HttpURLConnection u; + + private JDKHttpConnection c; + + @Before + public void setup() { + u = mock(HttpURLConnection.class); + c = new JDKHttpConnection(u); + headers.put("ABC", asList("x")); + } + + @Test + public void testSingle() { + when(u.getHeaderFields()).thenReturn(headers); + assertValues("AbC", "x"); + } + + @Test + public void testMultiple1() { + headers.put("abc", asList("a")); + headers.put("aBC", asList("d", "e")); + headers.put("ABc", Collections.emptyList()); + headers.put("AbC", (List<String>) null); + when(u.getHeaderFields()).thenReturn(headers); + assertValues("AbC", "a", "d", "e", "x"); + } + + @Test + public void testMultiple2() { + headers.put("ab", asList("y", "z", "z")); + when(u.getHeaderFields()).thenReturn(headers); + assertValues("ab", "z", "y", "z"); + assertValues("abc", "x"); + assertValues("aBc", "x"); + assertValues("AbCd"); + } + + @Test + public void testCommaSeparatedList() { + headers.put("abc", asList("a,b,c", "d")); + when(u.getHeaderFields()).thenReturn(headers); + assertValues("Abc", "a,b,c", "x", "d"); + } + + private void assertValues(String key, String... values) { + List<String> l = new LinkedList<>(); + List<String> hf = c.getHeaderFields(key); + if (hf != null) { + l.addAll(hf); + } + for (String v : values) { + if (!l.remove(v)) { + fail("value " + v + " not found"); + } + } + assertTrue("found unexpected entries " + l, l.isEmpty()); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java index 934984f7e5..8afd49ab45 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java @@ -66,7 +66,7 @@ public class AbstractTreeIteratorTest { return s > 0 ? path.substring(0, s) : ""; } - public class FakeTreeIterator extends WorkingTreeIterator { + public static class FakeTreeIterator extends WorkingTreeIterator { public FakeTreeIterator(String pathName, FileMode fileMode) { super(prefix(pathName), new Config().get(WorkingTreeOptions.KEY)); mode = fileMode.getBits(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java index cba35d8042..ea5db09349 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.treewalk.filter; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -114,7 +115,7 @@ public class InterIndexDiffFilterTest extends LocalDiskRepositoryTestCase { } private ObjectId id(String data) { - byte[] bytes = data.getBytes(); + byte[] bytes = data.getBytes(UTF_8); return db.newObjectInserter().idFor(Constants.OBJ_BLOB, bytes); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java index 0a3de85f7c..dca9c57a64 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java @@ -47,6 +47,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.Iterator; @@ -84,18 +85,21 @@ public class BlockListTest { try { list.get(-1); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(-1), badIndex.getMessage()); } try { list.get(0); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(0), badIndex.getMessage()); } try { list.get(4); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(4), badIndex.getMessage()); } @@ -114,6 +118,7 @@ public class BlockListTest { try { list.get(3); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(3), badIndex.getMessage()); } @@ -125,18 +130,21 @@ public class BlockListTest { try { list.set(-1, "foo"); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(-1), badIndex.getMessage()); } try { list.set(0, "foo"); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(0), badIndex.getMessage()); } try { list.set(4, "foo"); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(4), badIndex.getMessage()); } @@ -161,6 +169,7 @@ public class BlockListTest { try { list.set(3, "bar"); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(3), badIndex.getMessage()); } @@ -323,12 +332,14 @@ public class BlockListTest { try { list.add(-1, Integer.valueOf(42)); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(-1), badIndex.getMessage()); } try { list.add(4, Integer.valueOf(42)); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(4), badIndex.getMessage()); } @@ -341,12 +352,14 @@ public class BlockListTest { try { list.remove(-1); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(-1), badIndex.getMessage()); } try { list.remove(4); + fail("accepted out-of-bounds index"); } catch (IndexOutOfBoundsException badIndex) { assertEquals(String.valueOf(4), badIndex.getMessage()); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java index e34c3cebd6..e5fcbf9d7c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java @@ -199,7 +199,8 @@ public class HookTest extends RepositoryTestCase { assumeSupportedPlatform(); writeHookFile(PreCommitHook.NAME, - "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\necho 1>&2 \"stderr\""); + "#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\n" + + "echo $GIT_DIR\necho $GIT_WORK_TREE\necho 1>&2 \"stderr\""); ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream err = new ByteArrayOutputStream(); ProcessResult res = FS.DETECTED.runHookIfPresent(db, @@ -208,7 +209,9 @@ public class HookTest extends RepositoryTestCase { "arg1", "arg2" }, new PrintStream(out), new PrintStream(err), "stdin"); - assertEquals("unexpected hook output", "test arg1 arg2\nstdin\n", + assertEquals("unexpected hook output", + "test arg1 arg2\nstdin\n" + db.getDirectory().getAbsolutePath() + + '\n' + db.getWorkTree().getAbsolutePath() + '\n', out.toString("UTF-8")); assertEquals("unexpected output on stderr stream", "stderr\n", err.toString("UTF-8")); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java index 928fb2ed9a..fa303ec286 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.util; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.BufferedReader; @@ -105,7 +106,7 @@ public class IOReadLineTest { private Reader newReader(String in) { Reader r = new InputStreamReader( - new ByteArrayInputStream(Constants.encode(in))); + new ByteArrayInputStream(Constants.encode(in)), UTF_8); if (buffered) { r = new BufferedReader(r); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java index 7630c11185..e7bfa000ac 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java @@ -85,10 +85,11 @@ public class RawParseUtils_LineMapTest { } @Test - public void testBinary() { + public void testNulByte() { final byte[] buf = "xxxfoo\nb\0ar".getBytes(ISO_8859_1); final IntList map = RawParseUtils.lineMap(buf, 3, buf.length); - assertArrayEquals(new int[]{Integer.MIN_VALUE, 3, buf.length}, asInts(map)); + assertArrayEquals(new int[] { Integer.MIN_VALUE, 3, 7, buf.length }, + asInts(map)); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java index 7c0985ef42..19af83611b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.util; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.ByteArrayInputStream; @@ -75,10 +76,10 @@ public class RunExternalScriptTest { File script = writeTempFile("cat -"); int rc = FS.DETECTED.runProcess( new ProcessBuilder("sh", script.getPath()), out, err, - new ByteArrayInputStream(inputStr.getBytes())); + new ByteArrayInputStream(inputStr.getBytes(UTF_8))); assertEquals(0, rc); - assertEquals(inputStr, new String(out.toByteArray())); - assertEquals("", new String(err.toByteArray())); + assertEquals(inputStr, new String(out.toByteArray(), UTF_8)); + assertEquals("", new String(err.toByteArray(), UTF_8)); } @Test @@ -88,8 +89,8 @@ public class RunExternalScriptTest { new ProcessBuilder("sh", script.getPath()), out, err, (InputStream) null); assertEquals(0, rc); - assertEquals("", new String(out.toByteArray())); - assertEquals("", new String(err.toByteArray())); + assertEquals("", new String(out.toByteArray(), UTF_8)); + assertEquals("", new String(err.toByteArray(), UTF_8)); } @Test @@ -99,8 +100,8 @@ public class RunExternalScriptTest { new ProcessBuilder("sh", script.getPath(), "a", "b", "c"), out, err, (InputStream) null); assertEquals(0, rc); - assertEquals("3,a,b,c,,,\n", new String(out.toByteArray())); - assertEquals("", new String(err.toByteArray())); + assertEquals("3,a,b,c,,,\n", new String(out.toByteArray(), UTF_8)); + assertEquals("", new String(err.toByteArray(), UTF_8)); } @Test @@ -110,8 +111,8 @@ public class RunExternalScriptTest { new ProcessBuilder("sh", script.getPath(), "a", "b", "c"), out, err, (InputStream) null); assertEquals(3, rc); - assertEquals("", new String(out.toByteArray())); - assertEquals("", new String(err.toByteArray())); + assertEquals("", new String(out.toByteArray(), UTF_8)); + assertEquals("", new String(err.toByteArray(), UTF_8)); } @Test @@ -121,8 +122,8 @@ public class RunExternalScriptTest { new ProcessBuilder("sh", script.getPath()), null, err, (InputStream) null); assertEquals(0, rc); - assertEquals("", new String(out.toByteArray())); - assertEquals("", new String(err.toByteArray())); + assertEquals("", new String(out.toByteArray(), UTF_8)); + assertEquals("", new String(err.toByteArray(), UTF_8)); } @Test @@ -132,8 +133,8 @@ public class RunExternalScriptTest { new ProcessBuilder("sh", script.getPath()), null, err, (InputStream) null); assertEquals(0, rc); - assertEquals("", new String(out.toByteArray())); - assertEquals("hi" + LF, new String(err.toByteArray())); + assertEquals("", new String(out.toByteArray(), UTF_8)); + assertEquals("hi" + LF, new String(err.toByteArray(), UTF_8)); } @Test @@ -142,10 +143,10 @@ public class RunExternalScriptTest { File script = writeTempFile("echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5"); int rc = FS.DETECTED.runProcess( new ProcessBuilder("sh", script.getPath(), "a", "b", "c"), - out, err, new ByteArrayInputStream(inputStr.getBytes())); + out, err, new ByteArrayInputStream(inputStr.getBytes(UTF_8))); assertEquals(5, rc); - assertEquals(inputStr, new String(out.toByteArray())); - assertEquals("3,a,b,c,,," + LF, new String(err.toByteArray())); + assertEquals(inputStr, new String(out.toByteArray(), UTF_8)); + assertEquals("3,a,b,c,,," + LF, new String(err.toByteArray(), UTF_8)); } @Test(expected = IOException.class) @@ -172,10 +173,11 @@ public class RunExternalScriptTest { File script = writeTempFile("cat -"); ProcessBuilder pb = new ProcessBuilder("sh", script.getPath()); ExecutionResult res = FS.DETECTED.execute(pb, - new ByteArrayInputStream(inputStr.getBytes())); + new ByteArrayInputStream(inputStr.getBytes(UTF_8))); assertEquals(0, res.getRc()); - assertEquals(inputStr, new String(res.getStdout().toByteArray())); - assertEquals("", new String(res.getStderr().toByteArray())); + assertEquals(inputStr, + new String(res.getStdout().toByteArray(), UTF_8)); + assertEquals("", new String(res.getStderr().toByteArray(), UTF_8)); } @Test @@ -184,8 +186,9 @@ public class RunExternalScriptTest { ProcessBuilder pb = new ProcessBuilder("sh", script.getPath()); ExecutionResult res = FS.DETECTED.execute(pb, null); assertEquals(0, res.getRc()); - assertEquals("", new String(res.getStdout().toByteArray())); - assertEquals("hi" + LF, new String(res.getStderr().toByteArray())); + assertEquals("", new String(res.getStdout().toByteArray(), UTF_8)); + assertEquals("hi" + LF, + new String(res.getStderr().toByteArray(), UTF_8)); } @Test @@ -197,11 +200,12 @@ public class RunExternalScriptTest { ProcessBuilder pb = new ProcessBuilder("sh", script.getPath(), "a", "b", "c"); ExecutionResult res = FS.DETECTED.execute(pb, - new ByteArrayInputStream(inputStr.getBytes())); + new ByteArrayInputStream(inputStr.getBytes(UTF_8))); assertEquals(5, res.getRc()); - assertEquals(inputStr, new String(res.getStdout().toByteArray())); + assertEquals(inputStr, + new String(res.getStdout().toByteArray(), UTF_8)); assertEquals("3,a,b,c,,," + LF, - new String(res.getStderr().toByteArray())); + new String(res.getStderr().toByteArray(), UTF_8)); } private File writeTempFile(String body) throws IOException { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java index 1272e16173..8f77c55af2 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFInputStreamTest.java @@ -43,6 +43,8 @@ package org.eclipse.jgit.util.io; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -89,8 +91,8 @@ public class AutoCRLFInputStreamTest { private void assertNoCrLfHelper(String expect, String input) throws IOException { - byte[] inbytes = input.getBytes(); - byte[] expectBytes = expect.getBytes(); + byte[] inbytes = input.getBytes(UTF_8); + byte[] expectBytes = expect.getBytes(UTF_8); for (int i = 0; i < 5; ++i) { byte[] buf = new byte[i]; try (ByteArrayInputStream bis = new ByteArrayInputStream(inbytes); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java index 0655827310..3a3dc8117f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java @@ -44,6 +44,8 @@ package org.eclipse.jgit.util.io; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -91,8 +93,8 @@ public class AutoCRLFOutputStreamTest { private void assertNoCrLfHelper(String expect, String input) throws IOException { - byte[] inbytes = input.getBytes(); - byte[] expectBytes = expect.getBytes(); + byte[] inbytes = input.getBytes(UTF_8); + byte[] expectBytes = expect.getBytes(UTF_8); for (int i = -4; i < 5; ++i) { int size = Math.abs(i); byte[] buf = new byte[size]; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java index a63b1cb02a..c35f90ca7d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java @@ -282,7 +282,7 @@ public class TimeoutOutputStreamTest { return System.currentTimeMillis(); } - private final class FullPipeInputStream extends PipedInputStream { + private static final class FullPipeInputStream extends PipedInputStream { FullPipeInputStream(PipedOutputStream src) throws IOException { super(src); src.write(new byte[PIPE_SIZE]); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java index b824fae9fd..a6e0eedfbc 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java @@ -167,6 +167,8 @@ public class UnionInputStreamTest { u.add(new ByteArrayInputStream(new byte[] { 20, 30 }) { @Override + @SuppressWarnings("UnsynchronizedOverridesSynchronized") + // This is only used in tests and is thread-safe public long skip(long n) { return 0; } @@ -259,6 +261,11 @@ public class UnionInputStreamTest { public int read() throws IOException { throw new IOException("Expected"); } + + @Override + public int read(byte b[], int off, int len) throws IOException { + throw new IOException("Expected"); + } }; @SuppressWarnings("resource" /* java 7 */) final UnionInputStream u = new UnionInputStream( diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs index 89394eca41..525ac67142 100644 --- a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit.ui/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit.ui/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF index 18d0bd55ac..380de32d25 100644 --- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF @@ -4,14 +4,14 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit.ui Bundle-SymbolicName: org.eclipse.jgit.ui -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Vendor: %provider_name Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Export-Package: org.eclipse.jgit.awtui;version="5.1.9" -Import-Package: org.eclipse.jgit.errors;version="[5.1.9,5.2.0)", - org.eclipse.jgit.lib;version="[5.1.9,5.2.0)", - org.eclipse.jgit.nls;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revplot;version="[5.1.9,5.2.0)", - org.eclipse.jgit.revwalk;version="[5.1.9,5.2.0)", - org.eclipse.jgit.transport;version="[5.1.9,5.2.0)", - org.eclipse.jgit.util;version="[5.1.9,5.2.0)" +Export-Package: org.eclipse.jgit.awtui;version="5.2.3" +Import-Package: org.eclipse.jgit.errors;version="[5.2.3,5.3.0)", + org.eclipse.jgit.lib;version="[5.2.3,5.3.0)", + org.eclipse.jgit.nls;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revplot;version="[5.2.3,5.3.0)", + org.eclipse.jgit.revwalk;version="[5.2.3,5.3.0)", + org.eclipse.jgit.transport;version="[5.2.3,5.3.0)", + org.eclipse.jgit.util;version="[5.2.3,5.3.0)" diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml index 0260d77ca2..0cb3d65c9d 100644 --- a/org.eclipse.jgit.ui/pom.xml +++ b/org.eclipse.jgit.ui/pom.xml @@ -52,7 +52,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit.ui</artifactId> diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index 524c591912..4131901d78 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -3,8 +3,8 @@ <resource path="META-INF/MANIFEST.MF"> <filter id="924844039"> <message_arguments> - <message_argument value="5.1.8"/> - <message_argument value="5.1.0"/> + <message_argument value="5.2.3"/> + <message_argument value="5.2.0"/> </message_arguments> </filter> </resource> @@ -22,22 +22,6 @@ </message_arguments> </filter> </resource> - <resource path="src/org/eclipse/jgit/lib/GitmoduleEntry.java" type="org.eclipse.jgit.lib.GitmoduleEntry"> - <filter id="1109393411"> - <message_arguments> - <message_argument value="4.7.5"/> - <message_argument value="org.eclipse.jgit.lib.GitmoduleEntry"/> - </message_arguments> - </filter> - </resource> - <resource path="src/org/eclipse/jgit/lib/ObjectChecker.java" type="org.eclipse.jgit.lib.ObjectChecker"> - <filter id="1142947843"> - <message_arguments> - <message_argument value="4.7.5"/> - <message_argument value="getGitsubmodules()"/> - </message_arguments> - </filter> - </resource> <resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig"> <filter id="336658481"> <message_arguments> diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs index 13c32a6d94..ef6f5e732f 100644 --- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs @@ -91,7 +91,7 @@ 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.unhandledWarningToken=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore diff --git a/org.eclipse.jgit/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit/.settings/org.eclipse.mylyn.team.ui.prefs index 0cba949fb7..2fca432276 100644 --- a/org.eclipse.jgit/.settings/org.eclipse.mylyn.team.ui.prefs +++ b/org.eclipse.jgit/.settings/org.eclipse.mylyn.team.ui.prefs @@ -1,3 +1,3 @@ #Tue Jul 19 20:11:28 CEST 2011 -commit.comment.template=${task.description} \n\nBug\: ${task.key} +commit.comment.template=${task.description}\n\nBug\: ${task.key} eclipse.preferences.version=1 diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 470871c503..b03ff4ac94 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -3,12 +3,12 @@ Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Automatic-Module-Name: org.eclipse.jgit Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 5.1.9.qualifier +Bundle-Version: 5.2.3.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy -Export-Package: org.eclipse.jgit.annotations;version="5.1.9", - org.eclipse.jgit.api;version="5.1.9"; +Export-Package: org.eclipse.jgit.annotations;version="5.2.3", + org.eclipse.jgit.api;version="5.2.3"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, @@ -22,65 +22,73 @@ Export-Package: org.eclipse.jgit.annotations;version="5.1.9", org.eclipse.jgit.submodule, org.eclipse.jgit.transport, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="5.1.9", - org.eclipse.jgit.blame;version="5.1.9"; + org.eclipse.jgit.api.errors;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", + org.eclipse.jgit.attributes;version="5.2.3", + org.eclipse.jgit.blame;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff", - org.eclipse.jgit.diff;version="5.1.9"; + org.eclipse.jgit.diff;version="5.2.3"; uses:="org.eclipse.jgit.patch, org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util", - org.eclipse.jgit.dircache;version="5.1.9"; + org.eclipse.jgit.dircache;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.util, org.eclipse.jgit.events, org.eclipse.jgit.attributes", - org.eclipse.jgit.errors;version="5.1.9"; + org.eclipse.jgit.errors;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.internal.storage.pack, org.eclipse.jgit.transport, org.eclipse.jgit.dircache", - org.eclipse.jgit.events;version="5.1.9";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="5.1.9", - org.eclipse.jgit.gitrepo;version="5.1.9"; + org.eclipse.jgit.events;version="5.2.3";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.fnmatch;version="5.2.3", + org.eclipse.jgit.gitrepo;version="5.2.3"; uses:="org.eclipse.jgit.api, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.xml.sax.helpers, org.xml.sax", - org.eclipse.jgit.gitrepo.internal;version="5.1.9";x-internal:=true, - org.eclipse.jgit.hooks;version="5.1.9";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="5.1.9", - org.eclipse.jgit.ignore.internal;version="5.1.9";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="5.1.9";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.fsck;version="5.1.9";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.ketch;version="5.1.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.dfs;version="5.1.9"; + org.eclipse.jgit.gitrepo.internal;version="5.2.3";x-internal:=true, + org.eclipse.jgit.hooks;version="5.2.3";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.ignore;version="5.2.3", + org.eclipse.jgit.ignore.internal;version="5.2.3";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal;version="5.2.3";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", + org.eclipse.jgit.internal.fsck;version="5.2.3";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal.ketch;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.revwalk;version="5.2.3";x-internal:=true, + org.eclipse.jgit.internal.storage.dfs;version="5.2.3"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.server, org.eclipse.jgit.http.test, org.eclipse.jgit.lfs.test", - org.eclipse.jgit.internal.storage.file;version="5.1.9"; + org.eclipse.jgit.internal.storage.file;version="5.2.3"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, org.eclipse.jgit.http.server, org.eclipse.jgit.lfs, org.eclipse.jgit.pgm, - org.eclipse.jgit.pgm.test", - org.eclipse.jgit.internal.storage.io;version="5.1.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.pack;version="5.1.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftable;version="5.1.9"; - x-friends:="org.eclipse.jgit.http.test,org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftree;version="5.1.9";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.lib;version="5.1.9"; + org.eclipse.jgit.pgm.test, + org.eclipse.jgit.ssh.apache", + org.eclipse.jgit.internal.storage.io;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.pack;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.reftable;version="5.2.3"; + x-friends:="org.eclipse.jgit.http.test, + org.eclipse.jgit.junit, + org.eclipse.jgit.test, + org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.storage.reftree;version="5.2.3";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.internal.submodule;version="5.2.3";x-internal:=true, + org.eclipse.jgit.internal.transport.parser;version="5.2.3";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal.transport.ssh;version="5.2.3";x-friends:="org.eclipse.jgit.ssh.apache", + org.eclipse.jgit.lib;version="5.2.3"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, @@ -90,33 +98,33 @@ Export-Package: org.eclipse.jgit.annotations;version="5.1.9", org.eclipse.jgit.treewalk, org.eclipse.jgit.transport, org.eclipse.jgit.submodule", - org.eclipse.jgit.lib.internal;version="5.1.9";x-internal:=true, - org.eclipse.jgit.merge;version="5.1.9"; + org.eclipse.jgit.lib.internal;version="5.2.3";x-internal:=true, + org.eclipse.jgit.merge;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.diff, org.eclipse.jgit.dircache, org.eclipse.jgit.api", - org.eclipse.jgit.nls;version="5.1.9", - org.eclipse.jgit.notes;version="5.1.9"; + org.eclipse.jgit.nls;version="5.2.3", + org.eclipse.jgit.notes;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="5.1.9"; + org.eclipse.jgit.patch;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", + org.eclipse.jgit.revplot;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", + org.eclipse.jgit.revwalk;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, org.eclipse.jgit.revwalk.filter", - org.eclipse.jgit.revwalk.filter;version="5.1.9";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="5.1.9";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", - org.eclipse.jgit.transport;version="5.1.9"; + org.eclipse.jgit.revwalk.filter;version="5.2.3";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.file;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.pack;version="5.2.3";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.submodule;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", + org.eclipse.jgit.transport;version="5.2.3"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.revwalk, org.eclipse.jgit.internal.storage.pack, @@ -128,24 +136,24 @@ Export-Package: org.eclipse.jgit.annotations;version="5.1.9", org.eclipse.jgit.transport.http, org.eclipse.jgit.errors, org.eclipse.jgit.storage.pack", - org.eclipse.jgit.transport.http;version="5.1.9";uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="5.1.9";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", - org.eclipse.jgit.treewalk;version="5.1.9"; + org.eclipse.jgit.transport.http;version="5.2.3";uses:="javax.net.ssl", + org.eclipse.jgit.transport.resolver;version="5.2.3";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", + org.eclipse.jgit.treewalk;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.attributes, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, org.eclipse.jgit.dircache", - org.eclipse.jgit.treewalk.filter;version="5.1.9";uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="5.1.9"; + org.eclipse.jgit.treewalk.filter;version="5.2.3";uses:="org.eclipse.jgit.treewalk", + org.eclipse.jgit.util;version="5.2.3"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.transport.http, org.eclipse.jgit.storage.file, org.ietf.jgss", - org.eclipse.jgit.util.io;version="5.1.9", - org.eclipse.jgit.util.sha1;version="5.1.9", - org.eclipse.jgit.util.time;version="5.1.9" + org.eclipse.jgit.util.io;version="5.2.3", + org.eclipse.jgit.util.sha1;version="5.2.3", + org.eclipse.jgit.util.time;version="5.2.3" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", com.jcraft.jsch;version="[0.1.37,0.2.0)", diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF index 8885e64cb9..8e22086cce 100644 --- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit - Sources Bundle-SymbolicName: org.eclipse.jgit.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 5.1.9.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="5.1.9.qualifier";roots="." +Bundle-Version: 5.2.3.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="5.2.3.qualifier";roots="." diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index eedb5e80d7..c74e26f6d4 100644 --- a/org.eclipse.jgit/pom.xml +++ b/org.eclipse.jgit/pom.xml @@ -53,7 +53,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit</artifactId> diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index b7805b19e6..12fded8361 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -302,7 +302,7 @@ expectedPktLineWithService=expected pkt-line with ''# service=-'', got ''{0}'' expectedReceivedContentType=expected Content-Type {0}; received Content-Type {1} expectedReportForRefNotReceived={0}: expected report for ref {1} not received failedAtomicFileCreation=Atomic file creation failed, number of hard links to file {0} was not 2 but {1}" -failedToDetermineFilterDefinition=An exception occured while determining filter definitions +failedToDetermineFilterDefinition=An exception occurred while determining filter definitions failedUpdatingRefs=failed updating refs failureDueToOneOfTheFollowing=Failure due to one of the following: failureUpdatingFETCH_HEAD=Failure updating FETCH_HEAD: {0} @@ -468,6 +468,7 @@ newIdMustNotBeNull=New ID must not be null newlineInQuotesNotAllowed=Newline in quotes not allowed noApplyInDelete=No apply in delete noClosingBracket=No closing {0} found for {1} at index {2}. +noCommitsSelectedForShallow=No commits selected for shallow request noCredentialsProvider=Authentication is required but no CredentialsProvider has been registered noHEADExistsAndNoExplicitStartingRevisionWasSpecified=No HEAD exists and no explicit starting revision was specified noHMACsupport=No {0} support: {1} @@ -638,8 +639,8 @@ sourceRefNotSpecifiedForRefspec=Source ref not specified for refspec: {0} squashCommitNotUpdatingHEAD=Squash commit -- not updating HEAD sshCommandFailed=Execution of ssh command ''{0}'' failed with error ''{1}'' sshUserNameError=Jsch error: failed to set SSH user name correctly to ''{0}''; using ''{1}'' picked up from SSH config file. -sslFailureExceptionMessage=Secure connection to {0} could not be stablished because of SSL problems -sslFailureInfo=A secure connection to {0}\ncould not be established because the server''s certificate could not be validated. +sslFailureExceptionMessage=Secure connection to {0} could not be established because of SSL problems +sslFailureInfo=A secure connection to {0} could not be established because the server''s certificate could not be validated. sslFailureCause=SSL reported: {0} sslFailureTrustExplanation=Do you want to skip SSL verification for this server? sslTrustAlways=Always skip SSL verification for this server from now on @@ -671,7 +672,6 @@ submodulePathInvalid=Invalid submodule path ''{0}'' submoduleUrlInvalid=Invalid submodule URL ''{0}'' submodulesNotSupported=Submodules are not supported supportOnlyPackIndexVersion2=Only support index version 2 -symlinkCannotBeWrittenAsTheLinkTarget=Symlink "{0}" cannot be written as the link target cannot be read from within Java. systemConfigFileInvalid=System wide config file {0} is invalid {1} tagAlreadyExists=tag ''{0}'' already exists tagNameInvalid=tag name {0} is invalid @@ -771,7 +771,9 @@ uriNotFound={0} not found uriNotFoundWithMessage={0} not found: {1} URINotSupported=URI not supported: {0} userConfigFileInvalid=User config file {0} invalid {1} +validatingGitModules=Validating .gitmodules files walkFailure=Walk failure. +wantNoSpaceWithCapabilities=No space between oid and first capability in first want line wantNotValid=want {0} not valid weeksAgo={0} weeks ago windowSizeMustBeLesserThanLimit=Window size must be < limit diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java index 5b84032b15..c6f3c671a9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java @@ -42,11 +42,14 @@ */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.File; import java.io.FileOutputStream; -import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; import java.nio.file.StandardCopyOption; import java.text.MessageFormat; import java.util.ArrayList; @@ -258,7 +261,8 @@ public class ApplyCommand extends GitCommand<ApplyResult> { if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } - try (FileWriter fw = new FileWriter(f)) { + try (Writer fw = new OutputStreamWriter(new FileOutputStream(f), + UTF_8)) { fw.write(sb.toString()); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java index 11edb10894..455a2e665f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java @@ -269,7 +269,7 @@ public class CheckoutCommand extends GitCommand<Ref> { try { dco = new DirCacheCheckout(repo, headTree, dc, newCommit.getTree()); - dco.setFailOnConflict(true); + dco.setFailOnConflict(!force); dco.setProgressMonitor(monitor); try { dco.checkout(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java index 5c06bac1f2..73af8ba16d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java @@ -283,12 +283,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { config.addURI(u); final String dst = (bare ? Constants.R_HEADS : Constants.R_REMOTES - + config.getName() + "/") + "*"; //$NON-NLS-1$//$NON-NLS-2$ - RefSpec refSpec = new RefSpec(); - refSpec = refSpec.setForceUpdate(true); - refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*", dst); //$NON-NLS-1$ + + config.getName() + '/') + '*'; + boolean fetchAll = cloneAllBranches || branchesToClone == null + || branchesToClone.isEmpty(); - config.addFetchRefSpec(refSpec); + config.setFetchRefSpecs(calculateRefSpecs(fetchAll, dst)); config.update(clonedRepo.getConfig()); clonedRepo.getConfig().save(); @@ -297,27 +296,25 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { FetchCommand command = new FetchCommand(clonedRepo); command.setRemote(remote); command.setProgressMonitor(monitor); - command.setTagOpt(TagOpt.FETCH_TAGS); + command.setTagOpt(fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW); configure(command); - List<RefSpec> specs = calculateRefSpecs(dst); - command.setRefSpecs(specs); - return command.call(); } - private List<RefSpec> calculateRefSpecs(String dst) { + private List<RefSpec> calculateRefSpecs(boolean fetchAll, String dst) { RefSpec wcrs = new RefSpec(); wcrs = wcrs.setForceUpdate(true); - wcrs = wcrs.setSourceDestination(Constants.R_HEADS + "*", dst); //$NON-NLS-1$ + wcrs = wcrs.setSourceDestination(Constants.R_HEADS + '*', dst); List<RefSpec> specs = new ArrayList<>(); - if (cloneAllBranches) - specs.add(wcrs); - else if (branchesToClone != null - && branchesToClone.size() > 0) { - for (String selectedRef : branchesToClone) - if (wcrs.matchSource(selectedRef)) + if (!fetchAll) { + for (String selectedRef : branchesToClone) { + if (wcrs.matchSource(selectedRef)) { specs.add(wcrs.expandFromSource(selectedRef)); + } + } + } else { + specs.add(wcrs); } return specs; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java index 28a27a90e0..29a51a0f02 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java @@ -44,6 +44,10 @@ */ package org.eclipse.jgit.api; +import static org.eclipse.jgit.lib.Constants.HEAD; +import static org.eclipse.jgit.lib.Constants.R_HEADS; +import static org.eclipse.jgit.lib.Constants.R_REMOTES; + import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; @@ -56,7 +60,6 @@ import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.internal.JGitText; -import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; @@ -113,17 +116,18 @@ public class ListBranchCommand extends GitCommand<List<Ref>> { Collection<Ref> refs = new ArrayList<>(); // Also return HEAD if it's detached - Ref head = repo.exactRef(Constants.HEAD); - if (head != null && head.getLeaf().getName().equals(Constants.HEAD)) + Ref head = repo.exactRef(HEAD); + if (head != null && head.getLeaf().getName().equals(HEAD)) { refs.add(head); + } if (listMode == null) { - refs.addAll(getRefs(Constants.R_HEADS)); + refs.addAll(repo.getRefDatabase().getRefsByPrefix(R_HEADS)); } else if (listMode == ListMode.REMOTE) { - refs.addAll(getRefs(Constants.R_REMOTES)); + refs.addAll(repo.getRefDatabase().getRefsByPrefix(R_REMOTES)); } else { - refs.addAll(getRefs(Constants.R_HEADS)); - refs.addAll(getRefs(Constants.R_REMOTES)); + refs.addAll(repo.getRefDatabase().getRefsByPrefix(R_HEADS, + R_REMOTES)); } resultRefs = new ArrayList<>(filterRefs(refs)); } catch (IOException e) { @@ -185,8 +189,4 @@ public class ListBranchCommand extends GitCommand<List<Ref>> { this.containsCommitish = containsCommitish; return this; } - - private Collection<Ref> getRefs(String prefix) throws IOException { - return repo.getRefDatabase().getRefsByPrefix(prefix); - } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java index 1783c4193e..9653c365b2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -43,6 +43,8 @@ */ package org.eclipse.jgit.api; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; @@ -1015,8 +1017,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { df.setRepository(repo); df.format(commitToPick.getParent(0), commitToPick); } - rebaseState.createFile(PATCH, new String(bos.toByteArray(), - Constants.CHARACTER_ENCODING)); + rebaseState.createFile(PATCH, new String(bos.toByteArray(), UTF_8)); rebaseState.createFile(STOPPED_SHA, repo.newObjectReader() .abbreviate( @@ -1733,7 +1734,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { throws IOException { File file = new File(parentDir, name); try (FileOutputStream fos = new FileOutputStream(file)) { - fos.write(content.getBytes(Constants.CHARACTER_ENCODING)); + fos.write(content.getBytes(UTF_8)); fos.write('\n'); } } @@ -1741,7 +1742,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { private static void appendToFile(File file, String content) throws IOException { try (FileOutputStream fos = new FileOutputStream(file, true)) { - fos.write(content.getBytes(Constants.CHARACTER_ENCODING)); + fos.write(content.getBytes(UTF_8)); fos.write('\n'); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java index 244a15686f..f92455a96a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java @@ -179,21 +179,6 @@ public class SubmoduleAddCommand extends // Use the path as the default. name = path; } - if (name.contains("/../") || name.contains("\\..\\") //$NON-NLS-1$ //$NON-NLS-2$ - || name.startsWith("../") || name.startsWith("..\\") //$NON-NLS-1$ //$NON-NLS-2$ - || name.endsWith("/..") || name.endsWith("\\..")) { //$NON-NLS-1$ //$NON-NLS-2$ - // Submodule names are used to store the submodule repositories - // under $GIT_DIR/modules. Having ".." in submodule names makes a - // vulnerability (CVE-2018-11235 - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=535027#c0) - // Reject the names with them. The callers need to make sure the - // names free from these. We don't automatically replace these - // characters or canonicalize by regarding the name as a file path. - // Since Path class is platform dependent, we manually check '/' and - // '\\' patterns here. - throw new IllegalArgumentException(MessageFormat - .format(JGitText.get().invalidNameContainsDotDot, name)); - } try { SubmoduleValidator.assertValidSubmoduleName(name); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportConfigCallback.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportConfigCallback.java index f60926c562..d73453c5af 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportConfigCallback.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportConfigCallback.java @@ -68,5 +68,5 @@ public interface TransportConfigCallback { * @param transport * a {@link org.eclipse.jgit.transport.Transport} object. */ - public void configure(Transport transport); + void configure(Transport transport); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java index f1d7d7be0e..2d1cde12e0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java @@ -63,7 +63,7 @@ public interface AttributesNodeProvider { * @throws java.io.IOException * if an error is raised while parsing the attributes file */ - public AttributesNode getInfoAttributesNode() throws IOException; + AttributesNode getInfoAttributesNode() throws IOException; /** * Retrieve the {@link org.eclipse.jgit.attributes.AttributesNode} that @@ -76,6 +76,6 @@ public interface AttributesNodeProvider { * attributes file * @see CoreConfig#getAttributesFile() */ - public AttributesNode getGlobalAttributesNode() throws IOException; + AttributesNode getGlobalAttributesNode() throws IOException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java index 1545e3523d..7b51f6dd32 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java @@ -53,5 +53,5 @@ public interface AttributesProvider { * * @return the currently active attributes */ - public Attributes getAttributes(); + Attributes getAttributes(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java index c4357d1297..0bb4516297 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommand.java @@ -95,7 +95,7 @@ public abstract class FilterCommand { * -1. -1 means that the {@link java.io.InputStream} is completely * processed. * @throws java.io.IOException - * when {@link java.io.IOException} occured while reading from + * when {@link java.io.IOException} occurred while reading from * {@link #in} or writing to {@link #out} */ public abstract int run() throws IOException; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandFactory.java index 11b76b0d90..78573d2a63 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/FilterCommandFactory.java @@ -69,7 +69,7 @@ public interface FilterCommandFactory { * thrown when the command constructor throws an * java.io.IOException */ - public FilterCommand create(Repository db, InputStream in, - OutputStream out) throws IOException; + FilterCommand create(Repository db, InputStream in, OutputStream out) + throws IOException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index ca1e3ab275..db6073f89a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -522,7 +522,7 @@ public class DirCacheCheckout { builder.finish(); // init progress reporting - int numTotal = removed.size() + updated.size(); + int numTotal = removed.size() + updated.size() + conflicts.size(); monitor.beginTask(JGitText.get().checkingOutFiles, numTotal); performingCheckout = true; @@ -597,6 +597,33 @@ public class DirCacheCheckout { } throw ex; } + for (String conflict : conflicts) { + // the conflicts are likely to have multiple entries in the + // dircache, we only want to check out the one for the "theirs" + // tree + int entryIdx = dc.findEntry(conflict); + if (entryIdx >= 0) { + while (entryIdx < dc.getEntryCount()) { + DirCacheEntry entry = dc.getEntry(entryIdx); + if (!entry.getPathString().equals(conflict)) { + break; + } + if (entry.getStage() == DirCacheEntry.STAGE_3) { + checkoutEntry(repo, entry, objectReader, false, + null); + break; + } + ++entryIdx; + } + } + + monitor.update(1); + if (monitor.isCancelled()) { + throw new CanceledException(MessageFormat.format( + JGitText.get().operationCanceled, + JGitText.get().checkingOutFiles)); + } + } monitor.endTask(); // commit the index builder - a new index is persisted diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java index 19c916f810..6196e758a9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java @@ -44,6 +44,8 @@ package org.eclipse.jgit.dircache; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.IOException; import java.io.InputStream; import java.util.Collections; @@ -75,7 +77,7 @@ import org.eclipse.jgit.util.RawParseUtils; public class DirCacheIterator extends AbstractTreeIterator { /** Byte array holding ".gitattributes" string */ private static final byte[] DOT_GIT_ATTRIBUTES_BYTES = Constants.DOT_GIT_ATTRIBUTES - .getBytes(); + .getBytes(UTF_8); /** The cache this iterator was created to walk. */ protected final DirCache cache; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java index 9fbcc4dd50..afd7889f8a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/WorkingTreeModifiedEvent.java @@ -94,7 +94,8 @@ public class WorkingTreeModifiedEvent * * @return the set */ - public @NonNull Collection<String> getModified() { + @NonNull + public Collection<String> getModified() { Collection<String> result = modified; if (result == null) { result = Collections.emptyList(); @@ -109,7 +110,8 @@ public class WorkingTreeModifiedEvent * * @return the set */ - public @NonNull Collection<String> getDeleted() { + @NonNull + public Collection<String> getDeleted() { Collection<String> result = deleted; if (result == null) { result = Collections.emptyList(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/Head.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/Head.java index 49839f8e6e..e8f6844dda 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/Head.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/Head.java @@ -54,5 +54,5 @@ interface Head { * the character which decides which heads are returned. * @return a list of heads based on the input. */ - public abstract List<Head> getNextHeads(char c); + List<Head> getNextHeads(char c); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java index 26e783ddd7..8e463415b8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java @@ -358,7 +358,8 @@ public class ManifestParser extends DefaultHandler { * * @return filtered projects list reference, never null */ - public @NonNull List<RepoProject> getFilteredProjects() { + @NonNull + public List<RepoProject> getFilteredProjects() { return filteredProjects; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java index 45a239da0e..e9d86dfa83 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.gitrepo; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME; import static org.eclipse.jgit.lib.Constants.R_REMOTES; @@ -58,12 +59,14 @@ import java.util.Objects; import java.util.StringJoiner; import java.util.TreeMap; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.SubmoduleAddCommand; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.InvalidRefNameException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheBuilder; @@ -79,7 +82,6 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; @@ -89,6 +91,7 @@ import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.util.FileUtils; /** @@ -143,7 +146,9 @@ public class RepoCommand extends GitCommand<RevCommit> { * @param uri * The URI of the remote repository * @param ref - * The ref (branch/tag/etc.) to read + * Name of the ref to lookup. May be a short-hand form, e.g. + * "master" which is is automatically expanded to + * "refs/heads/master" if "refs/heads/master" already exists. * @return the sha1 of the remote repository, or null if the ref does * not exist. * @throws GitAPIException @@ -164,13 +169,93 @@ public class RepoCommand extends GitCommand<RevCommit> { * @throws GitAPIException * @throws IOException * @since 3.5 + * + * @deprecated Use {@link #readFileWithMode(String, String, String)} + * instead + */ + @Deprecated + public default byte[] readFile(String uri, String ref, String path) + throws GitAPIException, IOException { + return readFileWithMode(uri, ref, path).getContents(); + } + + /** + * Read contents and mode (i.e. permissions) of the file from a remote + * repository. + * + * @param uri + * The URI of the remote repository + * @param ref + * Name of the ref to lookup. May be a short-hand form, e.g. + * "master" which is is automatically expanded to + * "refs/heads/master" if "refs/heads/master" already exists. + * @param path + * The relative path (inside the repo) to the file to read + * @return The contents and file mode of the file in the given + * repository and branch. Never null. + * @throws GitAPIException + * If the ref have an invalid or ambiguous name, or it does + * not exist in the repository, + * @throws IOException + * If the object does not exist or is too large + * @since 5.2 */ - public byte[] readFile(String uri, String ref, String path) + @NonNull + public RemoteFile readFileWithMode(String uri, String ref, String path) throws GitAPIException, IOException; } + /** + * Read-only view of contents and file mode (i.e. permissions) for a file in + * a remote repository. + * + * @since 5.2 + */ + public static final class RemoteFile { + @NonNull + private final byte[] contents; + + @NonNull + private final FileMode fileMode; + + /** + * @param contents + * Raw contents of the file. + * @param fileMode + * Git file mode for this file (e.g. executable or regular) + */ + public RemoteFile(@NonNull byte[] contents, + @NonNull FileMode fileMode) { + this.contents = Objects.requireNonNull(contents); + this.fileMode = Objects.requireNonNull(fileMode); + } + + /** + * Contents of the file. + * <p> + * Callers who receive this reference must not modify its contents (as + * it can point to internal cached data). + * + * @return Raw contents of the file. Do not modify it. + */ + @NonNull + public byte[] getContents() { + return contents; + } + + /** + * @return Git file mode for this file (e.g. executable or regular) + */ + @NonNull + public FileMode getFileMode() { + return fileMode; + } + + } + /** A default implementation of {@link RemoteReader} callback. */ public static class DefaultRemoteReader implements RemoteReader { + @Override public ObjectId sha1(String uri, String ref) throws GitAPIException { Map<String, Ref> map = Git @@ -182,38 +267,30 @@ public class RepoCommand extends GitCommand<RevCommit> { } @Override - public byte[] readFile(String uri, String ref, String path) + public RemoteFile readFileWithMode(String uri, String ref, String path) throws GitAPIException, IOException { File dir = FileUtils.createTempDir("jgit_", ".git", null); //$NON-NLS-1$ //$NON-NLS-2$ try (Git git = Git.cloneRepository().setBare(true).setDirectory(dir) .setURI(uri).call()) { - return readFileFromRepo(git.getRepository(), ref, path); + Repository repo = git.getRepository(); + ObjectId refCommitId = sha1(uri, ref); + if (refCommitId == null) { + throw new InvalidRefNameException(MessageFormat + .format(JGitText.get().refNotResolved, ref)); + } + RevCommit commit = repo.parseCommit(refCommitId); + TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree()); + + // TODO(ifrade): Cope better with big files (e.g. using + // InputStream instead of byte[]) + return new RemoteFile( + tw.getObjectReader().open(tw.getObjectId(0)) + .getCachedBytes(Integer.MAX_VALUE), + tw.getFileMode(0)); } finally { FileUtils.delete(dir, FileUtils.RECURSIVE); } } - - /** - * Read a file from the repository - * - * @param repo - * The repository containing the file - * @param ref - * The ref (branch/tag/etc.) to read - * @param path - * The relative path (inside the repo) to the file to read - * @return the file's content - * @throws GitAPIException - * @throws IOException - * @since 3.5 - */ - protected byte[] readFileFromRepo(Repository repo, - String ref, String path) throws GitAPIException, IOException { - try (ObjectReader reader = repo.newObjectReader()) { - ObjectId oid = repo.resolve(ref + ":" + path); //$NON-NLS-1$ - return reader.open(oid).getBytes(Integer.MAX_VALUE); - } - } } @SuppressWarnings("serial") @@ -586,12 +663,13 @@ public class RepoCommand extends GitCommand<RevCommit> { builder.add(dcEntry); for (CopyFile copyfile : proj.getCopyFiles()) { - byte[] src = callback.readFile( + RemoteFile rf = callback.readFileWithMode( url, proj.getRevision(), copyfile.src); - objectId = inserter.insert(Constants.OBJ_BLOB, src); + objectId = inserter.insert(Constants.OBJ_BLOB, + rf.getContents()); dcEntry = new DirCacheEntry(copyfile.dest); dcEntry.setObjectId(objectId); - dcEntry.setFileMode(FileMode.REGULAR_FILE); + dcEntry.setFileMode(rf.getFileMode()); builder.add(dcEntry); } for (LinkFile linkfile : proj.getLinkFiles()) { @@ -606,8 +684,7 @@ public class RepoCommand extends GitCommand<RevCommit> { } objectId = inserter.insert(Constants.OBJ_BLOB, - link.getBytes( - Constants.CHARACTER_ENCODING)); + link.getBytes(UTF_8)); dcEntry = new DirCacheEntry(linkfile.dest); dcEntry.setObjectId(objectId); dcEntry.setFileMode(FileMode.SYMLINK); @@ -620,7 +697,7 @@ public class RepoCommand extends GitCommand<RevCommit> { // create a new DirCacheEntry for .gitmodules file. final DirCacheEntry dcEntry = new DirCacheEntry(Constants.DOT_GIT_MODULES); ObjectId objectId = inserter.insert(Constants.OBJ_BLOB, - content.getBytes(Constants.CHARACTER_ENCODING)); + content.getBytes(UTF_8)); dcEntry.setObjectId(objectId); dcEntry.setFileMode(FileMode.REGULAR_FILE); builder.add(dcEntry); @@ -629,7 +706,7 @@ public class RepoCommand extends GitCommand<RevCommit> { // create a new DirCacheEntry for .gitattributes file. final DirCacheEntry dcEntryAttr = new DirCacheEntry(Constants.DOT_GIT_ATTRIBUTES); ObjectId attrId = inserter.insert(Constants.OBJ_BLOB, - attributes.toString().getBytes(Constants.CHARACTER_ENCODING)); + attributes.toString().getBytes(UTF_8)); dcEntryAttr.setObjectId(attrId); dcEntryAttr.setFileMode(FileMode.REGULAR_FILE); builder.add(dcEntryAttr); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java index 7ba83c7cbf..d79dfa8b2f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java @@ -136,6 +136,7 @@ public class RepoProject implements Comparable<RepoProject> { FileChannel channel = input.getChannel(); output.getChannel().transferFrom(channel, 0, channel.size()); } + destFile.setExecutable(srcFile.canExecute()); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java index 051a1d1c81..431944f9d4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java @@ -164,12 +164,7 @@ public class PrePushHook extends GitHook<String> { */ public void setRefs(Collection<RemoteRefUpdate> toRefs) { StringBuilder b = new StringBuilder(); - boolean first = true; for (RemoteRefUpdate u : toRefs) { - if (!first) - b.append("\n"); //$NON-NLS-1$ - else - first = false; b.append(u.getSrcRef()); b.append(" "); //$NON-NLS-1$ b.append(u.getNewObjectId().getName()); @@ -179,6 +174,7 @@ public class PrePushHook extends GitHook<String> { ObjectId ooid = u.getExpectedOldObjectId(); b.append((ooid == null) ? ObjectId.zeroId().getName() : ooid .getName()); + b.append("\n"); //$NON-NLS-1$ } refs = b.toString(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java index 9b255b41c3..41923eed18 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java @@ -443,7 +443,7 @@ public class Strings { if (in_brackets > 0) throw new InvalidPatternException("Not closed bracket?", pattern); //$NON-NLS-1$ try { - return Pattern.compile(sb.toString()); + return Pattern.compile(sb.toString(), Pattern.DOTALL); } catch (PatternSyntaxException e) { throw new InvalidPatternException( MessageFormat.format(JGitText.get().invalidIgnoreRule, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index da0ba4a775..8f884b44f5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -529,6 +529,7 @@ public class JGitText extends TranslationBundle { /***/ public String newlineInQuotesNotAllowed; /***/ public String noApplyInDelete; /***/ public String noClosingBracket; + /***/ public String noCommitsSelectedForShallow; /***/ public String noCredentialsProvider; /***/ public String noHEADExistsAndNoExplicitStartingRevisionWasSpecified; /***/ public String noHMACsupport; @@ -729,10 +730,8 @@ public class JGitText extends TranslationBundle { /***/ public String submoduleNameInvalid; /***/ public String submoduleParentRemoteUrlInvalid; /***/ public String submodulePathInvalid; - /***/ public String submodulesNotSupported; /***/ public String submoduleUrlInvalid; /***/ public String supportOnlyPackIndexVersion2; - /***/ public String symlinkCannotBeWrittenAsTheLinkTarget; /***/ public String systemConfigFileInvalid; /***/ public String tagAlreadyExists; /***/ public String tagNameInvalid; @@ -832,7 +831,9 @@ public class JGitText extends TranslationBundle { /***/ public String uriNotFoundWithMessage; /***/ public String URINotSupported; /***/ public String userConfigFileInvalid; + /***/ public String validatingGitModules; /***/ public String walkFailure; + /***/ public String wantNoSpaceWithCapabilities; /***/ public String wantNotValid; /***/ public String weeksAgo; /***/ public String windowSizeMustBeLesserThanLimit; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckError.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckError.java index 131b0048ae..335ac667cc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckError.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckError.java @@ -61,20 +61,21 @@ public class FsckError { final int type; - ObjectChecker.ErrorType errorType; + @Nullable + final ObjectChecker.ErrorType errorType; /** * @param id * the object identifier. * @param type * type of the object. + * @param errorType + * kind of error */ - public CorruptObject(ObjectId id, int type) { + public CorruptObject(ObjectId id, int type, + @Nullable ObjectChecker.ErrorType errorType) { this.id = id; this.type = type; - } - - void setErrorType(ObjectChecker.ErrorType errorType) { this.errorType = errorType; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckPackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckPackParser.java index 5397ba4798..50594df1dd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckPackParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/fsck/FsckPackParser.java @@ -174,12 +174,8 @@ public class FsckPackParser extends PackParser { try { super.verifySafeObject(id, type, data); } catch (CorruptObjectException e) { - // catch the exception and continue parse the pack file - CorruptObject o = new CorruptObject(id.toObjectId(), type); - if (e.getErrorType() != null) { - o.setErrorType(e.getErrorType()); - } - corruptObjects.add(o); + corruptObjects.add( + new CorruptObject(id.toObjectId(), type, e.getErrorType())); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsFsck.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsFsck.java index 3f96d0919b..c0e24c02cf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsFsck.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsFsck.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.internal.storage.dfs; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX; import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK; @@ -54,12 +55,18 @@ import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.fsck.FsckError; import org.eclipse.jgit.internal.fsck.FsckError.CorruptIndex; +import org.eclipse.jgit.internal.fsck.FsckError.CorruptObject; import org.eclipse.jgit.internal.fsck.FsckPackParser; import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; +import org.eclipse.jgit.internal.submodule.SubmoduleValidator; +import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException; +import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.GitmoduleEntry; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectChecker; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.ObjectWalk; @@ -102,6 +109,7 @@ public class DfsFsck { FsckError errors = new FsckError(); if (!connectivityOnly) { + objChecker.reset(); checkPacks(pm, errors); } checkConnectivity(pm, errors); @@ -128,6 +136,8 @@ public class DfsFsck { } } } + + checkGitModules(pm, errors); } private void verifyPack(ProgressMonitor pm, FsckError errors, DfsReader ctx, @@ -142,6 +152,28 @@ public class DfsFsck { fpp.verifyIndex(pack.getPackIndex(ctx)); } + private void checkGitModules(ProgressMonitor pm, FsckError errors) + throws IOException { + pm.beginTask(JGitText.get().validatingGitModules, + objChecker.getGitsubmodules().size()); + for (GitmoduleEntry entry : objChecker.getGitsubmodules()) { + AnyObjectId blobId = entry.getBlobId(); + ObjectLoader blob = objdb.open(blobId, Constants.OBJ_BLOB); + + try { + SubmoduleValidator.assertValidGitModulesFile( + new String(blob.getBytes(), UTF_8)); + } catch (SubmoduleValidationException e) { + CorruptObject co = new FsckError.CorruptObject( + blobId.toObjectId(), Constants.OBJ_BLOB, + e.getFsckMessageId()); + errors.getCorruptObjects().add(co); + } + pm.update(1); + } + pm.endTask(); + } + private void checkConnectivity(ProgressMonitor pm, FsckError errors) throws IOException { pm.beginTask(JGitText.get().countingObjects, ProgressMonitor.UNKNOWN); @@ -179,6 +211,9 @@ public class DfsFsck { * Use a customized object checker instead of the default one. Caller can * specify a skip list to ignore some errors. * + * It will be reset at the start of each {{@link #check(ProgressMonitor)} + * call. + * * @param objChecker * A customized object checker. */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java index 9b98250884..d5e17224cf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java @@ -57,7 +57,7 @@ public interface ReadableChannel extends ReadableByteChannel { * @throws java.io.IOException * the channel's current position cannot be obtained. */ - public long position() throws IOException; + long position() throws IOException; /** * Seek the current position of the channel to a new offset. @@ -70,7 +70,7 @@ public interface ReadableChannel extends ReadableByteChannel { * channel only supports block aligned IO and the current * position is not block aligned. */ - public void position(long newPosition) throws IOException; + void position(long newPosition) throws IOException; /** * Get the total size of the channel. @@ -83,7 +83,7 @@ public interface ReadableChannel extends ReadableByteChannel { * @throws java.io.IOException * the size cannot be determined. */ - public long size() throws IOException; + long size() throws IOException; /** * Get the recommended alignment for reads. @@ -102,7 +102,7 @@ public interface ReadableChannel extends ReadableByteChannel { * @return recommended alignment size for randomly positioned reads. Does * not need to be a power of 2. */ - public int blockSize(); + int blockSize(); /** * Recommend the channel maintain a read-ahead buffer. @@ -131,5 +131,5 @@ public interface ReadableChannel extends ReadableByteChannel { * @throws java.io.IOException * if the read ahead cannot be adjusted. */ - public void setReadAheadBytes(int bufferSize) throws IOException; + void setReadAheadBytes(int bufferSize) throws IOException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java index e35b9c9e4a..35522667e0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java @@ -43,6 +43,7 @@ package org.eclipse.jgit.internal.storage.file; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX; import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK; @@ -50,7 +51,6 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.Files; @@ -1036,8 +1036,8 @@ public class ObjectDirectory extends FileObjectDatabase { } private static BufferedReader open(File f) - throws FileNotFoundException { - return new BufferedReader(new FileReader(f)); + throws IOException, FileNotFoundException { + return Files.newBufferedReader(f.toPath(), UTF_8); } private AlternateHandle openAlternate(String location) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java index 70695880d3..964cc4d120 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java @@ -207,7 +207,7 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex } /** An entry in the old PackBitmapIndex. */ - public final class Entry extends ObjectId { + public static final class Entry extends ObjectId { private final int flags; Entry(AnyObjectId src, int flags) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java index f759e23ef4..2c806235a6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java @@ -79,7 +79,7 @@ public interface ObjectReuseAsIs { * the Git type of the object that will be packed. * @return a new instance for this object. */ - public ObjectToPack newObjectToPack(AnyObjectId objectId, int type); + ObjectToPack newObjectToPack(AnyObjectId objectId, int type); /** * Select the best object representation for a packer. @@ -114,7 +114,7 @@ public interface ObjectReuseAsIs { * @throws java.io.IOException * the repository cannot be accessed. Packing will abort. */ - public void selectObjectRepresentation(PackWriter packer, + void selectObjectRepresentation(PackWriter packer, ProgressMonitor monitor, Iterable<ObjectToPack> objects) throws IOException, MissingObjectException; @@ -155,7 +155,7 @@ public interface ObjectReuseAsIs { * the stream cannot be written to, or one or more required * objects cannot be accessed from the object database. */ - public void writeObjects(PackOutputStream out, List<ObjectToPack> list) + void writeObjects(PackOutputStream out, List<ObjectToPack> list) throws IOException; /** @@ -200,7 +200,7 @@ public interface ObjectReuseAsIs { * the stream's write method threw an exception. Packing will * abort. */ - public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp, + void copyObjectAsIs(PackOutputStream out, ObjectToPack otp, boolean validate) throws IOException, StoredObjectRepresentationNotAvailableException; @@ -216,7 +216,7 @@ public interface ObjectReuseAsIs { * @throws java.io.IOException * the pack cannot be read, or stream did not accept a write. */ - public abstract void copyPackAsIs(PackOutputStream out, CachedPack pack) + void copyPackAsIs(PackOutputStream out, CachedPack pack) throws IOException; /** @@ -234,6 +234,6 @@ public interface ObjectReuseAsIs { * Callers may choose to ignore this and continue as-if there * were no cached packs. */ - public Collection<CachedPack> getCachedPacksAndUpdate( + Collection<CachedPack> getCachedPacksAndUpdate( BitmapBuilder needBitmap) throws IOException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java index 7f38a7b51a..eb777be809 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackOutputStream.java @@ -187,6 +187,7 @@ public final class PackOutputStream extends OutputStream { * @throws java.io.IOException * the underlying stream refused to accept the header. */ + @SuppressWarnings("ShortCircuitBoolean") public final void writeHeader(ObjectToPack otp, long rawLength) throws IOException { ObjectToPack b = otp.getDeltaBase(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java index 3651631573..7b872b1860 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/submodule/SubmoduleValidator.java @@ -45,13 +45,17 @@ package org.eclipse.jgit.internal.submodule; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PATH; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_URL; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SUBMODULE_SECTION; +import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_NAME; +import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_PARSE; +import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_PATH; +import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.GITMODULES_URL; -import java.io.IOException; import java.text.MessageFormat; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ObjectChecker; /** * Validations for the git submodule fields (name, path, uri). @@ -66,15 +70,30 @@ public class SubmoduleValidator { */ public static class SubmoduleValidationException extends Exception { + private static final long serialVersionUID = 1L; + + private final ObjectChecker.ErrorType fsckMessageId; + /** * @param message * Description of the problem + * @param fsckMessageId + * Error identifier, following the git fsck fsck.<msg-id> + * format */ - public SubmoduleValidationException(String message) { + SubmoduleValidationException(String message, + ObjectChecker.ErrorType fsckMessageId) { super(message); + this.fsckMessageId = fsckMessageId; } - private static final long serialVersionUID = 1L; + + /** + * @return the error identifier + */ + public ObjectChecker.ErrorType getFsckMessageId() { + return fsckMessageId; + } } /** @@ -100,13 +119,15 @@ public class SubmoduleValidator { // Since Path class is platform dependent, we manually check '/' and // '\\' patterns here. throw new SubmoduleValidationException(MessageFormat - .format(JGitText.get().invalidNameContainsDotDot, name)); + .format(JGitText.get().invalidNameContainsDotDot, name), + GITMODULES_NAME); } if (name.startsWith("-")) { //$NON-NLS-1$ throw new SubmoduleValidationException( MessageFormat.format( - JGitText.get().submoduleNameInvalid, name)); + JGitText.get().submoduleNameInvalid, name), + GITMODULES_NAME); } } @@ -123,7 +144,8 @@ public class SubmoduleValidator { if (uri.startsWith("-")) { //$NON-NLS-1$ throw new SubmoduleValidationException( MessageFormat.format( - JGitText.get().submoduleUrlInvalid, uri)); + JGitText.get().submoduleUrlInvalid, uri), + GITMODULES_URL); } } @@ -140,19 +162,22 @@ public class SubmoduleValidator { if (path.startsWith("-")) { //$NON-NLS-1$ throw new SubmoduleValidationException( MessageFormat.format( - JGitText.get().submodulePathInvalid, path)); + JGitText.get().submodulePathInvalid, path), + GITMODULES_PATH); } } /** + * Validate a .gitmodules file + * * @param gitModulesContents * Contents of a .gitmodule file. They will be parsed internally. - * @throws IOException - * If the contents + * @throws SubmoduleValidationException + * if the contents don't look like a configuration file or field + * values are not valid */ public static void assertValidGitModulesFile(String gitModulesContents) - throws IOException { - // Validate .gitmodules file + throws SubmoduleValidationException { Config c = new Config(); try { c.fromText(gitModulesContents); @@ -173,12 +198,9 @@ public class SubmoduleValidator { } } } catch (ConfigInvalidException e) { - throw new IOException( - MessageFormat.format( - JGitText.get().invalidGitModules, - e)); - } catch (SubmoduleValidationException e) { - throw new IOException(e.getMessage(), e); + throw new SubmoduleValidationException( + JGitText.get().invalidGitModules, + GITMODULES_PARSE); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java new file mode 100644 index 0000000000..2dae021702 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/parser/FirstWant.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2018, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.internal.transport.parser; + +import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.errors.PackProtocolException; +import org.eclipse.jgit.internal.JGitText; + +/** + * In the pack negotiation phase (protocol v0/v1), the client sends a list of + * wants. The first "want" line is special, as it (can) have a list of + * capabilities appended. + * + * E.g. "want oid cap1 cap2 cap3" + * + * Do not confuse this line with the first one in the reference advertisement, + * which is sent by the server, looks like + * "b8f7c471373b8583ced0025cfad8c9916c484b76 HEAD\0 cap1 cap2 cap3" and is + * parsed by the BasePackConnection.readAdvertisedRefs method. + * + * This class parses the input want line and holds the results: the actual want + * line and the capabilities. + * + * @since 5.2 + */ +public class FirstWant { + private final String line; + + private final Set<String> capabilities; + + @Nullable + private final String agent; + + private static final String AGENT_PREFIX = OPTION_AGENT + '='; + + /** + * Parse the first want line in the protocol v0/v1 pack negotiation. + * + * @param line + * line from the client. + * @return an instance of FirstWant + * @throws PackProtocolException + * if the line doesn't follow the protocol format. + */ + public static FirstWant fromLine(String line) throws PackProtocolException { + String wantLine; + Set<String> capabilities; + String agent = null; + + if (line.length() > 45) { + String opt = line.substring(45); + if (!opt.startsWith(" ")) { //$NON-NLS-1$ + throw new PackProtocolException(JGitText.get().wantNoSpaceWithCapabilities); + } + opt = opt.substring(1); + + HashSet<String> opts = new HashSet<>(); + for (String clientCapability : opt.split(" ")) { //$NON-NLS-1$ + if (clientCapability.startsWith(AGENT_PREFIX)) { + agent = clientCapability.substring(AGENT_PREFIX.length()); + } else { + opts.add(clientCapability); + } + } + wantLine = line.substring(0, 45); + capabilities = Collections.unmodifiableSet(opts); + } else { + wantLine = line; + capabilities = Collections.emptySet(); + } + + return new FirstWant(wantLine, capabilities, agent); + } + + private FirstWant(String line, Set<String> capabilities, + @Nullable String agent) { + this.line = line; + this.capabilities = capabilities; + this.agent = agent; + } + + /** @return non-capabilities part of the line. */ + public String getLine() { + return line; + } + + /** + * @return capabilities parsed from the line as an immutable set (excluding + * agent). + */ + public Set<String> getCapabilities() { + return capabilities; + } + + /** @return client user agent parsed from the line. */ + @Nullable + public String getAgent() { + return agent; + } +} 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 new file mode 100644 index 0000000000..e8a6ba7308 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java @@ -0,0 +1,922 @@ +/* + * Copyright (C) 2008, 2017, Google Inc. + * Copyright (C) 2017, 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ + +package org.eclipse.jgit.internal.transport.ssh; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.errors.InvalidPatternException; +import org.eclipse.jgit.fnmatch.FileNameMatcher; +import org.eclipse.jgit.transport.SshConstants; +import org.eclipse.jgit.util.StringUtils; +import org.eclipse.jgit.util.SystemReader; + +/** + * Fairly complete configuration parser for the openssh ~/.ssh/config file. + * <p> + * Both JSch 0.1.54 and Apache MINA sshd 2.1.0 have parsers for this, but both + * are buggy. Therefore we implement our own parser to read an openssh + * configuration file. + * </p> + * <p> + * Limitations compared to the full openssh 7.5 parser: + * </p> + * <ul> + * <li>This parser does not handle Match or Include keywords. + * <li>This parser does not do host name canonicalization. + * </ul> + * <p> + * Note that openssh's readconf.c is a validating parser; this parser does not + * validate entries. + * </p> + * <p> + * This config does %-substitutions for the following tokens: + * </p> + * <ul> + * <li>%% - single % + * <li>%C - short-hand for %l%h%p%r. + * <li>%d - home directory path + * <li>%h - remote host name + * <li>%L - local host name without domain + * <li>%l - FQDN of the local host + * <li>%n - host name as specified in {@link #lookup(String, int, String)} + * <li>%p - port number; if not given in {@link #lookup(String, int, String)} + * replaced only if set in the config + * <li>%r - remote user name; if not given in + * {@link #lookup(String, int, String)} replaced only if set in the config + * <li>%u - local user name + * </ul> + * <p> + * %i is not handled; Java has no concept of a "user ID". %T is always replaced + * by NONE. + * </p> + * + * @see <a href="http://man.openbsd.org/OpenBSD-current/man5/ssh_config.5">man + * ssh-config</a> + */ +public class OpenSshConfigFile { + + /** + * "Host" name of the HostEntry for the default options before the first + * host block in a config file. + */ + private static final String DEFAULT_NAME = ""; //$NON-NLS-1$ + + /** The user's home directory, as key files may be relative to here. */ + private final File home; + + /** The .ssh/config file we read and monitor for updates. */ + private final File configFile; + + /** User name of the user on the host OS. */ + private final String localUserName; + + /** Modification time of {@link #configFile} when it was last loaded. */ + private long lastModified; + + /** + * Encapsulates entries read out of the configuration file, and a cache of + * fully resolved entries created from that. + */ + private static class State { + // Keyed by pattern; if a "Host" line has multiple patterns, we generate + // duplicate HostEntry objects + Map<String, HostEntry> entries = new LinkedHashMap<>(); + + // Keyed by user@hostname:port + Map<String, HostEntry> hosts = new HashMap<>(); + + @Override + @SuppressWarnings("nls") + public String toString() { + return "State [entries=" + entries + ", hosts=" + hosts + "]"; + } + } + + /** State read from the config file, plus the cache. */ + private State state; + + /** + * Creates a new {@link OpenSshConfigFile} that will read the config from + * file {@code config} use the given file {@code home} as "home" directory. + * + * @param home + * user's home directory for the purpose of ~ replacement + * @param config + * file to load. + * @param localUserName + * user name of the current user on the local host OS + */ + public OpenSshConfigFile(@NonNull File home, @NonNull File config, + @NonNull String localUserName) { + this.home = home; + this.configFile = config; + this.localUserName = localUserName; + state = new State(); + } + + /** + * Locate the configuration for a specific host request. + * + * @param hostName + * the name the user has supplied to the SSH tool. This may be a + * real host name, or it may just be a "Host" block in the + * configuration file. + * @param port + * the user supplied; <= 0 if none + * @param userName + * the user supplied, may be {@code null} or empty if none given + * @return r configuration for the requested name. + */ + @NonNull + public HostEntry lookup(@NonNull String hostName, int port, + String userName) { + final State cache = refresh(); + String cacheKey = toCacheKey(hostName, port, userName); + HostEntry h = cache.hosts.get(cacheKey); + if (h != null) { + return h; + } + HostEntry fullConfig = new HostEntry(); + // Initialize with default entries at the top of the file, before the + // first Host block. + fullConfig.merge(cache.entries.get(DEFAULT_NAME)); + for (Map.Entry<String, HostEntry> e : cache.entries.entrySet()) { + String pattern = e.getKey(); + if (isHostMatch(pattern, hostName)) { + fullConfig.merge(e.getValue()); + } + } + fullConfig.substitute(hostName, port, userName, localUserName, home); + cache.hosts.put(cacheKey, fullConfig); + return fullConfig; + } + + @NonNull + private String toCacheKey(@NonNull String hostName, int port, + String userName) { + String key = hostName; + if (port > 0) { + key = key + ':' + Integer.toString(port); + } + if (userName != null && !userName.isEmpty()) { + key = userName + '@' + key; + } + return key; + } + + private synchronized State refresh() { + final long mtime = configFile.lastModified(); + if (mtime != lastModified) { + State newState = new State(); + try (BufferedReader br = Files + .newBufferedReader(configFile.toPath(), UTF_8)) { + newState.entries = parse(br); + } catch (IOException | RuntimeException none) { + // Ignore -- we'll set and return an empty state + } + lastModified = mtime; + state = newState; + } + return state; + } + + private Map<String, HostEntry> parse(BufferedReader reader) + throws IOException { + final Map<String, HostEntry> entries = new LinkedHashMap<>(); + final List<HostEntry> current = new ArrayList<>(4); + String line; + + // The man page doesn't say so, but the openssh parser (readconf.c) + // starts out in active mode and thus always applies any lines that + // occur before the first host block. We gather those options in a + // HostEntry for DEFAULT_NAME. + HostEntry defaults = new HostEntry(); + current.add(defaults); + entries.put(DEFAULT_NAME, defaults); + + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { //$NON-NLS-1$ + 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()); + // 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$ + + if (StringUtils.equalsIgnoreCase(SshConstants.HOST, keyword)) { + current.clear(); + for (String name : parseList(argValue)) { + if (name == null || name.isEmpty()) { + // null should not occur, but better be safe than sorry. + continue; + } + HostEntry c = entries.get(name); + if (c == null) { + c = new HostEntry(); + entries.put(name, c); + } + current.add(c); + } + continue; + } + + if (current.isEmpty()) { + // We received an option outside of a Host block. We + // don't know who this should match against, so skip. + continue; + } + + if (HostEntry.isListKey(keyword)) { + List<String> args = validate(keyword, parseList(argValue)); + for (HostEntry entry : current) { + entry.setValue(keyword, args); + } + } else if (!argValue.isEmpty()) { + argValue = validate(keyword, dequote(argValue)); + for (HostEntry entry : current) { + entry.setValue(keyword, argValue); + } + } + } + + return entries; + } + + /** + * Splits the argument into a list of whitespace-separated elements. + * Elements containing whitespace must be quoted and will be de-quoted. + * + * @param argument + * argument part of the configuration line as read from the + * config file + * @return a {@link List} of elements, possibly empty and possibly + * containing empty elements, but not containing {@code null} + */ + private List<String> parseList(String argument) { + List<String> result = new ArrayList<>(4); + int start = 0; + int length = argument.length(); + while (start < length) { + // Skip whitespace + if (Character.isSpaceChar(argument.charAt(start))) { + 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 { + int stop = start + 1; + while (stop < length + && !Character.isSpaceChar(argument.charAt(stop))) { + stop++; + } + result.add(argument.substring(start, stop)); + start = stop + 1; + } + } + return result; + } + + /** + * Hook to perform validation on a single value, or to sanitize it. If this + * throws an (unchecked) exception, parsing of the file is abandoned. + * + * @param key + * of the entry + * @param value + * as read from the config file + * @return the validated and possibly sanitized value + */ + protected String validate(String key, String value) { + if (String.CASE_INSENSITIVE_ORDER.compare(key, + SshConstants.PREFERRED_AUTHENTICATIONS) == 0) { + return stripWhitespace(value); + } + return value; + } + + /** + * Hook to perform validation on values, or to sanitize them. If this throws + * an (unchecked) exception, parsing of the file is abandoned. + * + * @param key + * of the entry + * @param value + * list of arguments as read from the config file + * @return a {@link List} of values, possibly empty and possibly containing + * empty elements, but not containing {@code null} + */ + protected List<String> validate(String key, List<String> value) { + return value; + } + + private static boolean isHostMatch(String pattern, String name) { + if (pattern.startsWith("!")) { //$NON-NLS-1$ + return !patternMatchesHost(pattern.substring(1), name); + } else { + return patternMatchesHost(pattern, name); + } + } + + private static boolean patternMatchesHost(String pattern, String name) { + if (pattern.indexOf('*') >= 0 || pattern.indexOf('?') >= 0) { + final FileNameMatcher fn; + try { + fn = new FileNameMatcher(pattern, null); + } catch (InvalidPatternException e) { + return false; + } + fn.append(name); + return fn.isMatch(); + } else { + // Not a pattern but a full host 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(); + for (int i = 0; i < value.length(); i++) { + if (!Character.isSpaceChar(value.charAt(i))) + b.append(value.charAt(i)); + } + return b.toString(); + } + + private static File toFile(String path, File home) { + if (path.startsWith("~/") || path.startsWith("~" + File.separator)) { //$NON-NLS-1$ //$NON-NLS-2$ + return new File(home, path.substring(2)); + } + File ret = new File(path); + if (ret.isAbsolute()) { + return ret; + } + return new File(home, path); + } + + /** + * Converts a positive value into an {@code int}. + * + * @param value + * to convert + * @return the value, or -1 if it wasn't a positive integral value + */ + public static int positive(String value) { + if (value != null) { + try { + return Integer.parseUnsignedInt(value); + } catch (NumberFormatException e) { + // Ignore + } + } + return -1; + } + + /** + * Converts a ssh config flag value (yes/true/on - no/false/off) into an + * {@code boolean}. + * + * @param value + * to convert + * @return {@code true} if {@code value}Â is "yes", "on", or "true"; + * {@code false} otherwise + */ + public static boolean flag(String value) { + if (value == null) { + return false; + } + return SshConstants.YES.equals(value) || SshConstants.ON.equals(value) + || SshConstants.TRUE.equals(value); + } + + /** + * Retrieves the local user name as given in the constructor. + * + * @return the user name + */ + public String getLocalUserName() { + return localUserName; + } + + /** + * A host entry from the ssh config file. Any merging of global values and + * of several matching host entries, %-substitutions, and ~ replacement have + * all been done. + */ + public static class HostEntry { + + /** + * Keys that can be specified multiple times, building up a list. (I.e., + * those are the keys that do not follow the general rule of "first + * occurrence wins".) + */ + private static final Set<String> MULTI_KEYS = new TreeSet<>( + String.CASE_INSENSITIVE_ORDER); + + static { + MULTI_KEYS.add(SshConstants.CERTIFICATE_FILE); + MULTI_KEYS.add(SshConstants.IDENTITY_FILE); + MULTI_KEYS.add(SshConstants.LOCAL_FORWARD); + MULTI_KEYS.add(SshConstants.REMOTE_FORWARD); + MULTI_KEYS.add(SshConstants.SEND_ENV); + } + + /** + * Keys that take a whitespace-separated list of elements as argument. + * Because the dequote-handling is different, we must handle those in + * the parser. There are a few other keys that take comma-separated + * lists as arguments, but for the parser those are single arguments + * that must be quoted if they contain whitespace, and taking them apart + * is the responsibility of the user of those keys. + */ + private static final Set<String> LIST_KEYS = new TreeSet<>( + String.CASE_INSENSITIVE_ORDER); + + static { + LIST_KEYS.add(SshConstants.CANONICAL_DOMAINS); + LIST_KEYS.add(SshConstants.GLOBAL_KNOWN_HOSTS_FILE); + LIST_KEYS.add(SshConstants.SEND_ENV); + LIST_KEYS.add(SshConstants.USER_KNOWN_HOSTS_FILE); + } + + private Map<String, String> options; + + private Map<String, List<String>> multiOptions; + + private Map<String, List<String>> listOptions; + + /** + * Retrieves the value of a single-valued key, or the first is the key + * has multiple values. Keys are case-insensitive, so + * {@code getValue("HostName") == getValue("HOSTNAME")}. + * + * @param key + * to get the value of + * @return the value, or {@code null} if none + */ + public String getValue(String key) { + String result = options != null ? options.get(key) : null; + if (result == null) { + // Let's be lenient and return at least the first value from + // a list-valued or multi-valued key. + List<String> values = listOptions != null ? listOptions.get(key) + : null; + if (values == null) { + values = multiOptions != null ? multiOptions.get(key) + : null; + } + if (values != null && !values.isEmpty()) { + result = values.get(0); + } + } + return result; + } + + /** + * Retrieves the values of a multi or list-valued key. Keys are + * case-insensitive, so + * {@code getValue("HostName") == getValue("HOSTNAME")}. + * + * @param key + * to get the values of + * @return a possibly empty list of values + */ + public List<String> getValues(String key) { + List<String> values = listOptions != null ? listOptions.get(key) + : null; + if (values == null) { + values = multiOptions != null ? multiOptions.get(key) : null; + } + if (values == null || values.isEmpty()) { + return new ArrayList<>(); + } + return new ArrayList<>(values); + } + + /** + * Sets the value of a single-valued key if it not set yet, or adds a + * value to a multi-valued key. If the value is {@code null}, the key is + * removed altogether, whether it is single-, list-, or multi-valued. + * + * @param key + * to modify + * @param value + * to set or add + */ + public void setValue(String key, String value) { + if (value == null) { + if (multiOptions != null) { + multiOptions.remove(key); + } + if (listOptions != null) { + listOptions.remove(key); + } + if (options != null) { + options.remove(key); + } + return; + } + if (MULTI_KEYS.contains(key)) { + if (multiOptions == null) { + multiOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + List<String> values = multiOptions.get(key); + if (values == null) { + values = new ArrayList<>(4); + multiOptions.put(key, values); + } + values.add(value); + } else { + if (options == null) { + options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + if (!options.containsKey(key)) { + options.put(key, value); + } + } + } + + /** + * Sets the values of a multi- or list-valued key. + * + * @param key + * to set + * @param values + * a non-empty list of values + */ + public void setValue(String key, List<String> values) { + if (values.isEmpty()) { + return; + } + // Check multi-valued keys first; because of the replacement + // strategy, they must take precedence over list-valued keys + // which always follow the "first occurrence wins" strategy. + // + // Note that SendEnv is a multi-valued list-valued key. (It's + // rather immaterial for JGit, though.) + if (MULTI_KEYS.contains(key)) { + if (multiOptions == null) { + multiOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + List<String> items = multiOptions.get(key); + if (items == null) { + items = new ArrayList<>(values); + multiOptions.put(key, items); + } else { + items.addAll(values); + } + } else { + if (listOptions == null) { + listOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + if (!listOptions.containsKey(key)) { + listOptions.put(key, values); + } + } + } + + /** + * Does the key take a whitespace-separated list of values? + * + * @param key + * to check + * @return {@code true} if the key is a list-valued key. + */ + public static boolean isListKey(String key) { + return LIST_KEYS.contains(key.toUpperCase(Locale.ROOT)); + } + + void merge(HostEntry entry) { + if (entry == null) { + // Can occur if we could not read the config file + return; + } + if (entry.options != null) { + if (options == null) { + options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + for (Map.Entry<String, String> item : entry.options + .entrySet()) { + if (!options.containsKey(item.getKey())) { + options.put(item.getKey(), item.getValue()); + } + } + } + if (entry.listOptions != null) { + if (listOptions == null) { + listOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + for (Map.Entry<String, List<String>> item : entry.listOptions + .entrySet()) { + if (!listOptions.containsKey(item.getKey())) { + listOptions.put(item.getKey(), item.getValue()); + } + } + + } + if (entry.multiOptions != null) { + if (multiOptions == null) { + multiOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + for (Map.Entry<String, List<String>> item : entry.multiOptions + .entrySet()) { + List<String> values = multiOptions.get(item.getKey()); + if (values == null) { + values = new ArrayList<>(item.getValue()); + multiOptions.put(item.getKey(), values); + } else { + values.addAll(item.getValue()); + } + } + } + } + + private List<String> substitute(List<String> values, String allowed, + Replacer r) { + List<String> result = new ArrayList<>(values.size()); + for (String value : values) { + result.add(r.substitute(value, allowed)); + } + return result; + } + + private List<String> replaceTilde(List<String> values, File home) { + List<String> result = new ArrayList<>(values.size()); + for (String value : values) { + result.add(toFile(value, home).getPath()); + } + return result; + } + + void substitute(String originalHostName, int port, String userName, + String localUserName, File home) { + int p = port >= 0 ? port : positive(getValue(SshConstants.PORT)); + if (p < 0) { + p = SshConstants.SSH_DEFAULT_PORT; + } + String u = userName != null && !userName.isEmpty() ? userName + : getValue(SshConstants.USER); + if (u == null || u.isEmpty()) { + u = localUserName; + } + Replacer r = new Replacer(originalHostName, p, u, localUserName, + home); + if (options != null) { + // HOSTNAME first + String hostName = options.get(SshConstants.HOST_NAME); + if (hostName == null || hostName.isEmpty()) { + options.put(SshConstants.HOST_NAME, originalHostName); + } else { + hostName = r.substitute(hostName, "h"); //$NON-NLS-1$ + options.put(SshConstants.HOST_NAME, hostName); + r.update('h', hostName); + } + } + if (multiOptions != null) { + List<String> values = multiOptions + .get(SshConstants.IDENTITY_FILE); + if (values != null) { + values = substitute(values, "dhlru", r); //$NON-NLS-1$ + values = replaceTilde(values, home); + multiOptions.put(SshConstants.IDENTITY_FILE, values); + } + values = multiOptions.get(SshConstants.CERTIFICATE_FILE); + if (values != null) { + values = substitute(values, "dhlru", r); //$NON-NLS-1$ + values = replaceTilde(values, home); + multiOptions.put(SshConstants.CERTIFICATE_FILE, values); + } + } + if (listOptions != null) { + List<String> values = listOptions + .get(SshConstants.USER_KNOWN_HOSTS_FILE); + if (values != null) { + values = replaceTilde(values, home); + listOptions.put(SshConstants.USER_KNOWN_HOSTS_FILE, values); + } + } + if (options != null) { + // HOSTNAME already done above + String value = options.get(SshConstants.IDENTITY_AGENT); + if (value != null) { + value = r.substitute(value, "dhlru"); //$NON-NLS-1$ + 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"); //$NON-NLS-1$ + 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"); //$NON-NLS-1$ + options.put(SshConstants.LOCAL_COMMAND, value); + } + value = options.get(SshConstants.REMOTE_COMMAND); + if (value != null) { + value = r.substitute(value, "Cdhlnpru"); //$NON-NLS-1$ + options.put(SshConstants.REMOTE_COMMAND, value); + } + value = options.get(SshConstants.PROXY_COMMAND); + if (value != null) { + value = r.substitute(value, "hpr"); //$NON-NLS-1$ + options.put(SshConstants.PROXY_COMMAND, value); + } + } + // Match is not implemented and would need to be done elsewhere + // anyway. + } + + /** + * Retrieves an unmodifiable map of all single-valued options, with + * case-insensitive lookup by keys. + * + * @return all single-valued options + */ + @NonNull + public Map<String, String> getOptions() { + if (options == null) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(options); + } + + /** + * Retrieves an unmodifiable map of all multi-valued options, with + * case-insensitive lookup by keys. + * + * @return all multi-valued options + */ + @NonNull + public Map<String, List<String>> getMultiValuedOptions() { + if (listOptions == null && multiOptions == null) { + return Collections.emptyMap(); + } + Map<String, List<String>> allValues = new TreeMap<>( + String.CASE_INSENSITIVE_ORDER); + if (multiOptions != null) { + allValues.putAll(multiOptions); + } + if (listOptions != null) { + allValues.putAll(listOptions); + } + return Collections.unmodifiableMap(allValues); + } + + @Override + @SuppressWarnings("nls") + public String toString() { + return "HostEntry [options=" + options + ", multiOptions=" + + multiOptions + ", listOptions=" + listOptions + "]"; + } + } + + private static class Replacer { + private final Map<Character, String> replacements = new HashMap<>(); + + public Replacer(String host, int port, String user, + String localUserName, File home) { + replacements.put(Character.valueOf('%'), "%"); //$NON-NLS-1$ + replacements.put(Character.valueOf('d'), home.getPath()); + replacements.put(Character.valueOf('h'), host); + String localhost = SystemReader.getInstance().getHostname(); + replacements.put(Character.valueOf('l'), localhost); + int period = localhost.indexOf('.'); + if (period > 0) { + localhost = localhost.substring(0, period); + } + replacements.put(Character.valueOf('L'), localhost); + replacements.put(Character.valueOf('n'), host); + replacements.put(Character.valueOf('p'), Integer.toString(port)); + replacements.put(Character.valueOf('r'), user == null ? "" : user); //$NON-NLS-1$ + replacements.put(Character.valueOf('u'), localUserName); + replacements.put(Character.valueOf('C'), + substitute("%l%h%p%r", "hlpr")); //$NON-NLS-1$ //$NON-NLS-2$ + replacements.put(Character.valueOf('T'), "NONE"); //$NON-NLS-1$ + } + + public void update(char key, String value) { + replacements.put(Character.valueOf(key), value); + if ("lhpr".indexOf(key) >= 0) { //$NON-NLS-1$ + replacements.put(Character.valueOf('C'), + substitute("%l%h%p%r", "hlpr")); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + public String substitute(String input, String allowed) { + if (input == null || input.length() <= 1 + || input.indexOf('%') < 0) { + return input; + } + StringBuilder builder = new StringBuilder(); + int start = 0; + int length = input.length(); + while (start < length) { + int percent = input.indexOf('%', start); + if (percent < 0 || percent + 1 >= length) { + builder.append(input.substring(start)); + break; + } + String replacement = null; + char ch = input.charAt(percent + 1); + if (ch == '%' || allowed.indexOf(ch) >= 0) { + replacement = replacements.get(Character.valueOf(ch)); + } + if (replacement == null) { + builder.append(input.substring(start, percent + 2)); + } else { + builder.append(input.substring(start, percent)) + .append(replacement); + } + start = percent + 2; + } + return builder.toString(); + } + } + + /** {@inheritDoc} */ + @Override + @SuppressWarnings("nls") + public String toString() { + return "OpenSshConfig [home=" + home + ", configFile=" + configFile + + ", lastModified=" + lastModified + ", state=" + state + "]"; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectLoaderQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectLoaderQueue.java index b4ea0e907f..659c67c5ab 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectLoaderQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectLoaderQueue.java @@ -78,7 +78,7 @@ public interface AsyncObjectLoaderQueue<T extends ObjectId> extends * @throws java.io.IOException * the object store cannot be accessed. */ - public boolean next() throws MissingObjectException, IOException; + boolean next() throws MissingObjectException, IOException; /** * Get the current object, null if the implementation lost track. @@ -87,14 +87,14 @@ public interface AsyncObjectLoaderQueue<T extends ObjectId> extends * Implementations may for performance reasons discard the caller's * ObjectId and provider their own through {@link #getObjectId()}. */ - public T getCurrent(); + T getCurrent(); /** * Get the ObjectId of the current object. Never null. * * @return the ObjectId of the current object. Never null. */ - public ObjectId getObjectId(); + ObjectId getObjectId(); /** * Obtain a loader to read the object. @@ -115,5 +115,5 @@ public interface AsyncObjectLoaderQueue<T extends ObjectId> extends * @throws java.io.IOException * the object store cannot be accessed. */ - public ObjectLoader open() throws IOException; + ObjectLoader open() throws IOException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectSizeQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectSizeQueue.java index 03efcd295e..6b8642f119 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectSizeQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncObjectSizeQueue.java @@ -73,7 +73,7 @@ public interface AsyncObjectSizeQueue<T extends ObjectId> extends * @throws java.io.IOException * the object store cannot be accessed. */ - public boolean next() throws MissingObjectException, IOException; + boolean next() throws MissingObjectException, IOException; /** * <p>getCurrent.</p> @@ -82,19 +82,19 @@ public interface AsyncObjectSizeQueue<T extends ObjectId> extends * Implementations may for performance reasons discard the caller's * ObjectId and provider their own through {@link #getObjectId()}. */ - public T getCurrent(); + T getCurrent(); /** * Get the ObjectId of the current object. Never null. * * @return the ObjectId of the current object. Never null. */ - public ObjectId getObjectId(); + ObjectId getObjectId(); /** * Get the size of the current object. * * @return the size of the current object. */ - public long getSize(); + long getSize(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncOperation.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncOperation.java index 00555b0907..27b9c2038a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncOperation.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AsyncOperation.java @@ -68,10 +68,10 @@ public interface AsyncOperation { * @return false if the task could not be cancelled, typically because it * has already completed normally; true otherwise */ - public boolean cancel(boolean mayInterruptIfRunning); + boolean cancel(boolean mayInterruptIfRunning); /** * Release resources used by the operation, including cancellation. */ - public void release(); + void release(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java index 3fa3168327..7878351ce8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java @@ -55,7 +55,7 @@ import org.eclipse.jgit.errors.CorruptObjectException; */ public interface BlobObjectChecker { /** No-op implementation of {@link BlobObjectChecker}. */ - public static final BlobObjectChecker NULL_CHECKER = + BlobObjectChecker NULL_CHECKER = new BlobObjectChecker() { @Override public void update(byte[] in, int p, int len) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java index cfc0cc86d1..84ff0a8936 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CheckoutEntry.java @@ -12,13 +12,13 @@ public interface CheckoutEntry { * * @return the name of the branch before checkout */ - public abstract String getFromBranch(); + String getFromBranch(); /** * Get the name of the branch after checkout * * @return the name of the branch after checkout */ - public abstract String getToBranch(); + String getToBranch(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java index b666f21d0b..4726975d07 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java @@ -62,7 +62,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.events.ConfigChangedEvent; import org.eclipse.jgit.events.ConfigChangedListener; @@ -868,7 +867,7 @@ public class Config { boolean lastWasMatch = false; for (ConfigLine e : srcState.entryList) { - if (e.match(section, subsection)) { + if (e.includedFrom == null && e.match(section, subsection)) { // Skip this record, it's for the section we are removing. lastWasMatch = true; continue; @@ -923,7 +922,7 @@ public class Config { // while (entryIndex < entries.size() && valueIndex < values.size()) { final ConfigLine e = entries.get(entryIndex); - if (e.match(section, subsection, name)) { + if (e.includedFrom == null && e.match(section, subsection, name)) { entries.set(entryIndex, e.forValue(values.get(valueIndex++))); insertPosition = entryIndex + 1; } @@ -935,7 +934,8 @@ public class Config { if (valueIndex == values.size() && entryIndex < entries.size()) { while (entryIndex < entries.size()) { final ConfigLine e = entries.get(entryIndex++); - if (e.match(section, subsection, name)) + if (e.includedFrom == null + && e.match(section, subsection, name)) entries.remove(--entryIndex); } } @@ -948,7 +948,8 @@ public class Config { // is already a section available that matches. Insert // after the last key of that section. // - insertPosition = findSectionEnd(entries, section, subsection); + insertPosition = findSectionEnd(entries, section, subsection, + true); } if (insertPosition < 0) { // We didn't find any matching section header for this key, @@ -985,9 +986,14 @@ public class Config { } private static int findSectionEnd(final List<ConfigLine> entries, - final String section, final String subsection) { + final String section, final String subsection, + boolean skipIncludedLines) { for (int i = 0; i < entries.size(); i++) { ConfigLine e = entries.get(i); + if (e.includedFrom != null && skipIncludedLines) { + continue; + } + if (e.match(section, subsection, null)) { i++; while (i < entries.size()) { @@ -1011,6 +1017,8 @@ public class Config { public String toText() { final StringBuilder out = new StringBuilder(); for (ConfigLine e : state.get().entryList) { + if (e.includedFrom != null) + continue; if (e.prefix != null) out.append(e.prefix); if (e.section != null && e.name == null) { @@ -1060,11 +1068,11 @@ public class Config { * made to {@code this}. */ public void fromText(String text) throws ConfigInvalidException { - state.set(newState(fromTextRecurse(text, 1))); + state.set(newState(fromTextRecurse(text, 1, null))); } - private List<ConfigLine> fromTextRecurse(String text, int depth) - throws ConfigInvalidException { + private List<ConfigLine> fromTextRecurse(String text, int depth, + String includedFrom) throws ConfigInvalidException { if (depth > MAX_DEPTH) { throw new ConfigInvalidException( JGitText.get().tooManyIncludeRecursions); @@ -1073,6 +1081,7 @@ public class Config { final StringReader in = new StringReader(text); ConfigLine last = null; ConfigLine e = new ConfigLine(); + e.includedFrom = includedFrom; for (;;) { int input = in.read(); if (-1 == input) { @@ -1088,7 +1097,7 @@ public class Config { if (e.section != null) last = e; e = new ConfigLine(); - + e.includedFrom = includedFrom; } else if (e.suffix != null) { // Everything up until the end-of-line is in the suffix. e.suffix += c; @@ -1148,7 +1157,6 @@ public class Config { * if something went wrong while reading the config * @since 4.10 */ - @Nullable protected byte[] readIncludedConfig(String relPath) throws ConfigInvalidException { return null; @@ -1173,7 +1181,7 @@ public class Config { decoded = RawParseUtils.decode(bytes); } try { - newEntries.addAll(fromTextRecurse(decoded, depth + 1)); + newEntries.addAll(fromTextRecurse(decoded, depth + 1, line.value)); } catch (ConfigInvalidException e) { throw new ConfigInvalidException(MessageFormat .format(JGitText.get().cannotReadFile, line.value), e); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index d4a0280da6..9763fb72fe 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -119,6 +119,36 @@ public final class ConfigConstants { */ public static final String CONFIG_FILTER_SECTION = "filter"; + /** + * The "gpg" section + * @since 5.2 + */ + public static final String CONFIG_GPG_SECTION = "gpg"; + + /** + * The "format" key + * @since 5.2 + */ + public static final String CONFIG_KEY_FORMAT = "format"; + + /** + * The "signingKey" key + * @since 5.2 + */ + public static final String CONFIG_KEY_SIGNINGKEY = "signingKey"; + + /** + * The "commit" section + * @since 5.2 + */ + public static final String CONFIG_COMMIT_SECTION = "commit"; + + /** + * The "gpgSign" key + * @since 5.2 + */ + public static final String CONFIG_KEY_GPGSIGN = "gpgSign"; + /** The "algorithm" key */ public static final String CONFIG_KEY_ALGORITHM = "algorithm"; @@ -432,4 +462,18 @@ public final class ConfigConstants { * @since 4.11 */ public static final String CONFIG_SECTION_LFS = "lfs"; + + /** + * The "i18n" section + * + * @since 5.2 + */ + public static final String CONFIG_SECTION_I18N = "i18n"; + + /** + * The "logOutputEncoding" key + * + * @since 5.2 + */ + public static final String CONFIG_KEY_LOG_OUTPUT_ENCODING = "logOutputEncoding"; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigLine.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigLine.java index 937ba925c5..e623a8cebc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigLine.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigLine.java @@ -73,6 +73,9 @@ class ConfigLine { /** The text content after entry. */ String suffix; + /** The source from which this line was included from. */ + String includedFrom; + ConfigLine forValue(String newValue) { final ConfigLine e = new ConfigLine(); e.prefix = prefix; @@ -81,6 +84,7 @@ class ConfigLine { e.name = name; e.value = newValue; e.suffix = suffix; + e.includedFrom = includedFrom; return e; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index ed0055416b..4c55196961 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -232,11 +232,17 @@ public final class Constants { * * @deprecated Use {@link java.nio.charset.StandardCharsets#UTF_8} directly * instead. - **/ + */ @Deprecated public static final Charset CHARSET; - /** Native character encoding for commit messages, file names... */ + /** + * Native character encoding for commit messages, file names... + * + * @deprecated Use {@link java.nio.charset.StandardCharsets#UTF_8} directly + * instead. + */ + @Deprecated public static final String CHARACTER_ENCODING; /** Default main branch name */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java index 891c7f23b4..6a66cf682f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java @@ -293,7 +293,8 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter { /** {@inheritDoc} */ @Override - public @NonNull List<RefSpec> getRefSpecs(Config config, String section, + @NonNull + public List<RefSpec> getRefSpecs(Config config, String section, String subsection, String name) { String[] values = config.getStringList(section, subsection, name); List<RefSpec> result = new ArrayList<>(values.length); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java new file mode 100644 index 0000000000..a09bc00786 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018, Salesforce. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.lib; + +/** + * Typed access to GPG related configuration options. + * + * @since 5.2 + */ +public class GpgConfig { + + /** + * Config values for gpg.format. + */ + public enum GpgFormat implements Config.ConfigEnum { + + /** Value for openpgp */ + OPENPGP("openpgp"), //$NON-NLS-1$ + /** Value for x509 */ + X509("x509"); //$NON-NLS-1$ + + private final String configValue; + + private GpgFormat(String configValue) { + this.configValue = configValue; + } + + @Override + public boolean matchConfigValue(String s) { + return configValue.equals(s); + } + + @Override + public String toConfigValue() { + return configValue; + } + } + + private final Config config; + + /** + * Create a new GPG config, which will read configuration from config. + * + * @param config + * the config to read from + */ + public GpgConfig(Config config) { + this.config = config; + } + + /** + * Retrieves the config value of gpg.format. + * + * @return the {@link org.eclipse.jgit.lib.GpgConfig.GpgFormat} + */ + public GpgFormat getKeyFormat() { + return config.getEnum(GpgFormat.values(), + ConfigConstants.CONFIG_GPG_SECTION, null, + ConfigConstants.CONFIG_KEY_FORMAT, GpgFormat.OPENPGP); + } + + /** + * Retrieves the config value of user.signingKey. + * + * @return the value of user.signingKey (may be <code>null</code>) + */ + public String getSigningKey() { + return config.getString(ConfigConstants.CONFIG_USER_SECTION, null, + ConfigConstants.CONFIG_KEY_SIGNINGKEY); + } + + /** + * Retrieves the config value of commit.gpgSign. + * + * @return the value of commit.gpgSign (defaults to <code>false</code>) + */ + public boolean isSignCommits() { + return config.getBoolean(ConfigConstants.CONFIG_COMMIT_SECTION, + ConfigConstants.CONFIG_KEY_GPGSIGN, false); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java index 94b9ddc188..f37c310752 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java @@ -47,7 +47,11 @@ package org.eclipse.jgit.lib; +import java.io.File; import java.io.IOException; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; @@ -261,6 +265,8 @@ public class IndexDiff { private Set<String> missing = new HashSet<>(); + private Set<String> missingSubmodules = new HashSet<>(); + private Set<String> modified = new HashSet<>(); private Set<String> untracked = new HashSet<>(); @@ -501,9 +507,15 @@ public class IndexDiff { if (dirCacheIterator != null) { if (workingTreeIterator == null) { // in index, not in workdir => missing - if (!isEntryGitLink(dirCacheIterator) - || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL) - missing.add(treeWalk.getPathString()); + boolean isGitLink = isEntryGitLink(dirCacheIterator); + if (!isGitLink + || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL) { + String path = treeWalk.getPathString(); + missing.add(path); + if (isGitLink) { + missingSubmodules.add(path); + } + } } else { if (workingTreeIterator.isModified( dirCacheIterator.getDirCacheEntry(), true, @@ -543,8 +555,8 @@ public class IndexDiff { smw.getPath()), e); } try (Repository subRepo = smw.getRepository()) { + String subRepoPath = smw.getPath(); if (subRepo != null) { - String subRepoPath = smw.getPath(); ObjectId subHead = subRepo.resolve("HEAD"); //$NON-NLS-1$ if (subHead != null && !subHead.equals(smw.getObjectId())) { @@ -573,6 +585,21 @@ public class IndexDiff { recordFileMode(subRepoPath, FileMode.GITLINK); } } + } else if (missingSubmodules.remove(subRepoPath)) { + // If the directory is there and empty but the submodule + // repository in .git/modules doesn't exist yet it isn't + // "missing". + File gitDir = new File( + new File(repository.getDirectory(), + Constants.MODULES), + subRepoPath); + if (!gitDir.isDirectory()) { + File dir = SubmoduleWalk.getSubmoduleDirectory( + repository, subRepoPath); + if (dir.isDirectory() && !hasFiles(dir)) { + missing.remove(subRepoPath); + } + } } } } @@ -592,6 +619,15 @@ public class IndexDiff { return true; } + private boolean hasFiles(File directory) { + try (DirectoryStream<java.nio.file.Path> dir = Files + .newDirectoryStream(directory.toPath())) { + return dir.iterator().hasNext(); + } catch (DirectoryIteratorException | IOException e) { + return false; + } + } + private void recordFileMode(String path, FileMode mode) { Set<String> values = fileModes.get(mode); if (path != null) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java index d37fb21c93..127f019c46 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java @@ -109,7 +109,8 @@ import org.eclipse.jgit.util.StringUtils; * the caller can provide both of these validations on its own. * <p> * Instances of this class are not thread safe, but they may be reused to - * perform multiple object validations. + * perform multiple object validations, calling {@link #reset()} between them to + * clear the internal state (e.g. {@link #getGitsubmodules()}) */ public class ObjectChecker { /** Header "tree " */ @@ -173,6 +174,13 @@ public class ObjectChecker { /***/ BAD_TIMEZONE, /***/ MISSING_EMAIL, /***/ MISSING_SPACE_BEFORE_DATE, + /** @since 5.2 */ GITMODULES_BLOB, + /** @since 5.2 */ GITMODULES_LARGE, + /** @since 5.2 */ GITMODULES_NAME, + /** @since 5.2 */ GITMODULES_PARSE, + /** @since 5.2 */ GITMODULES_PATH, + /** @since 5.2 */ GITMODULES_SYMLINK, + /** @since 5.2 */ GITMODULES_URL, /***/ UNKNOWN_TYPE, // These are unique to JGit. @@ -1251,4 +1259,19 @@ public class ObjectChecker { public List<GitmoduleEntry> getGitsubmodules() { return gitsubmodules; } + + /** + * Reset the invocation-specific state from this instance. Specifically this + * clears the list of .gitmodules files encountered (see + * {@link #getGitsubmodules()}) + * + * Configurations like errors to filter, skip lists or the specified O.S. + * (set via {@link #setSafeForMacOS(boolean)} or + * {@link #setSafeForWindows(boolean)}) are NOT cleared. + * + * @since 5.2 + */ + public void reset() { + gitsubmodules.clear(); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java index d81ee45c9e..9d8d71a0be 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ProgressMonitor.java @@ -49,7 +49,7 @@ package org.eclipse.jgit.lib; */ public interface ProgressMonitor { /** Constant indicating the total work units cannot be predicted. */ - public static final int UNKNOWN = 0; + int UNKNOWN = 0; /** * Advise the monitor of the total number of subtasks. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java index b000558944..faabbf892f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java @@ -61,7 +61,7 @@ import org.eclipse.jgit.annotations.Nullable; */ public interface Ref { /** Location where a {@link Ref} is stored. */ - public static enum Storage { + enum Storage { /** * The ref does not exist yet, updating it may create it. * <p> @@ -131,7 +131,7 @@ public interface Ref { * @return name of this ref. */ @NonNull - public String getName(); + String getName(); /** * Test if this reference is a symbolic reference. @@ -144,7 +144,7 @@ public interface Ref { * @return true if this is a symbolic reference; false if this reference * contains its own ObjectId. */ - public abstract boolean isSymbolic(); + boolean isSymbolic(); /** * Traverse target references until {@link #isSymbolic()} is false. @@ -163,7 +163,7 @@ public interface Ref { * @return the reference that actually stores the ObjectId value. */ @NonNull - public abstract Ref getLeaf(); + Ref getLeaf(); /** * Get the reference this reference points to, or {@code this}. @@ -178,7 +178,7 @@ public interface Ref { * @return the target reference, or {@code this}. */ @NonNull - public abstract Ref getTarget(); + Ref getTarget(); /** * Cached value of this ref. @@ -188,7 +188,7 @@ public interface Ref { * symbolic ref pointing to an unborn branch. */ @Nullable - public abstract ObjectId getObjectId(); + ObjectId getObjectId(); /** * Cached value of <code>ref^{}</code> (the ref peeled to commit). @@ -198,14 +198,14 @@ public interface Ref { * does not refer to an annotated tag. */ @Nullable - public abstract ObjectId getPeeledObjectId(); + ObjectId getPeeledObjectId(); /** * Whether the Ref represents a peeled tag. * * @return whether the Ref represents a peeled tag. */ - public abstract boolean isPeeled(); + boolean isPeeled(); /** * How was this ref obtained? @@ -216,5 +216,5 @@ public interface Ref { * @return type of ref. */ @NonNull - public abstract Storage getStorage(); + Storage getStorage(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java index 3170787dd9..68929b4220 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java @@ -415,6 +415,31 @@ public abstract class RefDatabase { } /** + * Returns refs whose names start with one of the given prefixes. + * <p> + * The default implementation uses {@link #getRefsByPrefix(String)}. + * Implementors of {@link RefDatabase} should override this method directly + * if a better implementation is possible. + * + * @param prefixes + * strings that names of refs should start with. + * @return immutable list of refs whose names start with one of + * {@code prefixes}. Refs can be unsorted and may contain duplicates + * if the prefixes overlap. + * @throws java.io.IOException + * the reference space cannot be accessed. + * @since 5.2 + */ + @NonNull + public List<Ref> getRefsByPrefix(String... prefixes) throws IOException { + List<Ref> result = new ArrayList<>(); + for (String prefix : prefixes) { + result.addAll(getRefsByPrefix(prefix)); + } + return Collections.unmodifiableList(result); + } + + /** * Check if any refs exist in the ref database. * <p> * This uses the same definition of refs as {@link #getRefs()}. In diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java index 5cd593ed5e..fc3ea8467a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java @@ -823,7 +823,7 @@ public abstract class RefUpdate { * Handle the abstraction of storing a ref update. This is because both * updating and deleting of a ref have merge testing in common. */ - private abstract class Store { + private static abstract class Store { abstract Result execute(Result status) throws IOException; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java index 51f2ea0ab7..824bbc4201 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogEntry.java @@ -57,7 +57,7 @@ public interface ReflogEntry { * * @since 4.9 */ - public static final String PREFIX_CREATED = "created"; //$NON-NLS-1$ + String PREFIX_CREATED = "created"; //$NON-NLS-1$ /** * Prefix used in reflog messages when the ref was updated with a fast @@ -69,7 +69,7 @@ public interface ReflogEntry { * * @since 4.9 */ - public static final String PREFIX_FAST_FORWARD = "fast-forward"; //$NON-NLS-1$ + String PREFIX_FAST_FORWARD = "fast-forward"; //$NON-NLS-1$ /** * Prefix used in reflog messages when the ref was force updated. @@ -80,35 +80,35 @@ public interface ReflogEntry { * * @since 4.9 */ - public static final String PREFIX_FORCED_UPDATE = "forced-update"; //$NON-NLS-1$ + String PREFIX_FORCED_UPDATE = "forced-update"; //$NON-NLS-1$ /** * Get the commit id before the change * * @return the commit id before the change */ - public abstract ObjectId getOldId(); + ObjectId getOldId(); /** * Get the commit id after the change * * @return the commit id after the change */ - public abstract ObjectId getNewId(); + ObjectId getNewId(); /** * Get user performing the change * * @return user performing the change */ - public abstract PersonIdent getWho(); + PersonIdent getWho(); /** * Get textual description of the change * * @return textual description of the change */ - public abstract String getComment(); + String getComment(); /** * Parse checkout @@ -117,6 +117,6 @@ public interface ReflogEntry { * information about a branch switch, or null if the entry is not a * checkout */ - public abstract CheckoutEntry parseCheckout(); + CheckoutEntry parseCheckout(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java index f97b07e08c..4f104d2d7c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ReflogReader.java @@ -59,7 +59,7 @@ public interface ReflogReader { * @return the latest reflog entry, or null if no log * @throws java.io.IOException */ - public abstract ReflogEntry getLastEntry() throws IOException; + ReflogEntry getLastEntry() throws IOException; /** * Get all reflog entries in reverse order @@ -67,7 +67,7 @@ public interface ReflogReader { * @return all reflog entries in reverse order * @throws java.io.IOException */ - public abstract List<ReflogEntry> getReverseEntries() throws IOException; + List<ReflogEntry> getReverseEntries() throws IOException; /** * Get specific entry in the reflog relative to the last entry which is @@ -77,7 +77,7 @@ public interface ReflogReader { * @return reflog entry or null if not found * @throws java.io.IOException */ - public abstract ReflogEntry getReverseEntry(int number) throws IOException; + ReflogEntry getReverseEntry(int number) throws IOException; /** * Get all reflog entries in reverse order @@ -87,7 +87,5 @@ public interface ReflogReader { * @return all reflog entries in reverse order * @throws java.io.IOException */ - public abstract List<ReflogEntry> getReverseEntries(int max) - throws IOException; - + List<ReflogEntry> getReverseEntries(int max) throws IOException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index d73c05e243..77d268a3bd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -49,6 +49,7 @@ package org.eclipse.jgit.lib; import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX; +import static java.nio.charset.StandardCharsets.UTF_8; import java.io.BufferedOutputStream; import java.io.File; @@ -1965,7 +1966,7 @@ public abstract class Repository implements AutoCloseable { private void writeCommitMsg(File msgFile, String msg) throws IOException { if (msg != null) { try (FileOutputStream fos = new FileOutputStream(msgFile)) { - fos.write(msg.getBytes(Constants.CHARACTER_ENCODING)); + fos.write(msg.getBytes(UTF_8)); } } else { FileUtils.delete(msgFile, FileUtils.SKIP_MISSING); @@ -1980,7 +1981,6 @@ public abstract class Repository implements AutoCloseable { * empty * @throws IOException */ - @Nullable private byte[] readGitDirectoryFile(String filename) throws IOException { File file = new File(getDirectory(), filename); try { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java index 036917e62a..479670873d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.merge; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -63,7 +64,7 @@ public class MergeFormatter { * that are LF-separated lines. * * @param out - * the outputstream where to write the textual presentation + * the output stream where to write the textual presentation * @param res * the merge result which should be presented * @param seqName @@ -72,13 +73,44 @@ public class MergeFormatter { * " or ">>>>>>> " conflict markers. The * names for the sequences are given in this list * @param charsetName - * the name of the characterSet used when writing conflict + * the name of the character set used when writing conflict * metadata * @throws java.io.IOException + * @deprecated Use + * {@link #formatMerge(OutputStream, MergeResult, List, Charset)} + * instead. */ + @Deprecated public void formatMerge(OutputStream out, MergeResult<RawText> res, List<String> seqName, String charsetName) throws IOException { - new MergeFormatterPass(out, res, seqName, charsetName).formatMerge(); + formatMerge(out, res, seqName, Charset.forName(charsetName)); + } + + /** + * Formats the results of a merge of {@link org.eclipse.jgit.diff.RawText} + * objects in a Git conformant way. This method also assumes that the + * {@link org.eclipse.jgit.diff.RawText} objects being merged are line + * oriented files which use LF as delimiter. This method will also use LF to + * separate chunks and conflict metadata, therefore it fits only to texts + * that are LF-separated lines. + * + * @param out + * the output stream where to write the textual presentation + * @param res + * the merge result which should be presented + * @param seqName + * When a conflict is reported each conflicting range will get a + * name. This name is following the "<<<<<<< + * " or ">>>>>>> " conflict markers. The + * names for the sequences are given in this list + * @param charset + * the character set used when writing conflict metadata + * @throws java.io.IOException + * @since 5.2 + */ + public void formatMerge(OutputStream out, MergeResult<RawText> res, + List<String> seqName, Charset charset) throws IOException { + new MergeFormatterPass(out, res, seqName, charset).formatMerge(); } /** @@ -100,17 +132,51 @@ public class MergeFormatter { * @param theirsName * the name ranges from theirs should get * @param charsetName - * the name of the characterSet used when writing conflict + * the name of the character set used when writing conflict * metadata * @throws java.io.IOException + * @deprecated use + * {@link #formatMerge(OutputStream, MergeResult, String, String, String, Charset)} + * instead. */ - @SuppressWarnings("unchecked") + @Deprecated public void formatMerge(OutputStream out, MergeResult res, String baseName, String oursName, String theirsName, String charsetName) throws IOException { + formatMerge(out, res, baseName, oursName, theirsName, + Charset.forName(charsetName)); + } + + /** + * Formats the results of a merge of exactly two + * {@link org.eclipse.jgit.diff.RawText} objects in a Git conformant way. + * This convenience method accepts the names for the three sequences (base + * and the two merged sequences) as explicit parameters and doesn't require + * the caller to specify a List + * + * @param out + * the {@link java.io.OutputStream} where to write the textual + * presentation + * @param res + * the merge result which should be presented + * @param baseName + * the name ranges from the base should get + * @param oursName + * the name ranges from ours should get + * @param theirsName + * the name ranges from theirs should get + * @param charset + * the character set used when writing conflict metadata + * @throws java.io.IOException + * @since 5.2 + */ + @SuppressWarnings("unchecked") + public void formatMerge(OutputStream out, MergeResult res, String baseName, + String oursName, String theirsName, Charset charset) + throws IOException { List<String> names = new ArrayList<>(3); names.add(baseName); names.add(oursName); names.add(theirsName); - formatMerge(out, res, names, charsetName); + formatMerge(out, res, names, charset); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java index 060f06884a..e1a8d3110e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java @@ -46,6 +46,7 @@ package org.eclipse.jgit.merge; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.Charset; import java.util.List; import org.eclipse.jgit.diff.RawText; @@ -59,19 +60,33 @@ class MergeFormatterPass { private final List<String> seqName; - private final String charsetName; + private final Charset charset; private final boolean threeWayMerge; private String lastConflictingName; // is set to non-null whenever we are in // a conflict - MergeFormatterPass(OutputStream out, MergeResult<RawText> res, List<String> seqName, - String charsetName) { + /** + * @param out + * the {@link java.io.OutputStream} where to write the textual + * presentation + * @param res + * the merge result which should be presented + * @param seqName + * When a conflict is reported each conflicting range will get a + * name. This name is following the "<<<<<<< + * " or ">>>>>>> " conflict markers. The + * names for the sequences are given in this list + * @param charset + * the character set used when writing conflict metadata + */ + MergeFormatterPass(OutputStream out, MergeResult<RawText> res, + List<String> seqName, Charset charset) { this.out = new EolAwareOutputStream(out); this.res = res; this.seqName = seqName; - this.charsetName = charsetName; + this.charset = charset; this.threeWayMerge = (res.getSequences().size() == 3); } @@ -133,7 +148,7 @@ class MergeFormatterPass { private void writeln(String s) throws IOException { out.beginln(); - out.write((s + "\n").getBytes(charsetName)); //$NON-NLS-1$ + out.write((s + "\n").getBytes(charset)); //$NON-NLS-1$ } private void writeLine(RawText seq, int i) throws IOException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index f60c95f647..412d9bba73 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -46,10 +46,10 @@ */ package org.eclipse.jgit.merge; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM; -import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import java.io.BufferedOutputStream; @@ -1026,7 +1026,7 @@ public class ResolveMerger extends ThreeWayMerger { db != null ? nonNullRepo().getDirectory() : null, inCoreLimit); try { new MergeFormatter().formatMerge(buf, result, - Arrays.asList(commitNames), CHARACTER_ENCODING); + Arrays.asList(commitNames), UTF_8); buf.close(); } catch (IOException e) { buf.destroy(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AsyncRevObjectQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AsyncRevObjectQueue.java index d263184622..98654f14c2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AsyncRevObjectQueue.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AsyncRevObjectQueue.java @@ -66,5 +66,5 @@ public interface AsyncRevObjectQueue extends AsyncOperation { * @throws java.io.IOException * the object store cannot be accessed. */ - public RevObject next() throws MissingObjectException, IOException; + RevObject next() throws MissingObjectException, IOException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java index eaec305b47..5154920393 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthGenerator.java @@ -48,6 +48,7 @@ import java.io.IOException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.ObjectId; /** * Only produce commits which are below a specified depth. @@ -59,6 +60,8 @@ class DepthGenerator extends Generator { private final int depth; + private final int deepenSince; + private final RevWalk walk; /** @@ -79,6 +82,11 @@ class DepthGenerator extends Generator { private final RevFlag REINTERESTING; /** + * Commits reachable from commits that the client specified using --shallow-exclude. + */ + private final RevFlag DEEPEN_NOT; + + /** * @param w * @param s Parent generator * @throws MissingObjectException @@ -91,8 +99,10 @@ class DepthGenerator extends Generator { walk = (RevWalk)w; this.depth = w.getDepth(); + this.deepenSince = w.getDeepenSince(); this.UNSHALLOW = w.getUnshallowFlag(); this.REINTERESTING = w.getReinterestingFlag(); + this.DEEPEN_NOT = w.getDeepenNotFlag(); s.shareFreeList(pending); @@ -105,6 +115,37 @@ class DepthGenerator extends Generator { if (((DepthWalk.Commit) c).getDepth() == 0) pending.add(c); } + + // Mark DEEPEN_NOT on all deepen-not commits and their ancestors. + // TODO(jonathantanmy): This implementation is somewhat + // inefficient in that any "deepen-not <ref>" in the request + // results in all commits reachable from that ref being parsed + // and marked, even if the commit topology is such that it is + // not necessary. + for (ObjectId oid : w.getDeepenNots()) { + RevCommit c; + try { + c = walk.parseCommit(oid); + } catch (IncorrectObjectTypeException notCommit) { + // The C Git implementation silently tolerates + // non-commits, so do the same here. + continue; + } + + FIFORevQueue queue = new FIFORevQueue(); + queue.add(c); + while ((c = queue.next()) != null) { + if (c.has(DEEPEN_NOT)) { + continue; + } + + walk.parseHeaders(c); + c.add(DEEPEN_NOT); + for (RevCommit p : c.getParents()) { + queue.add(p); + } + } + } } @Override @@ -132,6 +173,14 @@ class DepthGenerator extends Generator { if ((c.flags & RevWalk.PARSED) == 0) c.parseHeaders(walk); + if (c.getCommitTime() < deepenSince) { + continue; + } + + if (c.has(DEEPEN_NOT)) { + continue; + } + int newDepth = c.depth + 1; for (RevCommit p : c.parents) { @@ -142,12 +191,29 @@ class DepthGenerator extends Generator { // this depth is guaranteed to be the smallest value that // any path could produce. if (dp.depth == -1) { + boolean failsDeepenSince = false; + if (deepenSince != 0) { + if ((p.flags & RevWalk.PARSED) == 0) { + p.parseHeaders(walk); + } + failsDeepenSince = + p.getCommitTime() < deepenSince; + } + dp.depth = newDepth; - // If the parent is not too deep, add it to the queue - // so that we can produce it later - if (newDepth <= depth) + // If the parent is not too deep and was not excluded, add + // it to the queue so that we can produce it later + if (newDepth <= depth && !failsDeepenSince && + !p.has(DEEPEN_NOT)) { pending.add(p); + } else { + dp.makesChildBoundary = true; + } + } + + if (dp.makesChildBoundary) { + c.isBoundary = true; } // If the current commit has become unshallowed, everything @@ -160,8 +226,7 @@ class DepthGenerator extends Generator { } } - // Produce all commits less than the depth cutoff - boolean produce = c.depth <= depth; + boolean produce = true; // Unshallow commits are uninteresting, but still need to be sent // up to the PackWriter so that it will exclude objects correctly. @@ -169,6 +234,10 @@ class DepthGenerator extends Generator { if ((c.flags & RevWalk.UNINTERESTING) != 0 && !c.has(UNSHALLOW)) produce = false; + if (c.getCommitTime() < deepenSince) { + produce = false; + } + if (produce) return c; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java index 06a5272b98..0201f0b602 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java @@ -45,10 +45,14 @@ package org.eclipse.jgit.revwalk; import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Objects; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; @@ -61,7 +65,24 @@ public interface DepthWalk { * * @return Depth to filter to. */ - public int getDepth(); + int getDepth(); + + /** + * @return the deepen-since value; if not 0, this walk only returns commits + * whose commit time is at or after this limit + * @since 5.2 + */ + default int getDeepenSince() { + return 0; + } + + /** + * @return the objects specified by the client using --shallow-exclude + * @since 5.2 + */ + default List<ObjectId> getDeepenNots() { + return Collections.emptyList(); + } /** @return flag marking commits that should become unshallow. */ /** @@ -69,26 +90,49 @@ public interface DepthWalk { * * @return flag marking commits that should become unshallow. */ - public RevFlag getUnshallowFlag(); + RevFlag getUnshallowFlag(); /** * Get flag marking commits that are interesting again. * * @return flag marking commits that are interesting again. */ - public RevFlag getReinterestingFlag(); + RevFlag getReinterestingFlag(); + + /** + * @return flag marking commits that are to be excluded because of --shallow-exclude + * @since 5.2 + */ + RevFlag getDeepenNotFlag(); /** RevCommit with a depth (in commits) from a root. */ public static class Commit extends RevCommit { /** Depth of this commit in the graph, via shortest path. */ int depth; + boolean isBoundary; + + /** + * True if this commit was excluded due to a shallow fetch + * setting. All its children are thus boundary commits. + */ + boolean makesChildBoundary; + /** @return depth of this commit, as found by the shortest path. */ public int getDepth() { return depth; } /** + * @return true if at least one of this commit's parents was excluded + * due to a shallow fetch setting, false otherwise + * @since 5.2 + */ + public boolean isBoundary() { + return isBoundary; + } + + /** * Initialize a new commit. * * @param id @@ -104,10 +148,16 @@ public interface DepthWalk { public class RevWalk extends org.eclipse.jgit.revwalk.RevWalk implements DepthWalk { private final int depth; + private int deepenSince; + + private List<ObjectId> deepenNots; + private final RevFlag UNSHALLOW; private final RevFlag REINTERESTING; + private final RevFlag DEEPEN_NOT; + /** * @param repo Repository to walk * @param depth Maximum depth to return @@ -116,8 +166,10 @@ public interface DepthWalk { super(repo); this.depth = depth; + this.deepenNots = Collections.emptyList(); this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$ this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$ + this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$ } /** @@ -128,8 +180,10 @@ public interface DepthWalk { super(or); this.depth = depth; + this.deepenNots = Collections.emptyList(); this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$ this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$ + this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$ } /** @@ -159,6 +213,39 @@ public interface DepthWalk { } @Override + public int getDeepenSince() { + return deepenSince; + } + + /** + * Sets the deepen-since value. + * + * @param limit + * new deepen-since value + * @since 5.2 + */ + public void setDeepenSince(int limit) { + deepenSince = limit; + } + + @Override + public List<ObjectId> getDeepenNots() { + return deepenNots; + } + + /** + * Mark objects that the client specified using + * --shallow-exclude. Objects that are not commits have no + * effect. + * + * @param deepenNots specified objects + * @since 5.2 + */ + public void setDeepenNots(List<ObjectId> deepenNots) { + this.deepenNots = Objects.requireNonNull(deepenNots); + } + + @Override public RevFlag getUnshallowFlag() { return UNSHALLOW; } @@ -168,12 +255,19 @@ public interface DepthWalk { return REINTERESTING; } + @Override + public RevFlag getDeepenNotFlag() { + return DEEPEN_NOT; + } + /** * @since 4.5 */ @Override public ObjectWalk toObjectWalkWithSameObjects() { ObjectWalk ow = new ObjectWalk(reader, depth); + ow.deepenSince = deepenSince; + ow.deepenNots = deepenNots; ow.objects = objects; ow.freeFlags = freeFlags; return ow; @@ -184,10 +278,16 @@ public interface DepthWalk { public class ObjectWalk extends org.eclipse.jgit.revwalk.ObjectWalk implements DepthWalk { private final int depth; + private int deepenSince; + + private List<ObjectId> deepenNots; + private final RevFlag UNSHALLOW; private final RevFlag REINTERESTING; + private final RevFlag DEEPEN_NOT; + /** * @param repo Repository to walk * @param depth Maximum depth to return @@ -196,8 +296,10 @@ public interface DepthWalk { super(repo); this.depth = depth; + this.deepenNots = Collections.emptyList(); this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$ this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$ + this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$ } /** @@ -208,8 +310,10 @@ public interface DepthWalk { super(or); this.depth = depth; + this.deepenNots = Collections.emptyList(); this.UNSHALLOW = newFlag("UNSHALLOW"); //$NON-NLS-1$ this.REINTERESTING = newFlag("REINTERESTING"); //$NON-NLS-1$ + this.DEEPEN_NOT = newFlag("DEEPEN_NOT"); //$NON-NLS-1$ } /** @@ -263,6 +367,16 @@ public interface DepthWalk { } @Override + public int getDeepenSince() { + return deepenSince; + } + + @Override + public List<ObjectId> getDeepenNots() { + return deepenNots; + } + + @Override public RevFlag getUnshallowFlag() { return UNSHALLOW; } @@ -271,5 +385,10 @@ public interface DepthWalk { public RevFlag getReinterestingFlag() { return REINTERESTING; } + + @Override + public RevFlag getDeepenNotFlag() { + return DEEPEN_NOT; + } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java index 86ecd8eaee..af4ec1f00b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java @@ -407,7 +407,7 @@ public class RevCommit extends RevObject { * @return contents of the gpg signature; null if the commit was not signed. * @since 5.1 */ - public final @Nullable byte[] getRawGpgSignature() { + public final byte[] getRawGpgSignature() { final byte[] raw = buffer; final byte[] header = {'g', 'p', 'g', 's', 'i', 'g'}; final int start = RawParseUtils.headerStart(header, raw, 0); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java index 4d555d2178..400ea33c21 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -54,6 +54,7 @@ import java.util.Iterator; import java.util.List; import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.LargeObjectException; @@ -1336,6 +1337,22 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { } /** + * Like {@link #next()}, but if a checked exception is thrown during the + * walk it is rethrown as a {@link RevWalkException}. + * + * @throws RevWalkException if an {@link IOException} was thrown. + * @return next most recent commit; null if traversal is over. + */ + @Nullable + private RevCommit nextForIterator() { + try { + return next(); + } catch (IOException e) { + throw new RevWalkException(e); + } + } + + /** * {@inheritDoc} * <p> * Returns an Iterator over the commits of this walker. @@ -1353,16 +1370,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { */ @Override public Iterator<RevCommit> iterator() { - final RevCommit first; - try { - first = RevWalk.this.next(); - } catch (MissingObjectException e) { - throw new RevWalkException(e); - } catch (IncorrectObjectTypeException e) { - throw new RevWalkException(e); - } catch (IOException e) { - throw new RevWalkException(e); - } + RevCommit first = nextForIterator(); return new Iterator<RevCommit>() { RevCommit next = first; @@ -1374,17 +1382,9 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { @Override public RevCommit next() { - try { - final RevCommit r = next; - next = RevWalk.this.next(); - return r; - } catch (MissingObjectException e) { - throw new RevWalkException(e); - } catch (IncorrectObjectTypeException e) { - throw new RevWalkException(e); - } catch (IOException e) { - throw new RevWalkException(e); - } + RevCommit r = next; + next = nextForIterator(); + return r; } @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java index 93b3baa61f..2b31ebd8e3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java @@ -57,7 +57,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.text.MessageFormat; -import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.LockFailedException; import org.eclipse.jgit.internal.JGitText; @@ -281,7 +280,6 @@ public class FileBasedConfig extends StoredConfig { * @since 4.10 */ @Override - @Nullable protected byte[] readIncludedConfig(String relPath) throws ConfigInvalidException { final File file; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java index ed05c733f3..72b4255df9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java @@ -55,7 +55,7 @@ public interface AdvertiseRefsHook { * {@link UploadPack#setAdvertisedRefs(java.util.Map)} and * {@link BaseReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}. */ - public static final AdvertiseRefsHook DEFAULT = new AdvertiseRefsHook() { + AdvertiseRefsHook DEFAULT = new AdvertiseRefsHook() { @Override public void advertiseRefs(UploadPack uploadPack) { // Do nothing. @@ -77,7 +77,7 @@ public interface AdvertiseRefsHook { * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException * abort; the message will be sent to the user. */ - public void advertiseRefs(UploadPack uploadPack) + void advertiseRefs(UploadPack uploadPack) throws ServiceMayNotContinueException; /** @@ -90,6 +90,6 @@ public interface AdvertiseRefsHook { * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException * abort; the message will be sent to the user. */ - public void advertiseRefs(BaseReceivePack receivePack) + void advertiseRefs(BaseReceivePack receivePack) throws ServiceMayNotContinueException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java index f6ec4b90eb..c5661e5083 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java @@ -542,7 +542,7 @@ public class AmazonS3 { } buf = b.toByteArray(); if (buf.length > 0) { - err.initCause(new IOException("\n" + new String(buf))); //$NON-NLS-1$ + err.initCause(new IOException("\n" + new String(buf, UTF_8))); //$NON-NLS-1$ } } return err; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java index d3419bc201..03763368a8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java @@ -72,12 +72,14 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.errors.InvalidObjectIdException; +import org.eclipse.jgit.errors.LargeObjectException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.errors.TooLargePackException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.file.PackLock; import org.eclipse.jgit.internal.submodule.SubmoduleValidator; +import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.Config; @@ -1528,8 +1530,12 @@ public abstract class BaseReceivePack { AnyObjectId blobId = entry.getBlobId(); ObjectLoader blob = odb.open(blobId, Constants.OBJ_BLOB); - SubmoduleValidator.assertValidGitModulesFile( - new String(blob.getBytes(), UTF_8)); + try { + SubmoduleValidator.assertValidGitModulesFile( + new String(blob.getBytes(), UTF_8)); + } catch (LargeObjectException | SubmoduleValidationException e) { + throw new IOException(e); + } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java index d4c514e636..19a1ab0b93 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java @@ -68,7 +68,7 @@ public interface Connection extends AutoCloseable { * modifiable. The collection can be empty if the remote side has no * refs (it is an empty/newly created repository). */ - public Map<String, Ref> getRefsMap(); + Map<String, Ref> getRefsMap(); /** * Get the complete list of refs advertised as available for fetching or @@ -82,7 +82,7 @@ public interface Connection extends AutoCloseable { * collection can be empty if the remote side has no refs (it is an * empty/newly created repository). */ - public Collection<Ref> getRefs(); + Collection<Ref> getRefs(); /** * Get a single advertised ref by name. @@ -95,7 +95,7 @@ public interface Connection extends AutoCloseable { * name of the ref to obtain. * @return the requested ref; null if the remote did not advertise this ref. */ - public Ref getRef(String name); + Ref getRef(String name); /** * {@inheritDoc} @@ -115,7 +115,7 @@ public interface Connection extends AutoCloseable { * the signature to prevent them from doing so. */ @Override - public void close(); + void close(); /** * Get the additional messages, if any, returned by the remote process. @@ -132,7 +132,7 @@ public interface Connection extends AutoCloseable { * newline (LF) character. The empty string is returned if the * remote produced no additional messages. */ - public String getMessages(); + String getMessages(); /** * User agent advertised by the remote server. @@ -141,5 +141,5 @@ public interface Connection extends AutoCloseable { * server does not advertise this version. * @since 4.0 */ - public String getPeerUserAgent(); + String getPeerUserAgent(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java index f0c45d5fb6..1eb7cbd93a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchConnection.java @@ -109,7 +109,7 @@ public interface FetchConnection extends Connection { * protocol error, or error on remote side, or connection was * already used for fetch. */ - public void fetch(final ProgressMonitor monitor, + void fetch(final ProgressMonitor monitor, final Collection<Ref> want, final Set<ObjectId> have) throws TransportException; @@ -151,7 +151,7 @@ public interface FetchConnection extends Connection { * already used for fetch. * @since 3.0 */ - public void fetch(final ProgressMonitor monitor, + void fetch(final ProgressMonitor monitor, final Collection<Ref> want, final Set<ObjectId> have, OutputStream out) throws TransportException; @@ -173,7 +173,7 @@ public interface FetchConnection extends Connection { * @return true if the last fetch call implicitly included tag objects; * false if tags were not implicitly obtained. */ - public boolean didFetchIncludeTags(); + boolean didFetchIncludeTags(); /** * Did the last {@link #fetch(ProgressMonitor, Collection, Set)} validate @@ -196,7 +196,7 @@ public interface FetchConnection extends Connection { * client side in order to succeed; false if the last fetch assumed * the remote peer supplied a complete graph. */ - public boolean didFetchTestConnectivity(); + boolean didFetchTestConnectivity(); /** * Set the lock message used when holding a pack out of garbage collection. @@ -208,7 +208,7 @@ public interface FetchConnection extends Connection { * * @param message message to use when holding a pack in place. */ - public void setPackLockMessage(String message); + void setPackLockMessage(String message); /** * All locks created by the last @@ -218,5 +218,5 @@ public interface FetchConnection extends Connection { * fetch. The caller must release these after refs are updated in * order to safely permit garbage collection. */ - public Collection<PackLock> getPackLocks(); + Collection<PackLock> getPackLocks(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java index c43ab18c35..211707e9ad 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java @@ -44,6 +44,7 @@ package org.eclipse.jgit.transport; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED; import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK; import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD; @@ -337,7 +338,7 @@ class FetchProcess { try { if (lock.lock()) { try (Writer w = new OutputStreamWriter( - lock.getOutputStream())) { + lock.getOutputStream(), UTF_8)) { for (FetchHeadRecord h : fetchHeadUpdates) { h.write(w); result.add(h); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java new file mode 100644 index 0000000000..40ba3a3ad2 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2018, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport; + +import static java.util.Objects.requireNonNull; + +import java.util.List; +import java.util.Set; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.lib.ObjectId; + +/** + * Common fields between v0/v1/v2 fetch requests. + */ +abstract class FetchRequest { + + final Set<ObjectId> wantIds; + + final int depth; + + final Set<ObjectId> clientShallowCommits; + + final long filterBlobLimit; + + final Set<String> clientCapabilities; + + final int deepenSince; + + final List<String> deepenNotRefs; + + @Nullable + final String agent; + + /** + * Initialize the common fields of a fetch request. + * + * @param wantIds + * list of want ids + * @param depth + * how deep to go in the tree + * @param clientShallowCommits + * commits the client has without history + * @param filterBlobLimit + * to exclude blobs on certain conditions + * @param clientCapabilities + * capabilities sent in the request + * @param deepenNotRefs + * Requests that the shallow clone/fetch should be cut at these + * specific revisions instead of a depth. + * @param deepenSince + * Requests that the shallow clone/fetch should be cut at a + * specific time, instead of depth + * @param agent + * agent as reported by the client in the request body + */ + FetchRequest(@NonNull Set<ObjectId> wantIds, int depth, + @NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit, + @NonNull Set<String> clientCapabilities, int deepenSince, + @NonNull List<String> deepenNotRefs, @Nullable String agent) { + this.wantIds = requireNonNull(wantIds); + this.depth = depth; + this.clientShallowCommits = requireNonNull(clientShallowCommits); + this.filterBlobLimit = filterBlobLimit; + this.clientCapabilities = requireNonNull(clientCapabilities); + this.deepenSince = deepenSince; + this.deepenNotRefs = requireNonNull(deepenNotRefs); + this.agent = agent; + } + + /** + * @return object ids in the "want" (and "want-ref") lines of the request + */ + @NonNull + Set<ObjectId> getWantIds() { + return wantIds; + } + + /** + * @return the depth set in a "deepen" line. 0 by default. + */ + int getDepth() { + return depth; + } + + /** + * Shallow commits the client already has. + * + * These are sent by the client in "shallow" request lines. + * + * @return set of commits the client has declared as shallow. + */ + @NonNull + Set<ObjectId> getClientShallowCommits() { + return clientShallowCommits; + } + + /** + * @return the blob limit set in a "filter" line (-1 if not set) + */ + long getFilterBlobLimit() { + return filterBlobLimit; + } + + /** + * Capabilities that the client wants enabled from the server. + * + * Capabilities are options that tune the expected response from the server, + * like "thin-pack", "no-progress" or "ofs-delta". This list should be a + * subset of the capabilities announced by the server in its first response. + * + * These options are listed and well-defined in the git protocol + * specification. + * + * The agent capability is not included in this set. It can be retrieved via + * {@link #getAgent()}. + * + * @return capabilities sent by the client (excluding the "agent" + * capability) + */ + @NonNull + Set<String> getClientCapabilities() { + return clientCapabilities; + } + + /** + * The value in a "deepen-since" line in the request, indicating the + * timestamp where to stop fetching/cloning. + * + * @return timestamp in seconds since the epoch, where to stop the shallow + * fetch/clone. Defaults to 0 if not set in the request. + */ + int getDeepenSince() { + return deepenSince; + } + + /** + * @return refs received in "deepen-not" lines. + */ + @NonNull + List<String> getDeepenNotRefs() { + return deepenNotRefs; + } + + /** + * @return string identifying the agent (as sent in the request body by the + * client) + */ + @Nullable + String getAgent() { + return agent; + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java new file mode 100644 index 0000000000..05f4a8155f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2018, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.lib.ObjectId; + +/** + * Fetch request in the V0/V1 protocol. + */ +final class FetchV0Request extends FetchRequest { + + FetchV0Request(@NonNull Set<ObjectId> wantIds, int depth, + @NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit, + @NonNull Set<String> clientCapabilities, @Nullable String agent) { + super(wantIds, depth, clientShallowCommits, filterBlobLimit, + clientCapabilities, 0, Collections.emptyList(), agent); + } + + static final class Builder { + + int depth; + + final Set<ObjectId> wantIds = new HashSet<>(); + + final Set<ObjectId> clientShallowCommits = new HashSet<>(); + + long filterBlobLimit = -1; + + final Set<String> clientCaps = new HashSet<>(); + + String agent; + + /** + * @param objectId + * object id received in a "want" line + * @return this builder + */ + Builder addWantId(ObjectId objectId) { + wantIds.add(objectId); + return this; + } + + /** + * @param d + * depth set in a "deepen" line + * @return this builder + */ + Builder setDepth(int d) { + depth = d; + return this; + } + + /** + * @param shallowOid + * object id received in a "shallow" line + * @return this builder + */ + Builder addClientShallowCommit(ObjectId shallowOid) { + clientShallowCommits.add(shallowOid); + return this; + } + + /** + * @param clientCapabilities + * client capabilities sent by the client in the first want + * line of the request + * @return this builder + */ + Builder addClientCapabilities(Collection<String> clientCapabilities) { + clientCaps.addAll(clientCapabilities); + return this; + } + + /** + * @param clientAgent + * agent line sent by the client in the request body + * @return this builder + */ + Builder setAgent(String clientAgent) { + agent = clientAgent; + return this; + } + + /** + * @param filterBlobLim + * blob limit set in a "filter" line + * @return this builder + */ + Builder setFilterBlobLimit(long filterBlobLim) { + filterBlobLimit = filterBlobLim; + return this; + } + + FetchV0Request build() { + return new FetchV0Request(wantIds, depth, clientShallowCommits, + filterBlobLimit, clientCaps, agent); + } + + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java index 34f3484951..ac6361cdeb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java @@ -42,70 +42,62 @@ */ package org.eclipse.jgit.transport; +import static java.util.Objects.requireNonNull; + import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.lib.ObjectId; /** - * fetch protocol v2 request. + * Fetch request from git protocol v2. * * <p> * This is used as an input to {@link ProtocolV2Hook}. * * @since 5.1 */ -public final class FetchV2Request { +public final class FetchV2Request extends FetchRequest { private final List<ObjectId> peerHas; private final List<String> wantedRefs; - private final Set<ObjectId> wantsIds; - - private final Set<ObjectId> clientShallowCommits; - - private final int deepenSince; - - private final List<String> deepenNotRefs; - - private final int depth; - - private final long filterBlobLimit; - - private final Set<String> options; - private final boolean doneReceived; - private FetchV2Request(List<ObjectId> peerHas, - List<String> wantedRefs, Set<ObjectId> wantsIds, - Set<ObjectId> clientShallowCommits, int deepenSince, - List<String> deepenNotRefs, int depth, long filterBlobLimit, - boolean doneReceived, Set<String> options) { - this.peerHas = peerHas; - this.wantedRefs = wantedRefs; - this.wantsIds = wantsIds; - this.clientShallowCommits = clientShallowCommits; - this.deepenSince = deepenSince; - this.deepenNotRefs = deepenNotRefs; - this.depth = depth; - this.filterBlobLimit = filterBlobLimit; + @NonNull + private final List<String> serverOptions; + + FetchV2Request(@NonNull List<ObjectId> peerHas, + @NonNull List<String> wantedRefs, + @NonNull Set<ObjectId> wantIds, + @NonNull Set<ObjectId> clientShallowCommits, int deepenSince, + @NonNull List<String> deepenNotRefs, int depth, + long filterBlobLimit, + boolean doneReceived, @NonNull Set<String> clientCapabilities, + @Nullable String agent, @NonNull List<String> serverOptions) { + super(wantIds, depth, clientShallowCommits, filterBlobLimit, + clientCapabilities, deepenSince, deepenNotRefs, agent); + this.peerHas = requireNonNull(peerHas); + this.wantedRefs = requireNonNull(wantedRefs); this.doneReceived = doneReceived; - this.options = options; + this.serverOptions = requireNonNull(serverOptions); } /** - * @return object ids in the "have" lines of the request + * @return object ids received in the "have" lines */ @NonNull List<ObjectId> getPeerHas() { - return this.peerHas; + return peerHas; } /** - * @return list of references in the "want-ref" lines of the request + * @return list of references received in "want-ref" lines */ @NonNull List<String> getWantedRefs() { @@ -113,59 +105,6 @@ public final class FetchV2Request { } /** - * @return object ids in the "want" (but not "want-ref") lines of the request - */ - @NonNull - Set<ObjectId> getWantsIds() { - return wantsIds; - } - - /** - * Shallow commits the client already has. - * - * These are sent by the client in "shallow" request lines. - * - * @return set of commits the client has declared as shallow. - */ - @NonNull - Set<ObjectId> getClientShallowCommits() { - return clientShallowCommits; - } - - /** - * The value in a "deepen-since" line in the request, indicating the - * timestamp where to stop fetching/cloning. - * - * @return timestamp in seconds since the epoch, where to stop the shallow - * fetch/clone. Defaults to 0 if not set in the request. - */ - int getDeepenSince() { - return deepenSince; - } - - /** - * @return the refs in "deepen-not" lines in the request. - */ - @NonNull - List<String> getDeepenNotRefs() { - return deepenNotRefs; - } - - /** - * @return the depth set in a "deepen" line. 0 by default. - */ - int getDepth() { - return depth; - } - - /** - * @return the blob limit set in a "filter" line (-1 if not set) - */ - long getFilterBlobLimit() { - return filterBlobLimit; - } - - /** * @return true if the request had a "done" line */ boolean wasDoneReceived() { @@ -173,17 +112,16 @@ public final class FetchV2Request { } /** - * Options that tune the expected response from the server, like - * "thin-pack", "no-progress" or "ofs-delta" + * Options received in server-option lines. The caller can choose to act on + * these in an application-specific way * - * These are options listed and well-defined in the git protocol - * specification + * @return Immutable list of server options received in the request * - * @return options found in the request lines + * @since 5.2 */ @NonNull - Set<String> getOptions() { - return options; + public List<String> getServerOptions() { + return serverOptions; } /** @return A builder of {@link FetchV2Request}. */ @@ -191,20 +129,19 @@ public final class FetchV2Request { return new Builder(); } - /** A builder for {@link FetchV2Request}. */ static final class Builder { - List<ObjectId> peerHas = new ArrayList<>(); + final List<ObjectId> peerHas = new ArrayList<>(); - List<String> wantedRefs = new ArrayList<>(); + final List<String> wantedRefs = new ArrayList<>(); - Set<ObjectId> wantsIds = new HashSet<>(); + final Set<ObjectId> wantIds = new HashSet<>(); - Set<ObjectId> clientShallowCommits = new HashSet<>(); + final Set<ObjectId> clientShallowCommits = new HashSet<>(); - List<String> deepenNotRefs = new ArrayList<>(); + final List<String> deepenNotRefs = new ArrayList<>(); - Set<String> options = new HashSet<>(); + final Set<String> clientCapabilities = new HashSet<>(); int depth; @@ -214,13 +151,18 @@ public final class FetchV2Request { boolean doneReceived; + @Nullable + String agent; + + final List<String> serverOptions = new ArrayList<>(); + private Builder() { } /** * @param objectId - * from a "have" line in a fetch request - * @return the builder + * object id received in a "have" line + * @return this builder */ Builder addPeerHas(ObjectId objectId) { peerHas.add(objectId); @@ -228,11 +170,11 @@ public final class FetchV2Request { } /** - * From a "want-ref" line in a fetch request + * Ref received in "want-ref" line and the object-id it refers to * * @param refName * reference name - * @return the builder + * @return this builder */ Builder addWantedRef(String refName) { wantedRefs.add(refName); @@ -240,42 +182,42 @@ public final class FetchV2Request { } /** - * @param option - * fetch request lines acting as options - * @return the builder + * @param clientCapability + * capability line sent by the client + * @return this builder */ - Builder addOption(String option) { - options.add(option); + Builder addClientCapability(String clientCapability) { + clientCapabilities.add(clientCapability); return this; } /** - * @param objectId - * from a "want" line in a fetch request - * @return the builder + * @param wantId + * object id received in a "want" line + * @return this builder */ - Builder addWantsId(ObjectId objectId) { - wantsIds.add(objectId); + Builder addWantId(ObjectId wantId) { + wantIds.add(wantId); return this; } /** * @param shallowOid - * from a "shallow" line in the fetch request - * @return the builder + * object id received in a "shallow" line + * @return this builder */ Builder addClientShallowCommit(ObjectId shallowOid) { - this.clientShallowCommits.add(shallowOid); + clientShallowCommits.add(shallowOid); return this; } /** * @param d - * from a "deepen" line in the fetch request - * @return the builder + * Depth received in a "deepen" line + * @return this builder */ Builder setDepth(int d) { - this.depth = d; + depth = d; return this; } @@ -284,32 +226,34 @@ public final class FetchV2Request { * 0 if not set. */ int getDepth() { - return this.depth; + return depth; } /** - * @return if there has been any "deepen not" line in the request + * @return true if there has been at least one "deepen not" line in the + * request so far */ boolean hasDeepenNotRefs() { return !deepenNotRefs.isEmpty(); } /** - * @param deepenNotRef reference in a "deepen not" line - * @return the builder + * @param deepenNotRef + * reference received in a "deepen not" line + * @return this builder */ Builder addDeepenNotRef(String deepenNotRef) { - this.deepenNotRefs.add(deepenNotRef); + deepenNotRefs.add(deepenNotRef); return this; } /** * @param value * Unix timestamp received in a "deepen since" line - * @return the builder + * @return this builder */ Builder setDeepenSince(int value) { - this.deepenSince = value; + deepenSince = value; return this; } @@ -318,35 +262,66 @@ public final class FetchV2Request { * by default. */ int getDeepenSince() { - return this.deepenSince; + return deepenSince; } /** - * @param filterBlobLimit + * @param filterBlobLim * set in a "filter" line - * @return the builder + * @return this builder */ - Builder setFilterBlobLimit(long filterBlobLimit) { - this.filterBlobLimit = filterBlobLimit; + Builder setFilterBlobLimit(long filterBlobLim) { + filterBlobLimit = filterBlobLim; return this; } /** * Mark that the "done" line has been received. * - * @return the builder + * @return this builder */ Builder setDoneReceived() { - this.doneReceived = true; + doneReceived = true; return this; } + + /** + * Value of an agent line received after the command and before the + * arguments. E.g. "agent=a.b.c/1.0" should set "a.b.c/1.0". + * + * @param agentValue + * the client-supplied agent capability, without the leading + * "agent=" + * @return this builder + */ + Builder setAgent(@Nullable String agentValue) { + agent = agentValue; + return this; + } + + /** + * Records an application-specific option supplied in a server-option + * line, for later retrieval with + * {@link FetchV2Request#getServerOptions}. + * + * @param value + * the client-supplied server-option capability, without + * leading "server-option=". + * @return this builder + */ + Builder addServerOption(@NonNull String value) { + serverOptions.add(value); + return this; + } + /** * @return Initialized fetch request */ FetchV2Request build() { - return new FetchV2Request(peerHas, wantedRefs, wantsIds, + return new FetchV2Request(peerHas, wantedRefs, wantIds, clientShallowCommits, deepenSince, deepenNotRefs, - depth, filterBlobLimit, doneReceived, options); + depth, filterBlobLimit, doneReceived, clientCapabilities, + agent, Collections.unmodifiableList(serverOptions)); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java new file mode 100644 index 0000000000..39e87b246c --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FtpChannel.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import java.util.concurrent.TimeUnit; + +/** + * An interface providing FTP operations over a {@link RemoteSession}. All + * operations are supposed to throw {@link FtpException} for remote file system + * errors and other IOExceptions on connection errors. + * + * @since 5.2 + */ +public interface FtpChannel { + + /** + * An {@link Exception} for reporting SFTP errors. + */ + static class FtpException extends IOException { + + private static final long serialVersionUID = 7176525179280330876L; + + public static final int OK = 0; + + public static final int EOF = 1; + + public static final int NO_SUCH_FILE = 2; + + public static final int NO_PERMISSION = 3; + + public static final int UNSPECIFIED_FAILURE = 4; + + public static final int PROTOCOL_ERROR = 5; + + public static final int UNSUPPORTED = 8; + + private final int status; + + public FtpException(String message, int status) { + super(message); + this.status = status; + } + + public FtpException(String message, int status, Throwable cause) { + super(message, cause); + this.status = status; + } + + public int getStatus() { + return status; + } + } + + /** + * Connects the {@link FtpChannel} to the remote end. + * + * @param timeout + * for establishing the FTP connection + * @param unit + * of the {@code timeout} + * @throws IOException + */ + void connect(int timeout, TimeUnit unit) throws IOException; + + /** + * Disconnects and {@link FtpChannel}. + */ + void disconnect(); + + /** + * @return whether the {@link FtpChannel} is connected + */ + boolean isConnected(); + + /** + * Changes the current remote directory. + * + * @param path + * target directory + * @throws IOException + * if the operation could not be performed remotely + */ + void cd(String path) throws IOException; + + /** + * @return the current remote directory path + * @throws IOException + */ + String pwd() throws IOException; + + /** + * Simplified remote directory entry. + */ + interface DirEntry { + String getFilename(); + + long getModifiedTime(); + + boolean isDirectory(); + } + + /** + * Lists contents of a remote directory + * + * @param path + * of the directory to list + * @return the directory entries + * @throws IOException + */ + Collection<DirEntry> ls(String path) throws IOException; + + /** + * Deletes a directory on the remote file system. The directory must be + * empty. + * + * @param path + * to delete + * @throws IOException + */ + void rmdir(String path) throws IOException; + + /** + * Creates a directory on the remote file system. + * + * @param path + * to create + * @throws IOException + */ + void mkdir(String path) throws IOException; + + /** + * Obtain an {@link InputStream} to read the contents of a remote file. + * + * @param path + * of the file to read + * + * @return the stream to read from + * @throws IOException + */ + InputStream get(String path) throws IOException; + + /** + * Obtain an {@link OutputStream} to write to a remote file. If the file + * exists already, it will be overwritten. + * + * @param path + * of the file to read + * + * @return the stream to read from + * @throws IOException + */ + OutputStream put(String path) throws IOException; + + /** + * Deletes a file on the remote file system. + * + * @param path + * to delete + * @throws IOException + * if the file does not exist or could otherwise not be deleted + */ + void rm(String path) throws IOException; + + /** + * Deletes a file on the remote file system. If the file does not exist, no + * exception is thrown. + * + * @param path + * to delete + * @throws IOException + * if the file exist but could not be deleted + */ + default void delete(String path) throws IOException { + try { + rm(path); + } catch (FileNotFoundException e) { + // Ignore; it's OK if the file doesn't exist + } catch (FtpException f) { + if (f.getStatus() == FtpException.NO_SUCH_FILE) { + return; + } + throw f; + } + } + + /** + * Renames a file on the remote file system. If {@code to} exists, it is + * replaced by {@code from}. (POSIX rename() semantics) + * + * @param from + * original name of the file + * @param to + * new name of the file + * @throws IOException + * @see <a href= + * "http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html">stdio.h: + * rename()</a> + */ + void rename(String from, String to) throws IOException; + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java index 760ac6c1d7..1561c93b95 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java @@ -245,6 +245,20 @@ public final class GitProtocolConstants { public static final String CAPABILITY_REF_IN_WANT = "ref-in-want"; //$NON-NLS-1$ /** + * The server supports arbitrary options + * + * @since 5.2 + */ + public static final String CAPABILITY_SERVER_OPTION = "server-option"; //$NON-NLS-1$ + + /** + * Option for passing application-specific options to the server. + * + * @since 5.2 + */ + public static final String OPTION_SERVER_OPTION = "server-option"; //$NON-NLS-1$ + + /** * The server supports listing refs using protocol v2. * * @since 5.0 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java index 732be63dc1..f05e0b8c7d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java @@ -46,6 +46,7 @@ package org.eclipse.jgit.transport; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import java.io.UncheckedIOException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.JGitText; @@ -103,10 +104,13 @@ class InternalPushConnection<C> extends BasePackPushConnection { // Ignored. Client cannot use this repository. } catch (ServiceNotAuthorizedException e) { // Ignored. Client cannot use this repository. - } catch (IOException err) { - // Client side of the pipes should report the problem. - } catch (RuntimeException err) { - // Clients side will notice we went away, and report. + } catch (IOException e) { + // Since the InternalPushConnection + // is used in tests, we want to avoid hiding exceptions + // because they can point to programming errors on the server + // side. By rethrowing, the default handler will dump it + // to stderr. + throw new UncheckedIOException(e); } finally { try { out_r.close(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java index 4e712a5567..0bdd6ba812 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java @@ -52,7 +52,6 @@ package org.eclipse.jgit.transport; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; -import static org.eclipse.jgit.transport.OpenSshConfig.SSH_PORT; import java.io.File; import java.io.FileInputStream; @@ -275,10 +274,11 @@ public abstract class JschConfigSessionFactory extends SshSessionFactory { } private static String hostName(Session s) { - if (s.getPort() == SSH_PORT) { + if (s.getPort() == SshConstants.SSH_DEFAULT_PORT) { return s.getHost(); } - return String.format("[%s]:%d", s.getHost(), s.getPort()); //$NON-NLS-1$ + return String.format("[%s]:%d", s.getHost(), //$NON-NLS-1$ + Integer.valueOf(s.getPort())); } private void copyConfigValueToSession(Session session, Config cfg, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java index e3ef832343..843b90c951 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java @@ -52,6 +52,11 @@ import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.JGitText; @@ -59,8 +64,10 @@ import org.eclipse.jgit.util.io.IsolatedOutputStream; import com.jcraft.jsch.Channel; import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpException; /** * Run remote commands using Jsch. @@ -109,12 +116,24 @@ public class JschSession implements RemoteSession { * @return a channel suitable for Sftp operations. * @throws com.jcraft.jsch.JSchException * on problems getting the channel. + * @deprecated since 5.2; use {@link #getFtpChannel()} instead */ + @Deprecated public Channel getSftpChannel() throws JSchException { return sock.openChannel("sftp"); //$NON-NLS-1$ } /** + * {@inheritDoc} + * + * @since 5.2 + */ + @Override + public FtpChannel getFtpChannel() { + return new JschFtpChannel(); + } + + /** * Implementation of Process for running a single command using Jsch. * <p> * Uses the Jsch session to do actual command execution and manage the @@ -233,4 +252,154 @@ public class JschSession implements RemoteSession { return exitValue(); } } + + private class JschFtpChannel implements FtpChannel { + + private ChannelSftp ftp; + + @Override + public void connect(int timeout, TimeUnit unit) throws IOException { + try { + ftp = (ChannelSftp) sock.openChannel("sftp"); //$NON-NLS-1$ + ftp.connect((int) unit.toMillis(timeout)); + } catch (JSchException e) { + ftp = null; + throw new IOException(e.getLocalizedMessage(), e); + } + } + + @Override + public void disconnect() { + ftp.disconnect(); + ftp = null; + } + + private <T> T map(Callable<T> op) throws IOException { + try { + return op.call(); + } catch (Exception e) { + if (e instanceof SftpException) { + throw new FtpChannel.FtpException(e.getLocalizedMessage(), + ((SftpException) e).id, e); + } + throw new IOException(e.getLocalizedMessage(), e); + } + } + + @Override + public boolean isConnected() { + return ftp != null && sock.isConnected(); + } + + @Override + public void cd(String path) throws IOException { + map(() -> { + ftp.cd(path); + return null; + }); + } + + @Override + public String pwd() throws IOException { + return map(() -> ftp.pwd()); + } + + @Override + public Collection<DirEntry> ls(String path) throws IOException { + return map(() -> { + List<DirEntry> result = new ArrayList<>(); + for (Object e : ftp.ls(path)) { + ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) e; + result.add(new DirEntry() { + + @Override + public String getFilename() { + return entry.getFilename(); + } + + @Override + public long getModifiedTime() { + return entry.getAttrs().getMTime(); + } + + @Override + public boolean isDirectory() { + return entry.getAttrs().isDir(); + } + }); + } + return result; + }); + } + + @Override + public void rmdir(String path) throws IOException { + map(() -> { + ftp.rm(path); + return null; + }); + } + + @Override + public void mkdir(String path) throws IOException { + map(() -> { + ftp.mkdir(path); + return null; + }); + } + + @Override + public InputStream get(String path) throws IOException { + return map(() -> ftp.get(path)); + } + + @Override + public OutputStream put(String path) throws IOException { + return map(() -> ftp.put(path)); + } + + @Override + public void rm(String path) throws IOException { + map(() -> { + ftp.rm(path); + return null; + }); + } + + @Override + public void rename(String from, String to) throws IOException { + map(() -> { + // Plain FTP rename will fail if "to" exists. Jsch knows about + // the FTP extension "posix-rename@openssh.com", which will + // remove "to" first if it exists. + if (hasPosixRename()) { + ftp.rename(from, to); + } else if (!to.equals(from)) { + // Try to remove "to" first. With git, we typically get this + // when a lock file is moved over the file locked. Note that + // the check for to being equal to from may still fail in + // the general case, but for use with JGit's TransportSftp + // it should be good enough. + delete(to); + ftp.rename(from, to); + } + return null; + }); + } + + /** + * Determine whether the server has the posix-rename extension. + * + * @return {@code true} if it is supported, {@code false} otherwise + * @see <a href= + * "https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL?annotate=HEAD">OpenSSH + * deviations and extensions to the published SSH protocol</a> + * @see <a href= + * "http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html">stdio.h: + * rename()</a> + */ + private boolean hasPosixRename() { + return "1".equals(ftp.getExtension("posix-rename@openssh.com")); //$NON-NLS-1$//$NON-NLS-2$ + } + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java index 3aff584a00..add373147c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java @@ -42,9 +42,15 @@ */ package org.eclipse.jgit.transport; +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; + /** * ls-refs protocol v2 request. * @@ -60,11 +66,20 @@ public final class LsRefsV2Request { private final boolean peel; + @Nullable + private final String agent; + + @NonNull + private final List<String> serverOptions; + private LsRefsV2Request(List<String> refPrefixes, boolean symrefs, - boolean peel) { + boolean peel, @Nullable String agent, + @NonNull List<String> serverOptions) { this.refPrefixes = refPrefixes; this.symrefs = symrefs; this.peel = peel; + this.agent = agent; + this.serverOptions = requireNonNull(serverOptions); } /** @return ref prefixes that the client requested. */ @@ -82,6 +97,34 @@ public final class LsRefsV2Request { return peel; } + /** + * @return agent as reported by the client + * + * @since 5.2 + */ + @Nullable + public String getAgent() { + return agent; + } + + /** + * Get application-specific options provided by the client using + * --server-option. + * <p> + * It returns just the content, without the "server-option=" prefix. E.g. a + * request with server-option=A and server-option=B lines returns the list + * [A, B]. + * + * @return application-specific options from the client as an unmodifiable + * list + * + * @since 5.2 + */ + @NonNull + public List<String> getServerOptions() { + return serverOptions; + } + /** @return A builder of {@link LsRefsV2Request}. */ public static Builder builder() { return new Builder(); @@ -95,6 +138,10 @@ public final class LsRefsV2Request { private boolean peel; + private final List<String> serverOptions = new ArrayList<>(); + + private String agent; + private Builder() { } @@ -125,10 +172,43 @@ public final class LsRefsV2Request { return this; } + /** + * Records an application-specific option supplied in a server-option + * line, for later retrieval with + * {@link LsRefsV2Request#getServerOptions}. + * + * @param value + * the client-supplied server-option capability, without + * leading "server-option=". + * @return this builder + * @since 5.2 + */ + public Builder addServerOption(@NonNull String value) { + serverOptions.add(value); + return this; + } + + /** + * Value of an agent line received after the command and before the + * arguments. E.g. "agent=a.b.c/1.0" should set "a.b.c/1.0". + * + * @param value + * the client-supplied agent capability, without leading + * "agent=" + * @return this builder + * + * @since 5.2 + */ + public Builder setAgent(@Nullable String value) { + agent = value; + return this; + } + /** @return LsRefsV2Request */ public LsRefsV2Request build() { return new LsRefsV2Request( - Collections.unmodifiableList(refPrefixes), symrefs, peel); + Collections.unmodifiableList(refPrefixes), symrefs, peel, + agent, Collections.unmodifiableList(serverOptions)); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java index e688f6340d..8562376aa9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java @@ -42,10 +42,13 @@ package org.eclipse.jgit.transport; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.util.Collection; import java.util.HashMap; import java.util.Locale; @@ -211,7 +214,8 @@ public class NetRC { this.hosts.clear(); this.lastModified = this.netrc.lastModified(); - try (BufferedReader r = new BufferedReader(new FileReader(netrc))) { + try (BufferedReader r = new BufferedReader( + new InputStreamReader(new FileInputStream(netrc), UTF_8))) { String line = null; NetRCEntry entry = new NetRCEntry(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java index 51fe9070c0..fc22034340 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java @@ -66,7 +66,7 @@ public interface NonceGenerator { * @return The nonce to be signed by the pusher * @throws java.lang.IllegalStateException */ - public String createNonce(Repository db, long timestamp) + String createNonce(Repository db, long timestamp) throws IllegalStateException; /** @@ -91,6 +91,6 @@ public interface NonceGenerator { * @return a NonceStatus indicating the trustworthiness of the received * nonce. */ - public NonceStatus verify(String received, String sent, + NonceStatus verify(String received, String sent, Repository db, boolean allowSlop, int slop); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java index 480055c2e4..32e1dff234 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2017, Google Inc. + * Copyright (C) 2008, 2018, Google Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -43,29 +43,16 @@ package org.eclipse.jgit.transport; -import java.io.BufferedReader; +import static org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.positive; + import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Set; +import java.util.TreeMap; -import org.eclipse.jgit.errors.InvalidPatternException; -import org.eclipse.jgit.fnmatch.FileNameMatcher; -import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile; +import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile.HostEntry; import org.eclipse.jgit.util.FS; -import org.eclipse.jgit.util.StringUtils; -import org.eclipse.jgit.util.SystemReader; import com.jcraft.jsch.ConfigRepository; @@ -83,8 +70,7 @@ import com.jcraft.jsch.ConfigRepository; * <li>JSch's OpenSSHConfig doesn't monitor for config file changes. * </ul> * <p> - * Therefore implement our own parser to read an OpenSSH configuration file. It - * makes the critical options available to + * This parser makes the critical options available to * {@link org.eclipse.jgit.transport.SshSessionFactory} via * {@link org.eclipse.jgit.transport.OpenSshConfig.Host} objects returned by * {@link #lookup(String)}, and implements a fully conforming @@ -92,49 +78,11 @@ import com.jcraft.jsch.ConfigRepository; * {@link com.jcraft.jsch.ConfigRepository.Config}s via * {@link #getConfig(String)}. * </p> - * <p> - * Limitations compared to the full OpenSSH 7.5 parser: - * </p> - * <ul> - * <li>This parser does not handle Match or Include keywords. - * <li>This parser does not do host name canonicalization (Jsch ignores it - * anyway). - * </ul> - * <p> - * Note that OpenSSH's readconf.c is a validating parser; Jsch's - * ConfigRepository OTOH treats all option values as plain strings, so any - * validation must happen in Jsch outside of the parser. Thus this parser does - * not validate option values, except for a few options when constructing a - * {@link org.eclipse.jgit.transport.OpenSshConfig.Host} object. - * </p> - * <p> - * This config does %-substitutions for the following tokens: - * </p> - * <ul> - * <li>%% - single % - * <li>%C - short-hand for %l%h%p%r. See %p and %r below; the replacement may be - * done partially only and may leave %p or %r or both unreplaced. - * <li>%d - home directory path - * <li>%h - remote host name - * <li>%L - local host name without domain - * <li>%l - FQDN of the local host - * <li>%n - host name as specified in {@link #lookup(String)} - * <li>%p - port number; replaced only if set in the config - * <li>%r - remote user name; replaced only if set in the config - * <li>%u - local user name - * </ul> - * <p> - * If the config doesn't set the port or the remote user name, %p and %r remain - * un-substituted. It's the caller's responsibility to replace them with values - * obtained from the connection URI. %i is not handled; Java has no concept of a - * "user ID". - * </p> + * + * @see OpenSshConfigFile */ public class OpenSshConfig implements ConfigRepository { - /** IANA assigned port number for SSH. */ - static final int SSH_PORT = 22; - /** * Obtain the user's configuration data. * <p> @@ -153,43 +101,17 @@ public class OpenSshConfig implements ConfigRepository { if (home == null) home = new File(".").getAbsoluteFile(); //$NON-NLS-1$ - final File config = new File(new File(home, ".ssh"), Constants.CONFIG); //$NON-NLS-1$ - final OpenSshConfig osc = new OpenSshConfig(home, config); - osc.refresh(); - return osc; + final File config = new File(new File(home, SshConstants.SSH_DIR), + SshConstants.CONFIG); + return new OpenSshConfig(home, config); } - /** The user's home directory, as key files may be relative to here. */ - private final File home; - - /** The .ssh/config file we read and monitor for updates. */ - private final File configFile; - - /** Modification time of {@link #configFile} when it was last loaded. */ - private long lastModified; - - /** - * Encapsulates entries read out of the configuration file, and - * {@link Host}s created from that. - */ - private static class State { - Map<String, HostEntry> entries = new LinkedHashMap<>(); - Map<String, Host> hosts = new HashMap<>(); - - @Override - @SuppressWarnings("nls") - public String toString() { - return "State [entries=" + entries + ", hosts=" + hosts + "]"; - } - } - - /** State read from the config file, plus {@link Host}s created from it. */ - private State state; + /** The base file. */ + private OpenSshConfigFile configFile; OpenSshConfig(File h, File cfg) { - home = h; - configFile = cfg; - state = new State(); + configFile = new OpenSshConfigFile(h, cfg, + SshSessionFactory.getLocalUserName()); } /** @@ -202,603 +124,8 @@ public class OpenSshConfig implements ConfigRepository { * @return r configuration for the requested name. Never null. */ public Host lookup(String hostName) { - final State cache = refresh(); - Host h = cache.hosts.get(hostName); - if (h != null) { - return h; - } - HostEntry fullConfig = new HostEntry(); - // Initialize with default entries at the top of the file, before the - // first Host block. - fullConfig.merge(cache.entries.get(HostEntry.DEFAULT_NAME)); - for (Map.Entry<String, HostEntry> e : cache.entries.entrySet()) { - String key = e.getKey(); - if (isHostMatch(key, hostName)) { - fullConfig.merge(e.getValue()); - } - } - fullConfig.substitute(hostName, home); - h = new Host(fullConfig, hostName, home); - cache.hosts.put(hostName, h); - return h; - } - - private synchronized State refresh() { - final long mtime = configFile.lastModified(); - if (mtime != lastModified) { - State newState = new State(); - try (FileInputStream in = new FileInputStream(configFile)) { - newState.entries = parse(in); - } catch (IOException none) { - // Ignore -- we'll set and return an empty state - } - lastModified = mtime; - state = newState; - } - return state; - } - - private Map<String, HostEntry> parse(InputStream in) - throws IOException { - final Map<String, HostEntry> m = new LinkedHashMap<>(); - final BufferedReader br = new BufferedReader(new InputStreamReader(in)); - final List<HostEntry> current = new ArrayList<>(4); - String line; - - // The man page doesn't say so, but the OpenSSH parser (readconf.c) - // starts out in active mode and thus always applies any lines that - // occur before the first host block. We gather those options in a - // HostEntry for DEFAULT_NAME. - HostEntry defaults = new HostEntry(); - current.add(defaults); - m.put(HostEntry.DEFAULT_NAME, defaults); - - while ((line = br.readLine()) != null) { - line = line.trim(); - if (line.isEmpty() || line.startsWith("#")) { //$NON-NLS-1$ - 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()); - // 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$ - - if (StringUtils.equalsIgnoreCase("Host", keyword)) { //$NON-NLS-1$ - current.clear(); - for (String name : HostEntry.parseList(argValue)) { - if (name == null || name.isEmpty()) { - // null should not occur, but better be safe than sorry. - continue; - } - HostEntry c = m.get(name); - if (c == null) { - c = new HostEntry(); - m.put(name, c); - } - current.add(c); - } - continue; - } - - if (current.isEmpty()) { - // We received an option outside of a Host block. We - // don't know who this should match against, so skip. - continue; - } - - if (HostEntry.isListKey(keyword)) { - List<String> args = HostEntry.parseList(argValue); - for (HostEntry entry : current) { - entry.setValue(keyword, args); - } - } else if (!argValue.isEmpty()) { - argValue = dequote(argValue); - for (HostEntry entry : current) { - entry.setValue(keyword, argValue); - } - } - } - - return m; - } - - private static boolean isHostMatch(final String pattern, - final String name) { - if (pattern.startsWith("!")) { //$NON-NLS-1$ - return !patternMatchesHost(pattern.substring(1), name); - } else { - return patternMatchesHost(pattern, name); - } - } - - private static boolean patternMatchesHost(final String pattern, - final String name) { - if (pattern.indexOf('*') >= 0 || pattern.indexOf('?') >= 0) { - final FileNameMatcher fn; - try { - fn = new FileNameMatcher(pattern, null); - } catch (InvalidPatternException e) { - return false; - } - fn.append(name); - return fn.isMatch(); - } else { - // Not a pattern but a full host 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 nows(String value) { - final StringBuilder b = new StringBuilder(); - for (int i = 0; i < value.length(); i++) { - if (!Character.isSpaceChar(value.charAt(i))) - b.append(value.charAt(i)); - } - return b.toString(); - } - - private static Boolean yesno(String value) { - if (StringUtils.equalsIgnoreCase("yes", value)) //$NON-NLS-1$ - return Boolean.TRUE; - return Boolean.FALSE; - } - - private static File toFile(String path, File home) { - if (path.startsWith("~/")) { //$NON-NLS-1$ - return new File(home, path.substring(2)); - } - File ret = new File(path); - if (ret.isAbsolute()) { - return ret; - } - return new File(home, path); - } - - private static int positive(String value) { - if (value != null) { - try { - return Integer.parseUnsignedInt(value); - } catch (NumberFormatException e) { - // Ignore - } - } - return -1; - } - - static String userName() { - return AccessController.doPrivileged(new PrivilegedAction<String>() { - @Override - public String run() { - return SystemReader.getInstance() - .getProperty(Constants.OS_USER_NAME_KEY); - } - }); - } - - private static class HostEntry implements ConfigRepository.Config { - - /** - * "Host name" of the HostEntry for the default options before the first - * host block in a config file. - */ - public static final String DEFAULT_NAME = ""; //$NON-NLS-1$ - - // See com.jcraft.jsch.OpenSSHConfig. Translates some command-line keys - // to ssh-config keys. - private static final Map<String, String> KEY_MAP = new HashMap<>(); - - static { - KEY_MAP.put("kex", "KexAlgorithms"); //$NON-NLS-1$//$NON-NLS-2$ - KEY_MAP.put("server_host_key", "HostKeyAlgorithms"); //$NON-NLS-1$ //$NON-NLS-2$ - KEY_MAP.put("cipher.c2s", "Ciphers"); //$NON-NLS-1$ //$NON-NLS-2$ - KEY_MAP.put("cipher.s2c", "Ciphers"); //$NON-NLS-1$ //$NON-NLS-2$ - KEY_MAP.put("mac.c2s", "Macs"); //$NON-NLS-1$ //$NON-NLS-2$ - KEY_MAP.put("mac.s2c", "Macs"); //$NON-NLS-1$ //$NON-NLS-2$ - KEY_MAP.put("compression.s2c", "Compression"); //$NON-NLS-1$ //$NON-NLS-2$ - KEY_MAP.put("compression.c2s", "Compression"); //$NON-NLS-1$ //$NON-NLS-2$ - KEY_MAP.put("compression_level", "CompressionLevel"); //$NON-NLS-1$ //$NON-NLS-2$ - KEY_MAP.put("MaxAuthTries", "NumberOfPasswordPrompts"); //$NON-NLS-1$ //$NON-NLS-2$ - } - - /** - * Keys that can be specified multiple times, building up a list. (I.e., - * those are the keys that do not follow the general rule of "first - * occurrence wins".) - */ - private static final Set<String> MULTI_KEYS = new HashSet<>(); - - static { - MULTI_KEYS.add("CERTIFICATEFILE"); //$NON-NLS-1$ - MULTI_KEYS.add("IDENTITYFILE"); //$NON-NLS-1$ - MULTI_KEYS.add("LOCALFORWARD"); //$NON-NLS-1$ - MULTI_KEYS.add("REMOTEFORWARD"); //$NON-NLS-1$ - MULTI_KEYS.add("SENDENV"); //$NON-NLS-1$ - } - - /** - * Keys that take a whitespace-separated list of elements as argument. - * Because the dequote-handling is different, we must handle those in - * the parser. There are a few other keys that take comma-separated - * lists as arguments, but for the parser those are single arguments - * that must be quoted if they contain whitespace, and taking them apart - * is the responsibility of the user of those keys. - */ - private static final Set<String> LIST_KEYS = new HashSet<>(); - - static { - LIST_KEYS.add("CANONICALDOMAINS"); //$NON-NLS-1$ - LIST_KEYS.add("GLOBALKNOWNHOSTSFILE"); //$NON-NLS-1$ - LIST_KEYS.add("SENDENV"); //$NON-NLS-1$ - LIST_KEYS.add("USERKNOWNHOSTSFILE"); //$NON-NLS-1$ - } - - private Map<String, String> options; - - private Map<String, List<String>> multiOptions; - - private Map<String, List<String>> listOptions; - - @Override - public String getHostname() { - return getValue("HOSTNAME"); //$NON-NLS-1$ - } - - @Override - public String getUser() { - return getValue("USER"); //$NON-NLS-1$ - } - - @Override - public int getPort() { - return positive(getValue("PORT")); //$NON-NLS-1$ - } - - private static String mapKey(String key) { - String k = KEY_MAP.get(key); - if (k == null) { - k = key; - } - return k.toUpperCase(Locale.ROOT); - } - - private String findValue(String key) { - String k = mapKey(key); - String result = options != null ? options.get(k) : null; - if (result == null) { - // Also check the list and multi options. Modern OpenSSH treats - // UserKnownHostsFile and GlobalKnownHostsFile as list-valued, - // and so does this parser. Jsch 0.1.54 in general doesn't know - // about list-valued options (it _does_ know multi-valued - // options, though), and will ask for a single value for such - // options. - // - // Let's be lenient and return at least the first value from - // a list-valued or multi-valued key for which Jsch asks for a - // single value. - List<String> values = listOptions != null ? listOptions.get(k) - : null; - if (values == null) { - values = multiOptions != null ? multiOptions.get(k) : null; - } - if (values != null && !values.isEmpty()) { - result = values.get(0); - } - } - return result; - } - - @Override - public String getValue(String key) { - // See com.jcraft.jsch.OpenSSHConfig.MyConfig.getValue() for this - // special case. - if (key.equals("compression.s2c") //$NON-NLS-1$ - || key.equals("compression.c2s")) { //$NON-NLS-1$ - String foo = findValue(key); - if (foo == null || foo.equals("no")) { //$NON-NLS-1$ - return "none,zlib@openssh.com,zlib"; //$NON-NLS-1$ - } - return "zlib@openssh.com,zlib,none"; //$NON-NLS-1$ - } - return findValue(key); - } - - @Override - public String[] getValues(String key) { - String k = mapKey(key); - List<String> values = listOptions != null ? listOptions.get(k) - : null; - if (values == null) { - values = multiOptions != null ? multiOptions.get(k) : null; - } - if (values == null || values.isEmpty()) { - return new String[0]; - } - return values.toArray(new String[0]); - } - - public void setValue(String key, String value) { - String k = key.toUpperCase(Locale.ROOT); - if (MULTI_KEYS.contains(k)) { - if (multiOptions == null) { - multiOptions = new HashMap<>(); - } - List<String> values = multiOptions.get(k); - if (values == null) { - values = new ArrayList<>(4); - multiOptions.put(k, values); - } - values.add(value); - } else { - if (options == null) { - options = new HashMap<>(); - } - if (!options.containsKey(k)) { - options.put(k, value); - } - } - } - - public void setValue(String key, List<String> values) { - if (values.isEmpty()) { - // Can occur only on a missing argument: ignore. - return; - } - String k = key.toUpperCase(Locale.ROOT); - // Check multi-valued keys first; because of the replacement - // strategy, they must take precedence over list-valued keys - // which always follow the "first occurrence wins" strategy. - // - // Note that SendEnv is a multi-valued list-valued key. (It's - // rather immaterial for JGit, though.) - if (MULTI_KEYS.contains(k)) { - if (multiOptions == null) { - multiOptions = new HashMap<>(2 * MULTI_KEYS.size()); - } - List<String> items = multiOptions.get(k); - if (items == null) { - items = new ArrayList<>(values); - multiOptions.put(k, items); - } else { - items.addAll(values); - } - } else { - if (listOptions == null) { - listOptions = new HashMap<>(2 * LIST_KEYS.size()); - } - if (!listOptions.containsKey(k)) { - listOptions.put(k, values); - } - } - } - - public static boolean isListKey(String key) { - return LIST_KEYS.contains(key.toUpperCase(Locale.ROOT)); - } - - /** - * Splits the argument into a list of whitespace-separated elements. - * Elements containing whitespace must be quoted and will be de-quoted. - * - * @param argument - * argument part of the configuration line as read from the - * config file - * @return a {@link List} of elements, possibly empty and possibly - * containing empty elements - */ - public static List<String> parseList(String argument) { - List<String> result = new ArrayList<>(4); - int start = 0; - int length = argument.length(); - while (start < length) { - // Skip whitespace - if (Character.isSpaceChar(argument.charAt(start))) { - 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 { - int stop = start + 1; - while (stop < length - && !Character.isSpaceChar(argument.charAt(stop))) { - stop++; - } - result.add(argument.substring(start, stop)); - start = stop + 1; - } - } - return result; - } - - protected void merge(HostEntry entry) { - if (entry == null) { - // Can occur if we could not read the config file - return; - } - if (entry.options != null) { - if (options == null) { - options = new HashMap<>(); - } - for (Map.Entry<String, String> item : entry.options - .entrySet()) { - if (!options.containsKey(item.getKey())) { - options.put(item.getKey(), item.getValue()); - } - } - } - if (entry.listOptions != null) { - if (listOptions == null) { - listOptions = new HashMap<>(2 * LIST_KEYS.size()); - } - for (Map.Entry<String, List<String>> item : entry.listOptions - .entrySet()) { - if (!listOptions.containsKey(item.getKey())) { - listOptions.put(item.getKey(), item.getValue()); - } - } - - } - if (entry.multiOptions != null) { - if (multiOptions == null) { - multiOptions = new HashMap<>(2 * MULTI_KEYS.size()); - } - for (Map.Entry<String, List<String>> item : entry.multiOptions - .entrySet()) { - List<String> values = multiOptions.get(item.getKey()); - if (values == null) { - values = new ArrayList<>(item.getValue()); - multiOptions.put(item.getKey(), values); - } else { - values.addAll(item.getValue()); - } - } - } - } - - private class Replacer { - private final Map<Character, String> replacements = new HashMap<>(); - - public Replacer(String originalHostName, File home) { - replacements.put(Character.valueOf('%'), "%"); //$NON-NLS-1$ - replacements.put(Character.valueOf('d'), home.getPath()); - // Needs special treatment... - String host = getValue("HOSTNAME"); //$NON-NLS-1$ - replacements.put(Character.valueOf('h'), originalHostName); - if (host != null && host.indexOf('%') >= 0) { - host = substitute(host, "h"); //$NON-NLS-1$ - options.put("HOSTNAME", host); //$NON-NLS-1$ - } - if (host != null) { - replacements.put(Character.valueOf('h'), host); - } - String localhost = SystemReader.getInstance().getHostname(); - replacements.put(Character.valueOf('l'), localhost); - int period = localhost.indexOf('.'); - if (period > 0) { - localhost = localhost.substring(0, period); - } - replacements.put(Character.valueOf('L'), localhost); - replacements.put(Character.valueOf('n'), originalHostName); - replacements.put(Character.valueOf('p'), getValue("PORT")); //$NON-NLS-1$ - replacements.put(Character.valueOf('r'), getValue("USER")); //$NON-NLS-1$ - replacements.put(Character.valueOf('u'), userName()); - replacements.put(Character.valueOf('C'), - substitute("%l%h%p%r", "hlpr")); //$NON-NLS-1$ //$NON-NLS-2$ - } - - public String substitute(String input, String allowed) { - if (input == null || input.length() <= 1 - || input.indexOf('%') < 0) { - return input; - } - StringBuilder builder = new StringBuilder(); - int start = 0; - int length = input.length(); - while (start < length) { - int percent = input.indexOf('%', start); - if (percent < 0 || percent + 1 >= length) { - builder.append(input.substring(start)); - break; - } - String replacement = null; - char ch = input.charAt(percent + 1); - if (ch == '%' || allowed.indexOf(ch) >= 0) { - replacement = replacements.get(Character.valueOf(ch)); - } - if (replacement == null) { - builder.append(input.substring(start, percent + 2)); - } else { - builder.append(input.substring(start, percent)) - .append(replacement); - } - start = percent + 2; - } - return builder.toString(); - } - } - - private List<String> substitute(List<String> values, String allowed, - Replacer r) { - List<String> result = new ArrayList<>(values.size()); - for (String value : values) { - result.add(r.substitute(value, allowed)); - } - return result; - } - - private List<String> replaceTilde(List<String> values, File home) { - List<String> result = new ArrayList<>(values.size()); - for (String value : values) { - result.add(toFile(value, home).getPath()); - } - return result; - } - - protected void substitute(String originalHostName, File home) { - Replacer r = new Replacer(originalHostName, home); - if (multiOptions != null) { - List<String> values = multiOptions.get("IDENTITYFILE"); //$NON-NLS-1$ - if (values != null) { - values = substitute(values, "dhlru", r); //$NON-NLS-1$ - values = replaceTilde(values, home); - multiOptions.put("IDENTITYFILE", values); //$NON-NLS-1$ - } - values = multiOptions.get("CERTIFICATEFILE"); //$NON-NLS-1$ - if (values != null) { - values = substitute(values, "dhlru", r); //$NON-NLS-1$ - values = replaceTilde(values, home); - multiOptions.put("CERTIFICATEFILE", values); //$NON-NLS-1$ - } - } - if (listOptions != null) { - List<String> values = listOptions.get("GLOBALKNOWNHOSTSFILE"); //$NON-NLS-1$ - if (values != null) { - values = replaceTilde(values, home); - listOptions.put("GLOBALKNOWNHOSTSFILE", values); //$NON-NLS-1$ - } - values = listOptions.get("USERKNOWNHOSTSFILE"); //$NON-NLS-1$ - if (values != null) { - values = replaceTilde(values, home); - listOptions.put("USERKNOWNHOSTSFILE", values); //$NON-NLS-1$ - } - } - if (options != null) { - // HOSTNAME already done in Replacer constructor - String value = options.get("IDENTITYAGENT"); //$NON-NLS-1$ - if (value != null) { - value = r.substitute(value, "dhlru"); //$NON-NLS-1$ - value = toFile(value, home).getPath(); - options.put("IDENTITYAGENT", value); //$NON-NLS-1$ - } - } - // Match is not implemented and would need to be done elsewhere - // anyway. ControlPath, LocalCommand, ProxyCommand, and - // RemoteCommand are not used by Jsch. - } - - @Override - @SuppressWarnings("nls") - public String toString() { - return "HostEntry [options=" + options + ", multiOptions=" - + multiOptions + ", listOptions=" + listOptions + "]"; - } + HostEntry entry = configFile.lookup(hostName, -1, null); + return new Host(entry, hostName, configFile.getLocalUserName()); } /** @@ -829,8 +156,34 @@ public class OpenSshConfig implements ConfigRepository { int connectionAttempts; + private HostEntry entry; + private Config config; + // See com.jcraft.jsch.OpenSSHConfig. Translates some command-line keys + // to ssh-config keys. + private static final Map<String, String> KEY_MAP = new TreeMap<>( + String.CASE_INSENSITIVE_ORDER); + + static { + KEY_MAP.put("kex", SshConstants.KEX_ALGORITHMS); //$NON-NLS-1$ + KEY_MAP.put("server_host_key", SshConstants.HOST_KEY_ALGORITHMS); //$NON-NLS-1$ + KEY_MAP.put("cipher.c2s", SshConstants.CIPHERS); //$NON-NLS-1$ + KEY_MAP.put("cipher.s2c", SshConstants.CIPHERS); //$NON-NLS-1$ + KEY_MAP.put("mac.c2s", SshConstants.MACS); //$NON-NLS-1$ + KEY_MAP.put("mac.s2c", SshConstants.MACS); //$NON-NLS-1$ + KEY_MAP.put("compression.s2c", SshConstants.COMPRESSION); //$NON-NLS-1$ + KEY_MAP.put("compression.c2s", SshConstants.COMPRESSION); //$NON-NLS-1$ + KEY_MAP.put("compression_level", "CompressionLevel"); //$NON-NLS-1$ //$NON-NLS-2$ + KEY_MAP.put("MaxAuthTries", //$NON-NLS-1$ + SshConstants.NUMBER_OF_PASSWORD_PROMPTS); + } + + private static String mapKey(String key) { + String k = KEY_MAP.get(key); + return k != null ? k : key; + } + /** * Creates a new uninitialized {@link Host}. */ @@ -838,9 +191,9 @@ public class OpenSshConfig implements ConfigRepository { // For API backwards compatibility with pre-4.9 JGit } - Host(Config config, String hostName, File homeDir) { - this.config = config; - complete(hostName, homeDir); + Host(HostEntry entry, String hostName, String localUserName) { + this.entry = entry; + complete(hostName, localUserName); } /** @@ -910,42 +263,84 @@ public class OpenSshConfig implements ConfigRepository { } - private void complete(String initialHostName, File homeDir) { + private void complete(String initialHostName, String localUserName) { // Try to set values from the options. - hostName = config.getHostname(); - user = config.getUser(); - port = config.getPort(); + hostName = entry.getValue(SshConstants.HOST_NAME); + user = entry.getValue(SshConstants.USER); + port = positive(entry.getValue(SshConstants.PORT)); connectionAttempts = positive( - config.getValue("ConnectionAttempts")); //$NON-NLS-1$ - strictHostKeyChecking = config.getValue("StrictHostKeyChecking"); //$NON-NLS-1$ - String value = config.getValue("BatchMode"); //$NON-NLS-1$ - if (value != null) { - batchMode = yesno(value); - } - value = config.getValue("PreferredAuthentications"); //$NON-NLS-1$ - if (value != null) { - preferredAuthentications = nows(value); - } + entry.getValue(SshConstants.CONNECTION_ATTEMPTS)); + strictHostKeyChecking = entry + .getValue(SshConstants.STRICT_HOST_KEY_CHECKING); + batchMode = Boolean.valueOf(OpenSshConfigFile + .flag(entry.getValue(SshConstants.BATCH_MODE))); + preferredAuthentications = entry + .getValue(SshConstants.PREFERRED_AUTHENTICATIONS); // Fill in defaults if still not set - if (hostName == null) { + if (hostName == null || hostName.isEmpty()) { hostName = initialHostName; } - if (user == null) { - user = OpenSshConfig.userName(); + if (user == null || user.isEmpty()) { + user = localUserName; } if (port <= 0) { - port = OpenSshConfig.SSH_PORT; + port = SshConstants.SSH_DEFAULT_PORT; } if (connectionAttempts <= 0) { connectionAttempts = 1; } - String[] identityFiles = config.getValues("IdentityFile"); //$NON-NLS-1$ - if (identityFiles != null && identityFiles.length > 0) { - identityFile = toFile(identityFiles[0], homeDir); + List<String> identityFiles = entry + .getValues(SshConstants.IDENTITY_FILE); + if (identityFiles != null && !identityFiles.isEmpty()) { + identityFile = new File(identityFiles.get(0)); } } Config getConfig() { + if (config == null) { + config = new Config() { + + @Override + public String getHostname() { + return Host.this.getHostName(); + } + + @Override + public String getUser() { + return Host.this.getUser(); + } + + @Override + public int getPort() { + return Host.this.getPort(); + } + + @Override + public String getValue(String key) { + // See com.jcraft.jsch.OpenSSHConfig.MyConfig.getValue() + // for this special case. + if (key.equals("compression.s2c") //$NON-NLS-1$ + || key.equals("compression.c2s")) { //$NON-NLS-1$ + if (!OpenSshConfigFile.flag( + Host.this.entry.getValue(mapKey(key)))) { + return "none,zlib@openssh.com,zlib"; //$NON-NLS-1$ + } + return "zlib@openssh.com,zlib,none"; //$NON-NLS-1$ + } + return Host.this.entry.getValue(mapKey(key)); + } + + @Override + public String[] getValues(String key) { + List<String> values = Host.this.entry + .getValues(mapKey(key)); + if (values == null) { + return new String[0]; + } + return values.toArray(new String[0]); + } + }; + } return config; } @@ -957,7 +352,7 @@ public class OpenSshConfig implements ConfigRepository { + ", preferredAuthentications=" + preferredAuthentications + ", batchMode=" + batchMode + ", strictHostKeyChecking=" + strictHostKeyChecking + ", connectionAttempts=" - + connectionAttempts + ", config=" + config + "]"; + + connectionAttempts + ", entry=" + entry + "]"; } } @@ -977,9 +372,7 @@ public class OpenSshConfig implements ConfigRepository { /** {@inheritDoc} */ @Override - @SuppressWarnings("nls") public String toString() { - return "OpenSshConfig [home=" + home + ", configFile=" + configFile - + ", lastModified=" + lastModified + ", state=" + state + "]"; + return "OpenSshConfig [configFile=" + configFile + ']'; //$NON-NLS-1$ } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java index 5cbb6f5dfb..ba5d2f3c8f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java @@ -63,7 +63,7 @@ import java.util.Collection; */ public interface PostReceiveHook { /** A simple no-op hook. */ - public static final PostReceiveHook NULL = new PostReceiveHook() { + PostReceiveHook NULL = new PostReceiveHook() { @Override public void onPostReceive(final ReceivePack rp, final Collection<ReceiveCommand> commands) { @@ -81,6 +81,6 @@ public interface PostReceiveHook { * unmodifiable set of successfully completed commands. May be * the empty set. */ - public void onPostReceive(ReceivePack rp, + void onPostReceive(ReceivePack rp, Collection<ReceiveCommand> commands); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java index 09667eb01a..3aa3b127e5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java @@ -57,7 +57,7 @@ import org.eclipse.jgit.storage.pack.PackStatistics; */ public interface PostUploadHook { /** A simple no-op hook. */ - public static final PostUploadHook NULL = new PostUploadHook() { + PostUploadHook NULL = new PostUploadHook() { @Override public void onPostUpload(PackStatistics stats) { // Do nothing. @@ -72,5 +72,5 @@ public interface PostUploadHook { * {@link org.eclipse.jgit.internal.storage.pack.PackWriter} for * the uploaded pack */ - public void onPostUpload(PackStatistics stats); + void onPostUpload(PackStatistics stats); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java index 77c1a8af29..30845d3b68 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java @@ -79,7 +79,7 @@ import java.util.Collection; */ public interface PreReceiveHook { /** A simple no-op hook. */ - public static final PreReceiveHook NULL = new PreReceiveHook() { + PreReceiveHook NULL = new PreReceiveHook() { @Override public void onPreReceive(final ReceivePack rp, final Collection<ReceiveCommand> commands) { @@ -99,5 +99,5 @@ public interface PreReceiveHook { * unmodifiable set of valid commands still pending execution. * May be the empty set. */ - public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands); + void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java index 2e1cd5800a..65dc241584 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java @@ -59,7 +59,7 @@ import org.eclipse.jgit.lib.ObjectId; */ public interface PreUploadHook { /** A simple no-op hook. */ - public static final PreUploadHook NULL = new PreUploadHook() { + PreUploadHook NULL = new PreUploadHook() { @Override public void onBeginNegotiateRound(UploadPack up, Collection<? extends ObjectId> wants, int cntOffered) @@ -96,7 +96,7 @@ public interface PreUploadHook { * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException * abort; the message will be sent to the user. */ - public void onBeginNegotiateRound(UploadPack up, + void onBeginNegotiateRound(UploadPack up, Collection<? extends ObjectId> wants, int cntOffered) throws ServiceMayNotContinueException; @@ -120,7 +120,7 @@ public interface PreUploadHook { * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException * abort; the message will be sent to the user. */ - public void onEndNegotiateRound(UploadPack up, + void onEndNegotiateRound(UploadPack up, Collection<? extends ObjectId> wants, int cntCommon, int cntNotFound, boolean ready) throws ServiceMayNotContinueException; @@ -141,7 +141,7 @@ public interface PreUploadHook { * @throws org.eclipse.jgit.transport.ServiceMayNotContinueException * abort; the message will be sent to the user. */ - public void onSendPack(UploadPack up, Collection<? extends ObjectId> wants, + void onSendPack(UploadPack up, Collection<? extends ObjectId> wants, Collection<? extends ObjectId> haves) throws ServiceMayNotContinueException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java new file mode 100644 index 0000000000..21498d6f5c --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2018, Google LLC. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ +package org.eclipse.jgit.transport; + +import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER; + +import java.io.EOFException; +import java.io.IOException; +import java.text.MessageFormat; + +import org.eclipse.jgit.errors.PackProtocolException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.transport.parser.FirstWant; +import org.eclipse.jgit.lib.ObjectId; + +/** + * Parser for git protocol versions 0 and 1. + * + * It reads the lines coming through the {@link PacketLineIn} and builds a + * {@link FetchV0Request} object. + * + * It requires a transferConfig object to know if the server supports filters. + */ +final class ProtocolV0Parser { + + private final TransferConfig transferConfig; + + ProtocolV0Parser(TransferConfig transferConfig) { + this.transferConfig = transferConfig; + } + + /** + * Parse an incoming protocol v1 upload request arguments from the wire. + * + * The incoming PacketLineIn is consumed until an END line, but the caller + * is responsible for closing it (if needed). + * + * @param pckIn + * incoming lines. This method will read until an END line. + * @return a FetchV0Request with the data received in the wire. + * @throws PackProtocolException + * @throws IOException + */ + FetchV0Request recvWants(PacketLineIn pckIn) + throws PackProtocolException, IOException { + FetchV0Request.Builder reqBuilder = new FetchV0Request.Builder(); + + boolean isFirst = true; + boolean filterReceived = false; + + for (;;) { + String line; + try { + line = pckIn.readString(); + } catch (EOFException eof) { + if (isFirst) { + break; + } + throw eof; + } + + if (line == PacketLineIn.END) { + break; + } + + if (line.startsWith("deepen ")) { //$NON-NLS-1$ + int depth = Integer.parseInt(line.substring(7)); + if (depth <= 0) { + throw new PackProtocolException( + MessageFormat.format(JGitText.get().invalidDepth, + Integer.valueOf(depth))); + } + reqBuilder.setDepth(depth); + continue; + } + + if (line.startsWith("shallow ")) { //$NON-NLS-1$ + reqBuilder.addClientShallowCommit( + ObjectId.fromString(line.substring(8))); + continue; + } + + if (transferConfig.isAllowFilter() + && line.startsWith(OPTION_FILTER + " ")) { //$NON-NLS-1$ + String arg = line.substring(OPTION_FILTER.length() + 1); + + if (filterReceived) { + throw new PackProtocolException( + JGitText.get().tooManyFilters); + } + filterReceived = true; + + reqBuilder.setFilterBlobLimit(ProtocolV2Parser.filterLine(arg)); + continue; + } + + if (!line.startsWith("want ") || line.length() < 45) { //$NON-NLS-1$ + throw new PackProtocolException(MessageFormat + .format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$ + } + + if (isFirst) { + if (line.length() > 45) { + FirstWant firstLine = FirstWant.fromLine(line); + reqBuilder.addClientCapabilities(firstLine.getCapabilities()); + reqBuilder.setAgent(firstLine.getAgent()); + line = firstLine.getLine(); + } + } + + reqBuilder.addWantId(ObjectId.fromString(line.substring(5))); + isFirst = false; + } + + return reqBuilder.build(); + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java index 2cc50a7f38..8f4b86ee0a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java @@ -44,15 +44,20 @@ package org.eclipse.jgit.transport; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_DEEPEN_RELATIVE; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER; +import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA; +import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SERVER_OPTION; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WANT_REF; import java.io.IOException; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.internal.JGitText; @@ -73,6 +78,35 @@ final class ProtocolV2Parser { this.transferConfig = transferConfig; } + /* + * Read lines until DELIM or END, calling the appropiate consumer. + * + * Returns the last read line (so caller can check if there is more to read + * in the line). + */ + private static String consumeCapabilities(PacketLineIn pckIn, + Consumer<String> serverOptionConsumer, + Consumer<String> agentConsumer) throws IOException { + + String serverOptionPrefix = OPTION_SERVER_OPTION + '='; + String agentPrefix = OPTION_AGENT + '='; + + String line = pckIn.readString(); + while (line != PacketLineIn.DELIM && line != PacketLineIn.END) { + if (line.startsWith(serverOptionPrefix)) { + serverOptionConsumer + .accept(line.substring(serverOptionPrefix.length())); + } else if (line.startsWith(agentPrefix)) { + agentConsumer.accept(line.substring(agentPrefix.length())); + } else { + // Unrecognized capability. Ignore it. + } + line = pckIn.readString(); + } + + return line; + } + /** * Parse the incoming fetch request arguments from the wire. The caller must * be sure that what is comings is a fetch request before coming here. @@ -93,21 +127,26 @@ final class ProtocolV2Parser { // Packs are always sent multiplexed and using full 64K // lengths. - reqBuilder.addOption(OPTION_SIDE_BAND_64K); + reqBuilder.addClientCapability(OPTION_SIDE_BAND_64K); - String line; + String line = consumeCapabilities(pckIn, + serverOption -> reqBuilder.addServerOption(serverOption), + agent -> reqBuilder.setAgent(agent)); - // Currently, we do not support any capabilities, so the next - // line is DELIM. - if ((line = pckIn.readString()) != PacketLineIn.DELIM) { - throw new PackProtocolException(MessageFormat - .format(JGitText.get().unexpectedPacketLine, line)); + if (line == PacketLineIn.END) { + return reqBuilder.build(); + } + + if (line != PacketLineIn.DELIM) { + throw new PackProtocolException( + MessageFormat.format(JGitText.get().unexpectedPacketLine, + line)); } boolean filterReceived = false; while ((line = pckIn.readString()) != PacketLineIn.END) { if (line.startsWith("want ")) { //$NON-NLS-1$ - reqBuilder.addWantsId(ObjectId.fromString(line.substring(5))); + reqBuilder.addWantId(ObjectId.fromString(line.substring(5))); } else if (transferConfig.isAllowRefInWant() && line.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$ reqBuilder.addWantedRef(line.substring(OPTION_WANT_REF.length() + 1)); @@ -116,13 +155,13 @@ final class ProtocolV2Parser { } else if (line.equals("done")) { //$NON-NLS-1$ reqBuilder.setDoneReceived(); } else if (line.equals(OPTION_THIN_PACK)) { - reqBuilder.addOption(OPTION_THIN_PACK); + reqBuilder.addClientCapability(OPTION_THIN_PACK); } else if (line.equals(OPTION_NO_PROGRESS)) { - reqBuilder.addOption(OPTION_NO_PROGRESS); + reqBuilder.addClientCapability(OPTION_NO_PROGRESS); } else if (line.equals(OPTION_INCLUDE_TAG)) { - reqBuilder.addOption(OPTION_INCLUDE_TAG); + reqBuilder.addClientCapability(OPTION_INCLUDE_TAG); } else if (line.equals(OPTION_OFS_DELTA)) { - reqBuilder.addOption(OPTION_OFS_DELTA); + reqBuilder.addClientCapability(OPTION_OFS_DELTA); } else if (line.startsWith("shallow ")) { //$NON-NLS-1$ reqBuilder.addClientShallowCommit( ObjectId.fromString(line.substring(8))); @@ -149,7 +188,7 @@ final class ProtocolV2Parser { JGitText.get().deepenNotWithDeepen); } } else if (line.equals(OPTION_DEEPEN_RELATIVE)) { - reqBuilder.addOption(OPTION_DEEPEN_RELATIVE); + reqBuilder.addClientCapability(OPTION_DEEPEN_RELATIVE); } else if (line.startsWith("deepen-since ")) { //$NON-NLS-1$ int ts = Integer.parseInt(line.substring(13)); if (ts <= 0) { @@ -180,6 +219,57 @@ final class ProtocolV2Parser { } /** + * Parse the incoming ls-refs request arguments from the wire. This is meant + * for calling immediately after the caller has consumed a "command=ls-refs" + * line indicating the beginning of a ls-refs request. + * + * The incoming PacketLineIn is consumed until an END line, but the caller + * is responsible for closing it (if needed) + * + * @param pckIn + * incoming lines. This method will read until an END line. + * @return a LsRefsV2Request object with the data received in the wire. + * @throws PackProtocolException + * for inconsistencies in the protocol (e.g. unexpected lines) + * @throws IOException + * reporting problems reading the incoming messages from the + * wire + */ + LsRefsV2Request parseLsRefsRequest(PacketLineIn pckIn) + throws PackProtocolException, IOException { + LsRefsV2Request.Builder builder = LsRefsV2Request.builder(); + List<String> prefixes = new ArrayList<>(); + + String line = consumeCapabilities(pckIn, + serverOption -> builder.addServerOption(serverOption), + agent -> builder.setAgent(agent)); + + if (line == PacketLineIn.END) { + return builder.build(); + } + + if (line != PacketLineIn.DELIM) { + throw new PackProtocolException(MessageFormat + .format(JGitText.get().unexpectedPacketLine, line)); + } + + while ((line = pckIn.readString()) != PacketLineIn.END) { + if (line.equals("peel")) { //$NON-NLS-1$ + builder.setPeel(true); + } else if (line.equals("symrefs")) { //$NON-NLS-1$ + builder.setSymrefs(true); + } else if (line.startsWith("ref-prefix ")) { //$NON-NLS-1$ + prefixes.add(line.substring("ref-prefix ".length())); //$NON-NLS-1$ + } else { + throw new PackProtocolException(MessageFormat + .format(JGitText.get().unexpectedPacketLine, line)); + } + } + + return builder.setRefPrefixes(prefixes).build(); + } + + /* * Process the content of "filter" line from the protocol. It has a shape * like "blob:none" or "blob:limit=N", with limit a positive number. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java index ff2939a3d6..7f98d4dcc9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushConnection.java @@ -113,7 +113,7 @@ public interface PushConnection extends Connection { * created. Non-critical errors concerning only isolated refs * should be placed in refUpdates. */ - public void push(final ProgressMonitor monitor, + void push(final ProgressMonitor monitor, final Map<String, RemoteRefUpdate> refUpdates) throws TransportException; @@ -163,7 +163,7 @@ public interface PushConnection extends Connection { * should be placed in refUpdates. * @since 3.0 */ - public void push(final ProgressMonitor monitor, + void push(final ProgressMonitor monitor, final Map<String, RemoteRefUpdate> refUpdates, OutputStream out) throws TransportException; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java index 35fb0b17a7..577aaf4e9e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -76,8 +76,6 @@ public class ReceivePack extends BaseReceivePack { /** If {@link BasePackPushConnection#CAPABILITY_REPORT_STATUS} is enabled. */ private boolean reportStatus; - private boolean echoCommandFailures; - /** Whether the client intends to use push options. */ private boolean usePushOptions; private List<String> pushOptions; @@ -191,9 +189,11 @@ public class ReceivePack extends BaseReceivePack { * messages before sending the command results. This is usually * not necessary, but may help buggy Git clients that discard the * errors when all branches fail. + * @deprecated no widely used Git versions need this any more */ + @Deprecated public void setEchoCommandFailures(boolean echo) { - echoCommandFailures = echo; + // No-op. } /** @@ -269,36 +269,28 @@ public class ReceivePack extends BaseReceivePack { } } - if (unpackError == null) { - boolean atomic = isCapabilityEnabled(CAPABILITY_ATOMIC); - setAtomic(atomic); + try { + if (unpackError == null) { + boolean atomic = isCapabilityEnabled(CAPABILITY_ATOMIC); + setAtomic(atomic); - validateCommands(); - if (atomic && anyRejects()) - failPendingCommands(); + validateCommands(); + if (atomic && anyRejects()) { + failPendingCommands(); + } - preReceive.onPreReceive(this, filterCommands(Result.NOT_ATTEMPTED)); - if (atomic && anyRejects()) - failPendingCommands(); - executeCommands(); + preReceive.onPreReceive( + this, filterCommands(Result.NOT_ATTEMPTED)); + if (atomic && anyRejects()) { + failPendingCommands(); + } + executeCommands(); + } + } finally { + unlockPack(); } - unlockPack(); if (reportStatus) { - if (echoCommandFailures && msgOut != null) { - sendStatusReport(false, unpackError, new Reporter() { - @Override - void sendString(String s) throws IOException { - msgOut.write(Constants.encode(s + "\n")); //$NON-NLS-1$ - } - }); - msgOut.flush(); - try { - Thread.sleep(500); - } catch (InterruptedException wakeUp) { - // Ignore an early wake up. - } - } sendStatusReport(true, unpackError, new Reporter() { @Override void sendString(String s) throws IOException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java index 4662435ea7..6595cab71d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java @@ -207,7 +207,8 @@ public abstract class RefAdvertiser { * <p> * This method must be invoked prior to any of the following: * <ul> - * <li>{@link #send(Map)} + * <li>{@link #send(Map)}</li> + * <li>{@link #send(Collection)}</li> * </ul> * * @param deref @@ -223,8 +224,9 @@ public abstract class RefAdvertiser { * <p> * This method must be invoked prior to any of the following: * <ul> - * <li>{@link #send(Map)} - * <li>{@link #advertiseHave(AnyObjectId)} + * <li>{@link #send(Map)}</li> + * <li>{@link #send(Collection)}</li> + * <li>{@link #advertiseHave(AnyObjectId)}</li> * </ul> * * @param name @@ -257,8 +259,9 @@ public abstract class RefAdvertiser { * <p> * This method must be invoked prior to any of the following: * <ul> - * <li>{@link #send(Map)} - * <li>{@link #advertiseHave(AnyObjectId)} + * <li>{@link #send(Map)}</li> + * <li>{@link #send(Collection)}</li> + * <li>{@link #advertiseHave(AnyObjectId)}</li> * </ul> * * @param from diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java index 992ddc6e53..d6d6198f5b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java @@ -61,7 +61,7 @@ public interface RefFilter { /** * The default filter, allows all refs to be shown. */ - public static final RefFilter DEFAULT = new RefFilter() { + RefFilter DEFAULT = new RefFilter() { @Override public Map<String, Ref> filter (Map<String, Ref> refs) { return refs; @@ -76,5 +76,5 @@ public interface RefFilter { * @return * the filtered map of refs. */ - public Map<String, Ref> filter(Map<String, Ref> refs); + Map<String, Ref> filter(Map<String, Ref> refs); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java index 931653fa90..9a67f0f8fe 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java @@ -521,7 +521,7 @@ public class RemoteRefUpdate { : "(null)") + "..." + (newObjectId != null ? newObjectId.name() : "(null)") + (fastForward ? ", fastForward" : "") - + ", srcRef=" + srcRef + + ", srcRef=" + srcRef + (forceUpdate ? ", forceUpdate" : "") + ", message=" + (message != null ? "\"" + message + "\"" : "null") + "]"; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java index 525c895f45..e2109c2c5b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java @@ -78,10 +78,21 @@ public interface RemoteSession { * a TransportException may be thrown (a subclass of * java.io.IOException). */ - public Process exec(String commandName, int timeout) throws IOException; + Process exec(String commandName, int timeout) throws IOException; + + /** + * Obtain an {@link FtpChannel} for performing FTP operations over this + * {@link RemoteSession}. The default implementation returns {@code null}. + * + * @return the {@link FtpChannel} + * @since 5.2 + */ + default FtpChannel getFtpChannel() { + return null; + } /** * Disconnect the remote session */ - public void disconnect(); + void disconnect(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java index 90600cbb98..fde4401289 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java @@ -236,7 +236,7 @@ public class SideBandInputStream extends InputStream { messages.write(msg); if (out != null) - out.write(msg.getBytes()); + out.write(msg.getBytes(UTF_8)); } private void beginTask(int totalWorkUnits) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java new file mode 100644 index 0000000000..2b79d7105c --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2018 Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * 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. + */ + +package org.eclipse.jgit.transport; + +import org.eclipse.jgit.lib.Constants; + +/** + * Constants relating to ssh. + * + * @since 5.2 + */ +@SuppressWarnings("nls") +public final class SshConstants { + + private SshConstants() { + // No instances, please. + } + + /** IANA assigned port number for ssh. */ + public static final int SSH_DEFAULT_PORT = 22; + + /** URI scheme for ssh. */ + public static final String SSH_SCHEME = "ssh"; + + /** URI scheme for sftp. */ + public static final String SFTP_SCHEME = "sftp"; + + /** Default name for a ssh directory. */ + public static final String SSH_DIR = ".ssh"; + + /** Name of the ssh config file. */ + public static final String CONFIG = Constants.CONFIG; + + /** Default name of the user "known hosts" file. */ + public static final String KNOWN_HOSTS = "known_hosts"; + + // Config file keys + + /** Key in an ssh config file. */ + public static final String BATCH_MODE = "BatchMode"; + + /** Key in an ssh config file. */ + public static final String CANONICAL_DOMAINS = "CanonicalDomains"; + + /** Key in an ssh config file. */ + public static final String CERTIFICATE_FILE = "CertificateFile"; + + /** Key in an ssh config file. */ + public static final String CIPHERS = "Ciphers"; + + /** Key in an ssh config file. */ + public static final String COMPRESSION = "Compression"; + + /** Key in an ssh config file. */ + public static final String CONNECTION_ATTEMPTS = "ConnectionAttempts"; + + /** Key in an ssh config file. */ + public static final String CONTROL_PATH = "ControlPath"; + + /** Key in an ssh config file. */ + public static final String GLOBAL_KNOWN_HOSTS_FILE = "GlobalKnownHostsFile"; + + /** Key in an ssh config file. */ + public static final String HOST = "Host"; + + /** Key in an ssh config file. */ + public static final String HOST_KEY_ALGORITHMS = "HostKeyAlgorithms"; + + /** Key in an ssh config file. */ + public static final String HOST_NAME = "HostName"; + + /** Key in an ssh config file. */ + public static final String IDENTITIES_ONLY = "IdentitiesOnly"; + + /** Key in an ssh config file. */ + public static final String IDENTITY_AGENT = "IdentityAgent"; + + /** Key in an ssh config file. */ + public static final String IDENTITY_FILE = "IdentityFile"; + + /** Key in an ssh config file. */ + public static final String KEX_ALGORITHMS = "KexAlgorithms"; + + /** Key in an ssh config file. */ + public static final String LOCAL_COMMAND = "LocalCommand"; + + /** Key in an ssh config file. */ + public static final String LOCAL_FORWARD = "LocalForward"; + + /** Key in an ssh config file. */ + public static final String MACS = "MACs"; + + /** Key in an ssh config file. */ + public static final String NUMBER_OF_PASSWORD_PROMPTS = "NumberOfPasswordPrompts"; + + /** Key in an ssh config file. */ + public static final String PORT = "Port"; + + /** Key in an ssh config file. */ + public static final String PREFERRED_AUTHENTICATIONS = "PreferredAuthentications"; + + /** Key in an ssh config file. */ + public static final String PROXY_COMMAND = "ProxyCommand"; + + /** Key in an ssh config file. */ + public static final String REMOTE_COMMAND = "RemoteCommand"; + + /** Key in an ssh config file. */ + public static final String REMOTE_FORWARD = "RemoteForward"; + + /** Key in an ssh config file. */ + public static final String SEND_ENV = "SendEnv"; + + /** Key in an ssh config file. */ + public static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking"; + + /** Key in an ssh config file. */ + public static final String USER = "User"; + + /** Key in an ssh config file. */ + public static final String USER_KNOWN_HOSTS_FILE = "UserKnownHostsFile"; + + // Values + + /** Flag value. */ + public static final String YES = "yes"; + + /** Flag value. */ + public static final String ON = "on"; + + /** Flag value. */ + public static final String TRUE = "true"; + + /** Flag value. */ + public static final String NO = "no"; + + /** Flag value. */ + public static final String OFF = "off"; + + /** Flag value. */ + public static final String FALSE = "false"; + + // Default identity file names + + /** Name of the default RSA private identity file. */ + public static final String ID_RSA = "id_rsa"; + + /** Name of the default DSA private identity file. */ + public static final String ID_DSA = "id_dsa"; + + /** Name of the default ECDSA private identity file. */ + public static final String ID_ECDSA = "id_ecdsa"; + + /** Name of the default ECDSA private identity file. */ + public static final String ID_ED25519 = "id_ed25519"; + + /** All known default identity file names. */ + public static final String[] DEFAULT_IDENTITIES = { // + ID_RSA, ID_DSA, ID_ECDSA, ID_ED25519 + }; +} 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 ae357dfb75..005a0c2d0e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java @@ -44,8 +44,13 @@ package org.eclipse.jgit.transport; +import java.security.AccessController; +import java.security.PrivilegedAction; + import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.SystemReader; /** * Creates and destroys SSH connections to a remote system. @@ -88,21 +93,38 @@ public abstract class SshSessionFactory { } /** + * Retrieves the local user name as defined by the system property + * "user.name". + * + * @return the user name + * @since 5.2 + */ + public static String getLocalUserName() { + return AccessController.doPrivileged(new PrivilegedAction<String>() { + @Override + public String run() { + return SystemReader.getInstance() + .getProperty(Constants.OS_USER_NAME_KEY); + } + }); + } + + /** * Open (or reuse) a session to a host. * <p> * A reasonable UserInfo that can interact with the end-user (if necessary) * is installed on the returned session by this method. * <p> - * The caller must connect the session by invoking <code>connect()</code> - * if it has not already been connected. + * The caller must connect the session by invoking <code>connect()</code> if + * it has not already been connected. * * @param uri * URI information about the remote host * @param credentialsProvider * provider to support authentication, may be null. * @param fs - * the file system abstraction which will be necessary to - * perform certain file system operations. + * the file system abstraction which will be necessary to perform + * certain file system operations. * @param tms * Timeout value, in milliseconds. * @return a session that can contact the remote host. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java index db95396047..a3e655cd92 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java @@ -106,7 +106,8 @@ public class TransferConfig { this.name = name; } - static @Nullable ProtocolVersion parse(@Nullable String name) { + @Nullable + static ProtocolVersion parse(@Nullable String name) { if (name == null) { return null; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundle.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundle.java index 6a285e59f5..ee851cc620 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundle.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundle.java @@ -58,5 +58,5 @@ public interface TransportBundle extends PackTransport { /** * Bundle signature */ - public static final String V2_BUNDLE_SIGNATURE = "# v2 git bundle"; //$NON-NLS-1$ + String V2_BUNDLE_SIGNATURE = "# v2 git bundle"; //$NON-NLS-1$ } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java index f129ba34da..5c68308f90 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java @@ -53,13 +53,14 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; @@ -73,12 +74,6 @@ import org.eclipse.jgit.lib.Ref.Storage; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.SymbolicRef; -import com.jcraft.jsch.Channel; -import com.jcraft.jsch.ChannelSftp; -import com.jcraft.jsch.JSchException; -import com.jcraft.jsch.SftpATTRS; -import com.jcraft.jsch.SftpException; - /** * Transport over the non-Git aware SFTP (SSH based FTP) protocol. * <p> @@ -158,24 +153,16 @@ public class TransportSftp extends SshTransport implements WalkTransport { return r; } - ChannelSftp newSftp() throws TransportException { - final int tms = getTimeout() > 0 ? getTimeout() * 1000 : 0; - try { - // @TODO: Fix so that this operation is generic and casting to - // JschSession is no longer necessary. - final Channel channel = ((JschSession) getSession()) - .getSftpChannel(); - channel.connect(tms); - return (ChannelSftp) channel; - } catch (JSchException je) { - throw new TransportException(uri, je.getMessage(), je); - } + FtpChannel newSftp() throws IOException { + FtpChannel channel = getSession().getFtpChannel(); + channel.connect(getTimeout(), TimeUnit.SECONDS); + return channel; } class SftpObjectDB extends WalkRemoteObjectDatabase { private final String objectsPath; - private ChannelSftp ftp; + private FtpChannel ftp; SftpObjectDB(String path) throws TransportException { if (path.startsWith("/~")) //$NON-NLS-1$ @@ -187,13 +174,13 @@ public class TransportSftp extends SshTransport implements WalkTransport { ftp.cd(path); ftp.cd("objects"); //$NON-NLS-1$ objectsPath = ftp.pwd(); - } catch (TransportException err) { - close(); - throw err; - } catch (SftpException je) { + } catch (FtpChannel.FtpException f) { throw new TransportException(MessageFormat.format( JGitText.get().cannotEnterObjectsPath, path, - je.getMessage()), je); + f.getMessage()), f); + } catch (IOException ioe) { + close(); + throw new TransportException(uri, ioe.getMessage(), ioe); } } @@ -204,13 +191,13 @@ public class TransportSftp extends SshTransport implements WalkTransport { ftp.cd(parent.objectsPath); ftp.cd(p); objectsPath = ftp.pwd(); - } catch (TransportException err) { - close(); - throw err; - } catch (SftpException je) { + } catch (FtpChannel.FtpException f) { throw new TransportException(MessageFormat.format( JGitText.get().cannotEnterPathFromParent, p, - parent.objectsPath, je.getMessage()), je); + parent.objectsPath, f.getMessage()), f); + } catch (IOException ioe) { + close(); + throw new TransportException(uri, ioe.getMessage(), ioe); } } @@ -238,41 +225,32 @@ public class TransportSftp extends SshTransport implements WalkTransport { Collection<String> getPackNames() throws IOException { final List<String> packs = new ArrayList<>(); try { - @SuppressWarnings("unchecked") - final Collection<ChannelSftp.LsEntry> list = ftp.ls("pack"); //$NON-NLS-1$ - final HashMap<String, ChannelSftp.LsEntry> files; - final HashMap<String, Integer> mtimes; - - files = new HashMap<>(); - mtimes = new HashMap<>(); - - for (ChannelSftp.LsEntry ent : list) - files.put(ent.getFilename(), ent); - for (ChannelSftp.LsEntry ent : list) { - final String n = ent.getFilename(); - if (!n.startsWith("pack-") || !n.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$ + Collection<FtpChannel.DirEntry> list = ftp.ls("pack"); //$NON-NLS-1$ + Set<String> files = list.stream() + .map(FtpChannel.DirEntry::getFilename) + .collect(Collectors.toSet()); + HashMap<String, Long> mtimes = new HashMap<>(); + + for (FtpChannel.DirEntry ent : list) { + String n = ent.getFilename(); + if (!n.startsWith("pack-") || !n.endsWith(".pack")) { //$NON-NLS-1$ //$NON-NLS-2$ continue; - - final String in = n.substring(0, n.length() - 5) + ".idx"; //$NON-NLS-1$ - if (!files.containsKey(in)) + } + String in = n.substring(0, n.length() - 5) + ".idx"; //$NON-NLS-1$ + if (!files.contains(in)) { continue; - - mtimes.put(n, Integer.valueOf(ent.getAttrs().getMTime())); + } + mtimes.put(n, Long.valueOf(ent.getModifiedTime())); packs.add(n); } - Collections.sort(packs, new Comparator<String>() { - @Override - public int compare(String o1, String o2) { - return mtimes.get(o2).intValue() - - mtimes.get(o1).intValue(); - } - }); - } catch (SftpException je) { + Collections.sort(packs, + (o1, o2) -> mtimes.get(o2).compareTo(mtimes.get(o1))); + } catch (FtpChannel.FtpException f) { throw new TransportException( MessageFormat.format(JGitText.get().cannotListPackPath, - objectsPath, je.getMessage()), - je); + objectsPath, f.getMessage()), + f); } return packs; } @@ -280,27 +258,25 @@ public class TransportSftp extends SshTransport implements WalkTransport { @Override FileStream open(String path) throws IOException { try { - final SftpATTRS a = ftp.lstat(path); - return new FileStream(ftp.get(path), a.getSize()); - } catch (SftpException je) { - if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) + return new FileStream(ftp.get(path)); + } catch (FtpChannel.FtpException f) { + if (f.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) { throw new FileNotFoundException(path); + } throw new TransportException(MessageFormat.format( JGitText.get().cannotGetObjectsPath, objectsPath, path, - je.getMessage()), je); + f.getMessage()), f); } } @Override void deleteFile(String path) throws IOException { try { - ftp.rm(path); - } catch (SftpException je) { - if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) - return; + ftp.delete(path); + } catch (FtpChannel.FtpException f) { throw new TransportException(MessageFormat.format( JGitText.get().cannotDeleteObjectsPath, objectsPath, - path, je.getMessage()), je); + path, f.getMessage()), f); } // Prune any now empty directories. @@ -312,7 +288,7 @@ public class TransportSftp extends SshTransport implements WalkTransport { dir = dir.substring(0, s); ftp.rmdir(dir); s = dir.lastIndexOf('/'); - } catch (SftpException je) { + } catch (IOException je) { // If we cannot delete it, leave it alone. It may have // entries still in it, or maybe we lack write access on // the parent. Either way it isn't a fatal error. @@ -323,25 +299,31 @@ public class TransportSftp extends SshTransport implements WalkTransport { } @Override - OutputStream writeFile(final String path, - final ProgressMonitor monitor, final String monitorTask) - throws IOException { + OutputStream writeFile(String path, ProgressMonitor monitor, + String monitorTask) throws IOException { + Throwable err = null; try { return ftp.put(path); - } catch (SftpException je) { - if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { + } catch (FileNotFoundException e) { + mkdir_p(path); + } catch (FtpChannel.FtpException je) { + if (je.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) { mkdir_p(path); - try { - return ftp.put(path); - } catch (SftpException je2) { - je = je2; - } + } else { + err = je; } - - throw new TransportException(MessageFormat.format( - JGitText.get().cannotWriteObjectsPath, objectsPath, - path, je.getMessage()), je); } + if (err == null) { + try { + return ftp.put(path); + } catch (IOException e) { + err = e; + } + } + throw new TransportException( + MessageFormat.format(JGitText.get().cannotWriteObjectsPath, + objectsPath, path, err.getMessage()), + err); } @Override @@ -351,15 +333,15 @@ public class TransportSftp extends SshTransport implements WalkTransport { super.writeFile(lock, data); try { ftp.rename(lock, path); - } catch (SftpException je) { + } catch (IOException e) { throw new TransportException(MessageFormat.format( JGitText.get().cannotWriteObjectsPath, objectsPath, - path, je.getMessage()), je); + path, e.getMessage()), e); } } catch (IOException err) { try { ftp.rm(lock); - } catch (SftpException e) { + } catch (IOException e) { // Ignore deletion failure, we are already // failing anyway. } @@ -373,23 +355,30 @@ public class TransportSftp extends SshTransport implements WalkTransport { return; path = path.substring(0, s); + Throwable err = null; try { ftp.mkdir(path); - } catch (SftpException je) { - if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { + return; + } catch (FileNotFoundException f) { + mkdir_p(path); + } catch (FtpChannel.FtpException je) { + if (je.getStatus() == FtpChannel.FtpException.NO_SUCH_FILE) { mkdir_p(path); - try { - ftp.mkdir(path); - return; - } catch (SftpException je2) { - je = je2; - } + } else { + err = je; } - - throw new TransportException(MessageFormat.format( - JGitText.get().cannotMkdirObjectPath, objectsPath, path, - je.getMessage()), je); } + if (err == null) { + try { + ftp.mkdir(path); + return; + } catch (IOException e) { + err = e; + } + } + throw new TransportException(MessageFormat.format( + JGitText.get().cannotMkdirObjectPath, objectsPath, path, + err.getMessage()), err); } Map<String, Ref> readAdvertisedRefs() throws TransportException { @@ -400,34 +389,33 @@ public class TransportSftp extends SshTransport implements WalkTransport { return avail; } - @SuppressWarnings("unchecked") - private void readLooseRefs(final TreeMap<String, Ref> avail, - final String dir, final String prefix) - throws TransportException { - final Collection<ChannelSftp.LsEntry> list; + private void readLooseRefs(TreeMap<String, Ref> avail, String dir, + String prefix) throws TransportException { + final Collection<FtpChannel.DirEntry> list; try { list = ftp.ls(dir); - } catch (SftpException je) { + } catch (IOException e) { throw new TransportException(MessageFormat.format( JGitText.get().cannotListObjectsPath, objectsPath, dir, - je.getMessage()), je); + e.getMessage()), e); } - for (ChannelSftp.LsEntry ent : list) { - final String n = ent.getFilename(); + for (FtpChannel.DirEntry ent : list) { + String n = ent.getFilename(); if (".".equals(n) || "..".equals(n)) //$NON-NLS-1$ //$NON-NLS-2$ continue; - final String nPath = dir + "/" + n; //$NON-NLS-1$ - if (ent.getAttrs().isDir()) + String nPath = dir + "/" + n; //$NON-NLS-1$ + if (ent.isDirectory()) { readLooseRefs(avail, nPath, prefix + n + "/"); //$NON-NLS-1$ - else + } else { readRef(avail, nPath, prefix + n); + } } } - private Ref readRef(final TreeMap<String, Ref> avail, - final String path, final String name) throws TransportException { + private Ref readRef(TreeMap<String, Ref> avail, String path, + String name) throws TransportException { final String line; try (BufferedReader br = openReader(path)) { line = br.readLine(); @@ -439,10 +427,10 @@ public class TransportSftp extends SshTransport implements WalkTransport { err.getMessage()), err); } - if (line == null) + if (line == null) { throw new TransportException( MessageFormat.format(JGitText.get().emptyRef, name)); - + } if (line.startsWith("ref: ")) { //$NON-NLS-1$ final String target = line.substring("ref: ".length()); //$NON-NLS-1$ Ref r = avail.get(target); @@ -467,8 +455,9 @@ public class TransportSftp extends SshTransport implements WalkTransport { } private Storage loose(Ref r) { - if (r != null && r.getStorage() == Storage.PACKED) + if (r != null && r.getStorage() == Storage.PACKED) { return Storage.LOOSE_PACKED; + } return Storage.LOOSE; } @@ -476,8 +465,9 @@ public class TransportSftp extends SshTransport implements WalkTransport { void close() { if (ftp != null) { try { - if (ftp.isConnected()) + if (ftp.isConnected()) { ftp.disconnect(); + } } finally { ftp = null; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java index 026fd819c4..70fb1f0e56 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java @@ -48,10 +48,11 @@ package org.eclipse.jgit.transport; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.Serializable; -import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.net.URL; import java.util.BitSet; @@ -282,12 +283,7 @@ public class URIish implements Serializable { if (s.indexOf('%') < 0) return s; - byte[] bytes; - try { - bytes = s.getBytes(Constants.CHARACTER_ENCODING); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // can't happen - } + byte[] bytes = s.getBytes(UTF_8); byte[] os = new byte[bytes.length]; int j = 0; @@ -335,12 +331,7 @@ public class URIish implements Serializable { if (s == null) return null; ByteArrayOutputStream os = new ByteArrayOutputStream(s.length()); - byte[] bytes; - try { - bytes = s.getBytes(Constants.CHARACTER_ENCODING); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // cannot happen - } + byte[] bytes = s.getBytes(UTF_8); for (int i = 0; i < bytes.length; ++i) { int b = bytes[i] & 0xFF; if (b <= 32 || (encodeNonAscii && b > 127) || b == '%' diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index 48a3e0b38f..2fbcaa2928 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -49,6 +49,7 @@ import static org.eclipse.jgit.lib.Constants.R_TAGS; import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT; import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH; import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS; +import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT; @@ -70,11 +71,11 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UncheckedIOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -88,6 +89,7 @@ import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.pack.PackWriter; +import org.eclipse.jgit.internal.transport.parser.FirstWant; import org.eclipse.jgit.lib.BitmapIndex; import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder; import org.eclipse.jgit.lib.Constants; @@ -96,6 +98,7 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.AsyncRevObjectQueue; import org.eclipse.jgit.revwalk.BitmapWalker; @@ -179,44 +182,53 @@ public class UploadPack { throws PackProtocolException, IOException; } - /** Data in the first line of a request, the line itself plus options. */ + /** + * Data in the first line of a want-list, the line itself plus options. + * + * @deprecated Use {@link FirstWant} instead + */ + @Deprecated public static class FirstLine { - private final String line; - private final Set<String> options; + + private final FirstWant firstWant; /** - * Parse the first line of a receive-pack request. - * * @param line * line from the client. */ public FirstLine(String line) { - if (line.length() > 45) { - final HashSet<String> opts = new HashSet<>(); - String opt = line.substring(45); - if (opt.startsWith(" ")) //$NON-NLS-1$ - opt = opt.substring(1); - for (String c : opt.split(" ")) //$NON-NLS-1$ - opts.add(c); - this.line = line.substring(0, 45); - this.options = Collections.unmodifiableSet(opts); - } else { - this.line = line; - this.options = Collections.emptySet(); + try { + firstWant = FirstWant.fromLine(line); + } catch (PackProtocolException e) { + throw new UncheckedIOException(e); } } /** @return non-capabilities part of the line. */ public String getLine() { - return line; + return firstWant.getLine(); } - /** @return options parsed from the line. */ + /** @return capabilities parsed from the line. */ public Set<String> getOptions() { - return options; + if (firstWant.getAgent() != null) { + Set<String> caps = new HashSet<>(firstWant.getCapabilities()); + caps.add(OPTION_AGENT + '=' + firstWant.getAgent()); + return caps; + } + return firstWant.getCapabilities(); } } + /* + * {@link java.util.function.Consumer} doesn't allow throwing checked + * exceptions. Define our own to propagate IOExceptions. + */ + @FunctionalInterface + private static interface IOConsumer<R> { + void accept(R t) throws IOException; + } + /** Database we read the objects from. */ private final Repository db; @@ -288,12 +300,11 @@ public class UploadPack { /** Hook for taking post upload actions. */ private PostUploadHook postUploadHook = PostUploadHook.NULL; - /** Capabilities requested by the client. */ - private Set<String> options; + /** Caller user agent */ String userAgent; /** Raw ObjectIds the client has asked for, before validating them. */ - private final Set<ObjectId> wantIds = new HashSet<>(); + private Set<ObjectId> wantIds = new HashSet<>(); /** Objects the client wants to obtain. */ private final Set<RevObject> wantAll = new HashSet<>(); @@ -301,25 +312,6 @@ public class UploadPack { /** Objects on both sides, these don't have to be sent. */ private final Set<RevObject> commonBase = new HashSet<>(); - /** Shallow commits the client already has. */ - private Set<ObjectId> clientShallowCommits = new HashSet<>(); - - /** Desired depth from the client on a shallow request. */ - private int depth; - - /** - * Commit time of the newest objects the client has asked us using - * --shallow-since not to send. Cannot be nonzero if depth is nonzero. - */ - private int shallowSince; - - /** - * (Possibly short) ref names, ancestors of which the client has asked us - * not to send using --shallow-exclude. Cannot be non-empty if depth is - * nonzero. - */ - private List<String> deepenNotRefs = new ArrayList<>(); - /** Commit time of the oldest common commit, in seconds. */ private int oldestTime; @@ -353,7 +345,14 @@ public class UploadPack { private PackStatistics statistics; - private long filterBlobLimit = -1; + /** + * Request this instance is handling. + * + * We need to keep a reference to it for {@link PreUploadHook pre upload + * hooks}. They receive a reference this instance and invoke methods like + * getDepth() to get information about the request. + */ + private FetchRequest currentRequest; /** * Create a new pack upload for an open repository. @@ -695,10 +694,12 @@ public class UploadPack { * read. */ public boolean isSideBand() throws RequestNotYetReadException { - if (options == null) + if (currentRequest == null) { throw new RequestNotYetReadException(); - return (options.contains(OPTION_SIDE_BAND) - || options.contains(OPTION_SIDE_BAND_64K)); + } + Set<String> caps = currentRequest.getClientCapabilities(); + return caps.contains(OPTION_SIDE_BAND) + || caps.contains(OPTION_SIDE_BAND_64K); } /** @@ -829,12 +830,10 @@ public class UploadPack { } if (refs == null) { // Fast path: the advertised refs hook did not set advertised refs. - Map<String, Ref> rs = new HashMap<>(); - for (String p : refPrefixes) { - for (Ref r : db.getRefDatabase().getRefsByPrefix(p)) { - rs.put(r.getName(), r); - } - } + String[] prefixes = refPrefixes.toArray(new String[0]); + Map<String, Ref> rs = + db.getRefDatabase().getRefsByPrefix(prefixes).stream() + .collect(toMap(Ref::getName, identity(), (a, b) -> b)); if (refFilter != RefFilter.DEFAULT) { return refFilter.filter(rs); } @@ -880,12 +879,45 @@ public class UploadPack { return getAdvertisedOrDefaultRefs().get(name); } + /** + * Find a ref in the usual search path on behalf of the client. + * <p> + * This checks that the ref is present in the ref advertisement since + * otherwise the client might not be supposed to be able to read it. + * + * @param name + * short name of the ref to find, e.g. "master" to find + * "refs/heads/master". + * @return the requested Ref, or {@code null} if it is not visible or + * does not exist. + * @throws java.io.IOException + * on failure to read the ref or check it for visibility. + */ + @Nullable + private Ref findRef(String name) throws IOException { + if (refs != null) { + return RefDatabase.findRef(refs, name); + } + if (!advertiseRefsHookCalled) { + advertiseRefsHook.advertiseRefs(this); + advertiseRefsHookCalled = true; + } + if (refs == null && + refFilter == RefFilter.DEFAULT && + transferConfig.hasDefaultRefFilter()) { + // Fast path: no ref filtering is needed. + return db.getRefDatabase().getRef(name); + } + return RefDatabase.findRef(getAdvertisedOrDefaultRefs(), name); + } + private void service() throws IOException { boolean sendPack = false; // If it's a non-bidi request, we need to read the entire request before // writing a response. Buffer the response until then. PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator(); List<ObjectId> unshallowCommits = new ArrayList<>(); + FetchRequest req; try { if (biDirectionalPipe) sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut)); @@ -896,29 +928,46 @@ public class UploadPack { long negotiateStart = System.currentTimeMillis(); accumulator.advertised = advertised.size(); - recvWants(); - if (wantIds.isEmpty()) { - preUploadHook.onBeginNegotiateRound(this, wantIds, 0); - preUploadHook.onEndNegotiateRound(this, wantIds, 0, 0, false); + + ProtocolV0Parser parser = new ProtocolV0Parser(transferConfig); + req = parser.recvWants(pckIn); + currentRequest = req; + + wantIds = req.getWantIds(); + + if (req.getWantIds().isEmpty()) { + preUploadHook.onBeginNegotiateRound(this, req.getWantIds(), 0); + preUploadHook.onEndNegotiateRound(this, req.getWantIds(), 0, 0, + false); return; } - accumulator.wants = wantIds.size(); + accumulator.wants = req.getWantIds().size(); - if (options.contains(OPTION_MULTI_ACK_DETAILED)) { + if (req.getClientCapabilities().contains(OPTION_MULTI_ACK_DETAILED)) { multiAck = MultiAck.DETAILED; - noDone = options.contains(OPTION_NO_DONE); - } else if (options.contains(OPTION_MULTI_ACK)) + noDone = req.getClientCapabilities().contains(OPTION_NO_DONE); + } else if (req.getClientCapabilities().contains(OPTION_MULTI_ACK)) multiAck = MultiAck.CONTINUE; else multiAck = MultiAck.OFF; - if (!clientShallowCommits.isEmpty()) - verifyClientShallow(clientShallowCommits); - if (depth != 0) - processShallow(null, unshallowCommits, true); - if (!clientShallowCommits.isEmpty()) - walk.assumeShallow(clientShallowCommits); - sendPack = negotiate(accumulator); + if (!req.getClientShallowCommits().isEmpty()) { + verifyClientShallow(req.getClientShallowCommits()); + } + + if (req.getDepth() != 0 || req.getDeepenSince() != 0) { + computeShallowsAndUnshallows(req, shallow -> { + pckOut.writeString("shallow " + shallow.name() + '\n'); //$NON-NLS-1$ + }, unshallow -> { + pckOut.writeString("unshallow " + unshallow.name() + '\n'); //$NON-NLS-1$ + unshallowCommits.add(unshallow); + }, Collections.emptyList()); + pckOut.end(); + } + + if (!req.getClientShallowCommits().isEmpty()) + walk.assumeShallow(req.getClientShallowCommits()); + sendPack = negotiate(req, accumulator); accumulator.timeNegotiating += System.currentTimeMillis() - negotiateStart; @@ -968,35 +1017,14 @@ public class UploadPack { } if (sendPack) { - sendPack(accumulator, refs == null ? null : refs.values(), unshallowCommits); + sendPack(accumulator, req, refs == null ? null : refs.values(), + unshallowCommits, Collections.emptyList()); } } private void lsRefsV2() throws IOException { - LsRefsV2Request.Builder builder = LsRefsV2Request.builder(); - List<String> prefixes = new ArrayList<>(); - String line = pckIn.readString(); - // Currently, we do not support any capabilities, so the next - // line is DELIM if there are arguments or END if not. - if (line == PacketLineIn.DELIM) { - while ((line = pckIn.readString()) != PacketLineIn.END) { - if (line.equals("peel")) { //$NON-NLS-1$ - builder.setPeel(true); - } else if (line.equals("symrefs")) { //$NON-NLS-1$ - builder.setSymrefs(true); - } else if (line.startsWith("ref-prefix ")) { //$NON-NLS-1$ - prefixes.add(line.substring("ref-prefix ".length())); //$NON-NLS-1$ - } else { - throw new PackProtocolException(MessageFormat - .format(JGitText.get().unexpectedPacketLine, line)); - } - } - } else if (line != PacketLineIn.END) { - throw new PackProtocolException(MessageFormat - .format(JGitText.get().unexpectedPacketLine, line)); - } - LsRefsV2Request req = builder.setRefPrefixes(prefixes).build(); - + ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig); + LsRefsV2Request req = parser.parseLsRefsRequest(pckIn); protocolV2Hook.onLsRefs(req); rawOut.stopBuffering(); @@ -1029,20 +1057,23 @@ public class UploadPack { ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig); FetchV2Request req = parser.parseFetchRequest(pckIn); + currentRequest = req; rawOut.stopBuffering(); protocolV2Hook.onFetch(req); // TODO(ifrade): Refactor to pass around the Request object, instead of // copying data back to class fields - options = req.getOptions(); - clientShallowCommits = req.getClientShallowCommits(); - depth = req.getDepth(); - shallowSince = req.getDeepenSince(); - filterBlobLimit = req.getFilterBlobLimit(); - deepenNotRefs = req.getDeepenNotRefs(); - - wantIds.addAll(req.getWantsIds()); + List<ObjectId> deepenNots = new ArrayList<>(); + for (String s : req.getDeepenNotRefs()) { + Ref ref = findRef(s); + if (ref == null) { + throw new PackProtocolException(MessageFormat + .format(JGitText.get().invalidRefName, s)); + } + deepenNots.add(ref.getObjectId()); + } + Map<String, ObjectId> wantedRefs = new TreeMap<>(); for (String refName : req.getWantedRefs()) { Ref ref = getRef(refName); @@ -1055,21 +1086,27 @@ public class UploadPack { throw new PackProtocolException(MessageFormat .format(JGitText.get().invalidRefName, refName)); } - wantIds.add(oid); + // TODO(ifrade): Avoid mutating the parsed request. + req.getWantIds().add(oid); wantedRefs.put(refName, oid); } + wantIds = req.getWantIds(); boolean sectionSent = false; - @Nullable List<ObjectId> shallowCommits = null; + boolean mayHaveShallow = req.getDepth() != 0 + || req.getDeepenSince() != 0 + || !req.getDeepenNotRefs().isEmpty(); + List<ObjectId> shallowCommits = new ArrayList<>(); List<ObjectId> unshallowCommits = new ArrayList<>(); if (!req.getClientShallowCommits().isEmpty()) { verifyClientShallow(req.getClientShallowCommits()); } - if (req.getDepth() != 0 || req.getDeepenSince() != 0 - || !req.getDeepenNotRefs().isEmpty()) { - shallowCommits = new ArrayList<>(); - processShallow(shallowCommits, unshallowCommits, false); + if (mayHaveShallow) { + computeShallowsAndUnshallows(req, + shallowCommit -> shallowCommits.add(shallowCommit), + unshallowCommit -> unshallowCommits.add(unshallowCommit), + deepenNots); } if (!req.getClientShallowCommits().isEmpty()) walk.assumeShallow(req.getClientShallowCommits()); @@ -1095,7 +1132,7 @@ public class UploadPack { } if (req.wasDoneReceived() || okToGiveUp()) { - if (shallowCommits != null) { + if (mayHaveShallow) { if (sectionSent) pckOut.writeDelim(); pckOut.writeString("shallow-info\n"); //$NON-NLS-1$ @@ -1125,10 +1162,11 @@ public class UploadPack { pckOut.writeDelim(); pckOut.writeString("packfile\n"); //$NON-NLS-1$ sendPack(new PackStatistics.Accumulator(), - req.getOptions().contains(OPTION_INCLUDE_TAG) + req, + req.getClientCapabilities().contains(OPTION_INCLUDE_TAG) ? db.getRefDatabase().getRefsByPrefix(R_TAGS) : null, - unshallowCommits); + unshallowCommits, deepenNots); // sendPack invokes pckOut.end() for us, so we do not // need to invoke it here. } else { @@ -1179,6 +1217,7 @@ public class UploadPack { (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "") + //$NON-NLS-1$ (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "") + //$NON-NLS-1$ OPTION_SHALLOW); + caps.add(CAPABILITY_SERVER_OPTION); return caps; } @@ -1227,28 +1266,28 @@ public class UploadPack { } /* - * Determines what "shallow" and "unshallow" lines to send to the user. - * The information is written to shallowCommits (if not null) and - * unshallowCommits, and also written to #pckOut (if writeToPckOut is - * true). + * Determines what object ids must be marked as shallow or unshallow for the + * client. */ - private void processShallow(@Nullable List<ObjectId> shallowCommits, - List<ObjectId> unshallowCommits, - boolean writeToPckOut) throws IOException { - if (options.contains(OPTION_DEEPEN_RELATIVE) || - shallowSince != 0 || - !deepenNotRefs.isEmpty()) { - // TODO(jonathantanmy): Implement deepen-relative, deepen-since, - // and deepen-not. + private void computeShallowsAndUnshallows(FetchRequest req, + IOConsumer<ObjectId> shallowFunc, + IOConsumer<ObjectId> unshallowFunc, + List<ObjectId> deepenNots) + throws IOException { + if (req.getClientCapabilities().contains(OPTION_DEEPEN_RELATIVE)) { + // TODO(jonathantanmy): Implement deepen-relative throw new UnsupportedOperationException(); } - int walkDepth = depth - 1; + int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE + : req.getDepth() - 1; try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk( walk.getObjectReader(), walkDepth)) { + depthWalk.setDeepenSince(req.getDeepenSince()); + // Find all the commits which will be shallow - for (ObjectId o : wantIds) { + for (ObjectId o : req.getWantIds()) { try { depthWalk.markRoot(depthWalk.parseCommit(o)); } catch (IncorrectObjectTypeException notCommit) { @@ -1256,35 +1295,32 @@ public class UploadPack { } } + depthWalk.setDeepenNots(deepenNots); + RevCommit o; + boolean atLeastOne = false; while ((o = depthWalk.next()) != null) { DepthWalk.Commit c = (DepthWalk.Commit) o; + atLeastOne = true; + + boolean isBoundary = (c.getDepth() == walkDepth) || c.isBoundary(); // Commits at the boundary which aren't already shallow in // the client need to be marked as such - if (c.getDepth() == walkDepth - && !clientShallowCommits.contains(c)) { - if (shallowCommits != null) { - shallowCommits.add(c.copy()); - } - if (writeToPckOut) { - pckOut.writeString("shallow " + o.name()); //$NON-NLS-1$ - } + if (isBoundary && !req.getClientShallowCommits().contains(c)) { + shallowFunc.accept(c.copy()); } // Commits not on the boundary which are shallow in the client // need to become unshallowed - if (c.getDepth() < walkDepth - && clientShallowCommits.remove(c)) { - unshallowCommits.add(c.copy()); - if (writeToPckOut) { - pckOut.writeString("unshallow " + c.name()); //$NON-NLS-1$ - } + if (!isBoundary && req.getClientShallowCommits().remove(c)) { + unshallowFunc.accept(c.copy()); } } - } - if (writeToPckOut) { - pckOut.end(); + if (!atLeastOne) { + throw new PackProtocolException( + JGitText.get().noCommitsSelectedForShallow); + } } } @@ -1436,67 +1472,6 @@ public class UploadPack { return msgOut; } - private void recvWants() throws IOException { - boolean isFirst = true; - boolean filterReceived = false; - for (;;) { - String line; - try { - line = pckIn.readString(); - } catch (EOFException eof) { - if (isFirst) - break; - throw eof; - } - - if (line == PacketLineIn.END) - break; - - if (line.startsWith("deepen ")) { //$NON-NLS-1$ - depth = Integer.parseInt(line.substring(7)); - if (depth <= 0) { - throw new PackProtocolException( - MessageFormat.format(JGitText.get().invalidDepth, - Integer.valueOf(depth))); - } - continue; - } - - if (line.startsWith("shallow ")) { //$NON-NLS-1$ - clientShallowCommits.add(ObjectId.fromString(line.substring(8))); - continue; - } - - if (transferConfig.isAllowFilter() - && line.startsWith(OPTION_FILTER + " ")) { //$NON-NLS-1$ - String arg = line.substring(OPTION_FILTER.length() + 1); - - if (filterReceived) { - throw new PackProtocolException(JGitText.get().tooManyFilters); - } - filterReceived = true; - - filterBlobLimit = ProtocolV2Parser.filterLine(arg); - continue; - } - - if (!line.startsWith("want ") || line.length() < 45) //$NON-NLS-1$ - throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line)); //$NON-NLS-1$ - - if (isFirst) { - if (line.length() > 45) { - FirstLine firstLine = new FirstLine(line); - options = firstLine.getOptions(); - line = firstLine.getLine(); - } else - options = Collections.emptySet(); - } - - wantIds.add(ObjectId.fromString(line.substring(5))); - isFirst = false; - } - } - /** * Returns the clone/fetch depth. Valid only after calling recvWants(). A * depth of 1 means return only the wants. @@ -1505,9 +1480,9 @@ public class UploadPack { * @since 4.0 */ public int getDepth() { - if (options == null) + if (currentRequest == null) throw new RequestNotYetReadException(); - return depth; + return currentRequest.getDepth(); } /** @@ -1526,10 +1501,15 @@ public class UploadPack { * @since 4.0 */ public String getPeerUserAgent() { - return UserAgent.getAgent(options, userAgent); + if (currentRequest != null && currentRequest.getAgent() != null) { + return currentRequest.getAgent(); + } + + return userAgent; } - private boolean negotiate(PackStatistics.Accumulator accumulator) + private boolean negotiate(FetchRequest req, + PackStatistics.Accumulator accumulator) throws IOException { okToGiveUp = Boolean.FALSE; @@ -1545,7 +1525,7 @@ public class UploadPack { // disconnected, and will try another request with actual want/have. // Don't report the EOF here, its a bug in the protocol that the client // just disconnects without sending an END. - if (!biDirectionalPipe && depth > 0) + if (!biDirectionalPipe && req.getDepth() > 0) return false; throw eof; } @@ -1926,25 +1906,31 @@ public class UploadPack { * Send the requested objects to the client. * * @param accumulator - * where to write statistics about the content of the pack. + * where to write statistics about the content of the pack. + * @param req + * request in process * @param allTags - * refs to search for annotated tags to include in the pack - * if the {@link #OPTION_INCLUDE_TAG} capability was - * requested. + * refs to search for annotated tags to include in the pack if + * the {@link #OPTION_INCLUDE_TAG} capability was requested. * @param unshallowCommits - * shallow commits on the client that are now becoming - * unshallow + * shallow commits on the client that are now becoming unshallow + * @param deepenNots + * objects that the client specified using --shallow-exclude * @throws IOException - * if an error occured while generating or writing the pack. + * if an error occurred while generating or writing the pack. */ private void sendPack(PackStatistics.Accumulator accumulator, + FetchRequest req, @Nullable Collection<Ref> allTags, - List<ObjectId> unshallowCommits) throws IOException { - final boolean sideband = options.contains(OPTION_SIDE_BAND) - || options.contains(OPTION_SIDE_BAND_64K); + List<ObjectId> unshallowCommits, + List<ObjectId> deepenNots) throws IOException { + Set<String> caps = req.getClientCapabilities(); + boolean sideband = caps.contains(OPTION_SIDE_BAND) + || caps.contains(OPTION_SIDE_BAND_64K); if (sideband) { try { - sendPack(true, accumulator, allTags, unshallowCommits); + sendPack(true, req, accumulator, allTags, unshallowCommits, + deepenNots); } catch (ServiceMayNotContinueException noPack) { // This was already reported on (below). throw noPack; @@ -1965,7 +1951,7 @@ public class UploadPack { throw err; } } else { - sendPack(false, accumulator, allTags, unshallowCommits); + sendPack(false, req, accumulator, allTags, unshallowCommits, deepenNots); } } @@ -1989,35 +1975,39 @@ public class UploadPack { * Send the requested objects to the client. * * @param sideband - * whether to wrap the pack in side-band pkt-lines, - * interleaved with progress messages and errors. + * whether to wrap the pack in side-band pkt-lines, interleaved + * with progress messages and errors. + * @param req + * request being processed * @param accumulator - * where to write statistics about the content of the pack. + * where to write statistics about the content of the pack. * @param allTags - * refs to search for annotated tags to include in the pack - * if the {@link #OPTION_INCLUDE_TAG} capability was - * requested. + * refs to search for annotated tags to include in the pack if + * the {@link #OPTION_INCLUDE_TAG} capability was requested. * @param unshallowCommits - * shallow commits on the client that are now becoming - * unshallow + * shallow commits on the client that are now becoming unshallow + * @param deepenNots + * objects that the client specified using --shallow-exclude * @throws IOException - * if an error occured while generating or writing the pack. + * if an error occurred while generating or writing the pack. */ private void sendPack(final boolean sideband, + FetchRequest req, PackStatistics.Accumulator accumulator, @Nullable Collection<Ref> allTags, - List<ObjectId> unshallowCommits) throws IOException { + List<ObjectId> unshallowCommits, + List<ObjectId> deepenNots) throws IOException { ProgressMonitor pm = NullProgressMonitor.INSTANCE; OutputStream packOut = rawOut; if (sideband) { int bufsz = SideBandOutputStream.SMALL_BUF; - if (options.contains(OPTION_SIDE_BAND_64K)) + if (req.getClientCapabilities().contains(OPTION_SIDE_BAND_64K)) bufsz = SideBandOutputStream.MAX_BUF; packOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA, bufsz, rawOut); - if (!options.contains(OPTION_NO_PROGRESS)) { + if (!req.getClientCapabilities().contains(OPTION_NO_PROGRESS)) { msgOut = new SideBandOutputStream( SideBandOutputStream.CH_PROGRESS, bufsz, rawOut); pm = new SideBandProgressMonitor(msgOut); @@ -2053,17 +2043,20 @@ public class UploadPack { accumulator); try { pw.setIndexDisabled(true); - if (filterBlobLimit >= 0) { - pw.setFilterBlobLimit(filterBlobLimit); + if (req.getFilterBlobLimit() >= 0) { + pw.setFilterBlobLimit(req.getFilterBlobLimit()); pw.setUseCachedPacks(false); } else { pw.setUseCachedPacks(true); } - pw.setUseBitmaps(depth == 0 && clientShallowCommits.isEmpty()); - pw.setClientShallowCommits(clientShallowCommits); + pw.setUseBitmaps( + req.getDepth() == 0 + && req.getClientShallowCommits().isEmpty()); + pw.setClientShallowCommits(req.getClientShallowCommits()); pw.setReuseDeltaCommits(true); - pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA)); - pw.setThin(options.contains(OPTION_THIN_PACK)); + pw.setDeltaBaseAsOffset( + req.getClientCapabilities().contains(OPTION_OFS_DELTA)); + pw.setThin(req.getClientCapabilities().contains(OPTION_THIN_PACK)); pw.setReuseValidatingObjects(false); // Objects named directly by references go at the beginning @@ -2082,14 +2075,22 @@ public class UploadPack { } RevWalk rw = walk; - if (depth > 0) { - pw.setShallowPack(depth, unshallowCommits); - rw = new DepthWalk.RevWalk(walk.getObjectReader(), depth - 1); - rw.assumeShallow(clientShallowCommits); + if (req.getDepth() > 0 || req.getDeepenSince() != 0 || !deepenNots.isEmpty()) { + int walkDepth = req.getDepth() == 0 ? Integer.MAX_VALUE + : req.getDepth() - 1; + pw.setShallowPack(req.getDepth(), unshallowCommits); + + DepthWalk.RevWalk dw = new DepthWalk.RevWalk( + walk.getObjectReader(), walkDepth); + dw.setDeepenSince(req.getDeepenSince()); + dw.setDeepenNots(deepenNots); + dw.assumeShallow(req.getClientShallowCommits()); + rw = dw; } if (wantAll.isEmpty()) { - pw.preparePack(pm, wantIds, commonBase, clientShallowCommits); + pw.preparePack(pm, wantIds, commonBase, + req.getClientShallowCommits()); } else { walk.reset(); @@ -2098,7 +2099,8 @@ public class UploadPack { rw = ow; } - if (options.contains(OPTION_INCLUDE_TAG) && allTags != null) { + if (req.getClientCapabilities().contains(OPTION_INCLUDE_TAG) + && allTags != null) { for (Ref ref : allTags) { ObjectId objectId = ref.getObjectId(); if (objectId == null) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java index d815bc354e..c38b00287b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java @@ -58,6 +58,8 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; import javax.net.ssl.TrustManager; +import org.eclipse.jgit.annotations.NonNull; + /** * The interface of connections used during HTTP communication. This interface * is that subset of the interface exposed by {@link java.net.HttpURLConnection} @@ -69,25 +71,25 @@ public interface HttpConnection { /** * @see HttpURLConnection#HTTP_OK */ - public static final int HTTP_OK = java.net.HttpURLConnection.HTTP_OK; + int HTTP_OK = java.net.HttpURLConnection.HTTP_OK; /** * @see HttpURLConnection#HTTP_MOVED_PERM * @since 4.7 */ - public static final int HTTP_MOVED_PERM = java.net.HttpURLConnection.HTTP_MOVED_PERM; + int HTTP_MOVED_PERM = java.net.HttpURLConnection.HTTP_MOVED_PERM; /** * @see HttpURLConnection#HTTP_MOVED_TEMP * @since 4.9 */ - public static final int HTTP_MOVED_TEMP = java.net.HttpURLConnection.HTTP_MOVED_TEMP; + int HTTP_MOVED_TEMP = java.net.HttpURLConnection.HTTP_MOVED_TEMP; /** * @see HttpURLConnection#HTTP_SEE_OTHER * @since 4.9 */ - public static final int HTTP_SEE_OTHER = java.net.HttpURLConnection.HTTP_SEE_OTHER; + int HTTP_SEE_OTHER = java.net.HttpURLConnection.HTTP_SEE_OTHER; /** * HTTP 1.1 additional MOVED_TEMP status code; value = 307. @@ -95,22 +97,22 @@ public interface HttpConnection { * @see #HTTP_MOVED_TEMP * @since 4.9 */ - public static final int HTTP_11_MOVED_TEMP = 307; + int HTTP_11_MOVED_TEMP = 307; /** * @see HttpURLConnection#HTTP_NOT_FOUND */ - public static final int HTTP_NOT_FOUND = java.net.HttpURLConnection.HTTP_NOT_FOUND; + int HTTP_NOT_FOUND = java.net.HttpURLConnection.HTTP_NOT_FOUND; /** * @see HttpURLConnection#HTTP_UNAUTHORIZED */ - public static final int HTTP_UNAUTHORIZED = java.net.HttpURLConnection.HTTP_UNAUTHORIZED; + int HTTP_UNAUTHORIZED = java.net.HttpURLConnection.HTTP_UNAUTHORIZED; /** * @see HttpURLConnection#HTTP_FORBIDDEN */ - public static final int HTTP_FORBIDDEN = java.net.HttpURLConnection.HTTP_FORBIDDEN; + int HTTP_FORBIDDEN = java.net.HttpURLConnection.HTTP_FORBIDDEN; /** * Get response code @@ -119,7 +121,7 @@ public interface HttpConnection { * @return the HTTP Status-Code, or -1 * @throws java.io.IOException */ - public int getResponseCode() throws IOException; + int getResponseCode() throws IOException; /** * Get URL @@ -127,7 +129,7 @@ public interface HttpConnection { * @see HttpURLConnection#getURL() * @return the URL. */ - public URL getURL(); + URL getURL(); /** * Get response message @@ -136,15 +138,15 @@ public interface HttpConnection { * @return the HTTP response message, or <code>null</code> * @throws java.io.IOException */ - public String getResponseMessage() throws IOException; + String getResponseMessage() throws IOException; /** - * Get list of header fields + * Get map of header fields * * @see HttpURLConnection#getHeaderFields() * @return a Map of header fields */ - public Map<String, List<String>> getHeaderFields(); + Map<String, List<String>> getHeaderFields(); /** * Set request property @@ -156,7 +158,7 @@ public interface HttpConnection { * @param value * the value associated with it. */ - public void setRequestProperty(String key, String value); + void setRequestProperty(String key, String value); /** * Set request method @@ -170,7 +172,7 @@ public interface HttpConnection { * @throws java.net.ProtocolException * if any. */ - public void setRequestMethod(String method) + void setRequestMethod(String method) throws ProtocolException; /** @@ -181,7 +183,7 @@ public interface HttpConnection { * a <code>boolean</code> indicating whether or not to allow * caching */ - public void setUseCaches(boolean usecaches); + void setUseCaches(boolean usecaches); /** * Set connect timeout @@ -191,7 +193,7 @@ public interface HttpConnection { * an <code>int</code> that specifies the connect timeout value * in milliseconds */ - public void setConnectTimeout(int timeout); + void setConnectTimeout(int timeout); /** * Set read timeout @@ -201,7 +203,7 @@ public interface HttpConnection { * an <code>int</code> that specifies the timeout value to be * used in milliseconds */ - public void setReadTimeout(int timeout); + void setReadTimeout(int timeout); /** * Get content type @@ -210,7 +212,7 @@ public interface HttpConnection { * @return the content type of the resource that the URL references, or * <code>null</code> if not known. */ - public String getContentType(); + String getContentType(); /** * Get input stream @@ -222,10 +224,16 @@ public interface HttpConnection { * @throws java.io.IOException * if any. */ - public InputStream getInputStream() throws IOException; + InputStream getInputStream() throws IOException; /** - * Get header field + * Get header field. According to + * {@link <a href="https://tools.ietf.org/html/rfc2616#section-4.2">RFC + * 2616</a>} header field names are case insensitive. Header fields defined + * as a comma separated list can have multiple header fields with the same + * field name. This method only returns one of these header fields. If you + * want the union of all values of all multiple header fields with the same + * field name then use {@link #getHeaderFields(String)} * * @see HttpURLConnection#getHeaderField(String) * @param name @@ -233,7 +241,22 @@ public interface HttpConnection { * @return the value of the named header field, or <code>null</code> if * there is no such field in the header. */ - public String getHeaderField(String name); + String getHeaderField(@NonNull String name); + + /** + * Get all values of given header field. According to + * {@link <a href="https://tools.ietf.org/html/rfc2616#section-4.2">RFC + * 2616</a>} header field names are case insensitive. Header fields defined + * as a comma separated list can have multiple header fields with the same + * field name. This method does not validate if the given header field is + * defined as a comma separated list. + * + * @param name + * the name of a header field. + * @return the list of values of the named header field + * @since 5.2 + */ + List<String> getHeaderFields(@NonNull String name); /** * Get content length @@ -243,7 +266,7 @@ public interface HttpConnection { * references, {@code -1} if the content length is not known, or if * the content length is greater than Integer.MAX_VALUE. */ - public int getContentLength(); + int getContentLength(); /** * Set whether or not to follow HTTP redirects. @@ -253,7 +276,7 @@ public interface HttpConnection { * a <code>boolean</code> indicating whether or not to follow * HTTP redirects. */ - public void setInstanceFollowRedirects(boolean followRedirects); + void setInstanceFollowRedirects(boolean followRedirects); /** * Set if to do output @@ -262,7 +285,7 @@ public interface HttpConnection { * @param dooutput * the new value. */ - public void setDoOutput(boolean dooutput); + void setDoOutput(boolean dooutput); /** * Set fixed length streaming mode @@ -271,7 +294,7 @@ public interface HttpConnection { * @param contentLength * The number of bytes which will be written to the OutputStream. */ - public void setFixedLengthStreamingMode(int contentLength); + void setFixedLengthStreamingMode(int contentLength); /** * Get output stream @@ -280,7 +303,7 @@ public interface HttpConnection { * @return an output stream that writes to this connection. * @throws java.io.IOException */ - public OutputStream getOutputStream() throws IOException; + OutputStream getOutputStream() throws IOException; /** * Set chunked streaming mode @@ -290,7 +313,7 @@ public interface HttpConnection { * The number of bytes to write in each chunk. If chunklen is * less than or equal to zero, a default value will be used. */ - public void setChunkedStreamingMode(int chunklen); + void setChunkedStreamingMode(int chunklen); /** * Get request method @@ -298,7 +321,7 @@ public interface HttpConnection { * @see HttpURLConnection#getRequestMethod() * @return the HTTP request method */ - public String getRequestMethod(); + String getRequestMethod(); /** * Whether we use a proxy @@ -306,7 +329,7 @@ public interface HttpConnection { * @see HttpURLConnection#usingProxy() * @return a boolean indicating if the connection is using a proxy. */ - public boolean usingProxy(); + boolean usingProxy(); /** * Connect @@ -314,7 +337,7 @@ public interface HttpConnection { * @see HttpURLConnection#connect() * @throws java.io.IOException */ - public void connect() throws IOException; + void connect() throws IOException; /** * Configure the connection so that it can be used for https communication. @@ -332,7 +355,7 @@ public interface HttpConnection { * @throws java.security.NoSuchAlgorithmException * @throws java.security.KeyManagementException */ - public void configure(KeyManager[] km, TrustManager[] tm, + void configure(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws NoSuchAlgorithmException, KeyManagementException; @@ -345,6 +368,6 @@ public interface HttpConnection { * @throws java.security.NoSuchAlgorithmException * @throws java.security.KeyManagementException */ - public void setHostnameVerifier(HostnameVerifier hostnameverifier) + void setHostnameVerifier(HostnameVerifier hostnameverifier) throws NoSuchAlgorithmException, KeyManagementException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory.java index bd9d61fe66..11691451f2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnectionFactory.java @@ -62,7 +62,7 @@ public interface HttpConnectionFactory { * @return a {@link org.eclipse.jgit.transport.http.HttpConnection} * @throws java.io.IOException */ - public HttpConnection create(URL url) throws IOException; + HttpConnection create(URL url) throws IOException; /** * Creates a new connection to a destination defined by a @@ -75,6 +75,6 @@ public interface HttpConnectionFactory { * @return a {@link org.eclipse.jgit.transport.http.HttpConnection} * @throws java.io.IOException */ - public HttpConnection create(URL url, Proxy proxy) + HttpConnection create(URL url, Proxy proxy) throws IOException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java index 8241c59d2b..734b549294 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java @@ -53,6 +53,7 @@ import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -62,6 +63,7 @@ import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; +import org.eclipse.jgit.annotations.NonNull; /** * A {@link org.eclipse.jgit.transport.http.HttpConnection} which simply * delegates every call to a {@link java.net.HttpURLConnection}. This is the @@ -72,6 +74,11 @@ import javax.net.ssl.TrustManager; public class JDKHttpConnection implements HttpConnection { HttpURLConnection wrappedUrlConnection; + // used for mock testing + JDKHttpConnection(HttpURLConnection urlConnection) { + this.wrappedUrlConnection = urlConnection; + } + /** * Constructor for JDKHttpConnection. * @@ -170,10 +177,26 @@ public class JDKHttpConnection implements HttpConnection { /** {@inheritDoc} */ @Override - public String getHeaderField(String name) { + public String getHeaderField(@NonNull String name) { return wrappedUrlConnection.getHeaderField(name); } + @Override + public List<String> getHeaderFields(@NonNull String name) { + Map<String, List<String>> m = wrappedUrlConnection.getHeaderFields(); + List<String> fields = mapValuesToListIgnoreCase(name, m); + return fields; + } + + private static List<String> mapValuesToListIgnoreCase(String keyName, + Map<String, List<String>> m) { + List<String> fields = new LinkedList<>(); + m.entrySet().stream().filter(e -> keyName.equalsIgnoreCase(e.getKey())) + .filter(e -> e.getValue() != null) + .forEach(e -> fields.addAll(e.getValue())); + return fields; + } + /** {@inheritDoc} */ @Override public int getContentLength() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java index 4967169776..b850d1ef94 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java @@ -57,7 +57,7 @@ public interface ReceivePackFactory<C> { /** * A factory disabling the ReceivePack service for all repositories */ - public static final ReceivePackFactory<?> DISABLED = new ReceivePackFactory<Object>() { + ReceivePackFactory<?> DISABLED = new ReceivePackFactory<Object>() { @Override public ReceivePack create(Object req, Repository db) throws ServiceNotEnabledException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java index a305e4cea3..4816f21bcc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java @@ -57,7 +57,7 @@ public interface RepositoryResolver<C> { /** * Resolver configured to open nothing. */ - public static final RepositoryResolver<?> NONE = new RepositoryResolver<Object>() { + RepositoryResolver<?> NONE = new RepositoryResolver<Object>() { @Override public Repository open(Object req, String name) throws RepositoryNotFoundException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java index 40d1ffdc56..bb43b136d8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java @@ -57,7 +57,7 @@ public interface UploadPackFactory<C> { /** * A factory disabling the UploadPack service for all repositories. */ - public static final UploadPackFactory<?> DISABLED = new UploadPackFactory<Object>() { + UploadPackFactory<?> DISABLED = new UploadPackFactory<Object>() { @Override public UploadPack create(Object req, Repository db) throws ServiceNotEnabledException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java index 24b9ac086e..3d25c2314e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java @@ -46,6 +46,8 @@ package org.eclipse.jgit.treewalk; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -412,7 +414,7 @@ public class FileTreeIterator extends WorkingTreeIterator { public InputStream openInputStream() throws IOException { if (attributes.isSymbolicLink()) { return new ByteArrayInputStream(fs.readSymLink(getFile()) - .getBytes(Constants.CHARACTER_ENCODING)); + .getBytes(UTF_8)); } else { return new FileInputStream(getFile()); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index 7c6bfb9d63..1fa1db5847 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -1045,7 +1045,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } } if (FileMode.GITLINK == iMode - && FileMode.TREE == wtMode) { + && FileMode.TREE == wtMode && !getOptions().isDirNoGitLinks()) { return iMode; } if (FileMode.TREE == iMode diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index 180123e091..fb958872ac 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -43,6 +43,8 @@ package org.eclipse.jgit.util; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.Closeable; @@ -1222,6 +1224,13 @@ public abstract class FS { hookPath); ProcessBuilder hookProcess = runInShell(cmd, args); hookProcess.directory(runDirectory); + Map<String, String> environment = hookProcess.environment(); + environment.put(Constants.GIT_DIR_KEY, + repository.getDirectory().getAbsolutePath()); + if (!repository.isBare()) { + environment.put(Constants.GIT_WORK_TREE_KEY, + repository.getWorkTree().getAbsolutePath()); + } try { return new ProcessResult(runProcess(hookProcess, outRedirect, errRedirect, stdinArgs), Status.OK); @@ -1286,7 +1295,7 @@ public abstract class FS { OutputStream outRedirect, OutputStream errRedirect, String stdinArgs) throws IOException, InterruptedException { InputStream in = (stdinArgs == null) ? null : new ByteArrayInputStream( - stdinArgs.getBytes(Constants.CHARACTER_ENCODING)); + stdinArgs.getBytes(UTF_8)); return runProcess(processBuilder, outRedirect, errRedirect, in); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java index 6f92b37853..9190a5915a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java @@ -44,7 +44,7 @@ package org.eclipse.jgit.util; -import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING; +import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -181,7 +181,7 @@ public class HttpSupport { if (key == null || key.length() == 0) return; try { - urlstr.append(URLEncoder.encode(key, CHARACTER_ENCODING)); + urlstr.append(URLEncoder.encode(key, UTF_8.name())); } catch (UnsupportedEncodingException e) { throw new RuntimeException(JGitText.get().couldNotURLEncodeToUTF8, e); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java index 6d60ef3f4d..96636b7994 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LfsFactory.java @@ -152,7 +152,8 @@ public class LfsFactory { * @param outputStream * @return a {@link PrePushHook} implementation or <code>null</code> */ - public @Nullable PrePushHook getPrePushHook(Repository repo, + @Nullable + public PrePushHook getPrePushHook(Repository repo, PrintStream outputStream) { return null; } @@ -163,7 +164,8 @@ public class LfsFactory { * * @return a command to install LFS support. */ - public @Nullable LfsInstallCommand getInstallCommand() { + @Nullable + public LfsInstallCommand getInstallCommand() { return null; } @@ -294,6 +296,11 @@ public class LfsFactory { return stream.read(); } + @Override + public int read(byte b[], int off, int len) throws IOException { + return stream.read(b, off, len); + } + /** * @return the length of the stream */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java index 28f406a49e..a440cb275c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java @@ -677,10 +677,6 @@ public final class RawParseUtils { * <p> * The last element (index <code>map.size()-1</code>) always contains * <code>end</code>. - * <p> - * If the data contains a '\0' anywhere, the whole region is considered - * binary and a LineMap corresponding to a single line is returned. - * </p> * * @param buf * buffer to scan. @@ -689,18 +685,15 @@ public final class RawParseUtils { * line 1. * @param end * 1 past the end of the content within <code>buf</code>. - * @return a line map indicating the starting position of each line, or a - * map representing the entire buffer as a single line if - * <code>buf</code> contains a NUL byte. + * @return a line map indicating the starting position of each line. */ public static final IntList lineMap(byte[] buf, int ptr, int end) { - IntList map = lineMapOrNull(buf, ptr, end); - if (map == null) { - map = new IntList(3); - map.add(Integer.MIN_VALUE); + IntList map = new IntList((end - ptr) / 36); + map.fillTo(1, Integer.MIN_VALUE); + for (; ptr < end; ptr = nextLF(buf, ptr)) { map.add(ptr); - map.add(end); } + map.add(end); return map; } @@ -729,7 +722,8 @@ public final class RawParseUtils { return map; } - private static @Nullable IntList lineMapOrNull(byte[] buf, int ptr, int end) { + @Nullable + private static IntList lineMapOrNull(byte[] buf, int ptr, int end) { // Experimentally derived from multiple source repositories // the average number of bytes/line is 36. Its a rough guess // to initially size our map close to the target. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java index 822961f8de..d7c6bec219 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available * under the terms of the Eclipse Distribution License v1.0 which @@ -49,6 +50,7 @@ import org.eclipse.jgit.attributes.Attributes; import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.treewalk.TreeWalk.OperationType; import org.eclipse.jgit.treewalk.WorkingTreeOptions; +import org.eclipse.jgit.util.SystemReader; /** * Utility used to create input and output stream wrappers for @@ -57,7 +59,6 @@ import org.eclipse.jgit.treewalk.WorkingTreeOptions; * @since 4.3 */ public final class EolStreamTypeUtil { - private static final boolean FORCE_EOL_LF_ON_CHECKOUT = false; private EolStreamTypeUtil() { } @@ -164,11 +165,11 @@ public final class EolStreamTypeUtil { // old git system if (attrs.isSet("crlf")) {//$NON-NLS-1$ - return EolStreamType.TEXT_LF; + return EolStreamType.TEXT_LF; // Same as isSet("text") } else if (attrs.isUnset("crlf")) {//$NON-NLS-1$ - return EolStreamType.DIRECT; + return EolStreamType.DIRECT; // Same as isUnset("text") } else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$ - return EolStreamType.TEXT_LF; + return EolStreamType.TEXT_LF; // Same as eol=lf } // new git system @@ -196,6 +197,28 @@ public final class EolStreamTypeUtil { return EolStreamType.DIRECT; } + private static EolStreamType getOutputFormat(WorkingTreeOptions options) { + switch (options.getAutoCRLF()) { + case TRUE: + return EolStreamType.TEXT_CRLF; + default: + // no decision + } + switch (options.getEOL()) { + case CRLF: + return EolStreamType.TEXT_CRLF; + case NATIVE: + if (SystemReader.getInstance().isWindows()) { + return EolStreamType.TEXT_CRLF; + } + return EolStreamType.TEXT_LF; + case LF: + default: + break; + } + return EolStreamType.DIRECT; + } + private static EolStreamType checkOutStreamType(WorkingTreeOptions options, Attributes attrs) { if (attrs.isUnset("text")) {//$NON-NLS-1$ @@ -205,57 +228,35 @@ public final class EolStreamTypeUtil { // old git system if (attrs.isSet("crlf")) {//$NON-NLS-1$ - return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF - : EolStreamType.DIRECT; + return getOutputFormat(options); // Same as isSet("text") } else if (attrs.isUnset("crlf")) {//$NON-NLS-1$ - return EolStreamType.DIRECT; + return EolStreamType.DIRECT; // Same as isUnset("text") } else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$ - return EolStreamType.DIRECT; + return EolStreamType.DIRECT; // Same as eol=lf } // new git system String eol = attrs.getValue("eol"); //$NON-NLS-1$ - if (eol != null && "crlf".equals(eol)) //$NON-NLS-1$ - return EolStreamType.TEXT_CRLF; - if (eol != null && "lf".equals(eol)) //$NON-NLS-1$ - return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF - : EolStreamType.DIRECT; - - if (attrs.isSet("text")) { //$NON-NLS-1$ - switch (options.getAutoCRLF()) { - case TRUE: - return EolStreamType.TEXT_CRLF; - default: - // no decision - } - switch (options.getEOL()) { - case CRLF: + if (eol != null) { + if ("crlf".equals(eol)) {//$NON-NLS-1$ return EolStreamType.TEXT_CRLF; - case LF: - return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF - : EolStreamType.DIRECT; - case NATIVE: - default: + } else if ("lf".equals(eol)) { //$NON-NLS-1$ return EolStreamType.DIRECT; } } + if (attrs.isSet("text")) { //$NON-NLS-1$ + return getOutputFormat(options); + } if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$ - switch (options.getAutoCRLF()) { - case TRUE: + EolStreamType basic = getOutputFormat(options); + switch (basic) { + case TEXT_CRLF: return EolStreamType.AUTO_CRLF; + case TEXT_LF: + return EolStreamType.AUTO_LF; default: - // no decision - } - switch (options.getEOL()) { - case CRLF: - return EolStreamType.AUTO_CRLF; - case LF: - return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF - : EolStreamType.DIRECT; - case NATIVE: - default: - return EolStreamType.DIRECT; + return basic; } } @@ -51,7 +51,7 @@ <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> <packaging>pom</packaging> - <version>5.1.9-SNAPSHOT</version> + <version>5.2.3-SNAPSHOT</version> <name>JGit - Parent</name> <url>${jgit-url}</url> @@ -77,15 +77,12 @@ <developers> <developer> - <name>Chris Aniszczyk</name> + <name>Andrey Loskutov</name> </developer> <developer> <name>Christian Halstrick</name> </developer> <developer> - <name>Colby Ranger</name> - </developer> - <developer> <name>Dave Borowitz</name> </developer> <developer> @@ -98,28 +95,16 @@ <name>Jonathan Nieder</name> </developer> <developer> - <name>Kevin Sawicki</name> - </developer> - <developer> - <name>Mathias Kinzler</name> + <name>Jonathan Tan</name> </developer> <developer> <name>Matthias Sohn</name> </developer> <developer> - <name>Robin Rosenberg</name> - </developer> - <developer> - <name>Robin Stocker</name> - </developer> - <developer> <name>Sasa Zivkov</name> </developer> <developer> - <name>Shawn Pearce</name> - </developer> - <developer> - <name>Stefan Lay</name> + <name>Terry Parker</name> </developer> <developer> <name>Thomas Wolf</name> @@ -197,14 +182,15 @@ <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format> <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest> - <jgit-last-release-version>4.11.0.201803080745-r</jgit-last-release-version> + <jgit-last-release-version>5.1.0.201809111528-r</jgit-last-release-version> + <apache-sshd-version>2.0.0</apache-sshd-version> <jsch-version>0.1.54</jsch-version> <jzlib-version>1.1.1</jzlib-version> <javaewah-version>1.1.6</javaewah-version> <junit-version>4.12</junit-version> <test-fork-count>1C</test-fork-count> <args4j-version>2.33</args4j-version> - <commons-compress-version>1.15</commons-compress-version> + <commons-compress-version>1.18</commons-compress-version> <osgi-core-version>4.3.1</osgi-core-version> <servlet-api-version>3.1.0</servlet-api-version> <jetty-version>9.4.11.v20180605</jetty-version> @@ -216,9 +202,11 @@ <maven-javadoc-plugin-version>3.0.1</maven-javadoc-plugin-version> <tycho-extras-version>1.2.0</tycho-extras-version> <gson-version>2.8.2</gson-version> - <spotbugs-maven-plugin-version>3.1.6</spotbugs-maven-plugin-version> - <maven-surefire-report-plugin-version>2.21.0</maven-surefire-report-plugin-version> + <spotbugs-maven-plugin-version>3.1.8</spotbugs-maven-plugin-version> + <maven-surefire-version>2.22.1</maven-surefire-version> <maven-compiler-plugin-version>3.8.0</maven-compiler-plugin-version> + <maven-project-info-reports-plugin-version>3.0.0</maven-project-info-reports-plugin-version> + <maven-jxr-plugin-version>3.0.0</maven-jxr-plugin-version> <!-- Properties to enable jacoco code coverage analysis --> <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> @@ -275,7 +263,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.1.1</version> + <version>3.2.1</version> </plugin> <plugin> @@ -305,7 +293,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>2.21.0</version> + <version>${maven-surefire-version}</version> <configuration> <forkCount>${test-fork-count}</forkCount> <reuseForks>true</reuseForks> @@ -338,7 +326,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> - <version>3.10.0</version> + <version>3.11.0</version> <configuration> <sourceEncoding>utf-8</sourceEncoding> <minimumTokens>100</minimumTokens> @@ -376,7 +364,7 @@ <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> - <version>0.8.1</version> + <version>0.8.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> @@ -386,24 +374,24 @@ <dependency><!-- add support for ssh/scp --> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-ssh</artifactId> - <version>3.1.0</version> + <version>3.2.0</version> </dependency> </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId> - <version>${maven-surefire-report-plugin-version}</version> + <version>${maven-surefire-version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jxr-plugin</artifactId> - <version>2.5</version> + <version>${maven-jxr-plugin-version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> - <version>2.9</version> + <version>${maven-project-info-reports-plugin-version}</version> </plugin> </plugins> </pluginManagement> @@ -412,7 +400,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-enforcer-plugin</artifactId> - <version>3.0.0-M1</version> + <version>3.0.0-M2</version> <executions> <execution> <id>enforce-maven</id> @@ -560,7 +548,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jxr-plugin</artifactId> - <version>2.5</version> + <version>${maven-jxr-plugin-version}</version> </plugin> <plugin> <groupId>com.github.spotbugs</groupId> @@ -570,7 +558,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId> - <version>${maven-surefire-report-plugin-version}</version> + <version>${maven-surefire-version}</version> <configuration> <aggregate>true</aggregate> <alwaysGenerateSurefireReport>false</alwaysGenerateSurefireReport> @@ -582,16 +570,20 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> - <version>2.9</version> + <version>${maven-project-info-reports-plugin-version}</version> <reportSets> <reportSet> <reports> <report>dependencies</report> - <report>project-team</report> - <report>mailing-list</report> - <report>cim</report> - <report>issue-tracking</report> - <report>license</report> + <report>dependency-convergence</report> + <report>dependency-management</report> + <report>index</report> + <report>summary</report> + <report>team</report> + <report>mailing-lists</report> + <report>ci-management</report> + <report>issue-management</report> + <report>licenses</report> <report>scm</report> </reports> </reportSet> @@ -798,7 +790,7 @@ <dependency> <groupId>com.google.errorprone</groupId> <artifactId>error_prone_core</artifactId> - <version>2.3.1</version> + <version>2.3.2</version> </dependency> </dependencies> </plugin> @@ -836,7 +828,7 @@ <dependency> <groupId>org.eclipse.jdt</groupId> <artifactId>ecj</artifactId> - <version>3.13.102</version> + <version>3.15.0</version> </dependency> </dependencies> </plugin> @@ -931,11 +923,13 @@ <module>org.eclipse.jgit.ui</module> <module>org.eclipse.jgit.http.apache</module> <module>org.eclipse.jgit.http.server</module> + <module>org.eclipse.jgit.ssh.apache</module> <module>org.eclipse.jgit.pgm</module> <module>org.eclipse.jgit.lfs</module> <module>org.eclipse.jgit.lfs.server</module> <module>org.eclipse.jgit.junit</module> <module>org.eclipse.jgit.junit.http</module> + <module>org.eclipse.jgit.junit.ssh</module> <module>org.eclipse.jgit.test</module> <module>org.eclipse.jgit.ant.test</module> @@ -943,6 +937,7 @@ <module>org.eclipse.jgit.pgm.test</module> <module>org.eclipse.jgit.lfs.test</module> <module>org.eclipse.jgit.lfs.server.test</module> + <module>org.eclipse.jgit.ssh.apache.test</module> </modules> </project> diff --git a/tools/BUILD b/tools/BUILD index e69de29bb2..9025e0a6d3 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -0,0 +1,107 @@ +load( + "@bazel_tools//tools/jdk:default_java_toolchain.bzl", + "JDK9_JVM_OPTS", + "default_java_toolchain", +) + +default_java_toolchain( + name = "error_prone_warnings_toolchain", + bootclasspath = ["@bazel_tools//tools/jdk:platformclasspath.jar"], + jvm_opts = JDK9_JVM_OPTS, + package_configuration = [ + ":error_prone", + ], + visibility = ["//visibility:public"], +) + +# This EP warnings list borrowed from here: +# https://github.com/bazelbuild/BUILD_file_generator/blob/master/tools/bazel_defs/java.bzl +java_package_configuration( + name = "error_prone", + javacopts = [ + "-XepDisableWarningsInGeneratedCode", + "-Xep:MissingCasesInEnumSwitch:ERROR", + "-Xep:ReferenceEquality:WARN", + "-Xep:StringEquality:WARN", + "-Xep:WildcardImport:WARN", + "-Xep:AmbiguousMethodReference:WARN", + "-Xep:BadAnnotationImplementation:WARN", + "-Xep:BadComparable:WARN", + "-Xep:BoxedPrimitiveConstructor:ERROR", + "-Xep:CannotMockFinalClass:WARN", + "-Xep:ClassCanBeStatic:ERROR", + "-Xep:ClassNewInstance:WARN", + "-Xep:DefaultCharset:ERROR", + "-Xep:DoubleCheckedLocking:WARN", + "-Xep:ElementsCountedInLoop:WARN", + "-Xep:EqualsHashCode:WARN", + "-Xep:EqualsIncompatibleType:WARN", + "-Xep:ExpectedExceptionChecker:ERROR", + "-Xep:Finally:WARN", + "-Xep:FloatingPointLiteralPrecision:WARN", + "-Xep:FragmentInjection:WARN", + "-Xep:FragmentNotInstantiable:WARN", + "-Xep:FunctionalInterfaceClash:WARN", + "-Xep:FutureReturnValueIgnored:WARN", + "-Xep:GetClassOnEnum:WARN", + "-Xep:ImmutableAnnotationChecker:WARN", + "-Xep:ImmutableEnumChecker:WARN", + "-Xep:IncompatibleModifiers:WARN", + "-Xep:InjectOnConstructorOfAbstractClass:WARN", + "-Xep:InputStreamSlowMultibyteRead:WARN", + "-Xep:IterableAndIterator:WARN", + "-Xep:JUnit3FloatingPointComparisonWithoutDelta:WARN", + "-Xep:JUnitAmbiguousTestClass:WARN", + "-Xep:LiteralClassName:WARN", + "-Xep:MissingFail:ERROR", + "-Xep:MissingOverride:WARN", + "-Xep:MutableConstantField:WARN", + "-Xep:NarrowingCompoundAssignment:WARN", + "-Xep:NonAtomicVolatileUpdate:WARN", + "-Xep:NonOverridingEquals:WARN", + "-Xep:NullableConstructor:WARN", + "-Xep:NullablePrimitive:WARN", + "-Xep:NullableVoid:WARN", + "-Xep:OperatorPrecedence:WARN", + "-Xep:OverridesGuiceInjectableMethod:WARN", + "-Xep:PreconditionsInvalidPlaceholder:WARN", + "-Xep:ProtoFieldPreconditionsCheckNotNull:WARN", + "-Xep:ProtocolBufferOrdinal:WARN", + "-Xep:RequiredModifiers:WARN", + "-Xep:ShortCircuitBoolean:WARN", + "-Xep:SimpleDateFormatConstant:WARN", + "-Xep:StaticGuardedByInstance:WARN", + "-Xep:SynchronizeOnNonFinalField:WARN", + "-Xep:TruthConstantAsserts:WARN", + "-Xep:TypeParameterShadowing:WARN", + "-Xep:TypeParameterUnusedInFormals:WARN", + "-Xep:URLEqualsHashCode:WARN", + "-Xep:UnsynchronizedOverridesSynchronized:WARN", + "-Xep:WaitNotInLoop:WARN", + ], + packages = ["error_prone_packages"], +) + +package_group( + name = "error_prone_packages", + packages = [ + "//org.eclipse.jgit.ant.test/...", + "//org.eclipse.jgit.ant/...", + "//org.eclipse.jgit.archive/...", + "//org.eclipse.jgit.http.apache/...", + "//org.eclipse.jgit.http.server/...", + "//org.eclipse.jgit.http.test/...", + "//org.eclipse.jgit.junit.http/...", + "//org.eclipse.jgit.junit/...", + "//org.eclipse.jgit.lfs.server.test/...", + "//org.eclipse.jgit.lfs.server/...", + "//org.eclipse.jgit.lfs.test/...", + "//org.eclipse.jgit.lfs/...", + "//org.eclipse.jgit.packaging/...", + "//org.eclipse.jgit.pgm.test/...", + "//org.eclipse.jgit.pgm/...", + "//org.eclipse.jgit.test/...", + "//org.eclipse.jgit.ui/...", + "//org.eclipse.jgit/...", + ], +) diff --git a/tools/maven-central/deploy.rb b/tools/maven-central/deploy.rb index 2744e772a1..7cab32236b 100755 --- a/tools/maven-central/deploy.rb +++ b/tools/maven-central/deploy.rb @@ -55,9 +55,11 @@ artifacts = [group, group + '.http.server', group + '.junit', group + '.junit.http', + group + '.junit.ssh', group + '.lfs', group + '.lfs.server', group + '.pgm', + group + '.ssh.apache', group + '.ui'] prefix = ["mvn", "gpg:sign-and-deploy-file", "-Dgpg.passphrase=#{passphrase}", diff --git a/tools/maven-central/download.rb b/tools/maven-central/download.rb index b6c2671742..543ae87a5d 100755 --- a/tools/maven-central/download.rb +++ b/tools/maven-central/download.rb @@ -15,9 +15,11 @@ artifacts = [group, group + '.http.server', group + '.junit', group + '.junit.http', + group + '.junit.ssh', group + '.lfs', group + '.lfs.server', group + '.pgm', + group + '.ssh.apache', group + '.ui'] puts 'Deleting current files' |