View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.openr66.context.task;
21  
22  import org.waarp.common.digest.FilesystemBasedDigest;
23  import org.waarp.common.logging.WaarpLogger;
24  import org.waarp.common.logging.WaarpLoggerFactory;
25  import org.waarp.ftp.client.WaarpFtp4jClient;
26  import org.waarp.openr66.context.ErrorCode;
27  import org.waarp.openr66.context.R66Result;
28  import org.waarp.openr66.context.R66Session;
29  import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
30  import org.waarp.openr66.protocol.configuration.Configuration;
31  import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
32  
33  import java.io.File;
34  import java.io.IOException;
35  
36  /**
37   * Ftp Transfer task: synchronous<br>
38   * <p>
39   * Result of arguments will be as FTP command.<br>
40   * Format is the following:<br>
41   * "-file filepath <br>
42   * -to requestedHost <br>
43   * -port port <br>
44   * -user user <br>
45   * -pwd pwd <br>
46   * [-account account] <br>
47   * [-mode active/passive] <br>
48   * [-ssl no/implicit/explicit]<br>
49   * [-cwd remotepath] <br>
50   * [-digest (crc,md5,sha1)] <br>
51   * [-pre extraCommand1 with ',' as separator of arguments] <br>
52   * -command command from (get,put,append) <br>
53   * [-post extraCommand2 with ',' as separator of arguments]" <br>
54   * <br>
55   * <br>
56   * The order of commands will be:<br>
57   * 1) connection to requestHost on port (if ssl native => using native ssl
58   * link)<br>
59   * 2) User user<br>
60   * 3) PASS pwd<br>
61   * 4) if account => ACCT account<br>
62   * 5) if -ssl & auth => AUTH, PBSZ 0, PROT P <br>
63   * 6) if passive => PASV<br>
64   * 7) CWD remotepath; if error => MKD remotepath then CWD remotepath (and
65   * ignoring any error)<br>
66   * 8) if pre => extraCommand1 with ',' replaced by ' ' (note: do not use
67   * standard commands from FTP like
68   * ACCT,PASS,REIN,USER,APPE,STOR,STOU,RETR,RMD,RNFR,RNTO,ABOR,CWD,CDUP,MODE,PASV,PORT,STRU,TYPE,MDTM,MLSD,MLST,SIZE,AUTH)
69   * <br>
70   * 9) BINARY (binary format)<br>
71   * 10) if get => RETR filepath.basename ; if put => STOR filepath ; if append
72   * =>
73   * APPE filepath.basename<br>
74   * 11) if digest & get/put/append & remote site compatible with XCRC,XMD5,XSHA1
75   * => FEAT (parsing if found
76   * corresponding XCRC,XMD5,XSHA1) ; then XCRC/XMD5/XSHA1 filepath.basename ;
77   * then locally comparing this
78   * XCRC/XMD5/XSHA1 with the local file<br>
79   * 12) if post => extraCommand2 with ',' replaced by ' ' (note: do not use
80   * standard commands from FTP like
81   * ACCT,PASS,REIN,USER,APPE,STOR,STOU,RETR,RMD,RNFR,RNTO,ABOR,CWD,CDUP,MODE,PASV,PORT,STRU,TYPE,MDTM,MLSD,MLST,SIZE,AUTH)<br>
82   * 13) QUIT<br>
83   */
84  public class FtpTransferTask extends AbstractTask {
85    /**
86     * Internal Logger
87     */
88    private static final WaarpLogger logger =
89        WaarpLoggerFactory.getLogger(FtpTransferTask.class);
90  
91    /**
92     * @param argRule
93     * @param delay
94     * @param argTransfer
95     * @param session
96     */
97    public FtpTransferTask(final String argRule, final int delay,
98                           final String argTransfer, final R66Session session) {
99      super(TaskType.FTP, delay, argRule, argTransfer, session);
100   }
101 
102   /**
103    * "-file filepath <br> -to requestedHost <br> -port port <br> -user user <br> -pwd pwd <br> [-account
104    * account] <br> [-mode active/passive] <br> [-ssl no/implicit/explicit]<br> [-cwd remotepath] <br> [-digest
105    * (crc,md5,sha1)] <br> [-pre extraCommand1 with ',' as separator of arguments] <br> -command command from
106    * (get,put,append) <br> [-post extraCommand2 with ',' as separator of arguments]" <br>
107    **/
108 
109   @Override
110   public final void run() {
111     logger.info("FtpTransfer with {}:{} and {}", argRule, argTransfer, session);
112     if (argRule == null) {
113       logger.error(
114           "FtpTransfer cannot be done with " + argRule + ':' + argTransfer +
115           " and " + session);
116       futureCompletion.setFailure(
117           new OpenR66ProtocolSystemException("FtpTransfer cannot be done"));
118       return;
119     }
120     String finalname = argRule;
121     final String[] argFormat = BLANK.split(argTransfer);
122     if (argFormat != null && argFormat.length > 0) {
123       try {
124         finalname = String.format(finalname, (Object[]) argFormat);
125       } catch (final Exception e) {
126         // ignored error since bad argument in static rule info
127         logger.error(
128             "Bad format in Rule: {" + finalname + "} " + e.getMessage());
129       }
130     }
131     final String[] args = BLANK.split(finalname);
132     for (int i = 0; i < args.length; i++) {
133       args[i] = getReplacedValue(args[i], null);
134     }
135     final FtpArgs ftpArgs;
136     try {
137       ftpArgs = FtpArgs.getFtpArgs(args);
138     } catch (final OpenR66RunnerErrorException e) {
139       final OpenR66RunnerErrorException exception =
140           new OpenR66RunnerErrorException("Not enough argument in Transfer");
141       final R66Result result =
142           new R66Result(exception, session, false, ErrorCode.CommandNotFound,
143                         session.getRunner());
144       logger.error("Not enough arguments: " + e.getMessage());
145       futureCompletion.setResult(result);
146       futureCompletion.setFailure(exception);
147       return;
148     }
149 
150     final WaarpFtp4jClient ftpClient =
151         new WaarpFtp4jClient(ftpArgs.getRequested(), ftpArgs.getPort(),
152                              ftpArgs.getUser(), ftpArgs.getPwd(),
153                              ftpArgs.getAcct(), ftpArgs.isPassive(),
154                              ftpArgs.getSsl(), 5000,
155                              (int) Configuration.configuration.getTimeoutCon());
156     boolean status = false;
157     for (int i = 0; i < Configuration.RETRYNB; i++) {
158       if (ftpClient.connect()) {
159         status = true;
160         break;
161       }
162     }
163     if (!status) {
164       final OpenR66RunnerErrorException exception =
165           new OpenR66RunnerErrorException("Cannot connect to remote FTP host");
166       final R66Result result = new R66Result(exception, session, false,
167                                              ErrorCode.ConnectionImpossible,
168                                              session.getRunner());
169       futureCompletion.setResult(result);
170       futureCompletion.setFailure(exception);
171       logger.error(ftpClient.getResult());
172       return;
173     }
174     try {
175       if (ftpArgs.getCwd() != null && !ftpClient.changeDir(ftpArgs.getCwd())) {
176         ftpClient.makeDir(ftpArgs.getCwd());
177         try {
178           Thread.sleep(Configuration.RETRYINMS);
179         } catch (final InterruptedException e) {// NOSONAR
180           // Ignore
181         }
182         if (!ftpClient.changeDir(ftpArgs.getCwd())) {
183           logger.warn("Cannot change od directory: " + ftpArgs.getCwd());
184         }
185       }
186       if (ftpArgs.getPreArgs() != null) {
187         final String[] result = ftpClient.executeCommand(ftpArgs.getPreArgs());
188         for (final String string : result) {
189           logger.debug("PRE: {}", string);
190         }
191       }
192       if (!ftpClient.transferFile(ftpArgs.getFilepath(), ftpArgs.getFilename(),
193                                   ftpArgs.getCodeCommand())) {
194         final OpenR66RunnerErrorException exception =
195             new OpenR66RunnerErrorException(
196                 "Cannot transfert file from/to remote FTP host");
197         final R66Result result =
198             new R66Result(exception, session, false, ErrorCode.TransferError,
199                           session.getRunner());
200         futureCompletion.setResult(result);
201         futureCompletion.setFailure(exception);
202         logger.error(ftpClient.getResult());
203         return;
204       }
205       if (ftpArgs.getDigest() != null) {
206         String[] values = ftpClient.executeCommand(ftpArgs.getDigestCommand());
207         String hashresult = null;
208         if (values != null) {
209           values = BLANK.split(values[0]);
210           hashresult = values.length > 3? values[1] : values[0];
211         }
212         if (hashresult == null) {
213           final OpenR66RunnerErrorException exception =
214               new OpenR66RunnerErrorException(
215                   "Hash cannot be computed while FTP transfer is done");
216           final R66Result result =
217               new R66Result(exception, session, false, ErrorCode.TransferError,
218                             session.getRunner());
219           futureCompletion.setResult(result);
220           futureCompletion.setFailure(exception);
221           logger.error("Hash cannot be computed: " + ftpClient.getResult());
222           return;
223         }
224         // now check locally
225         String hash;
226         try {
227           hash = FilesystemBasedDigest.getHex(
228               FilesystemBasedDigest.getHash(new File(ftpArgs.getFilepath()),
229                                             false, ftpArgs.getDigest()));
230         } catch (final IOException e) {
231           hash = null;
232         }
233         if (hash == null || !hash.equalsIgnoreCase(hashresult)) {
234           final OpenR66RunnerErrorException exception =
235               new OpenR66RunnerErrorException(
236                   "Hash not equal while FTP transfer is done");
237           final R66Result result =
238               new R66Result(exception, session, false, ErrorCode.TransferError,
239                             session.getRunner());
240           futureCompletion.setResult(result);
241           futureCompletion.setFailure(exception);
242           logger.error("Hash not equal: " + ftpClient.getResult());
243           return;
244         }
245       }
246       if (ftpArgs.getPostArgs() != null) {
247         final String[] result = ftpClient.executeCommand(ftpArgs.getPostArgs());
248         for (final String string : result) {
249           logger.debug("POST: {}", string);
250         }
251       }
252     } finally {
253       ftpClient.logout();
254     }
255     final R66Result result = new R66Result(session, false, ErrorCode.TransferOk,
256                                            session.getRunner());
257     futureCompletion.setResult(result);
258     if (logger.isInfoEnabled()) {
259       logger.info("FTP transfer in     SUCCESS     " +
260                   session.getRunner().toShortString() + "     <REMOTE>" +
261                   ftpArgs.getRequested() + "</REMOTE>");
262     }
263     futureCompletion.setSuccess();
264   }
265 
266 }