From cadf3d49570911ff1a1f84acb20442ef7f7761f3 Mon Sep 17 00:00:00 2001 From: Samuel Carlsson Date: Fri, 29 Apr 2016 15:02:18 +0200 Subject: [PATCH] Quoting shell arguments with spaces --- src/se/vidstige/jadb/JadbDevice.java | 10 ++-- .../jadb/server/AdbDeviceResponder.java | 2 + .../jadb/server/AdbProtocolHandler.java | 7 +++ .../vidstige/jadb/test/MockedTestCases.java | 16 ++++++ .../jadb/test/fakes/FakeAdbServer.java | 56 ++++++++++++++++--- 5 files changed, 77 insertions(+), 14 deletions(-) diff --git a/src/se/vidstige/jadb/JadbDevice.java b/src/se/vidstige/jadb/JadbDevice.java index 989f62f..4af16f8 100644 --- a/src/se/vidstige/jadb/JadbDevice.java +++ b/src/se/vidstige/jadb/JadbDevice.java @@ -1,5 +1,7 @@ package se.vidstige.jadb; +import se.vidstige.jadb.managers.Bash; + import java.io.*; import java.util.ArrayList; import java.util.List; @@ -50,9 +52,7 @@ public class JadbDevice { StringBuilder shellLine = new StringBuilder(command); for (String arg : args) { shellLine.append(" "); - // TODO: throw if arg contains double quote - // TODO: quote arg if it contains space - shellLine.append(arg); + shellLine.append(Bash.quote(arg)); } send(transport, "shell:" + shellLine.toString()); return new AdbFilterInputStream(new BufferedInputStream(transport.getInputStream())); @@ -69,9 +69,7 @@ public class JadbDevice { StringBuilder shellLine = new StringBuilder(command); for (String arg : args) { shellLine.append(" "); - // TODO: throw if arg contains double quote - // TODO: quote arg if it contains space - shellLine.append(arg); + shellLine.append(Bash.quote(arg)); } send(transport, "shell:" + shellLine.toString()); if (output != null) { diff --git a/src/se/vidstige/jadb/server/AdbDeviceResponder.java b/src/se/vidstige/jadb/server/AdbDeviceResponder.java index 02f46ef..27b2421 100644 --- a/src/se/vidstige/jadb/server/AdbDeviceResponder.java +++ b/src/se/vidstige/jadb/server/AdbDeviceResponder.java @@ -15,4 +15,6 @@ public interface AdbDeviceResponder { void filePushed(RemoteFile path, int mode, ByteArrayOutputStream buffer) throws JadbException; void filePulled(RemoteFile path, ByteArrayOutputStream buffer) throws JadbException, IOException; + + void shell(String command) throws IOException; } diff --git a/src/se/vidstige/jadb/server/AdbProtocolHandler.java b/src/se/vidstige/jadb/server/AdbProtocolHandler.java index 06aff31..52c94ba 100644 --- a/src/se/vidstige/jadb/server/AdbProtocolHandler.java +++ b/src/se/vidstige/jadb/server/AdbProtocolHandler.java @@ -80,6 +80,9 @@ class AdbProtocolHandler implements Runnable { SyncTransport sync = new SyncTransport(output, input); sync.send("FAIL", e.getMessage()); } + } else if (command.startsWith("shell:")) { + shell(command.substring("shell:".length())); + output.writeBytes("OKAY"); } else { throw new ProtocolException("Unknown command: " + command); } @@ -91,6 +94,10 @@ class AdbProtocolHandler implements Runnable { } } + private void shell(String command) throws IOException { + selected.shell(command); + } + private int readInt(DataInput input) throws IOException { return Integer.reverseBytes(input.readInt()); } diff --git a/test/se/vidstige/jadb/test/MockedTestCases.java b/test/se/vidstige/jadb/test/MockedTestCases.java index bed0d1e..d6cd3e3 100644 --- a/test/se/vidstige/jadb/test/MockedTestCases.java +++ b/test/se/vidstige/jadb/test/MockedTestCases.java @@ -77,6 +77,22 @@ public class MockedTestCases { Assert.assertArrayEquals("foobar".getBytes(Charset.forName("utf-8")), buffer.toByteArray()); } + @Test + public void testExecuteShell() throws Exception { + server.add("serial-123"); + server.expectShell("serial-123", "ls -l"); + JadbDevice device = connection.getDevices().get(0); + device.executeShell("ls", "-l"); + } + + @Test + public void testExecuteShellQuotesSpace() throws Exception { + server.add("serial-123"); + server.expectShell("serial-123", "ls 'space file'"); + JadbDevice device = connection.getDevices().get(0); + device.executeShell("ls", "space file"); + } + private long parseDate(String date) throws ParseException { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); return dateFormat.parse(date).getTime(); diff --git a/test/se/vidstige/jadb/test/fakes/FakeAdbServer.java b/test/se/vidstige/jadb/test/fakes/FakeAdbServer.java index 1abc8fb..3cf3f32 100644 --- a/test/se/vidstige/jadb/test/fakes/FakeAdbServer.java +++ b/test/se/vidstige/jadb/test/fakes/FakeAdbServer.java @@ -8,6 +8,7 @@ import se.vidstige.jadb.server.AdbServer; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.net.ProtocolException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -75,6 +76,10 @@ public class FakeAdbServer implements AdbResponder { return findBySerial(serial).expectPull(path); } + public void expectShell(String serial, String commands) { + findBySerial(serial).expectShell(commands); + } + @Override public List getDevices() { return new ArrayList(devices); @@ -82,7 +87,8 @@ public class FakeAdbServer implements AdbResponder { private class DeviceResponder implements AdbDeviceResponder { private final String serial; - private List expectations = new ArrayList(); + private List fileExpectations = new ArrayList(); + private List shellExpectations = new ArrayList(); private DeviceResponder(String serial) { this.serial = serial; @@ -100,9 +106,9 @@ public class FakeAdbServer implements AdbResponder { @Override public void filePushed(RemoteFile path, int mode, ByteArrayOutputStream buffer) throws JadbException { - for (FileExpectation fe : expectations) { + for (FileExpectation fe : fileExpectations) { if (fe.matches(path)) { - expectations.remove(fe); + fileExpectations.remove(fe); fe.throwIfFail(); fe.verifyContent(buffer.toByteArray()); return; @@ -113,9 +119,9 @@ public class FakeAdbServer implements AdbResponder { @Override public void filePulled(RemoteFile path, ByteArrayOutputStream buffer) throws JadbException, IOException { - for (FileExpectation fe : expectations) { + for (FileExpectation fe : fileExpectations) { if (fe.matches(path)) { - expectations.remove(fe); + fileExpectations.remove(fe); fe.throwIfFail(); fe.returnFile(buffer); return; @@ -124,8 +130,20 @@ public class FakeAdbServer implements AdbResponder { throw new JadbException("Unexpected push to device " + serial + " at " + path); } + @Override + public void shell(String command) throws IOException { + for (ShellExpectation se : shellExpectations) { + if (se.matches(command)) { + shellExpectations.remove(se); + se.throwIfFail(); + return; + } + } + throw new ProtocolException("Unexpected shell to device " + serial + ": " + command); + } + public void verifyExpectations() { - org.junit.Assert.assertEquals(0, expectations.size()); + org.junit.Assert.assertEquals(0, fileExpectations.size()); } private class FileExpectation implements ExpectationBuilder { @@ -172,15 +190,37 @@ public class FakeAdbServer implements AdbResponder { } } + public class ShellExpectation { + private final String command; + + public ShellExpectation(String command) { + this.command = command; + } + + public boolean matches(String command) { + return command.equals(this.command); + } + + public void throwIfFail() { + + } + } + public ExpectationBuilder expectPush(RemoteFile path) { FileExpectation expectation = new FileExpectation(path); - expectations.add(expectation); + fileExpectations.add(expectation); return expectation; } public ExpectationBuilder expectPull(RemoteFile path) { FileExpectation expectation = new FileExpectation(path); - expectations.add(expectation); + fileExpectations.add(expectation); + return expectation; + } + + public ShellExpectation expectShell(String command) { + ShellExpectation expectation = new ShellExpectation(command); + shellExpectations.add(new ShellExpectation(command)); return expectation; } }