View Javadoc

1   /*
2    * Copyright 2001-2005 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.net.ftp.parser;
17  import java.text.ParseException;
18  
19  import org.apache.commons.net.ftp.FTPClientConfig;
20  import org.apache.commons.net.ftp.FTPFile;
21  
22  /**
23   * Implementation FTPFileEntryParser and FTPFileListParser for standard
24   * Unix Systems.
25   *
26   * This class is based on the logic of Daniel Savarese's
27   * DefaultFTPListParser, but adapted to use regular expressions and to fit the
28   * new FTPFileEntryParser interface.
29   * @version $Id: UnixFTPEntryParser.java 161712 2005-04-18 02:57:04Z scohen $
30   * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
31   */
32  public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
33  {
34      /**
35       * months abbreviations looked for by this parser.  Also used
36       * to determine which month is matched by the parser
37       */
38      private static final String DEFAULT_MONTHS =
39          "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
40      
41      static final String DEFAULT_DATE_FORMAT 
42  		= "MMM d yyyy"; //Nov 9 2001
43      
44      static final String DEFAULT_RECENT_DATE_FORMAT 
45  		= "MMM d HH:mm"; //Nov 9 20:06
46  
47      static final String NUMERIC_DATE_FORMAT 
48  		= "yyyy-MM-dd HH:mm"; //2001-11-09 20:06
49  
50      /**
51       * Some Linux distributions are now shipping an FTP server which formats
52       * file listing dates in an all-numeric format: 
53       * <code>"yyyy-MM-dd HH:mm</code>.  
54       * This is a very welcome development,  and hopefully it will soon become 
55       * the standard.  However, since it is so new, for now, and possibly 
56       * forever, we merely accomodate it, but do not make it the default.
57       * <p>
58       * For now end users may specify this format only via 
59       * <code>UnixFTPEntryParser(FTPClientConfig)</code>.
60       * Steve Cohen - 2005-04-17
61       */
62      public static final FTPClientConfig NUMERIC_DATE_CONFIG =
63          new FTPClientConfig(
64                  FTPClientConfig.SYST_UNIX,
65                  NUMERIC_DATE_FORMAT,
66                  null, null, null, null);
67  
68      /**
69       * this is the regular expression used by this parser.
70       *
71       * Permissions:
72       *    r   the file is readable
73       *    w   the file is writable
74       *    x   the file is executable
75       *    -   the indicated permission is not granted
76       *    L   mandatory locking occurs during access (the set-group-ID bit is
77       *        on and the group execution bit is off)
78       *    s   the set-user-ID or set-group-ID bit is on, and the corresponding
79       *        user or group execution bit is also on
80       *    S   undefined bit-state (the set-user-ID bit is on and the user
81       *        execution bit is off)
82       *    t   the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
83       *        execution is on
84       *    T   the 1000 bit is turned on, and execution is off (undefined bit-
85       *        state)
86       */
87      private static final String REGEX =
88          "([bcdlfmpSs-])"
89          +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+"
90          + "(\\d+)\\s+"
91          + "(\\S+)\\s+"
92          + "(?:(\\S+)\\s+)?"
93          + "(\\d+)\\s+"
94          
95          /*
96            numeric or standard format date
97          */
98          + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+))\\s+"
99  		
100         /* 
101            year (for non-recent standard format) 
102 		   or time (for numeric or recent standard format  
103 		*/
104 		+ "(\\d+(?::\\d+)?)\\s+"
105         
106 		+ "(\\S*)(\\s*.*)";
107 
108 
109     /**
110      * The default constructor for a UnixFTPEntryParser object.
111      *
112      * @exception IllegalArgumentException
113      * Thrown if the regular expression is unparseable.  Should not be seen
114      * under normal conditions.  It it is seen, this is a sign that
115      * <code>REGEX</code> is  not a valid regular expression.
116      */
117     public UnixFTPEntryParser()
118     {
119         this(null);
120     }
121 
122     /**
123      * This constructor allows the creation of a UnixFTPEntryParser object with
124      * something other than the default configuration.
125      *
126      * @param config The {@link FTPClientConfig configuration} object used to 
127      * configure this parser.
128      * @exception IllegalArgumentException
129      * Thrown if the regular expression is unparseable.  Should not be seen
130      * under normal conditions.  It it is seen, this is a sign that
131      * <code>REGEX</code> is  not a valid regular expression.
132      * @since 1.4
133      */
134     public UnixFTPEntryParser(FTPClientConfig config)
135     {
136         super(REGEX);
137         configure(config);
138     }
139 
140 
141     /**
142      * Parses a line of a unix (standard) FTP server file listing and converts
143      * it into a usable format in the form of an <code> FTPFile </code>
144      * instance.  If the file listing line doesn't describe a file,
145      * <code> null </code> is returned, otherwise a <code> FTPFile </code>
146      * instance representing the files in the directory is returned.
147      * <p>
148      * @param entry A line of text from the file listing
149      * @return An FTPFile instance corresponding to the supplied entry
150      */
151 	public FTPFile parseFTPEntry(String entry) {
152         FTPFile file = new FTPFile();
153         file.setRawListing(entry);
154         int type;
155         boolean isDevice = false;
156 
157         if (matches(entry))
158         {
159             String typeStr = group(1);
160             String hardLinkCount = group(15);
161             String usr = group(16);
162             String grp = group(17);
163             String filesize = group(18);
164             String datestr = group(19) + " " + group(20);
165             String name = group(21);
166             String endtoken = group(22);
167 
168             try
169             {
170                 file.setTimestamp(super.parseTimestamp(datestr));
171             }
172             catch (ParseException e)
173             {
174             	return null;  // this is a parsing failure too.
175             }
176             
177             
178             // bcdlfmpSs-
179             switch (typeStr.charAt(0))
180             {
181             case 'd':
182                 type = FTPFile.DIRECTORY_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     protected FTPClientConfig getDefaultConfiguration() {
287         return new FTPClientConfig(
288                 FTPClientConfig.SYST_UNIX,
289                 DEFAULT_DATE_FORMAT,
290                 DEFAULT_RECENT_DATE_FORMAT,
291                 null, null, null);
292     }
293     
294     
295     
296 
297 }