    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
    018    package org.apache.commons.net.ftp.parser;
    019    import java.text.ParseException;
    020
    021    import org.apache.commons.net.ftp.FTPClientConfig;
    022    import org.apache.commons.net.ftp.FTPFile;
    023
    024    /**
    025     * Implementation FTPFileEntryParser and FTPFileListParser for pre MacOS-X Systems.
    026     *
    027     * @version $Id: MacOsPeterFTPEntryParser.java 1243400 2012-02-13 03:02:29Z sebb $
    028     * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
    029     * @since 3.1
    030     */
    031    public class MacOsPeterFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
    032    {
    033
    034        static final String DEFAULT_DATE_FORMAT
    035            = "MMM d yyyy"; //Nov 9 2001
    036
    037        static final String DEFAULT_RECENT_DATE_FORMAT
    038            = "MMM d HH:mm"; //Nov 9 20:06
    039
    040        /**
    041         * this is the regular expression used by this parser.
    042         *
    043         * Permissions:
    044         *    r   the file is readable
    045         *    w   the file is writable
    046         *    x   the file is executable
    047         *    -   the indicated permission is not granted
    048         *    L   mandatory locking occurs during access (the set-group-ID bit is
    049         *        on and the group execution bit is off)
    050         *    s   the set-user-ID or set-group-ID bit is on, and the corresponding
    051         *        user or group execution bit is also on
    052         *    S   undefined bit-state (the set-user-ID bit is on and the user
    053         *        execution bit is off)
    054         *    t   the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
    055         *        execution is on
    056         *    T   the 1000 bit is turned on, and execution is off (undefined bit-
    057         *        state)
    058         *    e   z/OS external link bit
    059         */
    060        private static final String REGEX =
    061            "([bcdelfmpSs-])" // type (1)
    062            + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+" // permission
    063            + "("
    064            +   "(folder\\s+)"
    065            + "|"
    066            +   "((\\d+)\\s+(\\d+)\\s+)" // resource size & data size
    067            + ")"
    068            + "(\\d+)\\s+"              // size
    069            /*
    070             * numeric or standard format date:
    071             *   yyyy-mm-dd (expecting hh:mm to follow)
    072             *   MMM [d]d
    073             *   [d]d MMM
    074             *   N.B. use non-space for MMM to allow for languages such as German which use
    075             *   diacritics (e.g. umlaut) in some abbreviations.
    076            */
    077            + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3}))\\s+"
    078            /*
    079               year (for non-recent standard format) - yyyy
    080               or time (for numeric or recent standard format) [h]h:mm
    081            */
    082            + "(\\d+(?::\\d+)?)\\s+"
    083
    084            + "(\\S*)(\\s*.*)"; // the rest
    085
    086
    087        /**
    088         * The default constructor for a UnixFTPEntryParser object.
    089         *
    090         * @exception IllegalArgumentException
    091         * Thrown if the regular expression is unparseable.  Should not be seen
    092         * under normal conditions.  It it is seen, this is a sign that
    093         * <code>REGEX</code> is  not a valid regular expression.
    094         */
    095        public MacOsPeterFTPEntryParser()
    096        {
    097            this(null);
    098        }
    099
    100        /**
    101         * This constructor allows the creation of a UnixFTPEntryParser object with
    102         * something other than the default configuration.
    103         *
    104         * @param config The {@link FTPClientConfig configuration} object used to
    105         * configure this parser.
    106         * @exception IllegalArgumentException
    107         * Thrown if the regular expression is unparseable.  Should not be seen
    108         * under normal conditions.  It it is seen, this is a sign that
    109         * <code>REGEX</code> is  not a valid regular expression.
    110         * @since 1.4
    111         */
    112        public MacOsPeterFTPEntryParser(FTPClientConfig config)
    113        {
    114            super(REGEX);
    115            configure(config);
    116        }
    117
    118        /**
    119         * Parses a line of a unix (standard) FTP server file listing and converts
    120         * it into a usable format in the form of an <code> FTPFile </code>
    121         * instance.  If the file listing line doesn't describe a file,
    122         * <code> null </code> is returned, otherwise a <code> FTPFile </code>
    123         * instance representing the files in the directory is returned.
    124         * <p>
    125         * @param entry A line of text from the file listing
    126         * @return An FTPFile instance corresponding to the supplied entry
    127         */
    128        public FTPFile parseFTPEntry(String entry) {
    129            FTPFile file = new FTPFile();
    130            file.setRawListing(entry);
    131            int type;
    132            boolean isDevice = false;
    133
    134            if (matches(entry))
    135            {
    136                String typeStr = group(1);
    137                String hardLinkCount = "0";
    138                String usr = null;
    139                String grp = null;
    140                String filesize = group(20);
    141                String datestr = group(21) + " " + group(22);
    142                String name = group(23);
    143                String endtoken = group(24);
    144
    145                try
    146                {
    147                    file.setTimestamp(super.parseTimestamp(datestr));
    148                }
    149                catch (ParseException e)
    150                {
    151                     // intentionally do nothing
    152                }
    153
    154                // A 'whiteout' file is an ARTIFICIAL entry in any of several types of
    155                // 'translucent' filesystems, of which a 'union' filesystem is one.
    156
    157                // bcdelfmpSs-
    158                switch (typeStr.charAt(0))
    159                {
    160                case 'd':
    161                    type = FTPFile.DIRECTORY_TYPE;
    162                    break;
    163                case 'e': // NET-39 => z/OS external link
    164                    type = FTPFile.SYMBOLIC_LINK_TYPE;
    165                    break;
    166                case 'l':
    167                    type = FTPFile.SYMBOLIC_LINK_TYPE;
    168                    break;
    169                case 'b':
    170                case 'c':
    171                    isDevice = true;
    172                    type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented
    173                    break;
    174                case 'f':
    175                case '-':
    176                    type = FTPFile.FILE_TYPE;
    177                    break;
    178                default: // e.g. ? and w = whiteout
    179                    type = FTPFile.UNKNOWN_TYPE;
    180                }
    181
    182                file.setType(type);
    183
    184                int g = 4;
    185                for (int access = 0; access < 3; access++, g += 4)
    186                {
    187                    // Use != '-' to avoid having to check for suid and sticky bits
    188                    file.setPermission(access, FTPFile.READ_PERMISSION,
    189                                       (!group(g).equals("-")));
    190                    file.setPermission(access, FTPFile.WRITE_PERMISSION,
    191                                       (!group(g + 1).equals("-")));
    192
    193                    String execPerm = group(g + 2);
    194                    if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0)))
    195                    {
    196                        file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true);
    197                    }
    198                    else
    199                    {
    200                        file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false);
    201                    }
    202                }
    203
    204                if (!isDevice)
    205                {
    206                    try
    207                    {
    208                        file.setHardLinkCount(Integer.parseInt(hardLinkCount));
    209                    }
    210                    catch (NumberFormatException e)
    211                    {
    212                        // intentionally do nothing
    213                    }
    214                }
    215
    216                file.setUser(usr);
    217                file.setGroup(grp);
    218
    219                try
    220                {
    221                    file.setSize(Long.parseLong(filesize));
    222                }
    223                catch (NumberFormatException e)
    224                {
    225                    // intentionally do nothing
    226                }
    227
    228                if (null == endtoken)
    229                {
    230                    file.setName(name);
    231                }
    232                else
    233                {
    234                    // oddball cases like symbolic links, file names
    235                    // with spaces in them.
    236                    name += endtoken;
    237                    if (type == FTPFile.SYMBOLIC_LINK_TYPE)
    238                    {
    239
    240                        int end = name.indexOf(" -> ");
    241                        // Give up if no link indicator is present
    242                        if (end == -1)
    243                        {
    244                            file.setName(name);
    245                        }
    246                        else
    247                        {
    248                            file.setName(name.substring(0, end));
    249                            file.setLink(name.substring(end + 4));
    250                        }
    251
    252                    }
    253                    else
    254                    {
    255                        file.setName(name);
    256                    }
    257                }
    258                return file;
    259            }
    260            return null;
    261        }
    262
    263        /**
    264         * Defines a default configuration to be used when this class is
    265         * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
    266         * parameter being specified.
    267         * @return the default configuration for this parser.
    268         */
    269        @Override
    270        protected FTPClientConfig getDefaultConfiguration() {
    271            return new FTPClientConfig(
    272                    FTPClientConfig.SYST_UNIX,
    273                    DEFAULT_DATE_FORMAT,
    274                    DEFAULT_RECENT_DATE_FORMAT,
    275                    null, null, null);
    276        }
    277
    278    }