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