This is an automated email from the ASF dual-hosted git repository. lgoldstein pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 82c95e18d3f9a0b960cd0a65b9cd2c37dc0d9d2e Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Sat Feb 5 07:46:47 2022 +0200 Added support for quoted arguments in SftpCommandMain --- .../main/java/org/apache/sshd/cli/CliSupport.java | 72 +++++++++++++++++ .../apache/sshd/cli/client/SftpCommandMain.java | 43 +++------- .../CliSupportSplitCommandLineArgumentsTest.java | 94 ++++++++++++++++++++++ 3 files changed, 179 insertions(+), 30 deletions(-) diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java index f7306e7..e7cda7d 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java @@ -20,10 +20,14 @@ package org.apache.sshd.cli; import java.io.IOException; import java.io.PrintStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.net.SocketAddress; 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.Map; import java.util.logging.Level; @@ -341,4 +345,72 @@ public abstract class CliSupport { return new ArrayList<>(available); } + + public static String[] splitCommandLineArguments(String line) { + line = GenericUtils.trimToEmpty(line); + if (GenericUtils.isBlank(line)) { + return GenericUtils.EMPTY_STRING_ARRAY; + } + + Collection<String> args = Collections.emptyList(); + for (int index = 0; index < GenericUtils.QUOTES.length(); index++) { + char delim = GenericUtils.QUOTES.charAt(index); + int startPos = line.indexOf(delim); + int endPos = -1; + if (startPos >= 0) { + endPos = line.indexOf(delim, startPos + 1); + } + + if ((startPos >= 0) && (endPos > startPos)) { + if (GenericUtils.isEmpty(args)) { + args = new LinkedList<>(); + } + + String prefix = (startPos > 0) ? line.substring(0, startPos).trim() : ""; + String[] extra = GenericUtils.split(prefix, ' '); + if (!GenericUtils.isEmpty(extra)) { + args.addAll(Arrays.asList(extra)); + } + + String value = line.substring(startPos + 1, endPos); + args.add(value); + + line = (endPos < (line.length() - 1)) ? line.substring(endPos + 1).trim() : ""; + if (GenericUtils.isBlank(line)) { + break; + } + + index = -1; // start delimiters again + } + } + + // see if any leftovers + String[] extra = GenericUtils.split(line, ' '); + if (GenericUtils.isEmpty(args)) { + return extra; + } + + if (!GenericUtils.isEmpty(extra)) { + if (GenericUtils.isEmpty(args)) { + args = new LinkedList<>(); + } + args.addAll(Arrays.asList(extra)); + } + + return args.toArray(GenericUtils.EMPTY_STRING_ARRAY); + } + + public static void printFieldsValues(Object info, PrintStream stdout) throws Exception { + Field[] fields = info.getClass().getFields(); + for (Field f : fields) { + String name = f.getName(); + int mod = f.getModifiers(); + if (Modifier.isStatic(mod)) { + continue; + } + + Object value = f.get(info); + stdout.append(" ").append(name).append(": ").println(value); + } + } } diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java index 2e98012..e9e6a9c 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java @@ -26,8 +26,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.nio.channels.Channel; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; @@ -44,6 +42,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import org.apache.sshd.cli.CliLogger; +import org.apache.sshd.cli.CliSupport; import org.apache.sshd.cli.client.helper.SftpFileTransferProgressOutputStream; import org.apache.sshd.client.ClientFactoryManager; import org.apache.sshd.client.session.ClientSession; @@ -719,7 +718,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo public boolean executeCommand( String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); int numComps = GenericUtils.length(comps); String pathArg = (numComps <= 0) ? null : GenericUtils.trimToEmpty(comps[numComps - 1]); String flags = (numComps >= 2) ? GenericUtils.trimToEmpty(comps[0]) : null; @@ -763,7 +762,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo public boolean executeCommand( String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); int numComps = GenericUtils.length(comps); String pathArg = (numComps <= 0) ? null : GenericUtils.trimToEmpty(comps[numComps - 1]); String flags = (numComps >= 2) ? GenericUtils.trimToEmpty(comps[0]) : null; @@ -818,7 +817,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo public boolean executeCommand( String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); int numArgs = GenericUtils.length(comps); ValidateUtils.checkTrue(numArgs >= 1, "No arguments"); ValidateUtils.checkTrue(numArgs <= 2, "Too many arguments: %s", args); @@ -932,7 +931,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo public boolean executeCommand( String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); ValidateUtils.checkTrue(GenericUtils.length(comps) == 2, "Invalid number of arguments: %s", args); String oldPath = resolveRemotePath(GenericUtils.trimToEmpty(comps[0])); @@ -959,7 +958,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo public boolean executeCommand( String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); int numArgs = GenericUtils.length(comps); ValidateUtils.checkTrue(numArgs <= 1, "Invalid number of arguments: %s", args); @@ -967,7 +966,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo OpenSSHLimitsExtension ext = sftp.getExtension(OpenSSHLimitsExtension.class); ValidateUtils.checkTrue(ext.isSupported(), "Extension not supported by server: %s", ext.getName()); OpenSSHLimitsExtensionInfo info = ext.limits(); - printFieldsValues(info, stdout); + CliSupport.printFieldsValues(info, stdout); return false; } } @@ -988,7 +987,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo public boolean executeCommand( String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); int numArgs = GenericUtils.length(comps); ValidateUtils.checkTrue(numArgs <= 1, "Invalid number of arguments: %s", args); @@ -999,7 +998,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo String remPath = resolveRemotePath( (numArgs >= 1) ? GenericUtils.trimToEmpty(comps[0]) : GenericUtils.trimToEmpty(args)); OpenSSHStatExtensionInfo info = ext.stat(remPath); - printFieldsValues(info, stdout); + CliSupport.printFieldsValues(info, stdout); return false; } } @@ -1020,7 +1019,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo public boolean executeCommand( String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); ValidateUtils.checkTrue(GenericUtils.length(comps) <= 1, "Invalid number of arguments: %s", args); String path = GenericUtils.trimToEmpty(resolveRemotePath(args)); @@ -1046,7 +1045,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo @Override public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); ValidateUtils.checkTrue(GenericUtils.length(comps) <= 1, "Invalid number of arguments: %s", args); String path = GenericUtils.trimToEmpty(resolveRemotePath(args)); @@ -1202,7 +1201,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo } protected void executeCommand(String args, boolean upload, PrintStream stdout) throws IOException { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); int numArgs = GenericUtils.length(comps); ValidateUtils.checkTrue((numArgs >= 1) && (numArgs <= 3), "Invalid number of arguments: %s", args); @@ -1325,7 +1324,7 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo public boolean executeCommand( String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { - String[] comps = GenericUtils.split(args, ' '); + String[] comps = CliSupport.splitCommandLineArguments(args); int numArgs = GenericUtils.length(comps); if (numArgs <= 0) { stdout.append(" ").append(getName()).append(' ').println(isShowProgress() ? "on" : "off"); @@ -1345,20 +1344,4 @@ public class SftpCommandMain extends SshClientCliSupport implements SftpClientHo return false; } } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - public static void printFieldsValues(Object info, PrintStream stdout) throws Exception { - Field[] fields = info.getClass().getFields(); - for (Field f : fields) { - String name = f.getName(); - int mod = f.getModifiers(); - if (Modifier.isStatic(mod)) { - continue; - } - - Object value = f.get(info); - stdout.append(" ").append(name).append(": ").println(value); - } - } } diff --git a/sshd-cli/src/test/java/org/apache/sshd/cli/CliSupportSplitCommandLineArgumentsTest.java b/sshd-cli/src/test/java/org/apache/sshd/cli/CliSupportSplitCommandLineArgumentsTest.java new file mode 100644 index 0000000..dcdd29b --- /dev/null +++ b/sshd-cli/src/test/java/org/apache/sshd/cli/CliSupportSplitCommandLineArgumentsTest.java @@ -0,0 +1,94 @@ +/* + * 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.cli; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.util.test.BaseTestSupport; +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests +@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class) +public class CliSupportSplitCommandLineArgumentsTest extends BaseTestSupport { + private final String line; + private final String[] expected; + + public CliSupportSplitCommandLineArgumentsTest(String line, String[] expected) { + this.line = line; + this.expected = expected; + } + + @Parameters(name = "{0}") + public static List<Object[]> parameters() { + return new ArrayList<Object[]>() { + // not serializing it + private static final long serialVersionUID = 1L; + + { + addTestCase(null, GenericUtils.EMPTY_STRING_ARRAY); + addTestCase("", GenericUtils.EMPTY_STRING_ARRAY); + addTestCase(" ", GenericUtils.EMPTY_STRING_ARRAY); + addPaddedTestCase("hello", "hello"); + addPaddedTestCase("hello world", "hello", "world"); + + for (int index = 0; index < GenericUtils.QUOTES.length(); index++) { + char delim = GenericUtils.QUOTES.charAt(index); + addPaddedTestCase(delim + "hello world" + delim, "hello world"); + addPaddedTestCase(delim + "hello" + delim + " world", "hello", "world"); + addPaddedTestCase("hello " + delim + "world" + delim, "hello", "world"); + addPaddedTestCase(delim + "hello" + delim + " " + delim + "world" + delim, "hello", "world"); + } + } + + private void addPaddedTestCase(String line, String... expected) { + addTestCase(line, expected); + addTestCase(" " + line, expected); + addTestCase(line + " ", expected); + addTestCase(" " + line + " ", expected); + } + + private void addTestCase(String line, String... expected) { + add(new Object[] { line, expected }); + } + }; + } + + @Test + public void testSplitCommandLineArguments() { + String[] actual = CliSupport.splitCommandLineArguments(line); + assertArrayEquals(expected, actual); + } +}