001package lucee.runtime.net.ftp;
002
003import java.io.IOException;
004import java.io.InputStream;
005import java.io.OutputStream;
006import java.net.InetAddress;
007import java.net.SocketException;
008import java.util.ArrayList;
009import java.util.Iterator;
010import java.util.List;
011import java.util.Locale;
012import java.util.Vector;
013
014import lucee.print;
015import lucee.commons.io.IOUtil;
016import lucee.commons.lang.ExceptionUtil;
017import lucee.commons.lang.StringUtil;
018import lucee.runtime.exp.DatabaseException;
019import lucee.runtime.exp.PageException;
020import lucee.runtime.op.Caster;
021import lucee.runtime.tag.FileTag;
022import lucee.runtime.tag.Ftp;
023import lucee.runtime.type.Collection;
024import lucee.runtime.type.Collection.Key;
025import lucee.runtime.type.KeyImpl;
026import lucee.runtime.type.QueryImpl;
027import lucee.runtime.type.dt.DateTimeImpl;
028import lucee.runtime.type.util.KeyConstants;
029
030import org.apache.commons.net.ftp.FTPFile;
031
032import com.jcraft.jsch.*;
033
034public class SFTPClientImpl extends AFTPClient {
035        
036
037        private static final Key IS_DIRECTORY = new KeyImpl("isDirectory");
038        private JSch jsch;
039        private int timeout=60000;
040        private Session session;
041        private ChannelSftp channelSftp;
042        private InetAddress host;
043        private int port;
044        private String username;
045        private String password;
046        private boolean stopOnError;
047        private String fingerprint;
048        private String replyString;
049        private int replyCode;
050        private boolean positiveCompletion;
051
052        SFTPClientImpl(){
053                jsch = new JSch();
054        }
055        
056        @Override
057        public void init(InetAddress host, int port, String username, String password, String fingerprint, boolean stopOnError) throws SocketException, IOException {
058                if(port<1)port=22;
059                this.host=host;
060                this.port=port;
061                this.username=username;
062                this.password=password;
063                this.fingerprint=fingerprint==null?null:fingerprint.trim();
064                this.stopOnError=stopOnError;
065        }
066        
067
068        @Override
069        public void connect() throws SocketException, IOException {
070                try {
071                        //jsch.setKnownHosts("");
072                        session = jsch.getSession(username,host.getHostAddress() , port);
073                        /**/
074                        java.util.Properties config = new java.util.Properties(); 
075                        config.put("StrictHostKeyChecking", "no");
076                        session.setConfig(config);
077                        
078                        
079                        
080                        //UserInfo ui=new UserInfoImpl();
081                        if(!StringUtil.isEmpty(password)) 
082                                session.setPassword(password);
083                        
084                    //session.setUserInfo(ui);
085                        if(timeout>0) session.setTimeout(timeout);
086                    session.connect();
087                    
088                    Channel channel=session.openChannel("sftp");
089                    channel.connect();
090                    channelSftp = (ChannelSftp)channel;
091                    
092                    
093                    // check fingerprint
094                    
095                        if(!StringUtil.isEmpty(fingerprint)) {
096                                if(!fingerprint.equalsIgnoreCase(fingerprint())) {
097                                        disconnect();
098                                        throw new IOException("given fingerprint is not a match.");
099                                }
100                }
101                    handleSucess();
102                }
103                catch(JSchException e){
104                        handleFail(e, stopOnError);
105                }
106        }
107        
108
109        private String fingerprint() {
110                return session.getHostKey().getFingerPrint(jsch);
111        }
112
113        
114        public static void main(String[] args) throws SocketException, IOException {
115                SFTPClientImpl client=new SFTPClientImpl();
116                InetAddress host = InetAddress.getByName("virola.viviotech.net");
117                String user = "root";
118                String pass="Eunn:.TidkiWaj3";
119                String fingerprint="aa:e8:43:3e:6b:44:91:a3:d3:da:07:34:90:2a:c8:5b";
120                int port=10022;
121                
122                client.init(host, port, user, pass,null,true);
123                client.connect();
124                
125                // fingerprint
126                
127                // list files
128                FTPFile[] files = client.listFiles("/");
129                
130                
131                client.disconnect();
132                
133                // UnknownHostKey: 205.210.189.210. RSA key fingerprint is aa:e8:43:3e:6b:44:91:a3:d3:da:07:34:90:2a:c8:5b
134                
135        }
136
137        @Override
138        public boolean rename(String from, String to) throws IOException {
139                try {
140                        channelSftp.rename(from, to);
141                        handleSucess();
142                        return true;
143                }
144                catch (SftpException e) {
145                        handleFail(e, stopOnError);
146                }
147                return false;
148        }
149
150        @Override
151        public boolean removeDirectory(String pathname) throws IOException {
152                try {
153                        channelSftp.rmdir(pathname);
154                        handleSucess();
155                        return true;
156                }
157                catch(SftpException ioe) {
158                        handleFail(ioe, stopOnError);
159                }
160                return false;
161        }
162        
163
164        @Override
165        public boolean makeDirectory(String pathname) throws IOException {
166                try {
167                        channelSftp.mkdir(pathname);
168                        handleSucess();
169                        return true;
170                }
171                catch(SftpException ioe) {
172                        handleFail(ioe, stopOnError);
173                }
174                return false;
175        }
176
177        @Override
178        public boolean directoryExists(String pathname) throws IOException {
179                try {
180                        String pwd=channelSftp.pwd();
181                        channelSftp.cd(pathname);
182                        channelSftp.cd(pwd); // we change it back to what it was
183                        handleSucess();
184                        return true;
185                }
186                catch(SftpException e) {/*do nothing*/}
187                return false;
188        }
189        
190
191        @Override
192        public boolean changeWorkingDirectory(String pathname) throws IOException {
193                try {
194                        channelSftp.cd(pathname);
195                        handleSucess();
196                        return true;
197                }
198                catch(SftpException ioe) {
199                        handleFail(ioe, stopOnError);
200                }
201                return false;
202        }
203        
204
205        @Override
206        public String printWorkingDirectory() throws IOException {
207                try {
208                        String pwd = channelSftp.pwd();
209                        handleSucess();
210                        return pwd;
211                }
212                catch(SftpException ioe) {
213                        handleFail(ioe, stopOnError);
214                }
215                return null;
216        }
217
218        
219        @Override
220        public boolean deleteFile(String pathname) throws IOException {
221                try {
222                        channelSftp.rm(pathname);
223                        handleSucess();
224                        return true;
225                }
226                catch(SftpException ioe) {
227                        handleFail(ioe, stopOnError);
228                }
229                return false;
230        }
231        
232
233        @Override
234        public boolean retrieveFile(String remote, OutputStream local) throws IOException {
235                boolean success=false;
236                try {
237                        channelSftp.get(remote, local);
238                        handleSucess();
239                        success = true;
240                }
241                catch(SftpException ioe) {
242                        handleFail(ioe, stopOnError);
243                }
244                return success;
245        }
246        
247
248        
249        @Override
250        public boolean storeFile(String remote, InputStream local) throws IOException {
251                try {
252                        this.channelSftp.put(local, remote); // TODO add progress monitor?
253                        handleSucess();
254                        return true;
255                }
256                catch(SftpException ioe) {
257                        handleFail(ioe, stopOnError);
258                }
259                return false;
260        }
261        
262        @Override
263        public int getReplyCode() {
264                return replyCode;
265        }
266
267        @Override
268        public String getReplyString() {
269                return replyString;
270        }
271
272        @Override
273        public FTPFile[] listFiles(String pathname) throws IOException {
274                pathname=cleanPath(pathname);
275                List<FTPFile> files=new ArrayList<FTPFile>();
276                try {
277                        Vector list = channelSftp.ls(pathname);
278                        Iterator<ChannelSftp.LsEntry> it = list.iterator();
279                        ChannelSftp.LsEntry entry;
280                        SftpATTRS attrs;
281                        FTPFile file;
282                        String fileName;
283                        
284                        while(it.hasNext()){
285                                entry=it.next();
286                                attrs = entry.getAttrs();
287                                fileName=entry.getFilename();
288                                if(fileName.equals(".") || fileName.equals("..")) continue;
289                                
290                                file=new FTPFile();
291                                files.add(file);
292                                // is dir
293                                file.setType(attrs.isDir()?FTPFile.DIRECTORY_TYPE:FTPFile.FILE_TYPE);
294                                file.setTimestamp(Caster.toCalendar(attrs.getMTime()*1000L, null, Locale.ENGLISH));
295                                file.setSize(attrs.isDir()?0:attrs.getSize());
296                                FTPConstant.setPermission(file, attrs.getPermissions());
297                                file.setName(fileName);
298                        }
299                        handleSucess();
300                }
301                catch (SftpException e) {
302                        handleFail(e, stopOnError);
303                }
304                
305                return files.toArray(new FTPFile[files.size()]);
306        }
307        
308        
309        
310
311        
312        private String cleanPath(String pathname) {
313                if(!pathname.endsWith("/"))
314                        pathname = pathname + "/";
315                
316                return pathname;
317        }
318        
319        
320        
321        
322
323        @Override
324        public boolean setFileType(int fileType) throws IOException {
325                // not used
326                return true;
327        }
328
329
330        @Override
331        public String getPrefix() {
332                return "sftp";
333        }
334
335        @Override
336        public InetAddress getRemoteAddress() {
337                return host;
338        }
339
340        @Override
341        public boolean isConnected() {
342                return channelSftp.isConnected();
343        }
344
345        @Override
346        public int quit() throws IOException {
347                // do nothing
348                return 0;
349        }
350
351        @Override
352        public void disconnect() throws IOException {
353                if(session!=null && session.isConnected()) {
354                        session.disconnect();
355                        session=null;
356                }
357        }
358
359        @Override
360        public void setTimeout(int timeout) {
361                this.timeout=timeout;
362                if(session!=null) {
363                        try {
364                                session.setTimeout(timeout);
365                        }
366                        catch (JSchException e) {}
367                }
368        }
369
370        @Override
371        public int getDataConnectionMode() {
372                // not used
373                return -1;
374        }
375
376        @Override
377        public void enterLocalPassiveMode() {
378                // not used
379        }
380
381        @Override
382        public void enterLocalActiveMode() {
383                // not used
384
385        }
386
387        @Override
388        public boolean isPositiveCompletion() {
389                return positiveCompletion;
390        }
391        
392        private void handleSucess() {
393                replyCode=0;
394                replyString="SSH_FX_OK successful completion of the operation";
395                positiveCompletion=true;
396        }
397        
398
399        
400        private void handleFail(Exception e, boolean stopOnError) throws IOException {
401                String msg = e.getMessage()==null?"":e.getMessage();
402                if (StringUtil.indexOfIgnoreCase(msg, "AUTHENTICATION") != -1 || StringUtil.indexOfIgnoreCase(msg, "PRIVATEKEY") != -1) {
403                        replyCode = 51;
404                }
405                else replyCode = 82;
406                replyString=msg;
407                positiveCompletion=false;
408                
409                if (stopOnError) {
410                        disconnect();
411                        if(e instanceof IOException) throw (IOException)e;
412                        throw new IOException(e);
413                }
414        }
415
416}