View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2.provider.ftp;
18  
19  import static org.apache.commons.vfs2.VfsTestUtils.getTestDirectory;
20  
21  import java.io.IOException;
22  import java.net.URL;
23  import java.nio.charset.Charset;
24  import java.nio.charset.StandardCharsets;
25  import java.time.Duration;
26  
27  import org.apache.commons.vfs2.AbstractProviderTestCase;
28  import org.apache.commons.vfs2.AbstractProviderTestConfig;
29  import org.apache.commons.vfs2.FileObject;
30  import org.apache.commons.vfs2.FileSystemManager;
31  import org.apache.commons.vfs2.FileSystemOptions;
32  import org.apache.commons.vfs2.ProviderTestSuite;
33  import org.apache.commons.vfs2.impl.DecoratedFileObject;
34  import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
35  import org.apache.ftpserver.FtpServer;
36  import org.apache.ftpserver.FtpServerFactory;
37  import org.apache.ftpserver.command.CommandFactory;
38  import org.apache.ftpserver.ftplet.FileSystemFactory;
39  import org.apache.ftpserver.ftplet.FtpException;
40  import org.apache.ftpserver.ftplet.UserManager;
41  import org.apache.ftpserver.listener.ListenerFactory;
42  import org.apache.ftpserver.usermanager.Md5PasswordEncryptor;
43  import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
44  import org.apache.ftpserver.usermanager.impl.BaseUser;
45  import org.junit.jupiter.api.Assertions;
46  
47  import junit.framework.Test;
48  
49  /**
50   * Tests for FTP file systems.
51   */
52  public class FtpProviderTestCase extends AbstractProviderTestConfig {
53  
54      private static int socketPort;
55  
56      /**
57       * Use %40 for @ in URLs
58       */
59      private static String connectionUri;
60  
61      private static FtpServer server;
62  
63      private static final String TEST_URI = "test.ftp.uri";
64  
65      private static final String USER_PROPS_RES = "org.apache.ftpserver/users.properties";
66  
67      static String getConnectionUri() {
68          return connectionUri;
69      }
70  
71      static int getSocketPort() {
72          return socketPort;
73      }
74  
75      protected static String getSystemTestUriOverride() {
76          return System.getProperty(TEST_URI);
77      }
78  
79      /**
80       * Creates and starts an embedded Apache FTP Server (MINA).
81       *
82       * @param rootDirectory the local FTP server rootDirectory.
83       * @param fileSystemFactory optional local FTP server FileSystemFactory.
84       * @param commandFactory FTP server command factory.
85       * @throws FtpException
86       */
87      static void setUpClass(final String rootDirectory, final FileSystemFactory fileSystemFactory,
88          final CommandFactory commandFactory) throws FtpException {
89          if (server != null) {
90              return;
91          }
92          final FtpServerFactory serverFactory = new FtpServerFactory();
93          final PropertiesUserManagerFactory propertiesUserManagerFactory = new PropertiesUserManagerFactory();
94          // TODO Update to SHA512
95          propertiesUserManagerFactory.setPasswordEncryptor(new Md5PasswordEncryptor());
96          final URL userPropsResource = ClassLoader.getSystemClassLoader().getResource(USER_PROPS_RES);
97          Assertions.assertNotNull(userPropsResource, USER_PROPS_RES);
98          propertiesUserManagerFactory.setUrl(userPropsResource);
99          final UserManager userManager = propertiesUserManagerFactory.createUserManager();
100         final BaseUser user = (BaseUser) userManager.getUserByName("test");
101         // Pickup the home dir value at runtime even though we have it set in the user prop file
102         // The user prop file requires the "homedirectory" to be set
103         user.setHomeDirectory(rootDirectory);
104         userManager.save(user);
105         serverFactory.setUserManager(userManager);
106         if (fileSystemFactory != null) {
107             serverFactory.setFileSystem(fileSystemFactory);
108         }
109         if (commandFactory != null) {
110             serverFactory.setCommandFactory(commandFactory);
111         }
112         final ListenerFactory factory = new ListenerFactory();
113         // set the port of the listener
114         factory.setPort(0);
115 
116         // replace the default listener
117         serverFactory.addListener("default", factory.createListener());
118 
119         // start the server
120         server = serverFactory.createServer();
121         server.start();
122         socketPort = ((org.apache.ftpserver.impl.DefaultFtpServer) server).getListener("default").getPort();
123         connectionUri = "ftp://test:test@localhost:" + socketPort;
124     }
125 
126     /**
127      * Creates the test suite for the FTP file system.
128      */
129     public static Test suite() throws Exception {
130         return suite(new FtpProviderTestCase());
131     }
132 
133     /**
134      * Creates the test suite for subclasses of the FTP file system.
135      */
136     protected static Test suite(final FtpProviderTestCase testCase,
137         final Class<? extends AbstractProviderTestCase>... testClasses) throws Exception {
138         return new ProviderTestSuite(testCase) {
139 
140             @Override
141             protected void addBaseTests() throws Exception {
142                 if (testClasses.length == 0) {
143                     super.addBaseTests();
144                 } else {
145                     for (final Class<?> test : testClasses) {
146                         addTests(test);
147                     }
148                 }
149             }
150 
151             @Override
152             protected void setUp() throws Exception {
153                 if (getSystemTestUriOverride() == null) {
154                     setUpClass(testCase.getFtpRootDir(), testCase.getFtpFileSystem(), testCase.getCommandFactory());
155                 }
156                 super.setUp();
157             }
158 
159             @Override
160             protected void tearDown() throws Exception {
161                 try {
162                     // This will report running threads of the FTP server.
163                     // However, shutting down the FTP server first will always
164                     // report an exception closing the manager, because the
165                     // server is already down
166                     super.tearDown();
167                 } finally {
168                     tearDownClass();
169                 }
170             }
171         };
172     }
173 
174     /**
175      * Stops the embedded Apache FTP Server (MINA).
176      */
177     static void tearDownClass() {
178         if (server != null) {
179             server.stop();
180             server = null;
181         }
182     }
183 
184     private final boolean mdtmLastModifiedTime;
185 
186     public FtpProviderTestCase() {
187         this(false);
188     }
189 
190     public FtpProviderTestCase(final boolean mdtmLastModifiedTime) {
191         this.mdtmLastModifiedTime = mdtmLastModifiedTime;
192     }
193 
194     /**
195      * Returns the base folder for tests. You can override the DEFAULT_URI by using the system property name defined by
196      * TEST_URI.
197      */
198     @Override
199     public FileObject getBaseTestFolder(final FileSystemManager manager) throws Exception {
200         String uri = getSystemTestUriOverride();
201         if (uri == null) {
202             uri = connectionUri;
203         }
204         final FileSystemOptions options = new FileSystemOptions();
205         final FtpFileSystemConfigBuilder builder = FtpFileSystemConfigBuilder.getInstance();
206         init(builder, options);
207         // OPTS UTF-8
208         final FileObject remoteFolder = manager.resolveFile(uri, options);
209         final FtpFileObject ftpFileObject = remoteFolder instanceof DecoratedFileObject
210                 ? (FtpFileObject) ((DecoratedFileObject) remoteFolder).getDecoratedFileObject()
211                 : (FtpFileObject) remoteFolder;
212         final FtpFileSystem ftpFileSystem = (FtpFileSystem) ftpFileObject.getFileSystem();
213         final FTPClientWrapper client = (FTPClientWrapper) ftpFileSystem.getClient();
214         // TODO Needs Apache Commons Net 3.12.0
215         // client.sendOptions("UTF-8", "NLST");
216         return remoteFolder;
217     }
218 
219     /**
220      * Gets the FTP server command factory. Defaults to null for no override.
221      *
222      * @return the FTP server command factory or null.
223      */
224     protected CommandFactory getCommandFactory() {
225         return null;
226     }
227 
228     /**
229      * Gets option file system factory for local FTP server.
230      */
231     protected FileSystemFactory getFtpFileSystem() throws IOException {
232         // use default
233         return null;
234     }
235 
236     /**
237      * Gets the root of the local FTP Server file system.
238      */
239     protected String getFtpRootDir() {
240         return getTestDirectory();
241     }
242 
243     /**
244      * Gets the setting for UserDirIsRoot. Defaults to false.
245      */
246     protected boolean getUserDirIsRoot() {
247         return false;
248     }
249 
250     protected void init(final FtpFileSystemConfigBuilder builder, final FileSystemOptions options) {
251         builder.setUserDirIsRoot(options, getUserDirIsRoot());
252         builder.setPassiveMode(options, true);
253         // FtpFileType.BINARY is the default
254         builder.setFileType(options, FtpFileType.BINARY);
255         builder.setConnectTimeout(options, Duration.ofSeconds(10));
256         final Charset charset = StandardCharsets.UTF_8;
257         final String charsetName = charset.name();
258         builder.setControlEncoding(options, charsetName);
259         assertEquals(charset, builder.getControlEncodingCharset(options));
260         assertEquals(charsetName, builder.getControlEncoding(options));
261         builder.setControlEncoding(options, charset);
262         assertEquals(charset, builder.getControlEncodingCharset(options));
263         assertEquals(charsetName, builder.getControlEncoding(options));
264         builder.setControlKeepAliveReplyTimeout(options, Duration.ofSeconds(35));
265         builder.setControlKeepAliveTimeout(options, Duration.ofSeconds(30));
266         builder.setMdtmLastModifiedTime(options, mdtmLastModifiedTime);
267     }
268 
269     /**
270      * Prepares the file system manager.
271      */
272     @Override
273     public void prepare(final DefaultFileSystemManager manager) throws Exception {
274         manager.addProvider("ftp", new FtpFileProvider());
275     }
276 
277 }