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    package org.apache.camel.component.file.remote;
018    
019    import java.util.List;
020    
021    import com.jcraft.jsch.ChannelSftp;
022    import org.apache.camel.Processor;
023    import org.apache.camel.component.file.GenericFile;
024    import org.apache.camel.util.FileUtil;
025    import org.apache.camel.util.ObjectHelper;
026    
027    /**
028     * Secure FTP consumer
029     */
030    public class SftpConsumer extends RemoteFileConsumer<ChannelSftp.LsEntry> {
031    
032        private String endpointPath;
033    
034        public SftpConsumer(RemoteFileEndpoint<ChannelSftp.LsEntry> endpoint, Processor processor, RemoteFileOperations<ChannelSftp.LsEntry> operations) {
035            super(endpoint, processor, operations);
036            this.endpointPath = endpoint.getConfiguration().getDirectory();
037        }
038    
039        protected boolean pollDirectory(String fileName, List<GenericFile<ChannelSftp.LsEntry>> fileList) {
040            // must remember current dir so we stay in that directory after the poll
041            String currentDir = operations.getCurrentDirectory();
042    
043            // strip trailing slash
044            fileName = FileUtil.stripTrailingSeparator(fileName);
045    
046            boolean answer = doPollDirectory(fileName, null, fileList);
047    
048            operations.changeCurrentDirectory(currentDir);
049            return answer;
050        }
051    
052        protected boolean pollSubDirectory(String absolutePath, String dirName, List<GenericFile<ChannelSftp.LsEntry>> fileList) {
053            boolean answer = doPollDirectory(absolutePath, dirName, fileList);
054            // change back to parent directory when finished polling sub directory
055            operations.changeToParentDirectory();
056            return answer;
057        }
058    
059        protected boolean doPollDirectory(String absolutePath, String dirName, List<GenericFile<ChannelSftp.LsEntry>> fileList) {
060            if (log.isTraceEnabled()) {
061                log.trace("doPollDirectory from absolutePath: " + absolutePath + ", dirName: " + dirName);
062            }
063    
064            // remove trailing /
065            dirName = FileUtil.stripTrailingSeparator(dirName);
066            String dir = ObjectHelper.isNotEmpty(dirName) ? dirName : absolutePath;
067    
068            // change into directory (to ensure most FTP servers can list files)
069            operations.changeCurrentDirectory(dir);
070    
071            if (log.isTraceEnabled()) {
072                log.trace("Polling directory: " + dir);
073            }
074            List<ChannelSftp.LsEntry> files = operations.listFiles();
075            if (files == null || files.isEmpty()) {
076                // no files in this directory to poll
077                if (log.isTraceEnabled()) {
078                    log.trace("No files found in directory: " + dir);
079                }
080                return true;
081            } else {
082                // we found some files
083                if (log.isTraceEnabled()) {
084                    log.trace("Found " + files.size() + " in directory: " + dir);
085                }
086            }
087    
088            for (ChannelSftp.LsEntry file : files) {
089    
090                // check if we can continue polling in files
091                if (!canPollMoreFiles(fileList)) {
092                    return false;
093                }
094    
095                if (file.getAttrs().isDir()) {
096                    RemoteFile<ChannelSftp.LsEntry> remote = asRemoteFile(absolutePath, file);
097                    if (endpoint.isRecursive() && isValidFile(remote, true)) {
098                        // recursive scan and add the sub files and folders
099                        String subDirectory = file.getFilename();
100                        String path = absolutePath + "/" + subDirectory;
101                        boolean canPollMore = pollSubDirectory(path, subDirectory, fileList);
102                        if (!canPollMore) {
103                            return false;
104                        }
105                    }
106                    // we cannot use file.getAttrs().isLink on Windows, so we dont invoke the method
107                    // just assuming its a file we should poll
108                } else {
109                    RemoteFile<ChannelSftp.LsEntry> remote = asRemoteFile(absolutePath, file);
110                    if (isValidFile(remote, false)) {
111                        if (isInProgress(remote)) {
112                            if (log.isTraceEnabled()) {
113                                log.trace("Skipping as file is already in progress: " + remote.getFileName());
114                            }
115                        } else {
116                            // matched file so add
117                            fileList.add(remote);
118                        }
119                    }
120                }
121            }
122    
123            return true;
124        }
125    
126        private RemoteFile<ChannelSftp.LsEntry> asRemoteFile(String absolutePath, ChannelSftp.LsEntry file) {
127            RemoteFile<ChannelSftp.LsEntry> answer = new RemoteFile<ChannelSftp.LsEntry>();
128    
129            answer.setEndpointPath(endpointPath);
130            answer.setFile(file);
131            answer.setFileNameOnly(file.getFilename());
132            answer.setFileLength(file.getAttrs().getSize());
133            answer.setLastModified(file.getAttrs().getMTime() * 1000L);
134            answer.setHostname(((RemoteFileConfiguration) endpoint.getConfiguration()).getHost());
135    
136            // absolute or relative path
137            boolean absolute = FileUtil.hasLeadingSeparator(absolutePath);
138            answer.setAbsolute(absolute);
139    
140            // create a pseudo absolute name
141            String dir = FileUtil.stripTrailingSeparator(absolutePath);
142            String absoluteFileName = FileUtil.stripLeadingSeparator(dir + "/" + file.getFilename());
143            // if absolute start with a leading separator otherwise let it be relative
144            if (absolute) {
145                absoluteFileName = "/" + absoluteFileName;
146            }
147            answer.setAbsoluteFilePath(absoluteFileName);
148    
149            // the relative filename, skip the leading endpoint configured path
150            String relativePath = ObjectHelper.after(absoluteFileName, endpointPath);
151            // skip trailing /
152            relativePath = FileUtil.stripLeadingSeparator(relativePath);
153            answer.setRelativeFilePath(relativePath);
154    
155            // the file name should be the relative path
156            answer.setFileName(answer.getRelativeFilePath());
157    
158            return answer;
159        }
160    
161    }