Adding support for listing files using the STAT command (FTPSERVER-383)
git-svn-id: https://svn.apache.org/repos/asf/mina/ftpserver/trunk@992885 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/src/main/java/org/apache/ftpserver/command/impl/STAT.java b/core/src/main/java/org/apache/ftpserver/command/impl/STAT.java
index 28b6867..d807692 100644
--- a/core/src/main/java/org/apache/ftpserver/command/impl/STAT.java
+++ b/core/src/main/java/org/apache/ftpserver/command/impl/STAT.java
@@ -22,10 +22,18 @@
import java.io.IOException;
import org.apache.ftpserver.command.AbstractCommand;
+import org.apache.ftpserver.command.impl.listing.DirectoryLister;
+import org.apache.ftpserver.command.impl.listing.LISTFileFormater;
+import org.apache.ftpserver.command.impl.listing.ListArgument;
+import org.apache.ftpserver.command.impl.listing.ListArgumentParser;
+import org.apache.ftpserver.ftplet.FtpException;
+import org.apache.ftpserver.ftplet.FtpFile;
import org.apache.ftpserver.ftplet.FtpReply;
import org.apache.ftpserver.ftplet.FtpRequest;
import org.apache.ftpserver.impl.FtpIoSession;
import org.apache.ftpserver.impl.FtpServerContext;
+import org.apache.ftpserver.impl.LocalizedDataTransferFtpReply;
+import org.apache.ftpserver.impl.LocalizedFileActionFtpReply;
import org.apache.ftpserver.impl.LocalizedFtpReply;
/**
@@ -40,6 +48,10 @@
*/
public class STAT extends AbstractCommand {
+ private static final LISTFileFormater LIST_FILE_FORMATER = new LISTFileFormater();
+
+ private DirectoryLister directoryLister = new DirectoryLister();
+
/**
* Execute command
*/
@@ -50,9 +62,44 @@
// reset state variables
session.resetState();
- // write the status info
- session.write(LocalizedFtpReply.translate(session, request, context,
- FtpReply.REPLY_211_SYSTEM_STATUS_REPLY, "STAT", null));
+ if(request.getArgument() != null) {
+ ListArgument parsedArg = ListArgumentParser.parse(request.getArgument());
+
+ // check that the directory or file exists
+ FtpFile file = null;
+ try {
+ file = session.getFileSystemView().getFile(parsedArg.getFile());
+ if(!file.doesExist()) {
+ session.write(LocalizedDataTransferFtpReply.translate(session, request, context,
+ FtpReply.REPLY_450_REQUESTED_FILE_ACTION_NOT_TAKEN, "LIST",
+ null, file));
+ return;
+ }
+
+ String dirList = directoryLister.listFiles(parsedArg,
+ session.getFileSystemView(), LIST_FILE_FORMATER);
+
+ session
+ .write(new LocalizedFileActionFtpReply(
+ FtpReply.REPLY_200_COMMAND_OKAY,
+ dirList, file));
+
+ } catch (FtpException e) {
+ session
+ .write(LocalizedFileActionFtpReply
+ .translate(
+ session,
+ request,
+ context,
+ FtpReply.REPLY_450_REQUESTED_FILE_ACTION_NOT_TAKEN,
+ "STAT", null, file));
+ }
+
+ } else {
+ // write the status info
+ session.write(LocalizedFtpReply.translate(session, request, context,
+ FtpReply.REPLY_211_SYSTEM_STATUS_REPLY, "STAT", null));
+ }
}
}
diff --git a/core/src/main/resources/org/apache/ftpserver/message/FtpStatus.properties b/core/src/main/resources/org/apache/ftpserver/message/FtpStatus.properties
index 096343b..02a9493 100644
--- a/core/src/main/resources/org/apache/ftpserver/message/FtpStatus.properties
+++ b/core/src/main/resources/org/apache/ftpserver/message/FtpStatus.properties
@@ -232,6 +232,7 @@
213.SIZE={output.msg}
211.STAT=Apache FtpServer\nConnected to {server.ip}\nConnected from {client.ip}\nLogged in as {client.login.name}\nEnd of status.
+450.STAT=Non-existing file
501.STOR=Syntax error in parameters or arguments.
550.STOR.invalid={output.msg}\: Invalid path.
diff --git a/core/src/test/java/org/apache/ftpserver/clienttests/StatTest.java b/core/src/test/java/org/apache/ftpserver/clienttests/StatTest.java
new file mode 100644
index 0000000..876fa08
--- /dev/null
+++ b/core/src/test/java/org/apache/ftpserver/clienttests/StatTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.ftpserver.clienttests;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+
+/**
+*
+* @author <a href="http://mina.apache.org">Apache MINA Project</a>
+*
+*/
+public class StatTest extends ClientTestTemplate {
+
+ private static final String PATTERN = "^200[-\\s]-rw-------\\s\\s\\s1\\suser\\sgroup\\s{12}0\\s[A-Za-z0-9\\s]{6}\\s\\d\\d:\\d\\d\\stest\\d.txt$";
+
+ private static final File TEST_DIR = new File(ROOT_DIR, "test");
+ private static final File TEST_FILE1 = new File(TEST_DIR, "test1.txt");
+ private static final File TEST_FILE2 = new File(TEST_DIR, "test2.txt");
+
+ public void testStatDir() throws Exception {
+ assertTrue(TEST_DIR.mkdir());
+ assertTrue(TEST_FILE1.createNewFile());
+ assertTrue(TEST_FILE2.createNewFile());
+
+ client.login(ADMIN_USERNAME, ADMIN_PASSWORD);
+
+ assertEquals(200, client.stat(TEST_DIR.getName()));
+ String[] reply = client.getReplyString().split("\r\n");
+
+ assertTrue(reply[0], Pattern.matches(PATTERN, reply[0]));
+ assertTrue(reply[1], Pattern.matches(PATTERN, reply[1]));
+ }
+
+ public void testStatFile() throws Exception {
+ assertTrue(TEST_DIR.mkdir());
+ assertTrue(TEST_FILE1.createNewFile());
+
+ client.login(ADMIN_USERNAME, ADMIN_PASSWORD);
+
+ assertEquals(200, client.stat(TEST_DIR.getName() + "/" + TEST_FILE1.getName()));
+ String[] reply = client.getReplyString().split("\r\n");
+
+ assertTrue(reply[0], Pattern.matches(PATTERN, reply[0]));
+ }
+
+ public void testStat() throws Exception {
+ client.login(ADMIN_USERNAME, ADMIN_PASSWORD);
+
+ client.stat();
+ String[] reply = client.getReplyString().split("\r\n");
+
+ assertEquals("211-Apache FtpServer", reply[0]);
+ assertEquals("Connected to 127.0.0.1", reply[1]);
+ assertEquals("Connected from 127.0.0.1", reply[2]);
+ assertEquals("Logged in as admin", reply[3]);
+ assertEquals("211 End of status.", reply[4]);
+ }
+
+}
diff --git a/ftplet-api/src/main/java/org/apache/ftpserver/ftplet/DefaultFtpReply.java b/ftplet-api/src/main/java/org/apache/ftpserver/ftplet/DefaultFtpReply.java
index b208875..62cab9c 100644
--- a/ftplet-api/src/main/java/org/apache/ftpserver/ftplet/DefaultFtpReply.java
+++ b/ftplet-api/src/main/java/org/apache/ftpserver/ftplet/DefaultFtpReply.java
@@ -106,14 +106,23 @@
StringBuffer sb = new StringBuffer();
+ // remove any carriage returns
+ notNullMessage = notNullMessage.replace("\r", "");
+
+ // remove trailing line feeds
+ if(notNullMessage.endsWith("\n")) {
+ notNullMessage = notNullMessage.substring(0, notNullMessage.length() - 1);
+ }
+
+ String[] lines = notNullMessage.split("\n");
+
// no newline
- if (notNullMessage.indexOf('\n') == -1) {
+ if (lines.length == 1) {
sb.append(code);
sb.append(" ");
sb.append(notNullMessage);
sb.append(CRLF);
} else {
- String[] lines = notNullMessage.split("\n");
sb.append(code);
sb.append("-");
diff --git a/ftplet-api/src/test/java/org/apache/ftpserver/ftplet/DefaultFtpReplyTest.java b/ftplet-api/src/test/java/org/apache/ftpserver/ftplet/DefaultFtpReplyTest.java
index 6abd560..56b5db8 100644
--- a/ftplet-api/src/test/java/org/apache/ftpserver/ftplet/DefaultFtpReplyTest.java
+++ b/ftplet-api/src/test/java/org/apache/ftpserver/ftplet/DefaultFtpReplyTest.java
@@ -36,6 +36,18 @@
assertEquals("123 foo bar\r\n", response.toString());
}
+ public void testSingleLineWithTrailingLineFeedToString() {
+ DefaultFtpReply response = new DefaultFtpReply(123, "foo bar\n");
+
+ assertEquals("123 foo bar\r\n", response.toString());
+ }
+
+ public void testCarriageReturnToString() {
+ DefaultFtpReply response = new DefaultFtpReply(123, "foo \rbar\r\n");
+
+ assertEquals("123 foo bar\r\n", response.toString());
+ }
+
public void testNullToString() {
DefaultFtpReply response = new DefaultFtpReply(123, (String) null);