FTPFile.java

  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. package org.apache.commons.net.ftp;

  18. import java.io.Serializable;
  19. import java.time.Instant;
  20. import java.util.Calendar;
  21. import java.util.Date;
  22. import java.util.Formatter;
  23. import java.util.TimeZone;

  24. /**
  25.  * The FTPFile class is used to represent information about files stored on an FTP server.
  26.  *
  27.  * @see FTPFileEntryParser
  28.  * @see FTPClient#listFiles
  29.  */
  30. public class FTPFile implements Serializable {

  31.     private static final long serialVersionUID = 9010790363003271996L;

  32.     /** A constant indicating an FTPFile is a file. */
  33.     public static final int FILE_TYPE = 0;

  34.     /** A constant indicating an FTPFile is a directory. */
  35.     public static final int DIRECTORY_TYPE = 1;

  36.     /** A constant indicating an FTPFile is a symbolic link. */
  37.     public static final int SYMBOLIC_LINK_TYPE = 2;

  38.     /** A constant indicating an FTPFile is of unknown type. */
  39.     public static final int UNKNOWN_TYPE = 3;

  40.     /** A constant indicating user access permissions. */
  41.     public static final int USER_ACCESS = 0;

  42.     /** A constant indicating group access permissions. */
  43.     public static final int GROUP_ACCESS = 1;

  44.     /** A constant indicating world access permissions. */
  45.     public static final int WORLD_ACCESS = 2;

  46.     /** A constant indicating file/directory read permission. */
  47.     public static final int READ_PERMISSION = 0;

  48.     /** A constant indicating file/directory write permission. */
  49.     public static final int WRITE_PERMISSION = 1;

  50.     /** A constant indicating file execute permission or directory listing permission. */
  51.     public static final int EXECUTE_PERMISSION = 2;

  52.     private int type = UNKNOWN_TYPE;

  53.     /** 0 is invalid as a link count. */
  54.     private int hardLinkCount;

  55.     /** 0 is valid, so use -1. */
  56.     private long size = -1;
  57.     private String rawListing;
  58.     private String user = "";
  59.     private String group = "";
  60.     private String name;
  61.     private String link;

  62.     // TODO Consider changing internal representation to java.time.
  63.     private Calendar calendar;

  64.     /** If this is null, then list entry parsing failed. */
  65.     private final boolean[][] permissions; // e.g. _permissions[USER_ACCESS][READ_PERMISSION]

  66.     /** Creates an empty FTPFile. */
  67.     public FTPFile() {
  68.         permissions = new boolean[3][3];
  69.     }

  70.     /**
  71.      * Constructor for use by {@link FTPListParseEngine} only. Used to create FTPFile entries for failed parses
  72.      *
  73.      * @param rawListing line that could not be parsed.
  74.      * @since 3.4
  75.      */
  76.     FTPFile(final String rawListing) {
  77.         this.permissions = null; // flag that entry is invalid
  78.         this.rawListing = rawListing;
  79.     }

  80.     private char formatType() {
  81.         switch (type) {
  82.         case FILE_TYPE:
  83.             return '-';
  84.         case DIRECTORY_TYPE:
  85.             return 'd';
  86.         case SYMBOLIC_LINK_TYPE:
  87.             return 'l';
  88.         default:
  89.             return '?';
  90.         }
  91.     }

  92.     /**
  93.      * Gets the name of the group owning the file. Sometimes this will be a string representation of the group number.
  94.      *
  95.      * @return The name of the group owning the file.
  96.      */
  97.     public String getGroup() {
  98.         return group;
  99.     }

  100.     /**
  101.      * Gets the number of hard links to this file. This is not to be confused with symbolic links.
  102.      *
  103.      * @return The number of hard links to this file.
  104.      */
  105.     public int getHardLinkCount() {
  106.         return hardLinkCount;
  107.     }

  108.     /**
  109.      * If the FTPFile is a symbolic link, this method returns the name of the file being pointed to by the symbolic link.
  110.      * Otherwise, it returns {@code null}.
  111.      *
  112.      * @return The file pointed to by the symbolic link ({@code null} if the FTPFile is not a symbolic link).
  113.      */
  114.     public String getLink() {
  115.         return link;
  116.     }

  117.     /**
  118.      * Gets the name of the file.
  119.      *
  120.      * @return The name of the file.
  121.      */
  122.     public String getName() {
  123.         return name;
  124.     }

  125.     /**
  126.      * Gets the original FTP server raw listing used to initialize the FTPFile.
  127.      *
  128.      * @return The original FTP server raw listing used to initialize the FTPFile.
  129.      */
  130.     public String getRawListing() {
  131.         return rawListing;
  132.     }

  133.     /**
  134.      * Gets the file size in bytes.
  135.      *
  136.      * @return The file size in bytes.
  137.      */
  138.     public long getSize() {
  139.         return size;
  140.     }

  141.     /**
  142.      * Gets the file timestamp. This usually the last modification time.
  143.      *
  144.      * @return A Calendar instance representing the file timestamp.
  145.      */
  146.     public Calendar getTimestamp() {
  147.         return calendar;
  148.     }

  149.     /**
  150.      * Gets the file timestamp. This usually the last modification time.
  151.      *
  152.      * @return A Calendar instance representing the file timestamp.
  153.      * @since 3.9.0
  154.      */
  155.     public Instant getTimestampInstant() {
  156.         return calendar == null ? null : calendar.toInstant();
  157.     }

  158.     /**
  159.      * Gets the type of the file (one of the {@code _TYPE} constants), e.g., if it is a directory, a regular file, or a symbolic link.
  160.      *
  161.      * @return The type of the file.
  162.      */
  163.     public int getType() {
  164.         return type;
  165.     }

  166.     /**
  167.      * Gets the name of the user owning the file. Sometimes this will be a string representation of the user number.
  168.      *
  169.      * @return The name of the user owning the file.
  170.      */
  171.     public String getUser() {
  172.         return user;
  173.     }

  174.     /**
  175.      * Tests if the given access group (one of the {@code _ACCESS} constants) has the given access permission (one of the {@code _PERMISSION}
  176.      * constants) to the file.
  177.      *
  178.      * @param access     The access group (one of the {@code _ACCESS} constants)
  179.      * @param permission The access permission (one of the {@code _PERMISSION} constants)
  180.      * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range
  181.      * @return {@code true} if {@link #isValid()} is {@code true} and the associated permission is set; {@code false} otherwise.
  182.      */
  183.     public boolean hasPermission(final int access, final int permission) {
  184.         if (permissions == null) {
  185.             return false;
  186.         }
  187.         return permissions[access][permission];
  188.     }

  189.     /**
  190.      * Tests if the file is a directory.
  191.      *
  192.      * @return {@code true} if the file is of type {@code DIRECTORY_TYPE}, {@code false} if not.
  193.      */
  194.     public boolean isDirectory() {
  195.         return type == DIRECTORY_TYPE;
  196.     }

  197.     /**
  198.      * Tests if the file is a regular file.
  199.      *
  200.      * @return {@code true} if the file is of type {@code FILE_TYPE}, {@code false} if not.
  201.      */
  202.     public boolean isFile() {
  203.         return type == FILE_TYPE;
  204.     }

  205.     /**
  206.      * Tests if the file is a symbolic link.
  207.      *
  208.      * @return {@code true} if the file is of type {@code SYMBOLIC_LINK_TYPE}, {@code false} if not.
  209.      */
  210.     public boolean isSymbolicLink() {
  211.         return type == SYMBOLIC_LINK_TYPE;
  212.     }

  213.     /**
  214.      * Tests if the type of the file is unknown.
  215.      *
  216.      * @return {@code true} if the file is of type {@code UNKNOWN_TYPE}, {@code false} if not.
  217.      */
  218.     public boolean isUnknown() {
  219.         return type == UNKNOWN_TYPE;
  220.     }

  221.     /**
  222.      * Tests whether an entry is valid or not. If the entry is invalid, only the {@link #getRawListing()} method will be useful. Other methods may fail.
  223.      *
  224.      * Used in conjunction with list parsing that preserves entries that failed to parse.
  225.      *
  226.      * @see FTPClientConfig#setUnparseableEntries(boolean)
  227.      * @return {@code true} if the entry is valid; {@code false} otherwise
  228.      * @since 3.4
  229.      */
  230.     public boolean isValid() {
  231.         return permissions != null;
  232.     }

  233.     private String permissionToString(final int access) {
  234.         final StringBuilder sb = new StringBuilder();
  235.         if (hasPermission(access, READ_PERMISSION)) {
  236.             sb.append('r');
  237.         } else {
  238.             sb.append('-');
  239.         }
  240.         if (hasPermission(access, WRITE_PERMISSION)) {
  241.             sb.append('w');
  242.         } else {
  243.             sb.append('-');
  244.         }
  245.         if (hasPermission(access, EXECUTE_PERMISSION)) {
  246.             sb.append('x');
  247.         } else {
  248.             sb.append('-');
  249.         }
  250.         return sb.toString();
  251.     }

  252.     private void readObject(final java.io.ObjectInputStream in) {
  253.         throw new UnsupportedOperationException("Serialization is not supported");
  254.     }

  255.     /**
  256.      * Sets the name of the group owning the file. This may be a string representation of the group number.
  257.      *
  258.      * @param group The name of the group owning the file.
  259.      */
  260.     public void setGroup(final String group) {
  261.         this.group = group;
  262.     }

  263.     /**
  264.      * Sets the number of hard links to this file. This is not to be confused with symbolic links.
  265.      *
  266.      * @param links The number of hard links to this file.
  267.      */
  268.     public void setHardLinkCount(final int links) {
  269.         this.hardLinkCount = links;
  270.     }

  271.     /**
  272.      * If the FTPFile is a symbolic link, use this method to set the name of the file being pointed to by the symbolic link.
  273.      *
  274.      * @param link The file pointed to by the symbolic link.
  275.      */
  276.     public void setLink(final String link) {
  277.         this.link = link;
  278.     }

  279.     /**
  280.      * Sets the name of the file.
  281.      *
  282.      * @param name The name of the file.
  283.      */
  284.     public void setName(final String name) {
  285.         this.name = name;
  286.     }

  287.     /**
  288.      * Sets if the given access group (one of the {@code _ACCESS} constants) has the given access permission (one of the {@code _PERMISSION}
  289.      * constants) to the file.
  290.      *
  291.      * @param access     The access group (one of the {@code _ACCESS} constants)
  292.      * @param permission The access permission (one of the {@code _PERMISSION} constants)
  293.      * @param value      {@code true} if permission is allowed, {@code false} if not.
  294.      * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range
  295.      */
  296.     public void setPermission(final int access, final int permission, final boolean value) {
  297.         // TODO: only allow permission setting if file is valid
  298.         permissions[access][permission] = value;
  299.     }

  300.     /**
  301.      * Sets the original FTP server raw listing from which the FTPFile was created.
  302.      *
  303.      * @param rawListing The raw FTP server listing.
  304.      */
  305.     public void setRawListing(final String rawListing) {
  306.         this.rawListing = rawListing;
  307.     }

  308.     /**
  309.      * Sets the file size in bytes.
  310.      *
  311.      * @param size The file size in bytes.
  312.      */
  313.     public void setSize(final long size) {
  314.         this.size = size;
  315.     }

  316.     /**
  317.      * Sets the file timestamp. This usually the last modification time. The parameter is not cloned, so do not alter its value after calling this method.
  318.      *
  319.      * @param date A Calendar instance representing the file timestamp.
  320.      */
  321.     public void setTimestamp(final Calendar date) {
  322.         this.calendar = date;
  323.     }

  324.     /**
  325.      * Sets the type of the file ({@code DIRECTORY_TYPE}, {@code FILE_TYPE}, etc.).
  326.      *
  327.      * @param type The integer code representing the type of the file.
  328.      */
  329.     public void setType(final int type) {
  330.         this.type = type;
  331.     }

  332.     /**
  333.      * Sets the name of the user owning the file. This may be a string representation of the user number;
  334.      *
  335.      * @param user The name of the user owning the file.
  336.      */
  337.     public void setUser(final String user) {
  338.         this.user = user;
  339.     }

  340.     /**
  341.      * Gets a string representation of the FTPFile information. This currently mimics the UNIX listing format. This method uses the time zone of the Calendar
  342.      * entry, which is the server time zone (if one was provided) otherwise it is the local time zone.
  343.      * <p>
  344.      * Note: if the instance is not valid {@link #isValid()}, no useful information can be returned. In this case, use {@link #getRawListing()} instead.
  345.      * </p>
  346.      *
  347.      * @return A string representation of the FTPFile information.
  348.      * @since 3.0
  349.      */
  350.     public String toFormattedString() {
  351.         return toFormattedString(null);
  352.     }

  353.     /**
  354.      * Gets a string representation of the FTPFile information. This currently mimics the UNIX listing format. This method allows the Calendar time zone to be
  355.      * overridden.
  356.      * <p>
  357.      * Note: if the instance is not valid {@link #isValid()}, no useful information can be returned. In this case, use {@link #getRawListing()} instead.
  358.      * </p>
  359.      *
  360.      * @param timezone the time zone to use for displaying the time stamp If {@code null}, then use the Calendar ({@link #getTimestamp()}) entry
  361.      * @return A string representation of the FTPFile information.
  362.      * @since 3.4
  363.      */
  364.     public String toFormattedString(final String timezone) {

  365.         if (!isValid()) {
  366.             return "[Invalid: could not parse file entry]";
  367.         }
  368.         final StringBuilder sb = new StringBuilder();
  369.         try (final Formatter fmt = new Formatter(sb)) {
  370.             sb.append(formatType());
  371.             sb.append(permissionToString(USER_ACCESS));
  372.             sb.append(permissionToString(GROUP_ACCESS));
  373.             sb.append(permissionToString(WORLD_ACCESS));
  374.             fmt.format(" %4d", Integer.valueOf(getHardLinkCount()));
  375.             fmt.format(" %-8s %-8s", getUser(), getGroup());
  376.             fmt.format(" %8d", Long.valueOf(getSize()));
  377.             Calendar timestamp = getTimestamp();
  378.             if (timestamp != null) {
  379.                 if (timezone != null) {
  380.                     final TimeZone newZone = TimeZone.getTimeZone(timezone);
  381.                     if (!newZone.equals(timestamp.getTimeZone())) {
  382.                         final Date original = timestamp.getTime();
  383.                         final Calendar newStamp = Calendar.getInstance(newZone);
  384.                         newStamp.setTime(original);
  385.                         timestamp = newStamp;
  386.                     }
  387.                 }
  388.                 fmt.format(" %1$tY-%1$tm-%1$td", timestamp);
  389.                 // Only display time units if they are present
  390.                 if (timestamp.isSet(Calendar.HOUR_OF_DAY)) {
  391.                     fmt.format(" %1$tH", timestamp);
  392.                     if (timestamp.isSet(Calendar.MINUTE)) {
  393.                         fmt.format(":%1$tM", timestamp);
  394.                         if (timestamp.isSet(Calendar.SECOND)) {
  395.                             fmt.format(":%1$tS", timestamp);
  396.                             if (timestamp.isSet(Calendar.MILLISECOND)) {
  397.                                 fmt.format(".%1$tL", timestamp);
  398.                             }
  399.                         }
  400.                     }
  401.                     fmt.format(" %1$tZ", timestamp);
  402.                 }
  403.             }
  404.             sb.append(' ');
  405.             sb.append(getName());
  406.         }
  407.         return sb.toString();
  408.     }

  409.     /*
  410.      * Serialization is unnecessary for this class. Reject attempts to do so until such time as the Serializable attribute can be dropped.
  411.      */

  412.     /**
  413.      * Gets a string representation of the FTPFile information.
  414.      * Delegates to {@link #getRawListing()}
  415.      *
  416.      * @see #getRawListing()
  417.      * @return A string representation of the FTPFile information.
  418.      */
  419.     @Override
  420.     public String toString() {
  421.         return getRawListing();
  422.     }

  423.     private void writeObject(final java.io.ObjectOutputStream out) {
  424.         throw new UnsupportedOperationException("Serialization is not supported");
  425.     }

  426. }