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  
18  package org.apache.commons.net.ftp.parser;
19  import java.text.ParseException;
20  import java.util.List;
21  import java.util.ListIterator;
22  
23  import org.apache.commons.net.ftp.FTPClientConfig;
24  import org.apache.commons.net.ftp.FTPFile;
25  
26  /**
27   * Implementation FTPFileEntryParser and FTPFileListParser for standard
28   * Unix Systems.
29   *
30   * This class is based on the logic of Daniel Savarese's
31   * DefaultFTPListParser, but adapted to use regular expressions and to fit the
32   * new FTPFileEntryParser interface.
33   * @version $Id: UnixFTPEntryParser.java 1489361 2013-06-04 09:48:36Z sebb $
34   * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
35   */
36  public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
37  {
38  
39      static final String DEFAULT_DATE_FORMAT
40          = "MMM d yyyy"; //Nov 9 2001
41  
42      static final String DEFAULT_RECENT_DATE_FORMAT
43          = "MMM d HH:mm"; //Nov 9 20:06
44  
45      static final String NUMERIC_DATE_FORMAT
46          = "yyyy-MM-dd HH:mm"; //2001-11-09 20:06
47  
48      /**
49       * Some Linux distributions are now shipping an FTP server which formats
50       * file listing dates in an all-numeric format:
51       * <code>"yyyy-MM-dd HH:mm</code>.
52       * This is a very welcome development,  and hopefully it will soon become
53       * the standard.  However, since it is so new, for now, and possibly
54       * forever, we merely accomodate it, but do not make it the default.
55       * <p>
56       * For now end users may specify this format only via
57       * <code>UnixFTPEntryParser(FTPClientConfig)</code>.
58       * Steve Cohen - 2005-04-17
59       */
60      public static final FTPClientConfig NUMERIC_DATE_CONFIG =
61          new FTPClientConfig(
62                  FTPClientConfig.SYST_UNIX,
63                  NUMERIC_DATE_FORMAT,
64                  null, null, null, null);
65  
66      /**
67       * this is the regular expression used by this parser.
68       *
69       * Permissions:
70       *    r   the file is readable
71       *    w   the file is writable
72       *    x   the file is executable
73       *    -   the indicated permission is not granted
74       *    L   mandatory locking occurs during access (the set-group-ID bit is
75       *        on and the group execution bit is off)
76       *    s   the set-user-ID or set-group-ID bit is on, and the corresponding
77       *        user or group execution bit is also on
78       *    S   undefined bit-state (the set-user-ID bit is on and the user
79       *        execution bit is off)
80       *    t   the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
81       *        execution is on
82       *    T   the 1000 bit is turned on, and execution is off (undefined bit-
83       *        state)
84       *    e   z/OS external link bit
85       */
86      private static final String REGEX =
87          "([bcdelfmpSs-])"
88          +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s*"
89          + "(\\d+)\\s+"                                  // link count
90          + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?"                // owner name (optional spaces)
91          + "(?:(\\S+(?:\\s\\S+)*)\\s+)?"                 // group name (optional spaces)
92          + "(\\d+(?:,\\s*\\d+)?)\\s+"                    // size or n,m
93          /*
94           * numeric or standard format date:
95           *   yyyy-mm-dd (expecting hh:mm to follow)
96           *   MMM [d]d
97           *   [d]d MMM
98           *   N.B. use non-space for MMM to allow for languages such as German which use
99           *   diacritics (e.g. umlaut) in some abbreviations.
100         */
101         + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3}))\\s+"
102         /*
103            year (for non-recent standard format) - yyyy
104            or time (for numeric or recent standard format) [h]h:mm
105         */
106         + "(\\d+(?::\\d+)?)\\s+"
107 
108         + "(\\S*)(\\s*.*)"; // the rest
109 
110 
111     /**
112      * The default constructor for a UnixFTPEntryParser object.
113      *
114      * @exception IllegalArgumentException
115      * Thrown if the regular expression is unparseable.  Should not be seen
116      * under normal conditions.  It it is seen, this is a sign that
117      * <code>REGEX</code> is  not a valid regular expression.
118      */
119     public UnixFTPEntryParser()
120     {
121         this(null);
122     }
123 
124     /**
125      * This constructor allows the creation of a UnixFTPEntryParser object with
126      * something other than the default configuration.
127      *
128      * @param config The {@link FTPClientConfig configuration} object used to
129      * configure this parser.
130      * @exception IllegalArgumentException
131      * Thrown if the regular expression is unparseable.  Should not be seen
132      * under normal conditions.  It it is seen, this is a sign that
133      * <code>REGEX</code> is  not a valid regular expression.
134      * @since 1.4
135      */
136     public UnixFTPEntryParser(FTPClientConfig config)
137     {
138         super(REGEX);
139         configure(config);
140     }
141 
142     /**
143      * Preparse the list to discard "total nnn" lines
144      */
145     @Override
146     public List<String> preParse(List<String> original) {
147         ListIterator<String> iter = original.listIterator();
148         while (iter.hasNext()) {
149             String entry = iter.next();
150             if (entry.matches("^total \\d+$")) { // NET-389
151                 iter.remove();
152             }
153         }
154         return original;
155     }
156 
157     /**
158      * Parses a line of a unix (standard) FTP server file listing and converts
159      * it into a usable format in the form of an <code> FTPFile </code>
160      * instance.  If the file listing line doesn't describe a file,
161      * <code> null </code> is returned, otherwise a <code> FTPFile </code>
162      * instance representing the files in the directory is returned.
163      * <p>
164      * @param entry A line of text from the file listing
165      * @return An FTPFile instance corresponding to the supplied entry
166      */
167 //    @Override
168     public FTPFile parseFTPEntry(String entry) {
169         FTPFile file = new FTPFile();
170         file.setRawListing(entry);
171         int type;
172         boolean isDevice = false;
173 
174         if (matches(entry))
175         {
176             String typeStr = group(1);
177             String hardLinkCount = group(15);
178             String usr = group(16);
179             String grp = group(17);
180             String filesize = group(18);
181             String datestr = group(19) + " " + group(20);
182             String name = group(21);
183             String endtoken = group(22);
184 
185             try
186             {
187                 file.setTimestamp(super.parseTimestamp(datestr));
188             }
189             catch (ParseException e)
190             {
191                  // intentionally do nothing
192             }
193 
194             // A 'whiteout' file is an ARTIFICIAL entry in any of several types of
195             // 'translucent' filesystems, of which a 'union' filesystem is one.
196 
197             // bcdelfmpSs-
198             switch (typeStr.charAt(0))
199             {
200             case 'd':
201                 type = FTPFile.DIRECTORY_TYPE;
202                 break;
203             case 'e': // NET-39 => z/OS external link
204                 type = FTPFile.SYMBOLIC_LINK_TYPE;
205                 break;
206             case 'l':
207                 type = FTPFile.SYMBOLIC_LINK_TYPE;
208                 break;
209             case 'b':
210             case 'c':
211                 isDevice = true;
212                 type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented
213                 break;
214             case 'f':
215             case '-':
216                 type = FTPFile.FILE_TYPE;
217                 break;
218             default: // e.g. ? and w = whiteout
219                 type = FTPFile.UNKNOWN_TYPE;
220             }
221 
222             file.setType(type);
223 
224             int g = 4;
225             for (int access = 0; access < 3; access++, g += 4)
226             {
227                 // Use != '-' to avoid having to check for suid and sticky bits
228                 file.setPermission(access, FTPFile.READ_PERMISSION,
229                                    (!group(g).equals("-")));
230                 file.setPermission(access, FTPFile.WRITE_PERMISSION,
231                                    (!group(g + 1).equals("-")));
232 
233                 String execPerm = group(g + 2);
234                 if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0)))
235                 {
236                     file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true);
237                 }
238                 else
239                 {
240                     file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false);
241                 }
242             }
243 
244             if (!isDevice)
245             {
246                 try
247                 {
248                     file.setHardLinkCount(Integer.parseInt(hardLinkCount));
249                 }
250                 catch (NumberFormatException e)
251                 {
252                     // intentionally do nothing
253                 }
254             }
255 
256             file.setUser(usr);
257             file.setGroup(grp);
258 
259             try
260             {
261                 file.setSize(Long.parseLong(filesize));
262             }
263             catch (NumberFormatException e)
264             {
265                 // intentionally do nothing
266             }
267 
268             if (null == endtoken)
269             {
270                 file.setName(name);
271             }
272             else
273             {
274                 // oddball cases like symbolic links, file names
275                 // with spaces in them.
276                 name += endtoken;
277                 if (type == FTPFile.SYMBOLIC_LINK_TYPE)
278                 {
279 
280                     int end = name.indexOf(" -> ");
281                     // Give up if no link indicator is present
282                     if (end == -1)
283                     {
284                         file.setName(name);
285                     }
286                     else
287                     {
288                         file.setName(name.substring(0, end));
289                         file.setLink(name.substring(end + 4));
290                     }
291 
292                 }
293                 else
294                 {
295                     file.setName(name);
296                 }
297             }
298             return file;
299         }
300         return null;
301     }
302 
303     /**
304      * Defines a default configuration to be used when this class is
305      * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
306      * parameter being specified.
307      * @return the default configuration for this parser.
308      */
309     @Override
310     protected FTPClientConfig getDefaultConfiguration() {
311         return new FTPClientConfig(
312                 FTPClientConfig.SYST_UNIX,
313                 DEFAULT_DATE_FORMAT,
314                 DEFAULT_RECENT_DATE_FORMAT,
315                 null, null, null);
316     }
317 
318 }