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.net.ftp.parser;
18  
19  import java.util.Calendar;
20  
21  import org.apache.commons.net.ftp.FTPFile;
22  import org.apache.commons.net.ftp.FTPFileEntryParser;
23  
24  /**
25   */
26  public class UnixFTPEntryParserTest extends AbstractFTPParseTest {
27  
28      private static final String[] badsamples = { "zrwxr-xr-x   2 root     root         4096 Mar  2 15:13 zxbox",
29              "dxrwr-xr-x   2 root     root         4096 Aug 24  2001 zxjdbc", "drwxr-xr-x   2 root     root         4096 Jam  4 00:03 zziplib",
30              "drwxr-xr-x   2 root     99           4096 Feb 23 30:01 zzplayer", "drwxr-xr-x   2 root     root         4096 Aug 36  2001 zztpp",
31              "-rw-r--r--   1 14       staff       80284 Aug 22  zxJDBC-1.2.3.tar.gz", "-rw-r--r--   1 14       staff      119:26 Aug 22  2000 zxJDBC-1.2.3.zip",
32              /* "-rw-r--r--   1 ftp      no group    83853 Jan 22  2001 zxJDBC-1.2.4.tar.gz", */
33              "-rw-r--r--   1ftp       nogroup    126552 Jan 22  2001 zxJDBC-1.2.4.zip",
34              "-rw-r--r--   1 root     root       190144 2001-04-27 zxJDBC-2.0.1b1.zip",
35              "-rw-r--r--   1 root     root       111325 Apr -7 18:79 zxJDBC-2.0.1b1.tar.gz" };
36  
37      private static final String[] goodsamples = { "-rw-r--r--   1 500      500            21 Aug  8 14:14 JB3-TES1.gz",
38              "-rwxr-xr-x   2 root     root         4096 Mar  2 15:13 zxbox", "drwxr-xr-x   2 root     root         4096 Aug 24  2001 zxjdbc",
39              "drwxr-xr-x   2 root     root         4096 Jan  4 00:03 zziplib", "drwxr-xr-x   2 root     99           4096 Feb 23  2001 zzplayer",
40              "drwxr-xr-x   2 root     root         4096 Aug  6  2001 zztpp", "drwxr-xr-x 1 usernameftp 512 Jan 29 23:32 prog",
41              "lrw-r--r--   1 14       14          80284 Aug 22  2000 zxJDBC-1.2.3.tar.gz",
42              "frw-r--r--   1 14       staff      119926 Aug 22  2000 zxJDBC-1.2.3.zip",
43              "crw-r--r--   1 ftp      nogroup     83853 Jan 22  2001 zxJDBC-1.2.4.tar.gz",
44              "brw-r--r--   1 ftp      nogroup    126552 Jan 22  2001 zxJDBC-1.2.4.zip",
45              "-rw-r--r--   1 root     root       111325 Apr 27  2001 zxJDBC-2.0.1b1.tar.gz",
46              "-rw-r--r--   1 root     root       190144 Apr 27  2001 zxJDBC-2.0.1b1.zip",
47              "-rwxr-xr-x   2 500      500           166 Nov  2  2001 73131-testtes1.afp",
48              "-rw-r--r--   1 500      500           166 Nov  9  2001 73131-testtes1.AFP",
49              "-rw-r--r--   1 500      500           166 Nov 12  2001 73131-testtes2.afp",
50              "-rw-r--r--   1 500      500           166 Nov 12  2001 73131-testtes2.AFP",
51              "-rw-r--r--   1 500      500       2040000 Aug  5 07:35 testRemoteUPCopyNIX",
52              "-rw-r--r--   1 500      500       2040000 Aug  5 07:31 testRemoteUPDCopyNIX",
53              "-rw-r--r--   1 500      500       2040000 Aug  5 07:31 testRemoteUPVCopyNIX", "-rw-r--r-T   1 500      500             0 Mar 25 08:20 testSticky",
54              "-rwxr-xr-t   1 500      500             0 Mar 25 08:21 testStickyExec", "-rwSr-Sr--   1 500      500             0 Mar 25 08:22 testSuid",
55              "-rwsr-sr--   1 500      500             0 Mar 25 08:23 testSuidExec", "-rwsr-sr--   1 500      500             0 Mar 25 0:23 testSuidExec2",
56              "drwxrwx---+ 23 500     500    0 Jan 10 13:09 testACL", "-rw-r--r--   1 1        3518644 May 25 12:12 std",
57              "lrwxrwxrwx   1 neeme neeme             23 Mar  2 18:06 macros -> ./../../global/macros/.",
58              "-rw-r--r--   1 ftp      group with spaces in it as allowed in cygwin see bug 38634" + "   83853 Jan 22  2001 zxJDBC-1.2.4.tar.gz",
59              // Bug 38634 => NET-16
60              "crw-r----- 1 root kmem 0, 27 Jan 30 11:42 kmem", // FreeBSD device
61              "crw-------   1 root     sys      109,767 Jul  2  2004 pci@1c,600000:devctl", // Solaris device
62              "-rwxrwx---   1 ftp      ftp-admin 816026400 Oct  5  2008 bloplab 7 cd1.img", // NET-294
63  
64              // https://mail-archives.apache.org/mod_mbox/commons-dev/200408.mbox/%3c4122F3C1.9090402@tanukisoftware.com%3e
65              "-rw-r--r-- 1 1 3518644 May 25 12:12 std", "-rw-rw---- 1 ep1adm sapsys 0 6\u6708 3\u65e5 2003\u5e74 \u8a66\u9a13\u30d5\u30a1\u30a4\u30eb.csv",
66              "-rw-rw---- 1 ep1adm sapsys 0 8\u6708 17\u65e5 20:10 caerrinf",
67  
68      };
69  
70      public UnixFTPEntryParserTest(final String name) {
71          super(name);
72      }
73  
74      private void checkPermissions(final FTPFile f) {
75          assertTrue("Should have user read permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION));
76          assertTrue("Should have user write permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION));
77          assertTrue("Should have user execute permission.", f.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION));
78          assertTrue("Should have group read permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION));
79          assertFalse("Should NOT have group write permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION));
80          assertTrue("Should have group execute permission.", f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION));
81          assertTrue("Should have world read permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION));
82          assertFalse("Should NOT have world write permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION));
83          assertTrue("Should have world execute permission.", f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION));
84      }
85  
86      @Override
87      protected void doAdditionalGoodTests(final String test, final FTPFile f) {
88          final String link = f.getLink();
89          if (null != link) {
90              final int linklen = link.length();
91              if (linklen > 0) {
92                  assertEquals(link, test.substring(test.length() - linklen));
93                  assertEquals(f.getType(), FTPFile.SYMBOLIC_LINK_TYPE);
94              }
95          }
96          final int type = f.getType();
97          switch (test.charAt(0)) {
98          case 'd':
99              assertEquals("Type of " + test, type, FTPFile.DIRECTORY_TYPE);
100             break;
101         case 'l':
102             assertEquals("Type of " + test, type, FTPFile.SYMBOLIC_LINK_TYPE);
103             break;
104         case 'b':
105         case 'c':
106             assertEquals(0, f.getHardLinkCount());
107             //$FALL-THROUGH$ TODO this needs to be fixed if a device type is introduced
108         case 'f':
109         case '-':
110             assertEquals("Type of " + test, type, FTPFile.FILE_TYPE);
111             break;
112         default:
113             assertEquals("Type of " + test, type, FTPFile.UNKNOWN_TYPE);
114         }
115 
116         for (int access = FTPFile.USER_ACCESS; access <= FTPFile.WORLD_ACCESS; access++) {
117             for (int perm = FTPFile.READ_PERMISSION; perm <= FTPFile.EXECUTE_PERMISSION; perm++) {
118                 final int pos = 3 * access + perm + 1;
119                 final char permchar = test.charAt(pos);
120                 assertEquals("Permission " + test.substring(1, 10), Boolean.valueOf(f.hasPermission(access, perm)),
121                         Boolean.valueOf(permchar != '-' && !Character.isUpperCase(permchar)));
122             }
123         }
124 
125         assertNotNull("Expected to find a timestamp", f.getTimestamp());
126 // Perhaps check date range (need to ensure all good examples qualify)
127 //        assertTrue(test,f.getTimestamp().get(Calendar.YEAR)>=2000);
128     }
129 
130     @Override
131     protected String[] getBadListing() {
132         return badsamples;
133     }
134 
135     @Override
136     protected String[] getGoodListing() {
137         return goodsamples;
138     }
139 
140     @Override
141     protected FTPFileEntryParser getParser() {
142         return new UnixFTPEntryParser();
143     }
144 
145     public void testCorrectGroupNameParsing() {
146         final FTPFile f = getParser().parseFTPEntry("-rw-r--r--   1 ftpuser  ftpusers 12414535 Mar 17 11:07 test 1999 abc.pdf");
147         assertNotNull(f);
148         assertEquals(1, f.getHardLinkCount());
149         assertEquals("ftpuser", f.getUser());
150         assertEquals("ftpusers", f.getGroup());
151         assertEquals(12414535, f.getSize());
152         assertEquals("test 1999 abc.pdf", f.getName());
153 
154         final Calendar cal = Calendar.getInstance();
155         cal.set(Calendar.MONTH, Calendar.MARCH);
156         cal.set(Calendar.DAY_OF_MONTH, 17);
157         cal.set(Calendar.HOUR_OF_DAY, 11);
158         cal.set(Calendar.MINUTE, 7);
159         cal.set(Calendar.SECOND, 0);
160         cal.set(Calendar.MILLISECOND, 0);
161 
162         assertEquals(f.getTimestamp().get(Calendar.MONTH), cal.get(Calendar.MONTH));
163         assertEquals(f.getTimestamp().get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_MONTH));
164         assertEquals(f.getTimestamp().get(Calendar.HOUR_OF_DAY), cal.get(Calendar.HOUR_OF_DAY));
165         assertEquals(f.getTimestamp().get(Calendar.MINUTE), cal.get(Calendar.MINUTE));
166         assertEquals(f.getTimestamp().get(Calendar.SECOND), cal.get(Calendar.SECOND));
167     }
168 
169     @Override
170     public void testDefaultPrecision() {
171         testPrecision("drwxr-xr-x   2 user     group         4096 Mar  2 2014 zxbox", CalendarUnit.DAY_OF_MONTH);
172     }
173 
174     public void testFilenamesWithEmbeddedNumbers() {
175         final FTPFile f = getParser().parseFTPEntry("-rw-rw-rw-   1 user group 5840 Mar 19 09:34 123 456 abc.csv");
176         assertEquals("123 456 abc.csv", f.getName());
177         assertEquals(5840, f.getSize());
178         assertEquals("user", f.getUser());
179         assertEquals("group", f.getGroup());
180     }
181 
182     public void testGroupNameWithSpaces() {
183         final FTPFile f = getParser().parseFTPEntry("drwx------ 4 maxm Domain Users 512 Oct 2 10:59 .metadata");
184         assertNotNull(f);
185         assertEquals("maxm", f.getUser());
186         assertEquals("Domain Users", f.getGroup());
187     }
188 
189     public void testLeadingSpacesDefault() { // the default has been changed to keep spaces
190         final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x   2 john smith     group         4096 Mar  2 15:13   zxbox");
191         assertNotNull(f);
192         assertEquals("  zxbox", f.getName()); // leading spaces retained
193     }
194 
195     public void testLeadingSpacesNET566() { // check new behavior
196         final FTPFile f = new UnixFTPEntryParser(null, false).parseFTPEntry("drwxr-xr-x   2 john smith     group         4096 Mar  2 15:13   zxbox");
197         assertNotNull(f);
198         assertEquals("  zxbox", f.getName()); // leading spaces retained
199     }
200 
201     public void testNameWIthPunctuation() {
202         final FTPFile f = getParser().parseFTPEntry("drwx------ 4 maxm Domain Users 512 Oct 2 10:59 abc(test)123.pdf");
203         assertNotNull(f);
204         assertEquals("abc(test)123.pdf", f.getName());
205     }
206 
207     public void testNET294() {
208         final FTPFile f = getParser().parseFTPEntry("-rwxrwx---   1 ftp      ftp-admin 816026400 Oct  5  2008 bloplab 7 cd1.img");
209         assertNotNull(f);
210         assertEquals("ftp", f.getUser());
211         assertEquals("ftp-admin", f.getGroup());
212         assertEquals(816026400L, f.getSize());
213         assertNotNull("Timestamp should not be null", f.getTimestamp());
214         assertEquals(2008, f.getTimestamp().get(Calendar.YEAR));
215         assertEquals("bloplab 7 cd1.img", f.getName());
216     }
217 
218     public void testNoSpacesBeforeFileSize() {
219         final FTPFile f = getParser().parseFTPEntry("drwxr-x---+1464 chrism   chrism     41472 Feb 25 13:17 20090225");
220         assertNotNull(f);
221         assertEquals(41472, f.getSize());
222         assertEquals(f.getType(), FTPFile.DIRECTORY_TYPE);
223         assertEquals("chrism", f.getUser());
224         assertEquals("chrism", f.getGroup());
225         assertEquals(1464, f.getHardLinkCount());
226     }
227 
228     public void testNumericDateFormat() {
229         final String testNumericDF = "-rw-r-----   1 neeme neeme   346 2005-04-08 11:22 services.vsp";
230         final String testNumericDF2 = "lrwxrwxrwx   1 neeme neeme    23 2005-03-02 18:06 macros -> ./../../global/macros/.";
231 
232         final UnixFTPEntryParser parser = new UnixFTPEntryParser(UnixFTPEntryParser.NUMERIC_DATE_CONFIG);
233 
234         final FTPFile f = parser.parseFTPEntry(testNumericDF);
235         assertNotNull("Failed to parse " + testNumericDF, f);
236 
237         final Calendar cal = Calendar.getInstance();
238         cal.clear();
239         cal.set(Calendar.YEAR, 2005);
240         cal.set(Calendar.MONTH, Calendar.APRIL);
241 
242         cal.set(Calendar.DAY_OF_MONTH, 8);
243         cal.set(Calendar.HOUR_OF_DAY, 11);
244         cal.set(Calendar.MINUTE, 22);
245         assertEquals(cal.getTime(), f.getTimestamp().getTime());
246 
247         final FTPFile f2 = parser.parseFTPEntry(testNumericDF2);
248         assertNotNull("Failed to parse " + testNumericDF2, f2);
249         assertEquals("symbolic link", "./../../global/macros/.", f2.getLink());
250 
251     }
252 
253     public void testOwnerAndGroupNameWithSpaces() {
254         final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x   2 john smith     test group         4096 Mar  2 15:13 zxbox");
255         assertNotNull(f);
256         assertEquals("john smith", f.getUser());
257         assertEquals("test group", f.getGroup());
258     }
259 
260     public void testOwnerNameWithSpaces() {
261         final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x   2 john smith     group         4096 Mar  2 15:13 zxbox");
262         assertNotNull(f);
263         assertEquals("john smith", f.getUser());
264     }
265 
266     @Override
267     public void testParseFieldsOnDirectory() throws Exception {
268         final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x   2 user     group         4096 Mar  2 15:13 zxbox");
269         assertNotNull("Could not parse entry.", f);
270         assertTrue("Should have been a directory.", f.isDirectory());
271         checkPermissions(f);
272         assertEquals(2, f.getHardLinkCount());
273         assertEquals("user", f.getUser());
274         assertEquals("group", f.getGroup());
275         assertEquals("zxbox", f.getName());
276         assertEquals(4096, f.getSize());
277 
278         final Calendar cal = Calendar.getInstance();
279         cal.set(Calendar.MONTH, Calendar.MARCH);
280 
281         cal.set(Calendar.DAY_OF_MONTH, 1);
282         cal.set(Calendar.HOUR_OF_DAY, 0);
283         cal.set(Calendar.MINUTE, 0);
284         cal.set(Calendar.SECOND, 0);
285         if (f.getTimestamp().getTime().before(cal.getTime())) {
286             cal.add(Calendar.YEAR, -1);
287         }
288         cal.set(Calendar.DAY_OF_MONTH, 2);
289         cal.set(Calendar.HOUR_OF_DAY, 15);
290         cal.set(Calendar.MINUTE, 13);
291 
292         assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime()));
293     }
294 
295     @Override
296     public void testParseFieldsOnFile() throws Exception {
297         final FTPFile f = getParser().parseFTPEntry("-rwxr-xr-x   2 user     my group 500        5000000000 Mar  2 15:13 zxbox");
298         assertNotNull("Could not parse entry.", f);
299         assertTrue("Should have been a file.", f.isFile());
300         checkPermissions(f);
301         assertEquals(2, f.getHardLinkCount());
302         assertEquals("user", f.getUser());
303         assertEquals("my group 500", f.getGroup());
304         assertEquals("zxbox", f.getName());
305         assertEquals(5000000000L, f.getSize());
306 
307         final Calendar cal = Calendar.getInstance();
308         cal.set(Calendar.MONTH, Calendar.MARCH);
309 
310         cal.set(Calendar.DAY_OF_MONTH, 1);
311         cal.set(Calendar.HOUR_OF_DAY, 0);
312         cal.set(Calendar.MINUTE, 0);
313         cal.set(Calendar.SECOND, 0);
314         if (f.getTimestamp().getTime().before(cal.getTime())) {
315             cal.add(Calendar.YEAR, -1);
316         }
317         cal.set(Calendar.DAY_OF_MONTH, 2);
318         cal.set(Calendar.HOUR_OF_DAY, 15);
319         cal.set(Calendar.MINUTE, 13);
320         assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime()));
321     }
322 
323     // https://mail-archives.apache.org/mod_mbox/commons-dev/200408.mbox/%3c4122F3C1.9090402@tanukisoftware.com%3e
324     public void testParseFieldsOnFileJapaneseTime() {
325         final FTPFile f = getParser().parseFTPEntry("-rwxr-xr-x 2 user group 4096 3\u6708 2\u65e5 15:13 zxbox");
326         assertNotNull("Could not parse entry.", f);
327         assertTrue("Should have been a file.", f.isFile());
328         checkPermissions(f);
329         assertEquals(2, f.getHardLinkCount());
330         assertEquals("user", f.getUser());
331         assertEquals("group", f.getGroup());
332         assertEquals("zxbox", f.getName());
333         assertEquals(4096, f.getSize());
334 
335         assertNotNull("Timestamp not null", f.getTimestamp());
336         final Calendar cal = Calendar.getInstance();
337         cal.set(Calendar.MONTH, Calendar.MARCH);
338         cal.set(Calendar.DATE, 1);
339         cal.set(Calendar.HOUR_OF_DAY, 0);
340         cal.set(Calendar.MINUTE, 0);
341         cal.set(Calendar.SECOND, 0);
342         if (f.getTimestamp().getTime().before(cal.getTime())) {
343             cal.add(Calendar.YEAR, -1);
344         }
345         cal.set(Calendar.DATE, 2);
346         cal.set(Calendar.HOUR_OF_DAY, 15);
347         cal.set(Calendar.MINUTE, 13);
348         assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime()));
349     }
350 
351     // https://mail-archives.apache.org/mod_mbox/commons-dev/200408.mbox/%3c4122F3C1.9090402@tanukisoftware.com%3e
352     public void testParseFieldsOnFileJapaneseYear() {
353         final FTPFile f = getParser().parseFTPEntry("-rwxr-xr-x 2 user group 4096 3\u6708 2\u65e5 2003\u5e74 \u8a66\u9a13\u30d5\u30a1\u30a4\u30eb.csv");
354         assertNotNull("Could not parse entry.", f);
355         assertTrue("Should have been a file.", f.isFile());
356         checkPermissions(f);
357         assertEquals(2, f.getHardLinkCount());
358         assertEquals("user", f.getUser());
359         assertEquals("group", f.getGroup());
360         assertEquals("\u8a66\u9a13\u30d5\u30a1\u30a4\u30eb.csv", f.getName());
361         assertEquals(4096, f.getSize());
362 
363         assertNotNull("Timestamp not null", f.getTimestamp());
364         final Calendar cal = Calendar.getInstance();
365         cal.set(Calendar.YEAR, 2003);
366         cal.set(Calendar.MONTH, Calendar.MARCH);
367         cal.set(Calendar.DATE, 2);
368         cal.set(Calendar.HOUR_OF_DAY, 0);
369         cal.set(Calendar.MINUTE, 0);
370         cal.set(Calendar.SECOND, 0);
371         assertEquals(df.format(cal.getTime()), df.format(f.getTimestamp().getTime()));
372     }
373 
374     @Override
375     public void testRecentPrecision() {
376         testPrecision("drwxr-xr-x   2 user     group         4096 Mar  2 15:13 zxbox", CalendarUnit.MINUTE);
377     }
378 
379     public void testTrailingSpaces() {
380         final FTPFile f = getParser().parseFTPEntry("drwxr-xr-x   2 john smith     group         4096 Mar  2 15:13 zxbox     ");
381         assertNotNull(f);
382         assertEquals("zxbox     ", f.getName());
383     }
384 
385     public void testTrimLeadingSpacesNET566() { // check can trim spaces as before
386         final FTPFile f = new UnixFTPEntryParser(null, true).parseFTPEntry("drwxr-xr-x   2 john smith     group         4096 Mar  2 15:13   zxbox");
387         assertNotNull(f);
388         assertEquals("zxbox", f.getName()); // leading spaces trimmed
389     }
390 }