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        @Override
040        protected boolean pollDirectory(String fileName, List<GenericFile<ChannelSftp.LsEntry>> fileList, int depth) {
041            String currentDir = null;
042            if (isStepwise()) {
043                // must remember current dir so we stay in that directory after the poll
044                currentDir = operations.getCurrentDirectory();
045            }
046    
047            // strip trailing slash
048            fileName = FileUtil.stripTrailingSeparator(fileName);
049    
050            boolean answer = doPollDirectory(fileName, null, fileList, depth);
051            if (currentDir != null) {
052                operations.changeCurrentDirectory(currentDir);
053            }
054    
055            return answer;
056        }
057    
058        protected boolean pollSubDirectory(String absolutePath, String dirName, List<GenericFile<ChannelSftp.LsEntry>> fileList, int depth) {
059            boolean answer = doPollDirectory(absolutePath, dirName, fileList, depth);
060            // change back to parent directory when finished polling sub directory
061            if (isStepwise()) {
062                operations.changeToParentDirectory();
063            }
064            return answer;
065        }
066    
067        protected boolean doPollDirectory(String absolutePath, String dirName, List<GenericFile<ChannelSftp.LsEntry>> fileList, int depth) {
068            log.trace("doPollDirectory from absolutePath: {}, dirName: {}", absolutePath, dirName);
069    
070            depth++;
071    
072            // remove trailing /
073            dirName = FileUtil.stripTrailingSeparator(dirName);
074            // compute dir depending on stepwise is enabled or not
075            String dir;
076            if (isStepwise()) {
077                dir = ObjectHelper.isNotEmpty(dirName) ? dirName : absolutePath;
078                operations.changeCurrentDirectory(dir);
079            } else {
080                dir = absolutePath;
081            }
082    
083            log.trace("Polling directory: {}", dir);
084            List<ChannelSftp.LsEntry> files;
085            if (isStepwise()) {
086                files = operations.listFiles();
087            } else {
088                files = operations.listFiles(dir);
089            }
090            if (files == null || files.isEmpty()) {
091                // no files in this directory to poll
092                log.trace("No files found in directory: {}", dir);
093                return true;
094            } else {
095                // we found some files
096                log.trace("Found {} in directory: {}", files.size(), dir);
097            }
098    
099            for (ChannelSftp.LsEntry file : files) {
100    
101                // check if we can continue polling in files
102                if (!canPollMoreFiles(fileList)) {
103                    return false;
104                }
105    
106                if (file.getAttrs().isDir()) {
107                    RemoteFile<ChannelSftp.LsEntry> remote = asRemoteFile(absolutePath, file);
108                    if (endpoint.isRecursive() && isValidFile(remote, true) && depth < endpoint.getMaxDepth()) {
109                        // recursive scan and add the sub files and folders
110                        String subDirectory = file.getFilename();
111                        String path = absolutePath + "/" + subDirectory;
112                        boolean canPollMore = pollSubDirectory(path, subDirectory, fileList, depth);
113                        if (!canPollMore) {
114                            return false;
115                        }
116                    }
117                    // we cannot use file.getAttrs().isLink on Windows, so we dont invoke the method
118                    // just assuming its a file we should poll
119                } else {
120                    RemoteFile<ChannelSftp.LsEntry> remote = asRemoteFile(absolutePath, file);
121                    if (isValidFile(remote, false) && depth >= endpoint.getMinDepth()) {
122                        if (isInProgress(remote)) {
123                            if (log.isTraceEnabled()) {
124                                log.trace("Skipping as file is already in progress: {}", remote.getFileName());
125                            }
126                        } else {
127                            // matched file so add
128                            fileList.add(remote);
129                        }
130                    }
131                }
132            }
133    
134            return true;
135        }
136    
137        private RemoteFile<ChannelSftp.LsEntry> asRemoteFile(String absolutePath, ChannelSftp.LsEntry file) {
138            RemoteFile<ChannelSftp.LsEntry> answer = new RemoteFile<ChannelSftp.LsEntry>();
139    
140            answer.setEndpointPath(endpointPath);
141            answer.setFile(file);
142            answer.setFileNameOnly(file.getFilename());
143            answer.setFileLength(file.getAttrs().getSize());
144            answer.setLastModified(file.getAttrs().getMTime() * 1000L);
145            answer.setHostname(((RemoteFileConfiguration) endpoint.getConfiguration()).getHost());
146    
147            // absolute or relative path
148            boolean absolute = FileUtil.hasLeadingSeparator(absolutePath);
149            answer.setAbsolute(absolute);
150    
151            // create a pseudo absolute name
152            String dir = FileUtil.stripTrailingSeparator(absolutePath);
153            String absoluteFileName = FileUtil.stripLeadingSeparator(dir + "/" + file.getFilename());
154            // if absolute start with a leading separator otherwise let it be relative
155            if (absolute) {
156                absoluteFileName = "/" + absoluteFileName;
157            }
158            answer.setAbsoluteFilePath(absoluteFileName);
159    
160            // the relative filename, skip the leading endpoint configured path
161            String relativePath = ObjectHelper.after(absoluteFileName, endpointPath);
162            // skip trailing /
163            relativePath = FileUtil.stripLeadingSeparator(relativePath);
164            answer.setRelativeFilePath(relativePath);
165    
166            // the file name should be the relative path
167            answer.setFileName(answer.getRelativeFilePath());
168    
169            return answer;
170        }
171    
172        private boolean isStepwise() {
173            RemoteFileConfiguration config = (RemoteFileConfiguration) endpoint.getConfiguration();
174            return config.isStepwise();
175        }
176    
177    }