View Javadoc
1   /**
2    * This file is part of Waarp Project.
3    * 
4    * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the
5    * COPYRIGHT.txt in the distribution for a full listing of individual contributors.
6    * 
7    * All Waarp Project is free software: you can redistribute it and/or modify it under the terms of
8    * the GNU General Public License as published by the Free Software Foundation, either version 3 of
9    * the License, or (at your option) any later version.
10   * 
11   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
12   * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13   * Public License for more details.
14   * 
15   * You should have received a copy of the GNU General Public License along with Waarp . If not, see
16   * <http://www.gnu.org/licenses/>.
17   */
18  package org.waarp.ftp.client;
19  
20  import it.sauronsoftware.ftp4j.FTPAbortedException;
21  import it.sauronsoftware.ftp4j.FTPClient;
22  import it.sauronsoftware.ftp4j.FTPCommunicationListener;
23  import it.sauronsoftware.ftp4j.FTPConnector;
24  import it.sauronsoftware.ftp4j.FTPDataTransferException;
25  import it.sauronsoftware.ftp4j.FTPException;
26  import it.sauronsoftware.ftp4j.FTPFile;
27  import it.sauronsoftware.ftp4j.FTPIllegalReplyException;
28  import it.sauronsoftware.ftp4j.FTPListParseException;
29  import it.sauronsoftware.ftp4j.FTPReply;
30  
31  import java.io.File;
32  import java.io.IOException;
33  import java.net.SocketException;
34  import java.security.KeyManagementException;
35  import java.security.NoSuchAlgorithmException;
36  import java.security.SecureRandom;
37  import java.security.cert.X509Certificate;
38  
39  import javax.net.ssl.SSLContext;
40  import javax.net.ssl.SSLSocketFactory;
41  import javax.net.ssl.TrustManager;
42  import javax.net.ssl.X509TrustManager;
43  
44  import org.waarp.common.logging.WaarpLogger;
45  import org.waarp.common.logging.WaarpLoggerFactory;
46  
47  /**
48   * FTP client using FTP4J model (working in all modes)
49   * 
50   * @author "Frederic Bregier"
51   * 
52   */
53  public class WaarpFtp4jClient {
54      /**
55       * Internal Logger
56       */
57      private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(WaarpFtp4jClient.class);
58  
59      String server = null;
60      int port = 21;
61      String user = null;
62      String pwd = null;
63      String acct = null;
64      int timeout;
65      int keepalive;
66      boolean isPassive = false;
67      int ssl = 0; // -1 native, 1 auth
68      protected FTPClient ftpClient = null;
69      protected String result = null;
70      private boolean binaryTransfer = true;
71  
72      /**
73       * @param server
74       * @param port
75       * @param user
76       * @param pwd
77       * @param acct
78       * @param isPassive
79       * @param ssl
80       * @param timeout
81       */
82      public WaarpFtp4jClient(String server, int port,
83              String user, String pwd, String acct, boolean isPassive, int ssl, int keepalive,
84              int timeout) {
85          this.server = server;
86          this.port = port;
87          this.user = user;
88          this.pwd = pwd;
89          this.acct = acct;
90          this.isPassive = isPassive;
91          this.ssl = ssl;
92          this.keepalive = keepalive;
93          this.timeout = timeout;
94          this.ftpClient = new FTPClient();
95          if (this.ssl != 0) {
96              // implicit or explicit
97              TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() {
98                  public X509Certificate[] getAcceptedIssuers() {
99                      return null;
100                 }
101 
102                 public void checkClientTrusted(X509Certificate[] certs, String authType) {
103                 }
104 
105                 public void checkServerTrusted(X509Certificate[] certs, String authType) {
106                 }
107             } };
108             SSLContext sslContext = null;
109             try {
110                 sslContext = SSLContext.getInstance("SSL");
111                 sslContext.init(null, trustManager, new SecureRandom());
112             } catch (NoSuchAlgorithmException e) {
113                 throw new IllegalArgumentException("Bad algorithm", e);
114             } catch (KeyManagementException e) {
115                 throw new IllegalArgumentException("Bad KeyManagement", e);
116             }
117             SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
118             this.ftpClient.setSSLSocketFactory(sslSocketFactory);
119             if (this.ssl < 0) {
120                 this.ftpClient.setSecurity(FTPClient.SECURITY_FTPS);
121             } else {
122                 this.ftpClient.setSecurity(FTPClient.SECURITY_FTPES);
123             }
124         } else {
125             this.ftpClient = new FTPClient();
126         }
127         if (timeout > 0) {
128             System.setProperty("ftp4j.activeDataTransfer.acceptTimeout", "" + timeout);
129         }
130         System.setProperty("ftp4j.activeDataTransfer.hostAddress", "127.0.0.1");
131 
132         this.ftpClient.addCommunicationListener(new FTPCommunicationListener() {
133             public void sent(String arg0) {
134                 logger.debug("Command: " + arg0);
135             }
136 
137             public void received(String arg0) {
138                 logger.debug("Answer: " + arg0);
139             }
140         });
141         FTPConnector connector = this.ftpClient.getConnector();
142         connector.setCloseTimeout(timeout);
143         connector.setReadTimeout(timeout);
144         connector.setUseSuggestedAddressForDataConnections(true);
145     }
146 
147     /**
148      * @return the result associated with the last command
149      */
150     public String getResult() {
151         return result;
152     }
153 
154     /**
155      * Try to connect to the server and goes with the authentication
156      * 
157      * @return True if connected and authenticated, else False
158      */
159     public boolean connect() {
160         result = null;
161         boolean isActive = false;
162         try {
163             try {
164                 this.ftpClient.connect(this.server, this.port);
165             } catch (SocketException e) {
166                 result = "Connection in error";
167                 logger.error(result, e);
168                 return false;
169             } catch (IOException e) {
170                 result = "Connection in error";
171                 logger.error(result, e);
172                 return false;
173             } catch (IllegalStateException e) {
174                 result = "Connection in error";
175                 logger.error(result, e);
176                 return false;
177             } catch (FTPIllegalReplyException e) {
178                 result = "Connection in error";
179                 logger.error(result, e);
180                 return false;
181             } catch (FTPException e) {
182                 result = "Connection in error";
183                 logger.error(result, e);
184                 return false;
185             }
186             try {
187                 if (this.acct == null) {
188                     // no account
189                     this.ftpClient.login(this.user, this.pwd);
190                 } else {
191                     this.ftpClient.login(this.user, this.pwd, this.acct);
192                 }
193             } catch (IOException e) {
194                 result = "Login in error";
195                 logger.error(result, e);
196                 return false;
197             } catch (IllegalStateException e) {
198                 this.logout();
199                 result = "Login in error";
200                 logger.error(result);
201                 return false;
202             } catch (FTPIllegalReplyException e) {
203                 this.logout();
204                 result = "Login in error";
205                 logger.error(result);
206                 return false;
207             } catch (FTPException e) {
208                 this.logout();
209                 result = "Login in error";
210                 logger.error(result);
211                 return false;
212             }
213             try {
214                 this.ftpClient.setType(FTPClient.TYPE_BINARY);
215             } catch (IllegalArgumentException e1) {
216                 result = "Set BINARY in error";
217                 logger.error(result, e1);
218                 return false;
219             }
220             changeMode(isPassive);
221             if (keepalive > 0) {
222                 this.ftpClient.setAutoNoopTimeout(keepalive);
223             }
224             isActive = true;
225             return true;
226         } finally {
227             if ((!isActive) && !this.ftpClient.isPassive()) {
228                 this.disconnect();
229             }
230         }
231     }
232 
233     /**
234      * QUIT the control connection
235      */
236     public void logout() {
237         this.ftpClient.setAutoNoopTimeout(0);
238         logger.debug("QUIT");
239         if (this.executeCommand("QUIT") == null) {
240             try {
241                 this.ftpClient.logout();
242             } catch (IOException e) {
243                 // do nothing
244             } catch (IllegalStateException e) {
245                 // do nothing
246             } catch (FTPIllegalReplyException e) {
247                 // do nothing
248             } catch (FTPException e) {
249                 // do nothing
250             } finally {
251                 if (!this.ftpClient.isPassive()) {
252                     disconnect();
253                 }
254             }
255         }
256     }
257 
258     /**
259      * Disconnect the Ftp Client
260      */
261     public void disconnect() {
262         this.ftpClient.setAutoNoopTimeout(0);
263         try {
264             this.ftpClient.disconnect(false);
265         } catch (IOException e) {
266             logger.debug("Disconnection in error", e);
267         } catch (IllegalStateException e) {
268             logger.debug("Disconnection in error", e);
269         } catch (FTPIllegalReplyException e) {
270             logger.debug("Disconnection in error", e);
271         } catch (FTPException e) {
272             logger.debug("Disconnection in error", e);
273         }
274     }
275 
276     /**
277      * Create a new directory
278      * 
279      * @param newDir
280      * @return True if created
281      */
282     public boolean makeDir(String newDir) {
283         result = null;
284         try {
285             this.ftpClient.createDirectory(newDir);
286             return true;
287         } catch (IOException e) {
288             result = "MKDIR in error";
289             logger.info(result, e);
290             return false;
291         } catch (IllegalStateException e) {
292             result = "MKDIR in error";
293             logger.info(result, e);
294             return false;
295         } catch (FTPIllegalReplyException e) {
296             result = "MKDIR in error";
297             logger.info(result, e);
298             return false;
299         } catch (FTPException e) {
300             result = "MKDIR in error";
301             logger.info(result, e);
302             return false;
303         }
304     }
305 
306     /**
307      * Change remote directory
308      * 
309      * @param newDir
310      * @return True if the change is OK
311      */
312     public boolean changeDir(String newDir) {
313         result = null;
314         try {
315             this.ftpClient.changeDirectory(newDir);
316             return true;
317         } catch (IOException e) {
318             result = "CHDIR in error";
319             logger.info(result, e);
320             return false;
321         } catch (IllegalStateException e) {
322             result = "CHDIR in error";
323             logger.info(result, e);
324             return false;
325         } catch (FTPIllegalReplyException e) {
326             result = "CHDIR in error";
327             logger.info(result, e);
328             return false;
329         } catch (FTPException e) {
330             result = "CHDIR in error";
331             logger.info(result, e);
332             return false;
333         }
334     }
335 
336     /**
337      * Change the FileType of Transfer (Binary true, ASCII false)
338      * 
339      * @param binaryTransfer1
340      * @return True if the change is OK
341      */
342     public boolean changeFileType(boolean binaryTransfer1) {
343         result = null;
344         this.binaryTransfer = binaryTransfer1;
345         try {
346             if (this.binaryTransfer) {
347                 this.ftpClient.setType(FTPClient.TYPE_BINARY);
348             } else {
349                 this.ftpClient.setType(FTPClient.TYPE_TEXTUAL);
350             }
351             return true;
352         } catch (IllegalArgumentException e) {
353             result = "FileType in error";
354             logger.warn(result, e);
355             return false;
356         }
357     }
358 
359     /**
360      * Change to passive (true) or active (false) mode
361      * 
362      * @param passive
363      */
364     public void changeMode(boolean passive) {
365         this.isPassive = passive;
366         this.ftpClient.setPassive(passive);
367     }
368 
369     /**
370      * Ask to transfer a file
371      * 
372      * @param local
373      *            local filepath (full path)
374      * @param remote
375      *            filename (basename)
376      * @param getStoreOrAppend
377      *            -1 = get, 1 = store, 2 = append
378      * @return True if the file is correctly transfered
379      */
380     public boolean transferFile(String local, String remote, int getStoreOrAppend) {
381         result = null;
382         try {
383             if (getStoreOrAppend > 0) {
384                 File from = new File(local);
385                 result = "Cannot finalize store like operation";
386                 logger.debug("Will STOR: " + from);
387                 try {
388                     if (getStoreOrAppend == 1) {
389                         this.ftpClient.upload(from, new DataTimeOutListener(ftpClient, timeout,
390                                 "STOR", local));
391                     } else {
392                         // append
393                         this.ftpClient.append(from, new DataTimeOutListener(ftpClient, timeout,
394                                 "APPE", local));
395                     }
396                     result = null;
397                 } catch (IllegalStateException e) {
398                     logger.error(result, e);
399                     return false;
400                 } catch (FTPIllegalReplyException e) {
401                     logger.error(result, e);
402                     return false;
403                 } catch (FTPException e) {
404                     logger.error(result, e);
405                     return false;
406                 } catch (FTPDataTransferException e) {
407                     logger.error(result, e);
408                     return false;
409                 } catch (FTPAbortedException e) {
410                     logger.error(result, e);
411                     return false;
412                 }
413                 return true;
414             } else {
415                 result = "Cannot finalize retrieve like operation";
416                 if (local == null) {
417                     // test
418                     NullOutputStream nullOutputStream = new NullOutputStream();
419                     logger.debug("Will DLD nullStream: " + remote);
420                     try {
421                         this.ftpClient.download(remote, nullOutputStream, 0,
422                                 new DataTimeOutListener(ftpClient, timeout, "RETR", remote));
423                         result = null;
424                     } catch (IllegalStateException e) {
425                         logger.error(result, e);
426                         return false;
427                     } catch (FTPIllegalReplyException e) {
428                         logger.error(result, e);
429                         return false;
430                     } catch (FTPException e) {
431                         logger.error(result, e);
432                         return false;
433                     } catch (FTPDataTransferException e) {
434                         logger.error(result, e);
435                         return false;
436                     } catch (FTPAbortedException e) {
437                         logger.error(result, e);
438                         return false;
439                     }
440                 } else {
441                     logger.debug("Will DLD to local: " + remote + " into " + local);
442                     File to = new File(local);
443                     try {
444                         this.ftpClient.download(remote, to, new DataTimeOutListener(ftpClient,
445                                 timeout, "RETR", local));
446                         result = null;
447                     } catch (IllegalStateException e) {
448                         logger.error(result, e);
449                         return false;
450                     } catch (FTPIllegalReplyException e) {
451                         logger.error(result, e);
452                         return false;
453                     } catch (FTPException e) {
454                         logger.error(result, e);
455                         return false;
456                     } catch (FTPDataTransferException e) {
457                         logger.error(result, e);
458                         return false;
459                     } catch (FTPAbortedException e) {
460                         logger.error(result, e);
461                         return false;
462                     }
463                 }
464                 return true;
465             }
466         } catch (IOException e) {
467             result = "Cannot finalize operation";
468             logger.error(result, e);
469             return false;
470         }
471     }
472 
473     /**
474      * 
475      * @return the list of Files as given by FTP
476      */
477     public String[] listFiles() {
478         try {
479             FTPFile[] list = this.ftpClient.list();
480             String[] results = new String[list.length];
481             int i = 0;
482             for (FTPFile file : list) {
483                 results[i] = file.toString();
484                 i++;
485             }
486             return results;
487         } catch (IOException e) {
488             result = "Cannot finalize transfer operation";
489             logger.error(result, e);
490             return null;
491         } catch (IllegalStateException e) {
492             result = "Cannot finalize transfer operation";
493             logger.error(result, e);
494             return null;
495         } catch (FTPIllegalReplyException e) {
496             result = "Cannot finalize transfer operation";
497             logger.error(result, e);
498             return null;
499         } catch (FTPException e) {
500             result = "Cannot finalize transfer operation";
501             logger.error(result, e);
502             return null;
503         } catch (FTPDataTransferException e) {
504             result = "Cannot finalize transfer operation";
505             logger.error(result, e);
506             return null;
507         } catch (FTPAbortedException e) {
508             result = "Cannot finalize transfer operation";
509             logger.error(result, e);
510             return null;
511         } catch (FTPListParseException e) {
512             result = "Cannot finalize transfer operation";
513             logger.error(result, e);
514             return null;
515         }
516     }
517 
518     /**
519      * 
520      * @param feature
521      * @return True if the given feature is listed
522      */
523     public boolean featureEnabled(String feature) {
524         try {
525             FTPReply reply = this.ftpClient.sendCustomCommand("FEAT");
526             String[] msg = reply.getMessages();
527             for (String string : msg) {
528                 if (string.contains(feature.toUpperCase())) {
529                     return true;
530                 }
531             }
532             return false;
533         } catch (IOException e) {
534             result = "Cannot execute operation Feature";
535             logger.error(result, e);
536             return false;
537         } catch (IllegalStateException e) {
538             result = "Cannot execute operation Feature";
539             logger.error(result, e);
540             return false;
541         } catch (FTPIllegalReplyException e) {
542             result = "Cannot execute operation Feature";
543             logger.error(result, e);
544             return false;
545         }
546     }
547 
548     /**
549      * 
550      * @param params
551      * @return the string lines result for the command params
552      */
553     public String[] executeCommand(String params) {
554         result = null;
555         try {
556             logger.debug(params);
557             FTPReply reply = this.ftpClient.sendCustomCommand(params);
558             if (!reply.isSuccessCode()) {
559                 result = reply.toString();
560                 return null;
561             }
562             return reply.getMessages();
563         } catch (IOException e) {
564             result = "Cannot execute operation Site";
565             logger.error(result, e);
566             return null;
567         } catch (IllegalStateException e) {
568             result = "Cannot execute operation Site";
569             logger.error(result, e);
570             return null;
571         } catch (FTPIllegalReplyException e) {
572             result = "Cannot execute operation Site";
573             logger.error(result, e);
574             return null;
575         }
576     }
577 
578     /**
579      * 
580      * @param params
581      *            command without SITE in front
582      * @return the string lines result for the SITE command params
583      */
584     public String[] executeSiteCommand(String params) {
585         result = null;
586         try {
587             logger.debug("SITE " + params);
588             FTPReply reply = this.ftpClient.sendSiteCommand(params);
589             if (!reply.isSuccessCode()) {
590                 result = reply.toString();
591                 return null;
592             }
593             return reply.getMessages();
594         } catch (IOException e) {
595             result = "Cannot execute operation Site";
596             logger.error(result, e);
597             return null;
598         } catch (IllegalStateException e) {
599             result = "Cannot execute operation Site";
600             logger.error(result, e);
601             return null;
602         } catch (FTPIllegalReplyException e) {
603             result = "Cannot execute operation Site";
604             logger.error(result, e);
605             return null;
606         }
607     }
608 
609 }