1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.waarp.ftp.client;
21
22 import org.apache.commons.net.PrintCommandListener;
23 import org.apache.commons.net.ftp.FTP;
24 import org.apache.commons.net.ftp.FTPClient;
25 import org.apache.commons.net.ftp.FTPFile;
26 import org.apache.commons.net.ftp.FTPReply;
27 import org.apache.commons.net.ftp.FTPSClient;
28 import org.apache.commons.net.util.TrustManagerUtils;
29 import org.waarp.common.file.FileUtils;
30 import org.waarp.common.logging.SysErrLogger;
31 import org.waarp.common.logging.WaarpLogger;
32 import org.waarp.common.logging.WaarpLoggerFactory;
33
34 import java.io.FileInputStream;
35 import java.io.FileNotFoundException;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.OutputStream;
40 import java.io.PrintWriter;
41 import java.net.UnknownHostException;
42
43
44
45
46
47 public class WaarpFtpClient implements WaarpFtpClientInterface {
48
49
50
51 protected static final WaarpLogger logger =
52 WaarpLoggerFactory.getLogger(WaarpFtpClient.class);
53 private static final int DEFAULT_WAIT = 2;
54
55 final String server;
56 int port = 21;
57 final String user;
58 final String pwd;
59 final String acct;
60 int timeout;
61 boolean isPassive;
62 final int ssl;
63 protected final FTPClient ftpClient;
64 protected String result;
65 protected String directory = null;
66 protected String ipAddress = null;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public WaarpFtpClient(final String server, final int port, final String user,
82 final String pwd, final String acct,
83 final boolean isPassive, final int ssl,
84 final int controlTimeout, final int timeout) {
85 this(server, port, user, pwd, acct, isPassive, ssl, controlTimeout, timeout,
86 true);
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 public WaarpFtpClient(final String server, final int port, final String user,
103 final String pwd, final String acct,
104 final boolean isPassive, final int ssl,
105 final int controlTimeout, final int timeout,
106 final boolean trace) {
107 this.server = server;
108 this.port = port;
109 this.user = user;
110 this.pwd = pwd;
111 this.acct = acct;
112 this.isPassive = isPassive;
113 this.ssl = ssl;
114 if (this.ssl != 0) {
115
116 ftpClient = new FTPSClient(this.ssl == -1);
117 ((FTPSClient) ftpClient).setTrustManager(
118 TrustManagerUtils.getAcceptAllTrustManager());
119 } else {
120 ftpClient = new FTPClient();
121 }
122 ftpClient.setBufferSize(1024 * 1024);
123 if (controlTimeout > 0) {
124 ftpClient.setControlKeepAliveTimeout(controlTimeout / 1000);
125 ftpClient.setControlKeepAliveReplyTimeout(controlTimeout);
126 }
127 if (timeout > 0) {
128 ftpClient.setDataTimeout(timeout);
129 }
130 ftpClient.setListHiddenFiles(true);
131 if (trace) {
132 ftpClient.addProtocolCommandListener(
133 new PrintCommandListener(new PrintWriter(System.out), true));
134 }
135 }
136
137 @Override
138 public void setReportActiveExternalIPAddress(final String ipAddress) {
139 this.ipAddress = ipAddress;
140 if (this.ipAddress != null) {
141 try {
142 ftpClient.setReportActiveExternalIPAddress(ipAddress);
143 } catch (final UnknownHostException e) {
144 logger.error("Cannot set Ip Address since {}", e.getMessage());
145 }
146 }
147 }
148
149 @Override
150 public void setActiveDataTransferPortRange(final int from, final int to) {
151 ftpClient.setActivePortRange(from, to);
152 }
153
154 @Override
155 public final String getResult() {
156 return result;
157 }
158
159 private void reconnect() {
160 logout();
161 waitAfterDataCommand();
162 connect();
163 if (directory != null) {
164 changeDir(directory);
165 }
166 if (ipAddress != null) {
167 setReportActiveExternalIPAddress(ipAddress);
168 }
169 }
170
171 @Override
172 public final boolean connect() {
173 result = null;
174 boolean isActive = false;
175 try {
176 for (int j = 0; j < 3; j++) {
177 waitAfterDataCommand();
178 Exception lastExcemption = null;
179 for (int i = 0; i < 5; i++) {
180 try {
181 if (port > 0) {
182 ftpClient.connect(server, port);
183 } else {
184 ftpClient.connect(server);
185 }
186 lastExcemption = null;
187 break;
188 } catch (final Exception e) {
189 result = CONNECTION_IN_ERROR;
190 lastExcemption = e;
191 }
192 waitAfterDataCommand();
193 }
194 if (lastExcemption != null) {
195 logger.error(result + ": {}", lastExcemption.getMessage());
196 return false;
197 }
198 final int reply = ftpClient.getReplyCode();
199 if (FTPReply.isPositiveCompletion(reply)) {
200 break;
201 } else {
202 disconnect();
203 result = CONNECTION_IN_ERROR + ": " + reply;
204 }
205 }
206 final int reply = ftpClient.getReplyCode();
207 if (!FTPReply.isPositiveCompletion(reply)) {
208 disconnect();
209 result = CONNECTION_IN_ERROR + ": " + reply;
210 logger.error(result);
211 return false;
212 }
213 try {
214 if (acct == null) {
215
216 if (!ftpClient.login(user, pwd)) {
217 logout();
218 result = LOGIN_IN_ERROR;
219 logger.error(result);
220 return false;
221 }
222 } else if (!ftpClient.login(user, pwd, acct)) {
223 logout();
224 result = LOGIN_IN_ERROR;
225 logger.error(result);
226 return false;
227 }
228 } catch (final IOException e) {
229 result = LOGIN_IN_ERROR;
230 logger.error(result + ": {}", e.getMessage());
231 return false;
232 }
233 ftpClient.setUseEPSVwithIPv4(false);
234 if (ssl == 1) {
235
236 try {
237 ((FTPSClient) ftpClient).execPBSZ(0);
238 logger.debug("PBSZ 0");
239 ((FTPSClient) ftpClient).execPROT("P");
240 logger.debug("Info: {}",
241 ((FTPSClient) ftpClient).getEnableSessionCreation());
242 } catch (final IOException e) {
243 logout();
244 result = "Explicit SSL in error";
245 logger.error(result + ": {}", e.getMessage());
246 return false;
247 }
248 }
249 try {
250 ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
251 } catch (final IOException e1) {
252 result = SET_BINARY_IN_ERROR;
253 logger.error(result + ": {}", e1.getMessage());
254 return false;
255 }
256 changeMode(isPassive);
257 isActive = true;
258 return true;
259 } finally {
260 if (!isActive && ftpClient.getDataConnectionMode() ==
261 FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE) {
262 disconnect();
263 }
264 }
265 }
266
267 @Override
268 public final void logout() {
269 result = null;
270 try {
271 ftpClient.logout();
272 } catch (final IOException e) {
273
274 } finally {
275 try {
276 ftpClient.disconnect();
277 } catch (final IOException f) {
278
279 }
280 }
281 }
282
283 @Override
284 public final void disconnect() {
285 result = null;
286 try {
287 ftpClient.disconnect();
288 } catch (final IOException e) {
289 logger.debug(DISCONNECTION_IN_ERROR, e);
290 }
291 }
292
293 @Override
294 public final boolean makeDir(final String newDir) {
295 result = null;
296 try {
297 return ftpClient.makeDirectory(newDir);
298 } catch (final IOException e) {
299 try {
300 reconnect();
301 return ftpClient.makeDirectory(newDir);
302 } catch (final IOException e1) {
303 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
304 }
305 result = MKDIR_IN_ERROR;
306 logger.error(result + ": {}", e.getMessage());
307 waitAfterDataCommand();
308 return false;
309 }
310 }
311
312 @Override
313 public final boolean changeDir(final String newDir) {
314 result = null;
315 try {
316 directory = newDir;
317 return ftpClient.changeWorkingDirectory(newDir);
318 } catch (final IOException e) {
319 try {
320 reconnect();
321 return ftpClient.changeWorkingDirectory(newDir);
322 } catch (final IOException e1) {
323 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
324 }
325 result = CHDIR_IN_ERROR;
326 logger.error(result + ": {}", e.getMessage());
327 return false;
328 }
329 }
330
331 @Override
332 public final boolean changeFileType(final boolean binaryTransfer) {
333 result = null;
334 try {
335 return internalChangeFileType(binaryTransfer);
336 } catch (final IOException e) {
337 try {
338 reconnect();
339 return internalChangeFileType(binaryTransfer);
340 } catch (final IOException e1) {
341 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
342 }
343 result = FILE_TYPE_IN_ERROR;
344 logger.error(result + ": {}", e.getMessage());
345 return false;
346 }
347 }
348
349 private boolean internalChangeFileType(final boolean binaryTransfer)
350 throws IOException {
351 if (binaryTransfer) {
352 if (!ftpClient.setFileType(FTP.BINARY_FILE_TYPE)) {
353 reconnect();
354 return ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
355 }
356 return true;
357 } else {
358 if (!ftpClient.setFileType(FTP.ASCII_FILE_TYPE)) {
359 reconnect();
360 if (directory != null) {
361 changeDir(directory);
362 }
363 return ftpClient.setFileType(FTP.ASCII_FILE_TYPE);
364 }
365 return true;
366 }
367 }
368
369 @Override
370 public final void changeMode(final boolean passive) {
371 result = null;
372 isPassive = passive;
373 if (isPassive) {
374 ftpClient.enterLocalPassiveMode();
375 } else {
376 ftpClient.enterLocalActiveMode();
377 }
378 waitAfterDataCommand();
379 }
380
381 @Override
382 public void compressionMode(final boolean compression) {
383 logger.warn("Z Compression not supported by Apache mode");
384 }
385
386 @Override
387 public final boolean store(final String local, final String remote) {
388 return transferFile(local, remote, 1);
389 }
390
391 @Override
392 public final boolean store(final InputStream local, final String remote) {
393 return transferFile(local, remote, 1);
394 }
395
396 @Override
397 public final boolean append(final String local, final String remote) {
398 return transferFile(local, remote, 2);
399 }
400
401 @Override
402 public final boolean append(final InputStream local, final String remote) {
403 return transferFile(local, remote, 2);
404 }
405
406 @Override
407 public final boolean retrieve(final String local, final String remote) {
408 return transferFile(local, remote, -1);
409 }
410
411 @Override
412 public final boolean retrieve(final OutputStream local, final String remote) {
413 return transferFile(local, remote);
414 }
415
416 private boolean internalTransferFile(final String local, final String remote,
417 final int getStoreOrAppend)
418 throws FileNotFoundException {
419 OutputStream output = null;
420 FileInputStream fileInputStream = null;
421 try {
422 if (getStoreOrAppend > 0) {
423 fileInputStream = new FileInputStream(local);
424 return transferFile(fileInputStream, remote, getStoreOrAppend);
425 } else {
426 if (local == null) {
427
428 logger.debug("Will DLD nullStream: {}", remote);
429 output = new NullOutputStream();
430 } else {
431 logger.debug("Will DLD to local: {} into {}", remote, local);
432 output = new FileOutputStream(local);
433 }
434 return transferFile(output, remote);
435 }
436 } finally {
437 FileUtils.close(output);
438 FileUtils.close(fileInputStream);
439 }
440 }
441
442 @Override
443 public final boolean transferFile(final String local, final String remote,
444 final int getStoreOrAppend) {
445 result = null;
446 try {
447 if (!internalTransferFile(local, remote, getStoreOrAppend)) {
448 reconnect();
449 return internalTransferFile(local, remote, getStoreOrAppend);
450 }
451 return true;
452 } catch (final IOException e) {
453 try {
454 reconnect();
455 return internalTransferFile(local, remote, getStoreOrAppend);
456 } catch (final IOException e1) {
457 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
458 }
459 result = CANNOT_FINALIZE_TRANSFER_OPERATION;
460 logger.error(result + ": {}", e.getMessage());
461 return false;
462 }
463 }
464
465 private boolean internalTransferFile(final InputStream local,
466 final String remote,
467 final int getStoreOrAppend)
468 throws IOException {
469 final boolean status;
470 if (getStoreOrAppend == 1) {
471 status = ftpClient.storeFile(remote, local);
472 } else {
473
474 status = ftpClient.appendFile(remote, local);
475 }
476 if (!status) {
477 result = CANNOT_FINALIZE_STORE_LIKE_OPERATION;
478 logger.error(result);
479 return false;
480 }
481 return true;
482 }
483
484 @Override
485 public final boolean transferFile(final InputStream local,
486 final String remote,
487 final int getStoreOrAppend) {
488 result = null;
489 try {
490 if (!internalTransferFile(local, remote, getStoreOrAppend)) {
491 reconnect();
492 return internalTransferFile(local, remote, getStoreOrAppend);
493 }
494 return true;
495 } catch (final IOException e) {
496 try {
497 reconnect();
498 return internalTransferFile(local, remote, getStoreOrAppend);
499 } catch (final IOException e1) {
500 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
501 }
502 result = CANNOT_FINALIZE_STORE_LIKE_OPERATION;
503 logger.error(result + ": {}", e.getMessage());
504 return false;
505 } finally {
506 waitAfterDataCommand();
507 }
508 }
509
510 @Override
511 public final boolean transferFile(final OutputStream local,
512 final String remote) {
513 result = null;
514 boolean status;
515 try {
516 status = ftpClient.retrieveFile(remote, local);
517 if (!status) {
518 reconnect();
519 status = ftpClient.retrieveFile(remote, local);
520 if (!status) {
521 result = CANNOT_FINALIZE_RETRIEVE_LIKE_OPERATION;
522 logger.error(result);
523 return false;
524 }
525 }
526 return true;
527 } catch (final IOException e) {
528 try {
529 reconnect();
530 status = ftpClient.retrieveFile(remote, local);
531 if (!status) {
532 result = CANNOT_FINALIZE_RETRIEVE_LIKE_OPERATION;
533 logger.error(result);
534 return false;
535 }
536 return true;
537 } catch (final IOException e1) {
538 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
539 }
540 result = CANNOT_FINALIZE_RETRIEVE_LIKE_OPERATION;
541 logger.error(result + ": {}", e.getMessage());
542 return false;
543 } finally {
544 waitAfterDataCommand();
545 }
546 }
547
548 private String[] internalListFiles(final String remote, final boolean mlist)
549 throws IOException {
550 final FTPFile[] list;
551 if (mlist) {
552 list = ftpClient.listFiles(remote);
553 } else {
554 list = ftpClient.mlistDir(remote);
555 }
556 final String[] results = new String[list.length];
557 int i = 0;
558 for (final FTPFile file : list) {
559 results[i] = file.toFormattedString();
560 i++;
561 }
562 return results;
563 }
564
565 @Override
566 public final String[] listFiles(final String remote) {
567 result = null;
568 try {
569 return internalListFiles(remote, false);
570 } catch (final IOException e) {
571 try {
572 reconnect();
573 return internalListFiles(remote, false);
574 } catch (final IOException e1) {
575 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
576 }
577 result = CANNOT_FINALIZE_TRANSFER_OPERATION;
578 logger.error(result + ": {}", e.getMessage());
579 return null;
580 } finally {
581 waitAfterDataCommand();
582 }
583 }
584
585 @Override
586 public final String[] listFiles() {
587 return listFiles((String) null);
588 }
589
590 @Override
591 public final String[] mlistFiles(final String remote) {
592 result = null;
593 try {
594 return internalListFiles(remote, true);
595 } catch (final IOException e) {
596 try {
597 reconnect();
598 return internalListFiles(remote, true);
599 } catch (final IOException e1) {
600 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
601 }
602 result = CANNOT_FINALIZE_TRANSFER_OPERATION;
603 logger.error(result + ": {}", e.getMessage());
604 return null;
605 } finally {
606 waitAfterDataCommand();
607 }
608 }
609
610 @Override
611 public final String[] mlistFiles() {
612 return mlistFiles((String) null);
613 }
614
615 @Override
616 public final String[] features() {
617 result = null;
618 try {
619 return internalFeatures();
620 } catch (final IOException e) {
621 try {
622 reconnect();
623 return internalFeatures();
624 } catch (final IOException e1) {
625 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
626 }
627 result = CANNOT_EXECUTE_OPERATION_FEATURE;
628 logger.error(result + ": {}", e.getMessage());
629 return null;
630 }
631 }
632
633 private String[] internalFeatures() throws IOException {
634 if (ftpClient.features()) {
635 final String resultNew = ftpClient.getReplyString();
636 return resultNew.split("\\n");
637 }
638 return null;
639 }
640
641 @Override
642 public final boolean featureEnabled(final String feature) {
643 result = null;
644 try {
645 return internalFeatureEnabled(feature);
646 } catch (final IOException e) {
647 try {
648 reconnect();
649 return internalFeatureEnabled(feature);
650 } catch (final IOException e1) {
651 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
652 }
653 result = CANNOT_EXECUTE_OPERATION_FEATURE;
654 logger.error(result + ": {}", e.getMessage());
655 return false;
656 }
657 }
658
659 private boolean internalFeatureEnabled(final String feature)
660 throws IOException {
661 if (ftpClient.featureValue(feature) == null) {
662 final String resultNew = ftpClient.getReplyString();
663 return resultNew.contains(feature.toUpperCase());
664 }
665 return true;
666 }
667
668 @Override
669 public boolean deleteFile(final String remote) {
670 result = null;
671 try {
672 ftpClient.deleteFile(remote);
673 return true;
674 } catch (final IOException e) {
675 try {
676 reconnect();
677 ftpClient.deleteFile(remote);
678 return true;
679 } catch (final IOException e1) {
680 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
681 }
682 result = CANNOT_EXECUTE_OPERATION_SITE;
683 logger.error(result + ": {}", e.getMessage());
684 return false;
685 }
686 }
687
688 @Override
689 public final String[] executeCommand(final String params) {
690 result = null;
691 try {
692 return internalExecuteCommand(params);
693 } catch (final IOException e) {
694 try {
695 reconnect();
696 return internalExecuteCommand(params);
697 } catch (final IOException e1) {
698 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
699 }
700 result = CANNOT_EXECUTE_OPERATION_SITE;
701 logger.error(result + ": {}", e.getMessage());
702 return null;
703 }
704 }
705
706 private String[] internalExecuteCommand(final String params)
707 throws IOException {
708 final int pos = params.indexOf(' ');
709 String command = params;
710 String args = null;
711 if (pos > 0) {
712 command = params.substring(0, pos);
713 args = params.substring(pos + 1);
714 }
715 String[] results = ftpClient.doCommandAsStrings(command, args);
716 if (results == null) {
717 results = new String[1];
718 results[0] = ftpClient.getReplyString();
719 }
720 return results;
721 }
722
723 @Override
724 public final String[] executeSiteCommand(final String params) {
725 result = null;
726 try {
727 return internalExecuteSiteCommand(params);
728 } catch (final IOException e) {
729 try {
730 reconnect();
731 return internalExecuteSiteCommand(params);
732 } catch (final IOException e1) {
733 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
734 }
735 result = CANNOT_EXECUTE_OPERATION_SITE;
736 logger.error(result + ": {}", e.getMessage());
737 return null;
738 }
739 }
740
741 private String[] internalExecuteSiteCommand(final String params)
742 throws IOException {
743 String[] results = ftpClient.doCommandAsStrings("SITE", params);
744 if (results == null) {
745 results = new String[1];
746 results[0] = ftpClient.getReplyString();
747 }
748 return results;
749 }
750
751 @Override
752 public final void noop() {
753 try {
754 ftpClient.noop();
755 } catch (final IOException e) {
756 try {
757 reconnect();
758 ftpClient.noop();
759 } catch (final IOException e1) {
760 SysErrLogger.FAKE_LOGGER.ignoreLog(e1);
761 }
762 result = NOOP_ERROR;
763 logger.error(result + ": {}", e.getMessage());
764 }
765 }
766
767
768
769
770 static void waitAfterDataCommand() {
771 if (DEFAULT_WAIT > 0) {
772 try {
773 Thread.sleep(DEFAULT_WAIT);
774 } catch (final InterruptedException e) {
775 SysErrLogger.FAKE_LOGGER.ignoreLog(e);
776 }
777 } else {
778 Thread.yield();
779 }
780 }
781 }