git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [EGIT PATCH 1/2] Refactor SSH key loading so we don't duplicate keys
@ 2008-08-15 17:35 Shawn O. Pearce
  2008-08-15 17:35 ` [EGIT PATCH 2/2] Honor ~/.ssh/config whenever possible during SSH based transport Shawn O. Pearce
  0 siblings, 1 reply; 4+ messages in thread
From: Shawn O. Pearce @ 2008-08-15 17:35 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git

We only want to read each private key once, so we cache the
names of the keys we have processed before, adding keys which
we have not yet seen.  This allows us to alter add keys on
the fly and avoid duplication.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../jgit/transport/DefaultSshSessionFactory.java   |   20 ++++++++++++++------
 1 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java b/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java
index b4578d4..0484fc0 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java
@@ -78,6 +78,8 @@
 	/** IANA assigned port number for SSH. */
 	private static final int SSH_PORT = 22;
 
+	private Set<String> loadedIdentities;
+
 	private JSch userJSch;
 
 	@Override
@@ -106,10 +108,10 @@ public String run() {
 
 	private JSch getUserJSch() throws JSchException {
 		if (userJSch == null) {
-			final JSch sch = new JSch();
-			knownHosts(sch);
-			identities(sch);
-			userJSch = sch;
+			loadedIdentities = new HashSet<String>();
+			userJSch = new JSch();
+			knownHosts(userJSch);
+			identities();
 		}
 		return userJSch;
 	}
@@ -133,7 +135,7 @@ private void knownHosts(final JSch sch) throws JSchException {
 		}
 	}
 
-	private void identities(final JSch sch) throws JSchException {
+	private void identities() throws JSchException {
 		final File home = FS.userHome();
 		if (home == null)
 			return;
@@ -149,10 +151,16 @@ private void identities(final JSch sch) throws JSchException {
 			final File k = new File(sshdir, n.substring(0, n.length() - 4));
 			if (!k.isFile())
 				continue;
-			sch.addIdentity(k.getAbsolutePath());
+			addIdentity(k);
 		}
 	}
 
+	private void addIdentity(final File identityFile) throws JSchException {
+		final String path = identityFile.getAbsolutePath();
+		if (loadedIdentities.add(path))
+			userJSch.addIdentity(path);
+	}
+
 	private static class AWT_UserInfo implements UserInfo,
 			UIKeyboardInteractive {
 		private String passwd;
-- 
1.6.0.rc3.250.g8dd0

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [EGIT PATCH 2/2] Honor ~/.ssh/config whenever possible during SSH based transport
  2008-08-15 17:35 [EGIT PATCH 1/2] Refactor SSH key loading so we don't duplicate keys Shawn O. Pearce
@ 2008-08-15 17:35 ` Shawn O. Pearce
  2008-08-20 20:34   ` Robin Rosenberg
  0 siblings, 1 reply; 4+ messages in thread
From: Shawn O. Pearce @ 2008-08-15 17:35 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git

I rely on ~/.ssh/config to setup host aliases, especially for very
common destinations.  For example I have the following on most of
my systems:

	Host orcz
		HostName repo.or.cz
		User spearce
		IdentityFile .ssh/id_orcz

as not every system I use has my local user name as "spearce".  The
C Git transport honors these settings just fine for short URLs like
"orcz:/srv/git/egit.git" but jgit failed horribly on these as there
is no local system named "orcz" on any of my networks.

By reading (and caching) the ~/.ssh/config file jgit can now honor
the important aspects of the host configuration blocks, allowing it
to use the same URLs as C Git.

The JSch API does not seem to allow forcing a specific key identity
for a Session so we are forced to load the identity file listed in
the configuration into the core JSch object we are running with.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../spearce/egit/ui/EclipseSshSessionFactory.java  |   44 ++-
 .../spearce/jgit/transport/OpenSshConfigTest.java  |  131 ++++++++
 .../jgit/transport/DefaultSshSessionFactory.java   |   22 ++-
 .../org/spearce/jgit/transport/OpenSshConfig.java  |  318 ++++++++++++++++++++
 4 files changed, 499 insertions(+), 16 deletions(-)
 create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/transport/OpenSshConfigTest.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/OpenSshConfig.java

diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/EclipseSshSessionFactory.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/EclipseSshSessionFactory.java
index 8f80373..640a165 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/EclipseSshSessionFactory.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/EclipseSshSessionFactory.java
@@ -8,13 +8,15 @@
  *******************************************************************************/
 package org.spearce.egit.ui;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.eclipse.jsch.core.IJSchService;
 import org.eclipse.jsch.ui.UserInfoPrompter;
+import org.spearce.jgit.transport.OpenSshConfig;
 import org.spearce.jgit.transport.SshSessionFactory;
 
 import com.jcraft.jsch.JSchException;
@@ -23,15 +25,27 @@
 class EclipseSshSessionFactory extends SshSessionFactory {
 	private final IJSchService provider;
 
+	private final Set<String> loadedIdentities = new HashSet<String>();
+
+	private OpenSshConfig config;
+
 	EclipseSshSessionFactory(final IJSchService p) {
 		provider = p;
 	}
 
 	@Override
-	public Session getSession(final String user, final String pass,
-			final String host, final int port) throws JSchException {
-		final Session session = provider.createSession(host, port > 0 ? port
-				: -1, user != null ? user : userName());
+	public Session getSession(String user, String pass, String host, int port)
+			throws JSchException {
+		final OpenSshConfig.Host hc = getConfig().lookup(host);
+		host = hc.getHostName();
+		if (port <= 0)
+			port = hc.getPort();
+		if (user == null)
+			user = hc.getUser();
+
+		final Session session = provider.createSession(host, port, user);
+		if (hc.getIdentityFile() != null)
+			addIdentity(hc.getIdentityFile());
 		if (pass != null)
 			session.setPassword(pass);
 		else
@@ -39,12 +53,17 @@ public Session getSession(final String user, final String pass,
 		return session;
 	}
 
-	private static String userName() {
-		return AccessController.doPrivileged(new PrivilegedAction<String>() {
-			public String run() {
-				return System.getProperty("user.name");
-			}
-		});
+	private synchronized OpenSshConfig getConfig() {
+		if (config == null)
+			config = OpenSshConfig.get();
+		return config;
+	}
+
+	private void addIdentity(final File identityFile)
+			throws JSchException {
+		final String path = identityFile.getAbsolutePath();
+		if (loadedIdentities.add(path))
+			provider.getJSch().addIdentity(path);
 	}
 
 	@Override
@@ -52,6 +71,7 @@ public OutputStream getErrorStream() {
 		return new OutputStream() {
 
 			StringBuilder all = new StringBuilder();
+
 			StringBuilder sb = new StringBuilder();
 
 			public String toString() {
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/OpenSshConfigTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/OpenSshConfigTest.java
new file mode 100644
index 0000000..a250f9d
--- /dev/null
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/OpenSshConfigTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+import org.spearce.jgit.lib.RepositoryTestCase;
+import org.spearce.jgit.transport.OpenSshConfig.Host;
+
+public class OpenSshConfigTest extends RepositoryTestCase {
+	private File home;
+
+	private File configFile;
+
+	private OpenSshConfig osc;
+
+	public void setUp() throws Exception {
+		super.setUp();
+
+		home = new File(trash, "home");
+		home.mkdir();
+
+		configFile = new File(new File(home, ".ssh"), "config");
+		configFile.getParentFile().mkdir();
+
+		System.setProperty("user.name", "jex_junit");
+		osc = new OpenSshConfig(home, configFile);
+	}
+
+	private void config(final String data) throws IOException {
+		final OutputStreamWriter fw = new OutputStreamWriter(
+				new FileOutputStream(configFile), "UTF-8");
+		fw.write(data);
+		fw.close();
+	}
+
+	public void testNoConfig() {
+		final Host h = osc.lookup("repo.or.cz");
+		assertNotNull(h);
+		assertEquals("repo.or.cz", h.getHostName());
+		assertEquals("jex_junit", h.getUser());
+		assertEquals(22, h.getPort());
+		assertNull(h.getIdentityFile());
+	}
+
+	public void testAlias_DoesNotMatch() throws Exception {
+		config("Host orcz\n" + "\tHostName repo.or.cz\n");
+		final Host h = osc.lookup("repo.or.cz");
+		assertNotNull(h);
+		assertEquals("repo.or.cz", h.getHostName());
+		assertEquals("jex_junit", h.getUser());
+		assertEquals(22, h.getPort());
+		assertNull(h.getIdentityFile());
+	}
+
+	public void testAlias_OptionsSet() throws Exception {
+		config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\tPort 2222\n"
+				+ "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n"
+				+ "\tForwardX11 no\n");
+		final Host h = osc.lookup("orcz");
+		assertNotNull(h);
+		assertEquals("repo.or.cz", h.getHostName());
+		assertEquals("jex", h.getUser());
+		assertEquals(2222, h.getPort());
+		assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile());
+	}
+
+
+	public void testAlias_OptionsKeywordCaseInsensitive() throws Exception {
+		config("hOsT orcz\n" + "\thOsTnAmE repo.or.cz\n" + "\tPORT 2222\n"
+				+ "\tuser jex\n" + "\tidentityfile .ssh/id_jex\n"
+				+ "\tForwardX11 no\n");
+		final Host h = osc.lookup("orcz");
+		assertNotNull(h);
+		assertEquals("repo.or.cz", h.getHostName());
+		assertEquals("jex", h.getUser());
+		assertEquals(2222, h.getPort());
+		assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile());
+	}
+
+	public void testAlias_OptionsInherit() throws Exception {
+		config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
+				+ "\tHostName not.a.host.example.com\n" + "\tPort 2222\n"
+				+ "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n"
+				+ "\tForwardX11 no\n");
+		final Host h = osc.lookup("orcz");
+		assertNotNull(h);
+		assertEquals("repo.or.cz", h.getHostName());
+		assertEquals("jex", h.getUser());
+		assertEquals(2222, h.getPort());
+		assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile());
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java b/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java
index 0484fc0..a2437c2 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/DefaultSshSessionFactory.java
@@ -49,6 +49,8 @@
 import java.io.OutputStream;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
@@ -76,21 +78,27 @@
  */
 class DefaultSshSessionFactory extends SshSessionFactory {
 	/** IANA assigned port number for SSH. */
-	private static final int SSH_PORT = 22;
+	static final int SSH_PORT = 22;
 
 	private Set<String> loadedIdentities;
 
 	private JSch userJSch;
 
+	private OpenSshConfig config;
+
 	@Override
 	public synchronized Session getSession(String user, String pass,
 			String host, int port) throws JSchException {
+		final OpenSshConfig.Host hc = getConfig().lookup(host);
+		host = hc.getHostName();
 		if (port <= 0)
-			port = SSH_PORT;
+			port = hc.getPort();
 		if (user == null)
-			user = userName();
+			user = hc.getUser();
 
 		final Session session = getUserJSch().getSession(user, host, port);
+		if (hc.getIdentityFile() != null)
+			addIdentity(hc.getIdentityFile());
 		if (pass != null)
 			session.setPassword(pass);
 		else
@@ -98,7 +106,7 @@ public synchronized Session getSession(String user, String pass,
 		return session;
 	}
 
-	private static String userName() {
+	static String userName() {
 		return AccessController.doPrivileged(new PrivilegedAction<String>() {
 			public String run() {
 				return System.getProperty("user.name");
@@ -116,6 +124,12 @@ private JSch getUserJSch() throws JSchException {
 		return userJSch;
 	}
 
+	private OpenSshConfig getConfig() {
+		if (config == null)
+			config = OpenSshConfig.get();
+		return config;
+	}
+
 	private void knownHosts(final JSch sch) throws JSchException {
 		final File home = FS.userHome();
 		if (home == null)
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/OpenSshConfig.java b/org.spearce.jgit/src/org/spearce/jgit/transport/OpenSshConfig.java
new file mode 100644
index 0000000..f927b1a
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/OpenSshConfig.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.spearce.jgit.errors.InvalidPatternException;
+import org.spearce.jgit.fnmatch.FileNameMatcher;
+import org.spearce.jgit.util.FS;
+
+/**
+ * Simple configuration parser for the OpenSSH ~/.ssh/config file.
+ * <p>
+ * Since JSch does not (currently) have the ability to parse an OpenSSH
+ * configuration file this is a simple parser to read that file and make the
+ * critical options available to {@link SshSessionFactory}.
+ */
+public class OpenSshConfig {
+	/**
+	 * Obtain the user's configuration data.
+	 * <p>
+	 * The configuration file is always returned to the caller, even if no file
+	 * exists in the user's home directory at the time the call was made. Lookup
+	 * requests are cached and are automatically updated if the user modifies
+	 * the configuration file since the last time it was cached.
+	 * 
+	 * @return a caching reader of the user's configuration file.
+	 */
+	public static OpenSshConfig get() {
+		File home = FS.userHome();
+		if (home == null)
+			home = new File(".").getAbsoluteFile();
+
+		final File config = new File(new File(home, ".ssh"), "config");
+		final OpenSshConfig osc = new OpenSshConfig(home, config);
+		osc.refresh();
+		return osc;
+	}
+
+	/** 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 {@link #hosts} loaded. */
+	private long lastModified;
+
+	/** Cached entries read out of the configuration file. */
+	private Map<String, Host> hosts;
+
+	protected OpenSshConfig(final File h, final File cfg) {
+		home = h;
+		configFile = cfg;
+		hosts = Collections.emptyMap();
+	}
+
+	/**
+	 * 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.
+	 * @return r configuration for the requested name. Never null.
+	 */
+	public Host lookup(final String hostName) {
+		final Map<String, Host> cache = refresh();
+		Host h = cache.get(hostName);
+		if (h == null)
+			h = new Host();
+		if (h.patternsApplied)
+			return h;
+
+		for (final Map.Entry<String, Host> e : cache.entrySet()) {
+			if (!isHostPattern(e.getKey()))
+				continue;
+			if (!isHostMatch(e.getKey(), hostName))
+				continue;
+			h.copyFrom(e.getValue());
+		}
+
+		if (h.hostName == null)
+			h.hostName = hostName;
+		if (h.user == null)
+			h.user = DefaultSshSessionFactory.userName();
+		if (h.port == 0)
+			h.port = DefaultSshSessionFactory.SSH_PORT;
+		h.patternsApplied = true;
+		return h;
+	}
+
+	private synchronized Map<String, Host> refresh() {
+		final long mtime = configFile.lastModified();
+		if (mtime != lastModified) {
+			try {
+				final FileInputStream in = new FileInputStream(configFile);
+				try {
+					hosts = parse(in);
+				} finally {
+					in.close();
+				}
+			} catch (FileNotFoundException none) {
+				hosts = Collections.emptyMap();
+			} catch (IOException err) {
+				hosts = Collections.emptyMap();
+			}
+			lastModified = mtime;
+		}
+		return hosts;
+	}
+
+	private Map<String, Host> parse(final InputStream in) throws IOException {
+		final Map<String, Host> m = new LinkedHashMap<String, Host>();
+		final BufferedReader br = new BufferedReader(new InputStreamReader(in));
+		final List<Host> current = new ArrayList<Host>(4);
+		String line;
+
+		while ((line = br.readLine()) != null) {
+			line = line.trim();
+			if (line.length() == 0 || line.startsWith("#"))
+				continue;
+
+			final int sp = line.indexOf(' ');
+			final int eq = line.indexOf('=');
+			final int splitAt;
+			if (sp >= 0 && eq >= 0)
+				splitAt = Math.min(sp, eq);
+			else if (sp < 0)
+				splitAt = eq;
+			else
+				splitAt = sp;
+			final String keyword = line.substring(0, splitAt).trim();
+			final String argValue = line.substring(splitAt + 1).trim();
+
+			if ("Host".equalsIgnoreCase(keyword)) {
+				current.clear();
+				for (final String name : argValue.split("[ \t]")) {
+					Host c = m.get(name);
+					if (c == null) {
+						c = new Host();
+						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 ("HostName".equalsIgnoreCase(keyword)) {
+				for (final Host c : current)
+					if (c.hostName == null)
+						c.hostName = dequote(argValue);
+			} else if ("User".equalsIgnoreCase(keyword)) {
+				for (final Host c : current)
+					if (c.user == null)
+						c.user = dequote(argValue);
+			} else if ("Port".equalsIgnoreCase(keyword)) {
+				try {
+					final int port = Integer.parseInt(dequote(argValue));
+					for (final Host c : current)
+						if (c.port == 0)
+							c.port = port;
+				} catch (NumberFormatException nfe) {
+					// Bad port number. Don't set it.
+				}
+			} else if ("IdentityFile".equalsIgnoreCase(keyword)) {
+				for (final Host c : current)
+					if (c.identityFile == null)
+						c.identityFile = toFile(dequote(argValue));
+			}
+		}
+
+		return m;
+	}
+
+	private static boolean isHostPattern(final String s) {
+		return s.indexOf('*') >= 0 || s.indexOf('?') >= 0;
+	}
+
+	private static boolean isHostMatch(final String pattern, final String name) {
+		final FileNameMatcher fn;
+		try {
+			fn = new FileNameMatcher(pattern, null);
+		} catch (InvalidPatternException e) {
+			return false;
+		}
+		fn.append(name);
+		return fn.isMatch();
+	}
+
+	private static String dequote(final String value) {
+		if (value.startsWith("\"") && value.endsWith("\""))
+			return value.substring(1, value.length() - 2);
+		return value;
+	}
+
+	private File toFile(final String path) {
+		if (path.startsWith("~/"))
+			return new File(home, path.substring(2));
+		return new File(home, path);
+	}
+
+	/**
+	 * Configuration of one "Host" block in the configuration file.
+	 * <p>
+	 * If returned from {@link OpenSshConfig#lookup(String)} some or all of the
+	 * properties may not be populated. The properties which are not populated
+	 * should be defaulted by the caller.
+	 * <p>
+	 * When returned from {@link OpenSshConfig#lookup(String)} any wildcard
+	 * entries which appear later in the configuration file will have been
+	 * already merged into this block.
+	 */
+	public static class Host {
+		boolean patternsApplied;
+
+		String hostName;
+
+		int port;
+
+		File identityFile;
+
+		String user;
+
+		void copyFrom(final Host src) {
+			if (hostName == null)
+				hostName = src.hostName;
+			if (port == 0)
+				port = src.port;
+			if (identityFile == null)
+				identityFile = src.identityFile;
+			if (user == null)
+				user = src.user;
+		}
+
+		/**
+		 * @return the real IP address or host name to connect to; never null.
+		 */
+		public String getHostName() {
+			return hostName;
+		}
+
+		/**
+		 * @return the real port number to connect to; never 0.
+		 */
+		public int getPort() {
+			return port;
+		}
+
+		/**
+		 * @return path of the private key file to use for authentication; null
+		 *         if the caller should use default authentication strategies.
+		 */
+		public File getIdentityFile() {
+			return identityFile;
+		}
+
+		/**
+		 * @return the real user name to connect as; never null.
+		 */
+		public String getUser() {
+			return user;
+		}
+	}
+}
-- 
1.6.0.rc3.250.g8dd0

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [EGIT PATCH 2/2] Honor ~/.ssh/config whenever possible during SSH based transport
  2008-08-15 17:35 ` [EGIT PATCH 2/2] Honor ~/.ssh/config whenever possible during SSH based transport Shawn O. Pearce
