You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2018/09/06 16:03:37 UTC
[27/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
deleted file mode 100644
index da5f687..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * Monitors the {@code ~/.ssh/config} file of the user currently running
- * the client, re-loading it if necessary. It also (optionally) enforces
- * the same permissions regime as {@code OpenSSH}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultConfigFileHostEntryResolver extends ConfigFileHostEntryResolver {
- /**
- * The default instance that enforces the same permissions regime as {@code OpenSSH}
- */
- public static final DefaultConfigFileHostEntryResolver INSTANCE = new DefaultConfigFileHostEntryResolver(true);
-
- private final boolean strict;
-
- /**
- * @param strict If {@code true} then makes sure that the containing folder
- * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
- * does not check these permissions
- * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
- */
- public DefaultConfigFileHostEntryResolver(boolean strict) {
- this(HostConfigEntry.getDefaultHostConfigFile(), strict);
- }
-
- public DefaultConfigFileHostEntryResolver(File file, boolean strict) {
- this(Objects.requireNonNull(file, "No file provided").toPath(), strict, IoUtils.getLinkOptions(true));
- }
-
- public DefaultConfigFileHostEntryResolver(Path path, boolean strict, LinkOption... options) {
- super(path, options);
- this.strict = strict;
- }
-
- /**
- * @return If {@code true} then makes sure that the containing folder
- * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
- * does not check these permissions
- * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
- */
- public final boolean isStrict() {
- return strict;
- }
-
- @Override
- protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
- if (isStrict()) {
- if (log.isDebugEnabled()) {
- log.debug("reloadHostConfigEntries({}@{}:{}) check permissions of {}", username, host, port, path);
- }
-
- Map.Entry<String, ?> violation = validateStrictConfigFilePermissions(path);
- if (violation != null) {
- log.warn("reloadHostConfigEntries({}@{}:{}) invalid file={} permissions: {}",
- username, host, port, path, violation.getKey());
- updateReloadAttributes();
- return Collections.emptyList();
- }
- }
-
- return super.reloadHostConfigEntries(path, host, port, username);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
deleted file mode 100644
index f401cee..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
+++ /dev/null
@@ -1,1169 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.client.config.hosts;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.StreamCorruptedException;
-import java.io.Writer;
-import java.net.InetAddress;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.auth.MutableUserHolder;
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.config.keys.IdentityUtils;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
-
-/**
- * Represents an entry in the client's configuration file as defined by
- * the <A HREF="http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config">configuration
- * file format</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class HostConfigEntry extends HostPatternsHolder implements MutableUserHolder {
- /**
- * Standard OpenSSH config file name
- */
- public static final String STD_CONFIG_FILENAME = "config";
-
- public static final String HOST_CONFIG_PROP = "Host";
- public static final String HOST_NAME_CONFIG_PROP = "HostName";
- public static final String PORT_CONFIG_PROP = SshConfigFileReader.PORT_CONFIG_PROP;
- public static final String USER_CONFIG_PROP = "User";
- public static final String IDENTITY_FILE_CONFIG_PROP = "IdentityFile";
- /**
- * Use only the identities specified in the host entry (if any)
- */
- public static final String EXCLUSIVE_IDENTITIES_CONFIG_PROP = "IdentitiesOnly";
- public static final boolean DEFAULT_EXCLUSIVE_IDENTITIES = false;
-
- /**
- * A case <U>insensitive</U> {@link Set} of the properties that receive special handling
- */
- public static final Set<String> EXPLICIT_PROPERTIES =
- Collections.unmodifiableSet(
- GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER,
- HOST_CONFIG_PROP, HOST_NAME_CONFIG_PROP, PORT_CONFIG_PROP,
- USER_CONFIG_PROP, IDENTITY_FILE_CONFIG_PROP, EXCLUSIVE_IDENTITIES_CONFIG_PROP
- ));
-
- public static final String MULTI_VALUE_SEPARATORS = " ,";
-
- public static final char HOME_TILDE_CHAR = '~';
- public static final char PATH_MACRO_CHAR = '%';
- public static final char LOCAL_HOME_MACRO = 'd';
- public static final char LOCAL_USER_MACRO = 'u';
- public static final char LOCAL_HOST_MACRO = 'l';
- public static final char REMOTE_HOST_MACRO = 'h';
- public static final char REMOTE_USER_MACRO = 'r';
- // Extra - not part of the standard
- public static final char REMOTE_PORT_MACRO = 'p';
-
- private static final class LazyDefaultConfigFileHolder {
- private static final Path CONFIG_FILE =
- PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_CONFIG_FILENAME);
-
- private LazyDefaultConfigFileHolder() {
- throw new UnsupportedOperationException("No instance allowed");
- }
- }
-
- private String host;
- private String hostName;
- private int port;
- private String username;
- private Boolean exclusiveIdentites;
- private Collection<String> identities = Collections.emptyList();
- private Map<String, String> properties = Collections.emptyMap();
-
- public HostConfigEntry() {
- super();
- }
-
- public HostConfigEntry(String pattern, String host, int port, String username) {
- setHost(pattern);
- setHostName(host);
- setPort(port);
- setUsername(username);
- }
-
- /**
- * @return The <U>pattern(s)</U> represented by this entry
- */
- public String getHost() {
- return host;
- }
-
- public void setHost(String host) {
- this.host = host;
- setPatterns(parsePatterns(parseConfigValue(host)));
- }
-
- public void setHost(Collection<String> patterns) {
- this.host = GenericUtils.join(ValidateUtils.checkNotNullAndNotEmpty(patterns, "No patterns"), ',');
- setPatterns(parsePatterns(patterns));
- }
-
- /**
- * @return The effective host name to connect to if the pattern matches
- */
- public String getHostName() {
- return hostName;
- }
-
- public void setHostName(String hostName) {
- this.hostName = hostName;
- }
-
- public String resolveHostName(String originalHost) {
- return resolveHostName(originalHost, getHostName());
- }
-
- /**
- * @return A port override - if positive
- */
- public int getPort() {
- return port;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- /**
- * Resolves the effective port to use
- *
- * @param originalPort The original requested port
- * @return If the host entry port is positive, then it is used, otherwise
- * the original requested port
- * @see #resolvePort(int, int)
- */
- public int resolvePort(int originalPort) {
- return resolvePort(originalPort, getPort());
- }
-
- /**
- * @return A username override - if not {@code null}/empty
- */
- @Override
- public String getUsername() {
- return username;
- }
-
- @Override
- public void setUsername(String username) {
- this.username = username;
- }
-
- /**
- * Resolves the effective username
- *
- * @param originalUser The original requested username
- * @return If the configured host entry username is not {@code null}/empty
- * then it is used, otherwise the original one.
- * @see #resolveUsername(String)
- */
- public String resolveUsername(String originalUser) {
- return resolveUsername(originalUser, getUsername());
- }
-
- /**
- * @return The current identities file paths - may be {@code null}/empty
- */
- public Collection<String> getIdentities() {
- return identities;
- }
-
- /**
- * @param file A {@link File} that contains an identity key - never {@code null}
- */
- public void addIdentity(File file) {
- addIdentity(Objects.requireNonNull(file, "No file").toPath());
- }
-
- /**
- * @param path A {@link Path} to a file that contains an identity key
- * - never {@code null}
- */
- public void addIdentity(Path path) {
- addIdentity(Objects.requireNonNull(path, "No path").toAbsolutePath().normalize().toString());
- }
-
- /**
- * Adds a path to an identity file
- *
- * @param id The identity path to add - never {@code null}
- */
- public void addIdentity(String id) {
- String path = ValidateUtils.checkNotNullAndNotEmpty(id, "No identity provided");
- if (GenericUtils.isEmpty(identities)) {
- identities = new LinkedList<>();
- }
- identities.add(path);
- }
-
- public void setIdentities(Collection<String> identities) {
- this.identities = (identities == null) ? Collections.emptyList() : identities;
- }
-
- /**
- * @return {@code true} if must use only the identities in this entry
- */
- public boolean isIdentitiesOnly() {
- return (exclusiveIdentites == null) ? DEFAULT_EXCLUSIVE_IDENTITIES : exclusiveIdentites;
- }
-
- public void setIdentitiesOnly(boolean identitiesOnly) {
- exclusiveIdentites = identitiesOnly;
- }
-
- /**
- * @return A {@link Map} of extra properties that have been read - may be
- * {@code null}/empty, or even contain some values that have been parsed
- * and set as members of the entry (e.g., host, port, etc.). <B>Note:</B>
- * multi-valued keys use a comma-separated list of values
- */
- public Map<String, String> getProperties() {
- return properties;
- }
-
- /**
- * @param name Property name - never {@code null}/empty
- * @return Property value or {@code null} if no such property
- * @see #getProperty(String, String)
- */
- public String getProperty(String name) {
- return getProperty(name, null);
- }
-
- /**
- * @param name Property name - never {@code null}/empty
- * @param defaultValue Default value to return if no such property
- * @return The property value or the default one if no such property
- */
- public String getProperty(String name, String defaultValue) {
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- Map<String, String> props = getProperties();
- if (GenericUtils.isEmpty(props)) {
- return null;
- }
-
- String value = props.get(key);
- if (GenericUtils.isEmpty(value)) {
- return defaultValue;
- } else {
- return value;
- }
- }
-
- /**
- * Updates the values that are <U>not</U> already configured with those
- * from the global entry
- *
- * @param globalEntry The global entry - ignored if {@code null} or
- * same reference as this entry
- * @return {@code true} if anything updated
- */
- public boolean processGlobalValues(HostConfigEntry globalEntry) {
- if ((globalEntry == null) || (this == globalEntry)) {
- return false;
- }
-
- boolean modified = false;
- /*
- * NOTE !!! DO NOT TRY TO CHANGE THE ORDER OF THE OR-ing AS IT
- * WOULD CAUSE INVALID CODE EXECUTION
- */
- modified = updateGlobalPort(globalEntry.getPort()) || modified;
- modified = updateGlobalHostName(globalEntry.getHostName()) || modified;
- modified = updateGlobalUserName(globalEntry.getUsername()) || modified;
- modified = updateGlobalIdentities(globalEntry.getIdentities()) || modified;
- modified = updateGlobalIdentityOnly(globalEntry.isIdentitiesOnly()) || modified;
-
- Map<String, String> updated = updateGlobalProperties(globalEntry.getProperties());
- modified = (GenericUtils.size(updated) > 0) || modified;
-
- return modified;
- }
-
- /**
- * Sets all the properties for which no current value exists in the entry
- *
- * @param props The global properties - ignored if {@code null}/empty
- * @return A {@link Map} of the <U>updated</U> properties
- */
- public Map<String, String> updateGlobalProperties(Map<String, String> props) {
- if (GenericUtils.isEmpty(props)) {
- return Collections.emptyMap();
- }
-
- Map<String, String> updated = null;
- // Cannot use forEach because of the modification of the updated map value (non-final)
- for (Map.Entry<String, String> pe : props.entrySet()) {
- String key = pe.getKey();
- String curValue = getProperty(key);
- if (GenericUtils.length(curValue) > 0) {
- continue;
- }
-
- String newValue = pe.getValue();
- setProperty(key, newValue);
-
- if (updated == null) {
- updated = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- }
-
- updated.put(key, newValue);
- }
-
- if (updated == null) {
- return Collections.emptyMap();
- } else {
- return updated;
- }
- }
-
- /**
- * @param ids Global identities - ignored if {@code null}/empty or already
- * have configured identities
- * @return {@code true} if updated identities
- */
- public boolean updateGlobalIdentities(Collection<String> ids) {
- if (GenericUtils.isEmpty(ids) || (GenericUtils.size(getIdentities()) > 0)) {
- return false;
- }
-
- for (String id : ids) {
- addIdentity(id);
- }
-
- return true;
- }
-
- /**
- * @param user The global user name - ignored if {@code null}/empty or
- * already have a configured user
- * @return {@code true} if updated the username
- */
- public boolean updateGlobalUserName(String user) {
- if (GenericUtils.isEmpty(user) || (GenericUtils.length(getUsername()) > 0)) {
- return false;
- }
-
- setUsername(user);
- return true;
- }
-
- /**
- * @param name The global host name - ignored if {@code null}/empty or
- * already have a configured target host
- * @return {@code true} if updated the target host
- */
- public boolean updateGlobalHostName(String name) {
- if (GenericUtils.isEmpty(name) || (GenericUtils.length(getHostName()) > 0)) {
- return false;
- }
-
- setHostName(name);
- return true;
- }
-
- /**
- * @param portValue The global port value - ignored if not positive
- * or already have a configured port
- * @return {@code true} if updated the port value
- */
- public boolean updateGlobalPort(int portValue) {
- if ((portValue <= 0) || (getPort() > 0)) {
- return false;
- }
-
- setPort(portValue);
- return true;
- }
-
- /**
- * @param identitiesOnly Whether to use only the identities in this entry.
- * Ignored if already set
- * @return {@code true} if updated the option value
- */
- public boolean updateGlobalIdentityOnly(boolean identitiesOnly) {
- if (exclusiveIdentites != null) {
- return false;
- }
-
- setIdentitiesOnly(identitiesOnly);
- return true;
- }
-
- /**
- * @param name Property name - never {@code null}/empty
- * @param valsList The available values for the property
- * @param ignoreAlreadyInitialized If {@code false} and one of the "known"
- * properties is encountered then throws an exception
- * @throws IllegalArgumentException If an existing value is overwritten and
- * <tt>ignoreAlreadyInitialized</tt> is {@code false} (except for {@link #IDENTITY_FILE_CONFIG_PROP}
- * which is <U>cumulative</U>
- * @see #HOST_NAME_CONFIG_PROP
- * @see #PORT_CONFIG_PROP
- * @see #USER_CONFIG_PROP
- * @see #IDENTITY_FILE_CONFIG_PROP
- */
- public void processProperty(String name, Collection<String> valsList, boolean ignoreAlreadyInitialized) {
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- String joinedValue = GenericUtils.join(valsList, ',');
- appendPropertyValue(key, joinedValue);
-
- if (HOST_NAME_CONFIG_PROP.equalsIgnoreCase(key)) {
- ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target hosts N/A: %s", joinedValue);
-
- String curValue = getHostName();
- ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
- setHostName(joinedValue);
- } else if (PORT_CONFIG_PROP.equalsIgnoreCase(key)) {
- ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target ports N/A: %s", joinedValue);
-
- int curValue = getPort();
- ValidateUtils.checkTrue((curValue <= 0) || ignoreAlreadyInitialized, "Already initialized %s: %d", key, curValue);
-
- int newValue = Integer.parseInt(joinedValue);
- ValidateUtils.checkTrue(newValue > 0, "Bad new port value: %d", newValue);
- setPort(newValue);
- } else if (USER_CONFIG_PROP.equalsIgnoreCase(key)) {
- ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target users N/A: %s", joinedValue);
-
- String curValue = getUsername();
- ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
- setUsername(joinedValue);
- } else if (IDENTITY_FILE_CONFIG_PROP.equalsIgnoreCase(key)) {
- ValidateUtils.checkTrue(GenericUtils.size(valsList) > 0, "No identity files specified");
- for (String id : valsList) {
- addIdentity(id);
- }
- } else if (EXCLUSIVE_IDENTITIES_CONFIG_PROP.equalsIgnoreCase(key)) {
- setIdentitiesOnly(
- SshConfigFileReader.parseBooleanValue(
- ValidateUtils.checkNotNullAndNotEmpty(joinedValue, "No identities option value")));
- }
- }
-
- /**
- * Appends a value using a <U>comma</U> to an existing one. If no previous
- * value then same as calling {@link #setProperty(String, String)}.
- *
- * @param name Property name - never {@code null}/empty
- * @param value The value to be appended - ignored if {@code null}/empty
- * @return The value <U>before</U> appending - {@code null} if no previous value
- */
- public String appendPropertyValue(String name, String value) {
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- String curVal = getProperty(key);
- if (GenericUtils.isEmpty(value)) {
- return curVal;
- }
-
- if (GenericUtils.isEmpty(curVal)) {
- return setProperty(key, value);
- }
-
- return setProperty(key, curVal + ',' + value);
- }
-
- /**
- * Sets / Replaces the property value
- *
- * @param name Property name - never {@code null}/empty
- * @param value Property value - if {@code null}/empty then
- * {@link #removeProperty(String)} is called
- * @return The previous property value - {@code null} if no such name
- */
- public String setProperty(String name, String value) {
- if (GenericUtils.isEmpty(value)) {
- return removeProperty(name);
- }
-
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- if (GenericUtils.isEmpty(properties)) {
- properties = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- }
-
- return properties.put(key, value);
- }
-
- /**
- * @param name Property name - never {@code null}/empty
- * @return The removed property value - {@code null} if no such property name
- */
- public String removeProperty(String name) {
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- Map<String, String> props = getProperties();
- if (GenericUtils.isEmpty(props)) {
- return null;
- } else {
- return props.remove(key);
- }
- }
-
- /**
- * @param properties The properties to set - if {@code null} then an empty
- * map is effectively set. <B>Note:</B> it is highly recommended to use a
- * <U>case insensitive</U> key mapper.
- */
- public void setProperties(Map<String, String> properties) {
- this.properties = (properties == null) ? Collections.emptyMap() : properties;
- }
-
- public <A extends Appendable> A append(A sb) throws IOException {
- sb.append(HOST_CONFIG_PROP).append(' ').append(ValidateUtils.checkNotNullAndNotEmpty(getHost(), "No host pattern")).append(IoUtils.EOL);
- appendNonEmptyProperty(sb, HOST_NAME_CONFIG_PROP, getHostName());
- appendNonEmptyPort(sb, PORT_CONFIG_PROP, getPort());
- appendNonEmptyProperty(sb, USER_CONFIG_PROP, getUsername());
- appendNonEmptyValues(sb, IDENTITY_FILE_CONFIG_PROP, getIdentities());
- if (exclusiveIdentites != null) {
- appendNonEmptyProperty(sb, EXCLUSIVE_IDENTITIES_CONFIG_PROP, SshConfigFileReader.yesNoValueOf(exclusiveIdentites));
- }
- appendNonEmptyProperties(sb, getProperties());
- return sb;
- }
-
- @Override
- public String toString() {
- return getHost() + ": " + getUsername() + "@" + getHostName() + ":" + getPort();
- }
-
- /**
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param name The property name - never {@code null}/empty
- * @param port The port value - ignored if non-positive
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- * @see #appendNonEmptyProperty(Appendable, String, Object)
- */
- public static <A extends Appendable> A appendNonEmptyPort(A sb, String name, int port) throws IOException {
- return appendNonEmptyProperty(sb, name, (port > 0) ? Integer.toString(port) : null);
- }
-
- /**
- * Appends the extra properties - while skipping the {@link #EXPLICIT_PROPERTIES} ones
- *
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param props The {@link Map} of properties - ignored if {@code null}/empty
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- * @see #appendNonEmptyProperty(Appendable, String, Object)
- */
- public static <A extends Appendable> A appendNonEmptyProperties(A sb, Map<String, ?> props) throws IOException {
- if (GenericUtils.isEmpty(props)) {
- return sb;
- }
-
- // Cannot use forEach because of the IOException being thrown by appendNonEmptyProperty
- for (Map.Entry<String, ?> pe : props.entrySet()) {
- String name = pe.getKey();
- if (EXPLICIT_PROPERTIES.contains(name)) {
- continue;
- }
-
- appendNonEmptyProperty(sb, name, pe.getValue());
- }
-
- return sb;
- }
-
- /**
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param name The property name - never {@code null}/empty
- * @param value The property value - ignored if {@code null}. <B>Note:</B>
- * if the string representation of the value contains any commas, they are
- * assumed to indicate a multi-valued property which is broken down to
- * <U>individual</U> lines - one per value.
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- * @see #appendNonEmptyValues(Appendable, String, Object...)
- */
- public static <A extends Appendable> A appendNonEmptyProperty(A sb, String name, Object value) throws IOException {
- String s = Objects.toString(value, null);
- String[] vals = GenericUtils.split(s, ',');
- return appendNonEmptyValues(sb, name, (Object[]) vals);
- }
-
- /**
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param name The property name - never {@code null}/empty
- * @param values The values to be added - one per line - ignored if {@code null}/empty
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- * @see #appendNonEmptyValues(Appendable, String, Collection)
- */
- public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Object... values) throws IOException {
- return appendNonEmptyValues(sb, name, GenericUtils.isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
- }
-
- /**
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param name The property name - never {@code null}/empty
- * @param values The values to be added - one per line - ignored if {@code null}/empty
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- */
- public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Collection<?> values) throws IOException {
- String k = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- if (GenericUtils.isEmpty(values)) {
- return sb;
- }
-
- for (Object v : values) {
- sb.append(" ").append(k).append(' ').append(Objects.toString(v)).append(IoUtils.EOL);
- }
-
- return sb;
- }
-
- /**
- * @param entries The entries - ignored if {@code null}/empty
- * @return A {@link HostConfigEntryResolver} wrapper using the entries
- */
- public static HostConfigEntryResolver toHostConfigEntryResolver(final Collection<? extends HostConfigEntry> entries) {
- if (GenericUtils.isEmpty(entries)) {
- return HostConfigEntryResolver.EMPTY;
- } else {
- return (host1, port1, username1) -> {
- List<HostConfigEntry> matches = findMatchingEntries(host1, entries);
- int numMatches = GenericUtils.size(matches);
- if (numMatches <= 0) {
- return null;
- }
-
- HostConfigEntry match = (numMatches == 1) ? matches.get(0) : findBestMatch(matches);
- if (match == null) {
- ValidateUtils.throwIllegalArgumentException("No best match found for %s@%s:%d out of %d matches", username1, host1, port1, numMatches);
- }
-
- return normalizeEntry(match, host1, port1, username1);
- };
- }
- }
-
- /**
- * @param entry The original entry - ignored if {@code null}
- * @param host The original host name / address
- * @param port The original port
- * @param username The original user name
- * @return A <U>cloned</U> entry whose values are resolved - including
- * expanding macros in the identities files
- * @throws IOException If failed to normalize the entry
- * @see #resolveHostName(String)
- * @see #resolvePort(int)
- * @see #resolveUsername(String)
- * @see #resolveIdentityFilePath(String, String, int, String)
- */
- public static HostConfigEntry normalizeEntry(HostConfigEntry entry, String host, int port, String username) throws IOException {
- if (entry == null) {
- return null;
- }
-
- HostConfigEntry normal = new HostConfigEntry();
- normal.setHost(host);
- normal.setHostName(entry.resolveHostName(host));
- normal.setPort(entry.resolvePort(port));
- normal.setUsername(entry.resolveUsername(username));
-
- Map<String, String> props = entry.getProperties();
- if (GenericUtils.size(props) > 0) {
- normal.setProperties(new TreeMap<>(props));
- }
-
- Collection<String> ids = entry.getIdentities();
- if (GenericUtils.isEmpty(ids)) {
- return normal;
- }
-
- normal.setIdentities(Collections.emptyList()); // start fresh
- for (String id : ids) {
- String path = resolveIdentityFilePath(id, host, port, username);
- normal.addIdentity(path);
- }
-
- return normal;
- }
-
- /**
- * Resolves the effective target host
- *
- * @param originalName The original requested host
- * @param entryName The configured host
- * @return If the configured host entry is not {@code null}/empty
- * then it is used, otherwise the original one.
- */
- public static String resolveHostName(String originalName, String entryName) {
- if (GenericUtils.isEmpty(entryName)) {
- return originalName;
- } else {
- return entryName;
- }
- }
-
- /**
- * Resolves the effective username
- *
- * @param originalUser The original requested username
- * @param entryUser The configured host entry username
- * @return If the configured host entry username is not {@code null}/empty
- * then it is used, otherwise the original one.
- */
- public static String resolveUsername(String originalUser, String entryUser) {
- if (GenericUtils.isEmpty(entryUser)) {
- return originalUser;
- } else {
- return entryUser;
- }
- }
-
- /**
- * Resolves the effective port to use
- *
- * @param originalPort The original requested port
- * @param entryPort The configured host entry port
- * @return If the host entry port is positive, then it is used, otherwise
- * the original requested port
- */
- public static int resolvePort(int originalPort, int entryPort) {
- if (entryPort <= 0) {
- return originalPort;
- } else {
- return entryPort;
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(File file) throws IOException {
- return readHostConfigEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(Path path, OpenOption... options) throws IOException {
- try (InputStream input = Files.newInputStream(path, options)) {
- return readHostConfigEntries(input, true);
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(URL url) throws IOException {
- try (InputStream input = url.openStream()) {
- return readHostConfigEntries(input, true);
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(String filePath) throws IOException {
- try (InputStream inStream = new FileInputStream(filePath)) {
- return readHostConfigEntries(inStream, true);
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(InputStream inStream, boolean okToClose) throws IOException {
- try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
- return readHostConfigEntries(reader, true);
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(Reader rdr, boolean okToClose) throws IOException {
- try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
- return readHostConfigEntries(buf);
- }
- }
-
- /**
- * Reads configuration entries
- *
- * @param rdr The {@link BufferedReader} to use
- * @return The {@link List} of read {@link HostConfigEntry}-ies
- * @throws IOException If failed to parse the read configuration
- */
- public static List<HostConfigEntry> readHostConfigEntries(BufferedReader rdr) throws IOException {
- HostConfigEntry curEntry = null;
- HostConfigEntry globalEntry = null;
- List<HostConfigEntry> entries = null;
-
- int lineNumber = 1;
- for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
- line = GenericUtils.replaceWhitespaceAndTrim(line);
- if (GenericUtils.isEmpty(line)) {
- continue;
- }
-
- int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
- if (pos == 0) {
- continue;
- }
-
- if (pos > 0) {
- line = line.substring(0, pos);
- line = line.trim();
- }
-
- /*
- * Some options use '=', others use ' ' - try both
- * NOTE: we do not validate the format for each option separately
- */
- pos = line.indexOf(' ');
- if (pos < 0) {
- pos = line.indexOf('=');
- }
-
- if (pos < 0) {
- throw new StreamCorruptedException("No configuration value delimiter at line " + lineNumber + ": " + line);
- }
-
- String key = line.substring(0, pos);
- String value = line.substring(pos + 1);
- List<String> valsList = parseConfigValue(value);
-
- if (HOST_CONFIG_PROP.equalsIgnoreCase(key)) {
- if (GenericUtils.isEmpty(valsList)) {
- throw new StreamCorruptedException("Missing host pattern(s) at line " + lineNumber + ": " + line);
- }
-
- // If the all-hosts pattern is used, make sure no global section already active
- for (String name : valsList) {
- if (ALL_HOSTS_PATTERN.equalsIgnoreCase(name) && (globalEntry != null)) {
- throw new StreamCorruptedException("Overriding the global section with a specific one at line " + lineNumber + ": " + line);
- }
- }
-
- if (curEntry != null) {
- curEntry.processGlobalValues(globalEntry);
- }
-
- entries = updateEntriesList(entries, curEntry);
-
- curEntry = new HostConfigEntry();
- curEntry.setHost(valsList);
- } else if (curEntry == null) {
- // if 1st encountered property is NOT for a specific host, then configuration applies to ALL
- curEntry = new HostConfigEntry();
- curEntry.setHost(Collections.singletonList(ALL_HOSTS_PATTERN));
- globalEntry = curEntry;
- }
-
- try {
- curEntry.processProperty(key, valsList, false);
- } catch (RuntimeException e) {
- throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
- + " to process line #" + lineNumber + " (" + line + ")"
- + ": " + e.getMessage());
- }
- }
-
- if (curEntry != null) {
- curEntry.processGlobalValues(globalEntry);
- }
-
- entries = updateEntriesList(entries, curEntry);
- if (entries == null) {
- return Collections.emptyList();
- } else {
- return entries;
- }
- }
-
- /**
- * Finds the best match out of the given ones.
- *
- * @param matches The available matches - ignored if {@code null}/empty
- * @return The best match or {@code null} if no matches or no best match found
- * @see #findBestMatch(Iterator)
- */
- public static HostConfigEntry findBestMatch(Collection<? extends HostConfigEntry> matches) {
- if (GenericUtils.isEmpty(matches)) {
- return null;
- } else {
- return findBestMatch(matches.iterator());
- }
- }
-
- /**
- * Finds the best match out of the given ones.
- *
- * @param matches The available matches - ignored if {@code null}/empty
- * @return The best match or {@code null} if no matches or no best match found
- * @see #findBestMatch(Iterator)
- */
- public static HostConfigEntry findBestMatch(Iterable<? extends HostConfigEntry> matches) {
- if (matches == null) {
- return null;
- } else {
- return findBestMatch(matches.iterator());
- }
- }
-
- /**
- * Finds the best match out of the given ones. The best match is defined as one whose
- * pattern is as <U>specific</U> as possible (if more than one match is available).
- * I.e., a non-global match is preferred over global one, and a match with no wildcards
- * is preferred over one with such a pattern.
- *
- * @param matches The available matches - ignored if {@code null}/empty
- * @return The best match or {@code null} if no matches or no best match found
- * @see #isSpecificHostPattern(String)
- */
- public static HostConfigEntry findBestMatch(Iterator<? extends HostConfigEntry> matches) {
- if ((matches == null) || (!matches.hasNext())) {
- return null;
- }
-
- HostConfigEntry candidate = matches.next();
- int wildcardMatches = 0;
- while (matches.hasNext()) {
- HostConfigEntry entry = matches.next();
- String entryPattern = entry.getHost();
- String candidatePattern = candidate.getHost();
- // prefer non-global entry over global entry
- if (ALL_HOSTS_PATTERN.equalsIgnoreCase(candidatePattern)) {
- // unlikely, but handle it
- if (ALL_HOSTS_PATTERN.equalsIgnoreCase(entryPattern)) {
- wildcardMatches++;
- } else {
- candidate = entry;
- wildcardMatches = 0;
- }
- continue;
- }
-
- if (isSpecificHostPattern(entryPattern)) {
- // if both are specific then no best match
- if (isSpecificHostPattern(candidatePattern)) {
- return null;
- }
-
- candidate = entry;
- wildcardMatches = 0;
- continue;
- }
-
- wildcardMatches++;
- }
-
- String candidatePattern = candidate.getHost();
- // best match either has specific host or no wildcard matches
- if ((wildcardMatches <= 0) || (isSpecificHostPattern(candidatePattern))) {
- return candidate;
- }
-
- return null;
- }
-
- public static List<HostConfigEntry> updateEntriesList(List<HostConfigEntry> entries, HostConfigEntry curEntry) {
- if (curEntry == null) {
- return entries;
- }
-
- if (entries == null) {
- entries = new ArrayList<>();
- }
-
- entries.add(curEntry);
- return entries;
- }
-
- public static void writeHostConfigEntries(File file, Collection<? extends HostConfigEntry> entries) throws IOException {
- writeHostConfigEntries(Objects.requireNonNull(file, "No file").toPath(), entries, IoUtils.EMPTY_OPEN_OPTIONS);
- }
-
- public static void writeHostConfigEntries(Path path, Collection<? extends HostConfigEntry> entries, OpenOption... options) throws IOException {
- try (OutputStream outputStream = Files.newOutputStream(path, options)) {
- writeHostConfigEntries(outputStream, true, entries);
- }
- }
-
- public static void writeHostConfigEntries(OutputStream outputStream, boolean okToClose, Collection<? extends HostConfigEntry> entries) throws IOException {
- if (GenericUtils.isEmpty(entries)) {
- return;
- }
-
- try (Writer w = new OutputStreamWriter(NoCloseOutputStream.resolveOutputStream(outputStream, okToClose), StandardCharsets.UTF_8)) {
- appendHostConfigEntries(w, entries);
- }
- }
-
- public static <A extends Appendable> A appendHostConfigEntries(A sb, Collection<? extends HostConfigEntry> entries) throws IOException {
- if (GenericUtils.isEmpty(entries)) {
- return sb;
- }
-
- for (HostConfigEntry entry : entries) {
- entry.append(sb);
- }
-
- return sb;
- }
-
- /**
- * Checks if this is a multi-value - allow space and comma
- *
- * @param value The value - ignored if {@code null}/empty (after trimming)
- * @return A {@link List} of the encountered values
- */
- public static List<String> parseConfigValue(String value) {
- String s = GenericUtils.replaceWhitespaceAndTrim(value);
- if (GenericUtils.isEmpty(s)) {
- return Collections.emptyList();
- }
-
- for (int index = 0; index < MULTI_VALUE_SEPARATORS.length(); index++) {
- char sep = MULTI_VALUE_SEPARATORS.charAt(index);
- int pos = s.indexOf(sep);
- if (pos >= 0) {
- String[] vals = GenericUtils.split(s, sep);
- if (GenericUtils.isEmpty(vals)) {
- return Collections.emptyList();
- } else {
- return Arrays.asList(vals);
- }
- }
- }
-
- // this point is reached if no separators found
- return Collections.singletonList(s);
- }
-
- // The file name may use the tilde syntax to refer to a user’s home directory or one of the following escape characters:
- // '%d' (local user's home directory), '%u' (local user name), '%l' (local host name), '%h' (remote host name) or '%r' (remote user name).
- public static String resolveIdentityFilePath(String id, String host, int port, String username) throws IOException {
- if (GenericUtils.isEmpty(id)) {
- return id;
- }
-
- String path = id.replace('/', File.separatorChar); // make sure all separators are local
- String[] elements = GenericUtils.split(path, File.separatorChar);
- StringBuilder sb = new StringBuilder(path.length() + Long.SIZE);
- for (int index = 0; index < elements.length; index++) {
- String elem = elements[index];
- if (index > 0) {
- sb.append(File.separatorChar);
- }
-
- for (int curPos = 0; curPos < elem.length(); curPos++) {
- char ch = elem.charAt(curPos);
- if (ch == HOME_TILDE_CHAR) {
- ValidateUtils.checkTrue((curPos == 0) && (index == 0), "Home tilde must be first: %s", id);
- appendUserHome(sb);
- } else if (ch == PATH_MACRO_CHAR) {
- curPos++;
- ValidateUtils.checkTrue(curPos < elem.length(), "Missing macro modifier in %s", id);
- ch = elem.charAt(curPos);
- switch(ch) {
- case PATH_MACRO_CHAR:
- sb.append(ch);
- break;
- case LOCAL_HOME_MACRO:
- ValidateUtils.checkTrue((curPos == 1) && (index == 0), "Home macro must be first: %s", id);
- appendUserHome(sb);
- break;
- case LOCAL_USER_MACRO:
- sb.append(ValidateUtils.checkNotNullAndNotEmpty(OsUtils.getCurrentUser(), "No local user name value"));
- break;
- case LOCAL_HOST_MACRO: {
- InetAddress address = Objects.requireNonNull(InetAddress.getLocalHost(), "No local address");
- sb.append(ValidateUtils.checkNotNullAndNotEmpty(address.getHostName(), "No local name"));
- break;
- }
- case REMOTE_HOST_MACRO:
- sb.append(ValidateUtils.checkNotNullAndNotEmpty(host, "No remote host provided"));
- break;
- case REMOTE_USER_MACRO:
- sb.append(ValidateUtils.checkNotNullAndNotEmpty(username, "No remote user provided"));
- break;
- case REMOTE_PORT_MACRO:
- ValidateUtils.checkTrue(port > 0, "Bad remote port value: %d", port);
- sb.append(port);
- break;
- default:
- ValidateUtils.throwIllegalArgumentException("Bad modifier '%s' in %s", String.valueOf(ch), id);
- }
- } else {
- sb.append(ch);
- }
- }
- }
-
- return sb.toString();
- }
-
- public static StringBuilder appendUserHome(StringBuilder sb) {
- return appendUserHome(sb, IdentityUtils.getUserHomeFolder());
- }
-
- public static StringBuilder appendUserHome(StringBuilder sb, Path userHome) {
- return appendUserHome(sb, Objects.requireNonNull(userHome, "No user home folder").toString());
- }
-
- public static StringBuilder appendUserHome(StringBuilder sb, String userHome) {
- if (GenericUtils.isEmpty(userHome)) {
- return sb;
- }
-
- sb.append(userHome);
- // strip any ending separator since we add our own
- int len = sb.length();
- if (sb.charAt(len - 1) == File.separatorChar) {
- sb.setLength(len - 1);
- }
-
- return sb;
- }
-
- /**
- * @return The default {@link Path} location of the OpenSSH hosts entries configuration file
- */
- @SuppressWarnings("synthetic-access")
- public static Path getDefaultHostConfigFile() {
- return LazyDefaultConfigFileHolder.CONFIG_FILE;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
deleted file mode 100644
index a07cfcf..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.IOException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface HostConfigEntryResolver {
-
- /**
- * An "empty" implementation that does not resolve any entry
- */
- HostConfigEntryResolver EMPTY = new HostConfigEntryResolver() {
- @Override
- public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
- return null;
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- /**
- * Invoked when creating a new client session in order to allow for overriding
- * of the original parameters
- *
- * @param host The requested host - never {@code null}/empty
- * @param port The requested port
- * @param username The requested username
- * @return A {@link HostConfigEntry} for the actual target - {@code null} if use
- * original parameters. <B>Note:</B> if any identity files are attached to the
- * configuration then they must point to <U>existing</U> locations. This means
- * that any macros such as <code>~, %d, %h</code>, etc. must be resolved <U>prior</U>
- * to returning the value
- * @throws IOException If failed to resolve the configuration
- */
- HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
deleted file mode 100644
index 20d682f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Represents a pattern definition in the <U>known_hosts</U> file
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#About_the_Contents_of_the_known_hosts_Files">
- * OpenSSH cookbook - About the Contents of the known hosts Files</A>
- */
-public class HostPatternValue {
- private Pattern pattern;
- private int port;
- private boolean negated;
-
- public HostPatternValue() {
- super();
- }
-
- public HostPatternValue(Pattern pattern, boolean negated) {
- this(pattern, 0, negated);
- }
-
- public HostPatternValue(Pattern pattern, int port, boolean negated) {
- this.pattern = pattern;
- this.port = port;
- this.negated = negated;
- }
-
- public Pattern getPattern() {
- return pattern;
- }
-
- public void setPattern(Pattern pattern) {
- this.pattern = pattern;
- }
-
- public int getPort() {
- return port;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public boolean isNegated() {
- return negated;
- }
-
- public void setNegated(boolean negated) {
- this.negated = negated;
- }
-
- @Override
- public String toString() {
- Pattern p = getPattern();
- String purePattern = (p == null) ? null : p.pattern();
- StringBuilder sb = new StringBuilder(GenericUtils.length(purePattern) + Short.SIZE);
- if (isNegated()) {
- sb.append(HostPatternsHolder.NEGATION_CHAR_PATTERN);
- }
-
- int portValue = getPort();
- if (portValue > 0) {
- sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
- }
- sb.append(purePattern);
- if (portValue > 0) {
- sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM);
- sb.append(HostPatternsHolder.PORT_VALUE_DELIMITER);
- sb.append(portValue);
- }
-
- return sb.toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
deleted file mode 100644
index 9d90dac..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class HostPatternsHolder {
-
- /**
- * Used in a host pattern to denote zero or more consecutive characters
- */
- public static final char WILDCARD_PATTERN = '*';
- public static final String ALL_HOSTS_PATTERN = String.valueOf(WILDCARD_PATTERN);
-
- /**
- * Used in a host pattern to denote any <U>one</U> character
- */
- public static final char SINGLE_CHAR_PATTERN = '?';
-
- /**
- * Used to negate a host pattern
- */
- public static final char NEGATION_CHAR_PATTERN = '!';
-
- /**
- * The available pattern characters
- */
- public static final String PATTERN_CHARS = new String(new char[]{WILDCARD_PATTERN, SINGLE_CHAR_PATTERN, NEGATION_CHAR_PATTERN});
-
- /** Port value separator if non-standard port pattern used */
- public static final char PORT_VALUE_DELIMITER = ':';
-
- /** Non-standard port specification host pattern enclosure start delimiter */
- public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM = '[';
-
- /** Non-standard port specification host pattern enclosure end delimiter */
- public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM = ']';
-
- private Collection<HostPatternValue> patterns = new LinkedList<>();
-
- protected HostPatternsHolder() {
- super();
- }
-
- public Collection<HostPatternValue> getPatterns() {
- return patterns;
- }
-
- public void setPatterns(Collection<HostPatternValue> patterns) {
- this.patterns = patterns;
- }
-
- /**
- * Checks if a given host name / address matches the entry's host pattern(s)
- *
- * @param host The host name / address - ignored if {@code null}/empty
- * @param port The connection port
- * @return {@code true} if the name / address matches the pattern(s)
- * @see #isHostMatch(String, Pattern)
- */
- public boolean isHostMatch(String host, int port) {
- return isHostMatch(host, port, getPatterns());
- }
-
- /**
- * @param pattern The pattern to check - ignored if {@code null}/empty
- * @return {@code true} if the pattern is not empty and contains no wildcard characters
- * @see #WILDCARD_PATTERN
- * @see #SINGLE_CHAR_PATTERN
- * @see #SINGLE_CHAR_PATTERN
- */
- public static boolean isSpecificHostPattern(String pattern) {
- if (GenericUtils.isEmpty(pattern)) {
- return false;
- }
-
- for (int index = 0; index < PATTERN_CHARS.length(); index++) {
- char ch = PATTERN_CHARS.charAt(index);
- if (pattern.indexOf(ch) >= 0) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Locates all the matching entries for a give host name / address
- *
- * @param host The host name / address - ignored if {@code null}/empty
- * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
- * @return A {@link List} of all the matching entries
- * @see #isHostMatch(String, int)
- */
- public static List<HostConfigEntry> findMatchingEntries(String host, HostConfigEntry... entries) {
- // TODO in Java-8 use Stream(s) + predicate
- if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
- return Collections.emptyList();
- } else {
- return findMatchingEntries(host, Arrays.asList(entries));
- }
- }
-
- /**
- * Locates all the matching entries for a give host name / address
- *
- * @param host The host name / address - ignored if {@code null}/empty
- * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
- * @return A {@link List} of all the matching entries
- * @see #isHostMatch(String, int)
- */
- public static List<HostConfigEntry> findMatchingEntries(String host, Collection<? extends HostConfigEntry> entries) {
- // TODO in Java-8 use Stream(s) + predicate
- if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
- return Collections.emptyList();
- }
-
- List<HostConfigEntry> matches = null;
- for (HostConfigEntry entry : entries) {
- if (!entry.isHostMatch(host, 0 /* any port */)) {
- continue; // debug breakpoint
- }
-
- if (matches == null) {
- matches = new ArrayList<>(entries.size()); // in case ALL of them match
- }
-
- matches.add(entry);
- }
-
- if (matches == null) {
- return Collections.emptyList();
- } else {
- return matches;
- }
- }
-
- public static boolean isHostMatch(String host, int port, Collection<HostPatternValue> patterns) {
- if (GenericUtils.isEmpty(patterns)) {
- return false;
- }
-
- boolean matchFound = false;
- for (HostPatternValue pv : patterns) {
- boolean negated = pv.isNegated();
- /*
- * If already found a match we are interested only in negations
- */
- if (matchFound && (!negated)) {
- continue;
- }
-
- if (!isHostMatch(host, pv.getPattern())) {
- continue;
- }
-
- /*
- * According to https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5:
- *
- * If a negated entry is matched, then the Host entry is ignored,
- * regardless of whether any other patterns on the line match.
- */
- if (negated) {
- return false;
- }
-
- int pvPort = pv.getPort();
- if ((pvPort != 0) && (port != 0) && (pvPort != port)) {
- continue;
- }
-
- matchFound = true;
- }
-
- return matchFound;
- }
-
- /**
- * Checks if a given host name / address matches a host pattern
- *
- * @param host The host name / address - ignored if {@code null}/empty
- * @param pattern The host {@link Pattern} - ignored if {@code null}
- * @return {@code true} if the name / address matches the pattern
- */
- public static boolean isHostMatch(String host, Pattern pattern) {
- if (GenericUtils.isEmpty(host) || (pattern == null)) {
- return false;
- }
-
- Matcher m = pattern.matcher(host);
- return m.matches();
- }
-
- public static List<HostPatternValue> parsePatterns(CharSequence... patterns) {
- return parsePatterns(GenericUtils.isEmpty(patterns) ? Collections.emptyList() : Arrays.asList(patterns));
- }
-
- public static List<HostPatternValue> parsePatterns(Collection<? extends CharSequence> patterns) {
- if (GenericUtils.isEmpty(patterns)) {
- return Collections.emptyList();
- }
-
- List<HostPatternValue> result = new ArrayList<>(patterns.size());
- for (CharSequence p : patterns) {
- result.add(ValidateUtils.checkNotNull(toPattern(p), "No pattern for %s", p));
- }
-
- return result;
- }
-
- /**
- * Converts a host pattern string to a regular expression matcher.
- * <B>Note:</B> pattern matching is <U>case insensitive</U>
- *
- * @param patternString The original pattern string - ignored if {@code null}/empty
- * @return The regular expression matcher {@link Pattern} and the indication
- * whether it is a negating pattern or not - {@code null} if no original string
- * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM
- * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM
- * @see #WILDCARD_PATTERN
- * @see #SINGLE_CHAR_PATTERN
- * @see #NEGATION_CHAR_PATTERN
- */
- public static HostPatternValue toPattern(CharSequence patternString) {
- String pattern = GenericUtils.replaceWhitespaceAndTrim(Objects.toString(patternString, null));
- if (GenericUtils.isEmpty(pattern)) {
- return null;
- }
-
- int patternLen = pattern.length();
- int port = 0;
- // Check if non-standard port value used
- StringBuilder sb = new StringBuilder(patternLen);
- if (pattern.charAt(0) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM) {
- int pos = GenericUtils.lastIndexOf(pattern, HostPatternsHolder.PORT_VALUE_DELIMITER);
- ValidateUtils.checkTrue(pos > 0, "Missing non-standard port value delimiter in %s", pattern);
- ValidateUtils.checkTrue(pos < (patternLen - 1), "Missing non-standard port value number in %s", pattern);
- ValidateUtils.checkTrue(pattern.charAt(pos - 1) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM,
- "Invalid non-standard port value host pattern enclosure delimiters in %s", pattern);
-
- String csPort = pattern.substring(pos + 1, patternLen);
- port = Integer.parseInt(csPort);
- ValidateUtils.checkTrue((port > 0) && (port <= 0xFFFF), "Invalid non-start port value (%d) in %s", port, pattern);
-
- pattern = pattern.substring(1, pos - 1);
- patternLen = pattern.length();
- }
-
- boolean negated = false;
- for (int curPos = 0; curPos < patternLen; curPos++) {
- char ch = pattern.charAt(curPos);
- ValidateUtils.checkTrue(isValidPatternChar(ch), "Invalid host pattern char in %s", pattern);
-
- switch(ch) {
- case '.': // need to escape it
- sb.append('\\').append(ch);
- break;
- case SINGLE_CHAR_PATTERN:
- sb.append('.');
- break;
- case WILDCARD_PATTERN:
- sb.append(".*");
- break;
- case NEGATION_CHAR_PATTERN:
- ValidateUtils.checkTrue(!negated, "Double negation in %s", pattern);
- ValidateUtils.checkTrue(curPos == 0, "Negation must be 1st char: %s", pattern);
- negated = true;
- break;
- default:
- sb.append(ch);
- }
- }
-
- return new HostPatternValue(Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE), port, negated);
- }
-
- /**
- * Checks if the given character is valid for a host pattern. Valid
- * characters are:
- * <UL>
- * <LI>A-Z</LI>
- * <LI>a-z</LI>
- * <LI>0-9</LI>
- * <LI>Underscore (_)</LI>
- * <LI>Hyphen (-)</LI>
- * <LI>Dot (.)</LI>
- * <LI>The {@link #WILDCARD_PATTERN}</LI>
- * <LI>The {@link #SINGLE_CHAR_PATTERN}</LI>
- * </UL>
- *
- * @param ch The character to validate
- * @return {@code true} if valid pattern character
- */
- public static boolean isValidPatternChar(char ch) {
- if ((ch <= ' ') || (ch >= 0x7E)) {
- return false;
- }
- if ((ch >= 'a') && (ch <= 'z')) {
- return true;
- }
- if ((ch >= 'A') && (ch <= 'Z')) {
- return true;
- }
- if ((ch >= '0') && (ch <= '9')) {
- return true;
- }
- if ("-_.".indexOf(ch) >= 0) {
- return true;
- }
- return PATTERN_CHARS.indexOf(ch) >= 0;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
deleted file mode 100644
index 2d9a322..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.mac.BuiltinMacs;
-import org.apache.sshd.common.mac.Mac;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Available digesters for known hosts entries
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum KnownHostDigest implements NamedFactory<Mac> {
- SHA1("1", BuiltinMacs.hmacsha1);
-
- public static final Set<KnownHostDigest> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(KnownHostDigest.class));
-
- private final String name;
- private final Factory<Mac> factory;
-
- KnownHostDigest(String name, Factory<Mac> factory) {
- this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
- this.factory = Objects.requireNonNull(factory, "No factory");
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public Mac create() {
- return factory.create();
- }
-
- public static KnownHostDigest fromName(String name) {
- return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
deleted file mode 100644
index 4d4e97a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.client.config.hosts;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StreamCorruptedException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
-
-/**
- * Contains a representation of an entry in the <code>known_hosts</code> file
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://www.manpagez.com/man/8/sshd/">sshd(8) man page</A>
- */
-public class KnownHostEntry extends HostPatternsHolder {
- /**
- * Character that denotes that start of a marker
- */
- public static final char MARKER_INDICATOR = '@';
-
- /**
- * Standard OpenSSH config file name
- */
- public static final String STD_HOSTS_FILENAME = "known_hosts";
-
- private static final class LazyDefaultConfigFileHolder {
- private static final Path HOSTS_FILE =
- PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_HOSTS_FILENAME);
-
- private LazyDefaultConfigFileHolder() {
- throw new UnsupportedOperationException("No instance allowed");
- }
- }
-
- private String line;
- private String marker;
- private AuthorizedKeyEntry keyEntry;
- private KnownHostHashValue hashedEntry;
-
- public KnownHostEntry() {
- super();
- }
-
- /**
- * @param line The original line from which this entry was created
- */
- public KnownHostEntry(String line) {
- this.line = line;
- }
-
- /**
- * @return The original line from which this entry was created
- */
- public String getConfigLine() {
- return line;
- }
-
- public void setConfigLine(String line) {
- this.line = line;
- }
-
- public String getMarker() {
- return marker;
- }
-
- public void setMarker(String marker) {
- this.marker = marker;
- }
-
- public AuthorizedKeyEntry getKeyEntry() {
- return keyEntry;
- }
-
- public void setKeyEntry(AuthorizedKeyEntry keyEntry) {
- this.keyEntry = keyEntry;
- }
-
- public KnownHostHashValue getHashedEntry() {
- return hashedEntry;
- }
-
- public void setHashedEntry(KnownHostHashValue hashedEntry) {
- this.hashedEntry = hashedEntry;
- }
-
- @Override
- public boolean isHostMatch(String host, int port) {
- if (super.isHostMatch(host, port)) {
- return true;
- }
-
- KnownHostHashValue hash = getHashedEntry();
- return (hash != null) && hash.isHostMatch(host);
- }
-
- @Override
- public String toString() {
- return getConfigLine();
- }
-
- /**
- * @return The default {@link Path} location of the OpenSSH known hosts file
- */
- @SuppressWarnings("synthetic-access")
- public static Path getDefaultKnownHostsFile() {
- return LazyDefaultConfigFileHolder.HOSTS_FILE;
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(File file) throws IOException {
- return readKnownHostEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(Path path, OpenOption... options) throws IOException {
- try (InputStream input = Files.newInputStream(path, options)) {
- return readKnownHostEntries(input, true);
- }
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(URL url) throws IOException {
- try (InputStream input = url.openStream()) {
- return readKnownHostEntries(input, true);
- }
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(String filePath) throws IOException {
- try (InputStream inStream = new FileInputStream(filePath)) {
- return readKnownHostEntries(inStream, true);
- }
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(InputStream inStream, boolean okToClose) throws IOException {
- try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
- return readKnownHostEntries(reader, true);
- }
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(Reader rdr, boolean okToClose) throws IOException {
- try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
- return readKnownHostEntries(buf);
- }
- }
-
- /**
- * Reads configuration entries
- *
- * @param rdr The {@link BufferedReader} to use
- * @return The {@link List} of read {@link KnownHostEntry}-ies
- * @throws IOException If failed to parse the read configuration
- */
- public static List<KnownHostEntry> readKnownHostEntries(BufferedReader rdr) throws IOException {
- List<KnownHostEntry> entries = null;
-
- int lineNumber = 1;
- for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
- line = GenericUtils.trimToEmpty(line);
- if (GenericUtils.isEmpty(line)) {
- continue;
- }
-
- int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
- if (pos == 0) {
- continue;
- }
-
- if (pos > 0) {
- line = line.substring(0, pos);
- line = line.trim();
- }
-
- try {
- KnownHostEntry entry = parseKnownHostEntry(line);
- if (entry == null) {
- continue;
- }
-
- if (entries == null) {
- entries = new ArrayList<>();
- }
- entries.add(entry);
- } catch (RuntimeException | Error e) { // TODO consider consulting a user callback
- throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
- + " to parse line #" + lineNumber + " '" + line + "': " + e.getMessage());
- }
- }
-
- if (entries == null) {
- return Collections.emptyList();
- } else {
- return entries;
- }
- }
-
- public static KnownHostEntry parseKnownHostEntry(String line) {
- return parseKnownHostEntry(GenericUtils.isEmpty(line) ? null : new KnownHostEntry(), line);
- }
-
- public static <E extends KnownHostEntry> E parseKnownHostEntry(E entry, String data) {
- String line = GenericUtils.replaceWhitespaceAndTrim(data);
- if (GenericUtils.isEmpty(line) || (line.charAt(0) == PublicKeyEntry.COMMENT_CHAR)) {
- return entry;
- }
-
- entry.setConfigLine(line);
-
- if (line.charAt(0) == MARKER_INDICATOR) {
- int pos = line.indexOf(' ');
- ValidateUtils.checkTrue(pos > 0, "Missing marker name end delimiter in line=%s", data);
- ValidateUtils.checkTrue(pos > 1, "No marker name after indicator in line=%s", data);
- entry.setMarker(line.substring(1, pos));
- line = line.substring(pos + 1).trim();
- } else {
- entry.setMarker(null);
- }
-
- int pos = line.indexOf(' ');
- ValidateUtils.checkTrue(pos > 0, "Missing host patterns end delimiter in line=%s", data);
- String hostPattern = line.substring(0, pos);
- line = line.substring(pos + 1).trim();
-
- if (hostPattern.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) {
- KnownHostHashValue hash =
- ValidateUtils.checkNotNull(KnownHostHashValue.parse(hostPattern),
- "Failed to extract host hash value from line=%s", data);
- entry.setHashedEntry(hash);
- entry.setPatterns(null);
- } else {
- entry.setHashedEntry(null);
- entry.setPatterns(parsePatterns(GenericUtils.split(hostPattern, ',')));
- }
-
- AuthorizedKeyEntry key =
- ValidateUtils.checkNotNull(AuthorizedKeyEntry.parseAuthorizedKeyEntry(line),
- "No valid key entry recovered from line=%s", data);
- entry.setKeyEntry(key);
- return entry;
- }
-}