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);