@ 2008-08-20 20:34   ` Robin Rosenberg
  2008-08-20 20:38     ` Shawn O. Pearce
  0 siblings, 1 reply; 4+ messages in thread
From: Robin Rosenberg @ 2008-08-20 20:34 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git

fredagen den 15 augusti 2008 19.35.05 skrev Shawn O. Pearce:
> +	private void addIdentity(final File identityFile)
> +			throws JSchException {
> +		final String path = identityFile.getAbsolutePath();
> +		if (loadedIdentities.add(path))
> +			provider.getJSch().addIdentity(path);
>  	}

Ok, this breaks 3.3 compatibility and I accidentally pushed it to master. Now Marek also
has stuff that only works (well) with 3.4 becaujse of bugs in 3.3, that won't be fixed. So maybe
we'd better skip 3.3 in order to work faster. I.e. I won't revert this commit, but I am willing to
accept a patch to "fix" it in order to prolong 3.3 compatibilty.

-- robin

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [EGIT PATCH 2/2] Honor ~/.ssh/config whenever possible during SSH based transport
  2008-08-20 20:34   ` Robin Rosenberg
@ 2008-08-20 20:38     ` Shawn O. Pearce
  0 siblings, 0 replies; 4+ messages in thread
From: Shawn O. Pearce @ 2008-08-20 20:38 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git

Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
> fredagen den 15 augusti 2008 19.35.05 skrev Shawn O. Pearce:
> > +	private void addIdentity(final File identityFile)
> > +			throws JSchException {
> > +		final String path = identityFile.getAbsolutePath();
> > +		if (loadedIdentities.add(path))
> > +			provider.getJSch().addIdentity(path);
> >  	}
> 
> Ok, this breaks 3.3 compatibility and I accidentally pushed it to master. Now Marek also
> has stuff that only works (well) with 3.4 becaujse of bugs in 3.3, that won't be fixed. So maybe
> we'd better skip 3.3 in order to work faster. I.e. I won't revert this commit, but I am willing to
> accept a patch to "fix" it in order to prolong 3.3 compatibilty.

I agree.  But I'm not going to be supplying that fix myself.  I don't
want to spend a lot of time messing around with the SSH transport,
that was just annoying me one morning.

-- 
Shawn.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2008-08-20 20:39 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-15 17:35 [EGIT PATCH 1/2] Refactor SSH key loading so we don't duplicate keys Shawn O. Pearce
2008-08-15 17:35 ` [EGIT PATCH 2/2] Honor ~/.ssh/config whenever possible during SSH based transport Shawn O. Pearce
2008-08-20 20:34   ` Robin Rosenberg
2008-08-20 20:38     ` Shawn O. Pearce

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).