001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.net.ftp; 019 020import java.io.Serializable; 021import java.util.Calendar; 022import java.util.Date; 023import java.util.Formatter; 024import java.util.TimeZone; 025 026/** 027 * The FTPFile class is used to represent information about files stored on an FTP server. 028 * 029 * @see FTPFileEntryParser 030 * @see FTPClient#listFiles 031 */ 032public class FTPFile implements Serializable { 033 034 private static final long serialVersionUID = 9010790363003271996L; 035 036 /** A constant indicating an FTPFile is a file. */ 037 public static final int FILE_TYPE = 0; 038 039 /** A constant indicating an FTPFile is a directory. */ 040 public static final int DIRECTORY_TYPE = 1; 041 042 /** A constant indicating an FTPFile is a symbolic link. */ 043 public static final int SYMBOLIC_LINK_TYPE = 2; 044 045 /** A constant indicating an FTPFile is of unknown type. */ 046 public static final int UNKNOWN_TYPE = 3; 047 048 /** A constant indicating user access permissions. */ 049 public static final int USER_ACCESS = 0; 050 051 /** A constant indicating group access permissions. */ 052 public static final int GROUP_ACCESS = 1; 053 054 /** A constant indicating world access permissions. */ 055 public static final int WORLD_ACCESS = 2; 056 057 /** A constant indicating file/directory read permission. */ 058 public static final int READ_PERMISSION = 0; 059 060 /** A constant indicating file/directory write permission. */ 061 public static final int WRITE_PERMISSION = 1; 062 /** 063 * A constant indicating file execute permission or directory listing permission. 064 */ 065 public static final int EXECUTE_PERMISSION = 2; 066 067 private int type = UNKNOWN_TYPE; 068 069 /** 0 is invalid as a link count. */ 070 private int hardLinkCount; 071 072 /** 0 is valid, so use -1. */ 073 private long size = -1; 074 private String rawListing; 075 private String user = ""; 076 private String group = ""; 077 private String name; 078 private String link; 079 private Calendar date; 080 081 /** If this is null, then list entry parsing failed. */ 082 private final boolean[] permissions[]; // e.g. _permissions[USER_ACCESS][READ_PERMISSION] 083 084 /** Creates an empty FTPFile. */ 085 public FTPFile() { 086 permissions = new boolean[3][3]; 087 } 088 089 /** 090 * Constructor for use by {@link FTPListParseEngine} only. Used to create FTPFile entries for failed parses 091 * 092 * @param rawListing line that could not be parsed. 093 * @since 3.4 094 */ 095 FTPFile(final String rawListing) { 096 this.permissions = null; // flag that entry is invalid 097 this.rawListing = rawListing; 098 } 099 100 private char formatType() { 101 switch (type) { 102 case FILE_TYPE: 103 return '-'; 104 case DIRECTORY_TYPE: 105 return 'd'; 106 case SYMBOLIC_LINK_TYPE: 107 return 'l'; 108 default: 109 return '?'; 110 } 111 } 112 113 /** 114 * Gets the name of the group owning the file. Sometimes this will be a string representation of the group 115 * number. 116 * 117 * @return The name of the group owning the file. 118 */ 119 public String getGroup() { 120 return group; 121 } 122 123 /** 124 * Gets the number of hard links to this file. This is not to be confused with symbolic links. 125 * 126 * @return The number of hard links to this file. 127 */ 128 public int getHardLinkCount() { 129 return hardLinkCount; 130 } 131 132 /** 133 * If the FTPFile is a symbolic link, this method returns the name of the file being pointed to by the symbolic 134 * link. Otherwise it returns null. 135 * 136 * @return The file pointed to by the symbolic link (null if the FTPFile is not a symbolic link). 137 */ 138 public String getLink() { 139 return link; 140 } 141 142 /** 143 * Gets the name of the file. 144 * 145 * @return The name of the file. 146 */ 147 public String getName() { 148 return name; 149 } 150 151 /** 152 * Gets the original FTP server raw listing used to initialize the FTPFile. 153 * 154 * @return The original FTP server raw listing used to initialize the FTPFile. 155 */ 156 public String getRawListing() { 157 return rawListing; 158 } 159 160 /** 161 * Gets the file size in bytes. 162 * 163 * @return The file size in bytes. 164 */ 165 public long getSize() { 166 return size; 167 } 168 169 /** 170 * Gets the file timestamp. This usually the last modification time. 171 * 172 * @return A Calendar instance representing the file timestamp. 173 */ 174 public Calendar getTimestamp() { 175 return date; 176 } 177 178 /** 179 * Gets the type of the file (one of the <code>_TYPE</code> constants), e.g., if it is a directory, a regular 180 * file, or a symbolic link. 181 * 182 * @return The type of the file. 183 */ 184 public int getType() { 185 return type; 186 } 187 188 /** 189 * Gets the name of the user owning the file. Sometimes this will be a string representation of the user number. 190 * 191 * @return The name of the user owning the file. 192 */ 193 public String getUser() { 194 return user; 195 } 196 197 /** 198 * Tests if the given access group (one of the <code> _ACCESS </code> constants) has the given access 199 * permission (one of the <code> _PERMISSION </code> constants) to the file. 200 * 201 * @param access The access group (one of the <code> _ACCESS </code> constants) 202 * @param permission The access permission (one of the <code> _PERMISSION </code> constants) 203 * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range 204 * @return true if {@link #isValid()} is {@code true &&} the associated permission is set; {@code false} otherwise. 205 */ 206 public boolean hasPermission(final int access, final int permission) { 207 if (permissions == null) { 208 return false; 209 } 210 return permissions[access][permission]; 211 } 212 213 /** 214 * Tests if the file is a directory. 215 * 216 * @return True if the file is of type <code>DIRECTORY_TYPE</code>, false if not. 217 */ 218 public boolean isDirectory() { 219 return type == DIRECTORY_TYPE; 220 } 221 222 /** 223 * Tests if the file is a regular file. 224 * 225 * @return True if the file is of type <code>FILE_TYPE</code>, false if not. 226 */ 227 public boolean isFile() { 228 return type == FILE_TYPE; 229 } 230 231 /** 232 * Tests if the file is a symbolic link. 233 * 234 * @return True if the file is of type <code>UNKNOWN_TYPE</code>, false if not. 235 */ 236 public boolean isSymbolicLink() { 237 return type == SYMBOLIC_LINK_TYPE; 238 } 239 240 /** 241 * Tests if the type of the file is unknown. 242 * 243 * @return True if the file is of type <code>UNKNOWN_TYPE</code>, false if not. 244 */ 245 public boolean isUnknown() { 246 return type == UNKNOWN_TYPE; 247 } 248 249 /** 250 * Tests whether an entry is valid or not. If the entry is invalid, only the {@link #getRawListing()} 251 * method will be useful. Other methods may fail. 252 * 253 * Used in conjunction with list parsing that preseverves entries that failed to parse. 254 * 255 * @see FTPClientConfig#setUnparseableEntries(boolean) 256 * @return true if the entry is valid 257 * @since 3.4 258 */ 259 public boolean isValid() { 260 return permissions != null; 261 } 262 263 private String permissionToString(final int access) { 264 final StringBuilder sb = new StringBuilder(); 265 if (hasPermission(access, READ_PERMISSION)) { 266 sb.append('r'); 267 } else { 268 sb.append('-'); 269 } 270 if (hasPermission(access, WRITE_PERMISSION)) { 271 sb.append('w'); 272 } else { 273 sb.append('-'); 274 } 275 if (hasPermission(access, EXECUTE_PERMISSION)) { 276 sb.append('x'); 277 } else { 278 sb.append('-'); 279 } 280 return sb.toString(); 281 } 282 283 /** 284 * Sets the name of the group owning the file. This may be a string representation of the group number. 285 * 286 * @param group The name of the group owning the file. 287 */ 288 public void setGroup(final String group) { 289 this.group = group; 290 } 291 292 /** 293 * Sets the number of hard links to this file. This is not to be confused with symbolic links. 294 * 295 * @param links The number of hard links to this file. 296 */ 297 public void setHardLinkCount(final int links) { 298 this.hardLinkCount = links; 299 } 300 301 /** 302 * If the FTPFile is a symbolic link, use this method to set the name of the file being pointed to by the symbolic 303 * link. 304 * 305 * @param link The file pointed to by the symbolic link. 306 */ 307 public void setLink(final String link) { 308 this.link = link; 309 } 310 311 /** 312 * Sets the name of the file. 313 * 314 * @param name The name of the file. 315 */ 316 public void setName(final String name) { 317 this.name = name; 318 } 319 320 /** 321 * Sets if the given access group (one of the <code> _ACCESS </code> constants) has the given access permission (one 322 * of the <code> _PERMISSION </code> constants) to the file. 323 * 324 * @param access The access group (one of the <code> _ACCESS </code> constants) 325 * @param permission The access permission (one of the <code> _PERMISSION </code> constants) 326 * @param value True if permission is allowed, false if not. 327 * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range 328 */ 329 public void setPermission(final int access, final int permission, final boolean value) { 330 permissions[access][permission] = value; 331 } 332 333 /** 334 * Sets the original FTP server raw listing from which the FTPFile was created. 335 * 336 * @param rawListing The raw FTP server listing. 337 */ 338 public void setRawListing(final String rawListing) { 339 this.rawListing = rawListing; 340 } 341 342 /** 343 * Sets the file size in bytes. 344 * 345 * @param size The file size in bytes. 346 */ 347 public void setSize(final long size) { 348 this.size = size; 349 } 350 351 /** 352 * Sets the file timestamp. This usually the last modification time. The parameter is not cloned, so do not alter its 353 * value after calling this method. 354 * 355 * @param date A Calendar instance representing the file timestamp. 356 */ 357 public void setTimestamp(final Calendar date) { 358 this.date = date; 359 } 360 361 /** 362 * Sets the type of the file (<code>DIRECTORY_TYPE</code>, <code>FILE_TYPE</code>, etc.). 363 * 364 * @param type The integer code representing the type of the file. 365 */ 366 public void setType(final int type) { 367 this.type = type; 368 } 369 370 /** 371 * Sets the name of the user owning the file. This may be a string representation of the user number; 372 * 373 * @param user The name of the user owning the file. 374 */ 375 public void setUser(final String user) { 376 this.user = user; 377 } 378 379 /** 380 * Gets a string representation of the FTPFile information. This currently mimics the Unix listing format. This 381 * method uses the time zone of the Calendar entry, which is the server time zone (if one was provided) otherwise it 382 * is the local time zone. 383 * <p> 384 * Note: if the instance is not valid {@link #isValid()}, no useful information can be returned. In this case, use 385 * {@link #getRawListing()} instead. 386 * </p> 387 * 388 * @return A string representation of the FTPFile information. 389 * @since 3.0 390 */ 391 public String toFormattedString() { 392 return toFormattedString(null); 393 } 394 395 /** 396 * Gets a string representation of the FTPFile information. This currently mimics the Unix listing format. This 397 * method allows the Calendar time zone to be overridden. 398 * <p> 399 * Note: if the instance is not valid {@link #isValid()}, no useful information can be returned. In this case, use 400 * {@link #getRawListing()} instead. 401 * </p> 402 * 403 * @param timezone the time zone to use for displaying the time stamp If {@code null}, then use the Calendar entry 404 * 405 * @return A string representation of the FTPFile information. 406 * @since 3.4 407 */ 408 public String toFormattedString(final String timezone) { 409 410 if (!isValid()) { 411 return "[Invalid: could not parse file entry]"; 412 } 413 final StringBuilder sb = new StringBuilder(); 414 try (final Formatter fmt = new Formatter(sb)) { 415 sb.append(formatType()); 416 sb.append(permissionToString(USER_ACCESS)); 417 sb.append(permissionToString(GROUP_ACCESS)); 418 sb.append(permissionToString(WORLD_ACCESS)); 419 fmt.format(" %4d", Integer.valueOf(getHardLinkCount())); 420 fmt.format(" %-8s %-8s", getUser(), getGroup()); 421 fmt.format(" %8d", Long.valueOf(getSize())); 422 Calendar timestamp = getTimestamp(); 423 if (timestamp != null) { 424 if (timezone != null) { 425 final TimeZone newZone = TimeZone.getTimeZone(timezone); 426 if (!newZone.equals(timestamp.getTimeZone())) { 427 final Date original = timestamp.getTime(); 428 final Calendar newStamp = Calendar.getInstance(newZone); 429 newStamp.setTime(original); 430 timestamp = newStamp; 431 } 432 } 433 fmt.format(" %1$tY-%1$tm-%1$td", timestamp); 434 // Only display time units if they are present 435 if (timestamp.isSet(Calendar.HOUR_OF_DAY)) { 436 fmt.format(" %1$tH", timestamp); 437 if (timestamp.isSet(Calendar.MINUTE)) { 438 fmt.format(":%1$tM", timestamp); 439 if (timestamp.isSet(Calendar.SECOND)) { 440 fmt.format(":%1$tS", timestamp); 441 if (timestamp.isSet(Calendar.MILLISECOND)) { 442 fmt.format(".%1$tL", timestamp); 443 } 444 } 445 } 446 fmt.format(" %1$tZ", timestamp); 447 } 448 } 449 sb.append(' '); 450 sb.append(getName()); 451 } 452 return sb.toString(); 453 } 454 455 /** 456 * Gets a string representation of the FTPFile information. 457 * 458 * @return A string representation of the FTPFile information. 459 */ 460 @Override 461 public String toString() { 462 return getRawListing(); 463 } 464}