/* * Copyright 1999-2006 University of Chicago * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.dcache.ftp.client; import org.dcache.ftp.client.exception.FTPException; import java.util.StringTokenizer; /** * Represents the properties of a remote file * such as size, name, modification date and time, etc. * Can represent a regular file as well as a directory * or a soft link. */ public class FileInfo { public static final byte UNKNOWN_TYPE = 0; public static final byte FILE_TYPE = 1; public static final byte DIRECTORY_TYPE = 2; public static final byte SOFTLINK_TYPE = 3; public static final byte DEVICE_TYPE = 4; public static final String UNKNOWN_STRING = "?"; public static final int UNKNOWN_NUMBER = -1; private long size = UNKNOWN_NUMBER; private String name = UNKNOWN_STRING; private String date = UNKNOWN_STRING; private String time = UNKNOWN_STRING; private byte fileType; private int mode = 0; /** * Used internally by the FTPClient. */ public FileInfo() { } /** * Parses the file information from one line of response to * the FTP LIST command. Note: There is no commonly accepted * standard for the format of LIST response. * This parsing method only accepts * the most common Unix file listing formats: * System V or Berkeley (BSD) 'ls -l' * * @param unixListReply a single line from ls -l command * @see #parseUnixListReply(String reply) */ public FileInfo(String unixListReply) throws FTPException { parseUnixListReply(unixListReply); } /** * Given a line of reply received as the result of "LIST" command, * this method will set all the attributes(name,size,time,date and file type) * of the named file. This method requires the reply to be in * FTP server format, corresponding to either Unix System V or * Berkeley (BSD) output of 'ls -l'. For example, * <pre>drwxr-xr-x 2 guest other 1536 Jan 31 15:15 run.bat</pre> * or * <pre>-rw-rw-r-- 1 globus 117579 Nov 29 13:24 AdGriP.pdf</pre> * If the entry corresponds to a device file, only the file type * will be set and the other parameters will be set to UNKNOWN. * * @param reply reply of FTP server for "dir" command. * @throws FTPException if unable to parse the reply */ //protected void parseUnixListReply(String reply) public void parseUnixListReply(String reply) throws FTPException { if (reply == null) return; StringTokenizer tokens = new StringTokenizer(reply); String token, previousToken; int numTokens = tokens.countTokens(); if (numTokens < 8) { throw new FTPException(FTPException.UNSPECIFIED, "Invalid number of tokens in the list reply [" + reply + "]"); } token = tokens.nextToken(); // permissions switch (token.charAt(0)) { case 'd': setFileType(DIRECTORY_TYPE); break; case '-': setFileType(FILE_TYPE); break; case 'l': setFileType(SOFTLINK_TYPE); break; case 'c': case 'b': // do not try to parse device entries; // they aren't important anyway setFileType(DEVICE_TYPE); return; default: setFileType(UNKNOWN_TYPE); } try { for (int i = 1; i <= 9; i++) { if (token.charAt(i) != '-') { mode += 1 << (9 - i); } } } catch (IndexOutOfBoundsException e) { throw new FTPException(FTPException.UNSPECIFIED, "Could not parse access permission bits"); } // ??? can ignore tokens.nextToken(); // next token is the owner tokens.nextToken(); // In ls from System V, next token is the group // In ls from Berkeley (BSD), group token is missing previousToken = tokens.nextToken(); // size token = tokens.nextToken(); /* * if the group is missing this will try to parse the date field * as an integer and will fail. if so, then the previous field is the size field * and the current token is part of the date. */ try { setSize(Long.parseLong(token)); token = null; } catch (NumberFormatException e) { // this might mean that the group is missing // and this token is part of date. try { setSize(Long.parseLong(previousToken)); } catch (NumberFormatException ee) { throw new FTPException(FTPException.UNSPECIFIED, "Invalid size number in the ftp reply [" + previousToken + ", " + token + "]"); } } // date - two fields together if (token == null) { token = tokens.nextToken(); } String month = token; setDate(token + " " + tokens.nextToken()); //next token is either date or time token = tokens.nextToken(); this.setTime(token); // this is to handle spaces in the filenames // as well filenames with dates in them int ps = reply.indexOf(month); if (ps == -1) { // this should never happen throw new FTPException(FTPException.UNSPECIFIED, "Could not find date token"); } else { ps = reply.indexOf(this.time, ps + month.length()); if (ps == -1) { // this should never happen throw new FTPException(FTPException.UNSPECIFIED, "Could not find time token"); } else { this.setName(reply.substring(1 + ps + this.time.length())); } } } // -------------------------------- /** * Sets the file size. * * @param size size of the file */ public void setSize(long size) { this.size = size; } /** * Sets the file name. * * @param name name of the file. */ public void setName(String name) { this.name = name; } /** * Sets the file date. * * @param date date of the file. */ public void setDate(String date) { this.date = date; } /** * Sets modification time of the file. * * @param time time of the file. */ public void setTime(String time) { this.time = time; } /** * Sets the file type. * * @param type one of the file types, * e.g. FILE_TYPE, DIRECTORY_TYPE */ public void setFileType(byte type) { this.fileType = type; } // --------------------------------- /** * Returns size of the file. * * @return size of the file in bytes */ public long getSize() { return size; } /** * Returns name of the file. * * @return name of the file. */ public String getName() { return name; } /** * Returns date of the file. * * @return date of the file. */ public String getDate() { return date; } /** * Returns modification time of the file. * * @return time of the file. */ public String getTime() { return time; } /** * Tests if this file is a file. * * @return true if this represents a file, * otherwise, false. */ public boolean isFile() { return (fileType == FILE_TYPE); } /** * Tests if this file is a directory. * * @return true if this reprensets a directory, * otherwise, false. */ public boolean isDirectory() { return (fileType == DIRECTORY_TYPE); } /** * Tests if this file is a softlink. * * @return true if this reprensets a softlink, * otherwise, false. */ public boolean isSoftLink() { return (fileType == SOFTLINK_TYPE); } /** * Tests if this file is a device. */ public boolean isDevice() { return (fileType == DEVICE_TYPE); } public int getMode() { return mode; } public String getModeAsString() { StringBuilder modeStr = new StringBuilder(); for (int j = 2; j >= 0; j--) { int oct = 0; for (int i = 2; i >= 0; i--) { if ((mode & (1 << j * 3 + i)) != 0) { oct += (int) Math.pow(2, i); } } modeStr.append(String.valueOf(oct)); } return modeStr.toString(); } public boolean userCanRead() { return ((mode & (1 << 8)) != 0); } public boolean userCanWrite() { return ((mode & (1 << 7)) != 0); } public boolean userCanExecute() { return ((mode & (1 << 6)) != 0); } public boolean groupCanRead() { return ((mode & (1 << 5)) != 0); } public boolean groupCanWrite() { return ((mode & (1 << 4)) != 0); } public boolean groupCanExecute() { return ((mode & (1 << 3)) != 0); } public boolean allCanRead() { return ((mode & (1 << 2)) != 0); } public boolean allCanWrite() { return ((mode & (1 << 1)) != 0); } public boolean allCanExecute() { return ((mode & (1 << 0)) != 0); } // -------------------------------- public String toString() { StringBuilder buf = new StringBuilder(); buf.append("FileInfo: "); buf.append(getName()).append(" "); buf.append(getSize()).append(" "); buf.append(getDate()).append(" "); buf.append(getTime()).append(" "); switch (fileType) { case DIRECTORY_TYPE: buf.append("directory"); break; case FILE_TYPE: buf.append("file"); break; case SOFTLINK_TYPE: buf.append("softlink"); break; default: buf.append("unknown type"); } buf.append(" ").append(getModeAsString()); return buf.toString(); } }