1 package org.apache.commons.net.ftp;
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2001 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" and
29 * "Apache Commons" must not be used to endorse or promote products
30 * derived from this software without prior written permission. For
31 * written permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * nor may "Apache" appear in their name, without
35 * prior written permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 */
56
57 import java.io.BufferedReader;
58 import java.io.IOException;
59 import java.io.InputStream;
60 import java.io.InputStreamReader;
61 import java.util.Calendar;
62 import java.util.StringTokenizer;
63 import java.util.Vector;
64
65 /****
66 * DefaultFTPFileListParser is the default implementation of
67 * <a href="org.apache.commons.net.ftp.FTPFileListParser.html"> FTPFileListParser </a>
68 * used by <a href="org.apache.commons.net.ftp.FTPClient.html"> FTPClient </a>
69 * to parse file listings.
70 * Sometimes you will want to parse unusual listing formats, in which
71 * case you would create your own implementation of FTPFileListParser and
72 * if necessary, subclass FTPFile.
73 * <p>
74 * <p>
75 * @author Daniel F. Savarese
76 * @see FTPFileListParser
77 * @see FTPFile
78 * @see FTPClient#listFiles
79 ***/
80
81 public final class DefaultFTPFileListParser implements FTPFileListParser
82 {
83
84
85 // end is one beyond end
86 private int __charArrayToInt(char[] arr, int start, int end)
87 {
88 int value = 0, decimal;
89 decimal = 1;
90 while (end-- > start)
91 {
92 value += (decimal * (arr[end] - '0'));
93 decimal *= 10;
94 }
95 return value;
96 }
97
98 private long __charArrayToLong(char[] arr, int start, int end)
99 {
100 long value = 0, decimal;
101 decimal = 1;
102 while (end-- > start)
103 {
104 value += (decimal * (arr[end] - '0'));
105 decimal *= 10;
106 }
107 return value;
108 }
109
110 private int __skipWhitespace(char[] cToken, int start)
111 {
112 while (start < cToken.length && Character.isWhitespace(cToken[start]))
113 ++start;
114 return start;
115 }
116
117 private int __skipDigits(char[] cToken, int start)
118 {
119 while (start < cToken.length && Character.isDigit(cToken[start]))
120 ++start;
121 return start;
122 }
123
124 private int __skipNonWhitespace(char[] cToken, int start)
125 {
126 while (start < cToken.length && !Character.isWhitespace(cToken[start]))
127 ++start;
128 return start;
129 }
130
131 private int __skipNonWhitespaceToLower(char[] cToken, int start)
132 {
133 while (start < cToken.length && !Character.isWhitespace(cToken[start]))
134 {
135 cToken[start] = Character.toLowerCase(cToken[start]);
136 ++start;
137 }
138 return start;
139 }
140
141
142 /****
143 * Parses an FTP server listing entry (a single line) and returns an
144 * FTPFile instance with the resulting information. If the entry could
145 * not be parsed, returns null.
146 * <p>
147 * @param entry A single line of an FTP server listing with the
148 * end of line truncated.
149 * @return An FTPFile instance representing the file information. null if
150 * the entry could be parsed, returns null.
151 ***/
152 public FTPFile parseFTPEntry(String entry)
153 {
154 int access, start, end, type, month, year, hour, minutes;
155 boolean isDevice;
156 Calendar date;
157 StringTokenizer tokenizer;
158 String sToken;
159 char cToken[];
160 FTPFile file;
161
162 try
163 {
164 cToken = entry.toCharArray();
165
166 file = new FTPFile();
167 file.setRawListing(entry);
168
169 isDevice = (cToken[0] == 'b' || cToken[0] == 'c');
170
171 switch (cToken[0])
172 {
173 case 'd':
174 type = FTPFile.DIRECTORY_TYPE;
175 break;
176 case 'l':
177 type = FTPFile.SYMBOLIC_LINK_TYPE;
178 break;
179 default:
180 type = FTPFile.FILE_TYPE;
181 break;
182 }
183
184 file.setType(type);
185
186 for (access = 0, start = 1; access < 3; access++)
187 {
188 // We use != '-' so we avoid having to check for suid and sticky bits
189 file.setPermission(access, FTPFile.READ_PERMISSION,
190 (cToken[start++] != '-'));
191 file.setPermission(access, FTPFile.WRITE_PERMISSION,
192 (cToken[start++] != '-'));
193 file.setPermission(access, FTPFile.EXECUTE_PERMISSION,
194 (cToken[start++] != '-'));
195 }
196
197 start = __skipWhitespace(cToken, start);
198 end = __skipDigits(cToken, start);
199 file.setHardLinkCount(__charArrayToInt(cToken, start, end));
200
201 start = __skipWhitespace(cToken, end);
202 end = __skipNonWhitespace(cToken, start);
203 // Get user and group
204 file.setUser(new String(cToken, start, end - start));
205
206 start = __skipWhitespace(cToken, end);
207 end = __skipNonWhitespace(cToken, start);
208 file.setGroup(new String(cToken, start, end - start));
209
210 // Get size, if block or character device, set size to zero and skip
211 // next two tokens.
212 if (isDevice)
213 {
214 start = __skipWhitespace(cToken, end);
215 end = __skipNonWhitespace(cToken, start);
216 start = __skipWhitespace(cToken, end);
217 end = __skipNonWhitespace(cToken, start);
218 // Don't explicitly set size because it is zero by default
219 }
220 else
221 {
222 start = __skipWhitespace(cToken, end);
223 end = __skipDigits(cToken, start);
224 file.setSize(__charArrayToLong(cToken, start, end));
225 }
226
227 start = __skipWhitespace(cToken, end);
228 end = __skipNonWhitespaceToLower(cToken, start);
229
230 // Get month
231 switch (cToken[start])
232 {
233 case 'a':
234 if (cToken[end - 1] == 'r')
235 month = 3;
236 else
237 month = 7;
238 break;
239 case 'd':
240 month = 11;
241 break;
242 case 'f':
243 month = 1;
244 break;
245 case 'j':
246 if (cToken[end - 1] == 'l')
247 month = 6;
248 else if (cToken[start + 1] == 'a')
249 month = 0;
250 else
251 month = 5;
252 break;
253 case 'm':
254 if (cToken[end - 1] == 'y')
255 month = 4;
256 else
257 month = 2;
258 break;
259 case 'n':
260 month = 10;
261 break;
262 case 'o':
263 month = 9;
264 break;
265 case 's':
266 month = 8;
267 break;
268 default:
269 month = 0;
270 break;
271 }
272
273 // Get day, and store in access
274 start = __skipWhitespace(cToken, end);
275 end = __skipDigits(cToken, start);
276 access = __charArrayToInt(cToken, start, end);
277
278 start = __skipWhitespace(cToken, end);
279 end = __skipDigits(cToken, start);
280
281 date = Calendar.getInstance();
282
283 try
284 {
285 // If token contains a :, it must be a time, otherwise a year
286 if (cToken[end] == ':')
287 {
288 year = date.get(Calendar.YEAR);
289 hour = date.get(Calendar.MONTH);
290 if (hour < month)
291 --year;
292 hour = __charArrayToInt(cToken, start, end);
293 start = end + 1;
294 end = __skipDigits(cToken, start);
295 minutes = __charArrayToInt(cToken, start, end);
296 }
297 else
298 {
299 // Have to set minutes or compiler will complain not initialized
300 hour = minutes = -1;
301 year = __charArrayToInt(cToken, start, end);
302 }
303
304 date.clear();
305 date.set(Calendar.YEAR, year);
306 date.set(Calendar.MONTH, month);
307 date.set(Calendar.DATE, access);
308
309 if (hour != -1)
310 {
311 date.set(Calendar.HOUR, hour);
312 date.set(Calendar.MINUTE, minutes);
313 }
314
315 }
316 catch (IllegalArgumentException e)
317 {
318 // Do nothing
319 }
320
321 file.setTimestamp(date);
322
323 // This is dangerous, but we're going to assume there is only one
324 // space after the date. Most servers seem to use that format and
325 // we need to be able to preserve leading spacesin filenames.
326 //start = __skipWhitespace(cToken, end);
327 start = end + 1;
328 end = __skipNonWhitespace(cToken, start);
329
330 if (end >= cToken.length)
331 {
332 file.setName(new String(cToken, start, end - start));
333 return file;
334 }
335
336 // Now we have to deal with the possibilities of symbolic links and
337 // filenames with spaces. The filename and/or link may contain
338 // spaces, numbers, or appear like the date entry, group, etc.,
339
340 sToken = new String(cToken, start, cToken.length - start);
341
342 if (type == FTPFile.SYMBOLIC_LINK_TYPE)
343 {
344 end = sToken.indexOf(" -> ");
345 // Give up if no link indicator is present
346 if (end == -1)
347 {
348 file.setName(sToken);
349 return file;
350 }
351
352 file.setName(sToken.substring(0, end));
353 file.setLink(sToken.substring(end + 4));
354 return file;
355 }
356
357 // For other cases, just take the entire token
358
359 file.setName(sToken);
360 }
361 catch (ArrayIndexOutOfBoundsException e)
362 {
363 return null;
364 }
365 catch (StringIndexOutOfBoundsException e)
366 {
367 return null;
368 }
369
370 return file;
371 }
372
373
374 /****
375 * Parses an FTP server file listing and converts it into a usable format
376 * in the form of an array of <code> FTPFile </code> instances. If the
377 * file list contains no files, <code> null </code> is returned, otherwise
378 * an array of <code> FTPFile </code> instances representing the files in
379 * the directory is returned.
380 * <p>
381 * @param listStream The InputStream from which the file list should be
382 * read.
383 * @return The list of file information contained in the given path. null
384 * if the list could not be obtained or if there are no files in
385 * the directory.
386 * @exception IOException If an I/O error occurs reading the listStream.
387 ***/
388 public FTPFile[] parseFileList(InputStream listStream) throws IOException
389 {
390 String line;
391 Vector results;
392 BufferedReader reader;
393 FTPFile entry;
394
395 reader = new BufferedReader(new InputStreamReader(listStream));
396
397 if ((line = reader.readLine()) == null)
398 {
399 results = null;
400 }
401 else
402 {
403 results = new Vector();
404
405 // This is to handle a line at the beginning of the listing
406 // that says "total xx" or "Gemstat xx" or something else.
407 if (line.toLowerCase().startsWith("total"))
408 line = reader.readLine();
409 else
410 {
411 if ((entry = parseFTPEntry(line)) != null)
412 results.addElement(entry);
413 line = reader.readLine();
414 }
415
416 while (line != null)
417 {
418 if (line.length() == 0 || (entry = parseFTPEntry(line)) == null)
419 {
420 results = null;
421 break;
422 }
423 results.addElement(entry);
424 line = reader.readLine();
425 }
426 }
427
428 // Finish reading from stream just in case
429 if (line != null)
430 while ((line = reader.readLine()) != null)
431 ;
432
433 reader.close();
434
435 if (results != null)
436 {
437 FTPFile[] result;
438
439 result = new FTPFile[results.size()];
440 if (result.length > 0)
441 results.copyInto(result);
442 return result;
443 }
444
445 return null;
446 }
447
448 }
This page was automatically generated by Maven