diff --git a/cli/build.gradle b/cli/build.gradle index f8b76e73b..bdda901ac 100644 --- a/cli/build.gradle +++ b/cli/build.gradle @@ -41,7 +41,6 @@ dependencies { implementation project(':forge') implementation project(':issuetracker') implementation project(':proxy') - implementation project(':ssh') implementation project(':version') } diff --git a/cli/src/main/java/module-info.java b/cli/src/main/java/module-info.java index f0691a30e..35cbd74b3 100644 --- a/cli/src/main/java/module-info.java +++ b/cli/src/main/java/module-info.java @@ -30,7 +30,6 @@ requires org.openjdk.skara.host; requires org.openjdk.skara.forge; requires org.openjdk.skara.proxy; - requires org.openjdk.skara.ssh; requires org.openjdk.skara.version; requires java.net.http; diff --git a/cli/src/main/java/org/openjdk/skara/cli/Remote.java b/cli/src/main/java/org/openjdk/skara/cli/Remote.java index 385a459d3..18c2ebb04 100644 --- a/cli/src/main/java/org/openjdk/skara/cli/Remote.java +++ b/cli/src/main/java/org/openjdk/skara/cli/Remote.java @@ -22,54 +22,66 @@ */ package org.openjdk.skara.cli; -import org.openjdk.skara.ssh.SSHConfig; - import java.io.IOException; import java.net.URI; import java.nio.file.Path; import java.nio.file.Files; +import java.nio.charset.StandardCharsets; public class Remote { - public static URI toWebURI(String remotePath) throws IOException { - if (remotePath.startsWith("git+")) { - remotePath = remotePath.substring("git+".length()); - } - if (remotePath.endsWith(".git")) { - remotePath = remotePath.substring(0, remotePath.length() - ".git".length()); - } - if (remotePath.startsWith("http")) { - return URI.create(remotePath); - } else { - if (remotePath.startsWith("ssh://")) { - remotePath = remotePath.substring("ssh://".length()).replaceFirst("/", ":"); - } - var indexOfColon = remotePath.indexOf(':'); - var indexOfSlash = remotePath.indexOf('/'); - if (indexOfColon != -1) { - if (indexOfSlash == -1 || indexOfColon < indexOfSlash) { - var path = remotePath.contains("@") ? remotePath.split("@")[1] : remotePath; - var name = path.split(":")[0]; + private static URI sshCanonicalize(URI uri) throws IOException { + var arg = uri.getUserInfo() == null ? uri.getHost() : uri.getUserInfo() + "@" + uri.getHost(); + var pb = new ProcessBuilder("ssh", "-G", arg); + pb.redirectOutput(ProcessBuilder.Redirect.PIPE); + pb.redirectError(ProcessBuilder.Redirect.DISCARD); + var p = pb.start(); - // Could be a Host in the ~/.ssh/config file - var sshConfig = Path.of(System.getProperty("user.home"), ".ssh", "config"); - if (Files.exists(sshConfig)) { - for (var host : SSHConfig.parse(sshConfig).hosts()) { - if (host.name().equals(name)) { - var hostName = host.hostName(); - if (hostName != null) { - return URI.create("https://" + hostName + "/" + path.split(":")[1]); - } - } - } - } + var output = new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + try { + var res = p.waitFor(); + if (res != 0) { + throw new IOException("ssh -G " + arg + " exited with non-zero exit code: " + res); + } + } catch (InterruptedException e) { + throw new IOException(e); + } - // Otherwise is must be a domain - return URI.create("https://" + path.replace(":", "/")); + String hostname = null; + String username = null; + for (var line : output.split("\n")) { + var parts = line.trim().split(" "); + if (parts.length == 2) { + var key = parts[0]; + var value = parts[1]; + if (key.equals("hostname")) { + hostname = value; + } else if (key.equals("user")) { + username = value; } } } - throw new IOException("error: cannot find remote repository for " + remotePath); + if (hostname == null) { + throw new IOException("ssh -G " + arg + " did not output a hostname"); + } + + return username == null ? + URI.create("ssh://" + hostname + uri.getPath()) : + URI.create("ssh://" + username + "@" + hostname + uri.getPath()); + } + + public static URI toWebURI(String remotePath) throws IOException { + var uri = toURI(remotePath); + if (uri.getScheme().equals("file://")) { + throw new IOException("Cannot create web URI for file path: " + uri.toString()); + } + + // Use https://, drop eventual .git from path and drop authority + var path = uri.getPath(); + if (path.endsWith(".git")) { + path = path.substring(0, path.length() - ".git".length()); + } + return URI.create("https://" + uri.getHost() + path); } public static URI toURI(String remotePath) throws IOException { @@ -88,33 +100,11 @@ public static URI toURI(String remotePath) throws IOException { var indexOfSlash = remotePath.indexOf('/'); if (indexOfColon != -1) { if (indexOfSlash == -1 || indexOfColon < indexOfSlash) { - var path = remotePath.contains("@") ? remotePath.split("@")[1] : remotePath; - var name = path.split(":")[0]; - - // Could be a Host in the ~/.ssh/config file - var sshConfig = Path.of(System.getProperty("user.home"), ".ssh", "config"); - if (Files.exists(sshConfig)) { - for (var host : SSHConfig.parse(sshConfig).hosts()) { - if (host.name().equals(name)) { - var hostName = host.hostName(); - if (hostName != null) { - var username = host.user(); - if (username == null) { - username = remotePath.contains("@") ? remotePath.split("@")[0] : null; - } - var userPrefix = username == null ? "" : username + "@"; - return URI.create("ssh://" + userPrefix + hostName + "/" + path.split(":")[1]); - } - } - } - } - - // Otherwise is must be a domain - var userPrefix = remotePath.contains("@") ? remotePath.split("@")[0] + "@" : ""; - return URI.create("ssh://" + userPrefix + path.replace(":", "/")); + var uri = URI.create("ssh://" + remotePath.replace(":", "/")); + return sshCanonicalize(uri); } } - throw new IOException("error: cannot construct proper URI for " + remotePath); + throw new IOException("Cannot construct URI for " + remotePath); } } diff --git a/ssh/build.gradle b/ssh/build.gradle deleted file mode 100644 index a15ad4e0f..000000000 --- a/ssh/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -module { - name = 'org.openjdk.skara.ssh' - test { - requires 'org.junit.jupiter.api' - opens 'org.openjdk.skara.ssh' to 'org.junit.platform.commons' - } -} - -publishing { - publications { - ssh(MavenPublication) { - from components.java - } - } -} diff --git a/ssh/src/main/java/module-info.java b/ssh/src/main/java/module-info.java deleted file mode 100644 index 6bc81bdea..000000000 --- a/ssh/src/main/java/module-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -module org.openjdk.skara.ssh { - exports org.openjdk.skara.ssh; -} diff --git a/ssh/src/main/java/org/openjdk/skara/ssh/SSHConfig.java b/ssh/src/main/java/org/openjdk/skara/ssh/SSHConfig.java deleted file mode 100644 index da8b58ed1..000000000 --- a/ssh/src/main/java/org/openjdk/skara/ssh/SSHConfig.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package org.openjdk.skara.ssh; - -import java.io.IOException; -import java.nio.file.*; -import java.util.*; - -public class SSHConfig { - public static class Host { - private final String name; - private final String user; - private final String hostName; - private final int port; - private final List preferredAuthentications; - private final Path identifyFile; - private final String proxyCommand; - private final boolean forwardAgent; - private final boolean tcpKeepAlive; - private final boolean identitiesOnly; - - Host(String name, Map fields) { - this.name = name; - user = fields.get("User"); - hostName = fields.get("Hostname"); - port = Integer.parseInt(fields.getOrDefault("Port", "22")); - - if (fields.containsKey("PreferredAuthentications")) { - preferredAuthentications = Arrays.asList(fields.get("PreferredAuthentications").split(",")); - } else { - preferredAuthentications = List.of(); - } - - if (fields.containsKey("IdentityFile")) { - identifyFile = Path.of(fields.get("IdentityFile")); - } else { - identifyFile = null; - } - - proxyCommand = fields.get("proxyCommand"); - forwardAgent = Objects.equals(fields.get("ForwardAgent"), "yes"); - tcpKeepAlive = Objects.equals(fields.get("TCPKeepAlive"), "yes"); - identitiesOnly = Objects.equals(fields.get("IdentitiesOnly"), "yes"); - } - - public String name() { - return name; - } - - public String user() { - return user; - } - - public String hostName() { - return hostName; - } - - public int port() { - return port; - } - - public List preferredAuthentications() { - return preferredAuthentications; - } - - public Path identityFile() { - return identifyFile; - } - - public String proxyCommand() { - return proxyCommand; - } - - public boolean forwardAgent() { - return forwardAgent; - } - - public boolean tcpKeepAlive() { - return tcpKeepAlive; - } - - public boolean identitiesOnly() { - return identitiesOnly; - } - } - - private final List hosts; - - public SSHConfig(List hosts) { - this.hosts = hosts; - } - - public List hosts() { - return hosts; - } - - public static SSHConfig parse(Path p) throws IOException { - return parse(Files.readAllLines(p)); - } - - public static SSHConfig parse(List lines) { - var hosts = new ArrayList(); - var i = 0; - while (i < lines.size()) { - var line = lines.get(i); - if (line.startsWith("Host")) { - var name = line.split(" ")[1]; - i++; - - var fields = new HashMap(); - while (i < lines.size() && !lines.get(i).startsWith("Host")) { - var field = lines.get(i); - i++; - if (!field.isEmpty()) { - var nameAndValue = field.trim().split(" "); - fields.put(nameAndValue[0], nameAndValue[1]); - } - } - - hosts.add(new Host(name, fields)); - } - } - - return new SSHConfig(hosts); - } -} diff --git a/ssh/src/test/java/org/openjdk/ssh/SSHConfigTests.java b/ssh/src/test/java/org/openjdk/ssh/SSHConfigTests.java deleted file mode 100644 index ccb9e0fb3..000000000 --- a/ssh/src/test/java/org/openjdk/ssh/SSHConfigTests.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package org.openjdk.skara.ssh; - -import java.util.*; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class SSHConfigTests { - @Test - void testSimpleConfig() { - var lines = List.of( - "Host test", - " User git", - " HostName git.openjdk.java.net", - " Port 22" - ); - var config = SSHConfig.parse(lines); - var hosts = config.hosts(); - assertEquals(1, hosts.size()); - var host = hosts.get(0); - assertEquals("test", host.name()); - assertEquals("git", host.user()); - assertEquals(22, host.port()); - } -}