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 1299238 2012-03-10 17:12:28Z 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    }