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.gateway.ftp.exec;
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.common.utility.WaarpStringUtils;
26  import org.waarp.ftp.client.WaarpFtp4jClient;
27  import org.waarp.ftp.core.config.FtpInternalConfiguration;
28  import org.waarp.openr66.context.task.FtpArgs;
29  import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
30  
31  import java.io.File;
32  import java.io.IOException;
33  import java.util.regex.Pattern;
34  
35  /**
36   * FTP client compatible for Waarp Gateway Kernel as JavaExecutor<br>
37   * <br>
38   * Ftp Transfer task: synchronous<br>
39   * <p>
40   * Result of arguments will be as FTP command.<br>
41   * Format is the following:<br>
42   * "-file filepath <br>
43   * -to requestedHost <br>
44   * -port port <br>
45   * -user user <br>
46   * -pwd pwd <br>
47   * [-account account] <br>
48   * [-mode active/passive] <br>
49   * [-ssl no/implicit/explicit]<br>
50   * [-cwd remotepath] <br>
51   * [-digest (crc,md5,sha1)] <br>
52   * [-pre extraCommand1 with ',' as separator of arguments] <br>
53   * -command command from (get,put,append) <br>
54   * [-post extraCommand2 with ',' as separator of arguments]" <br>
55   * <br>
56   * <br>
57   * The order of commands will be:<br>
58   * 1) connection to requestHost on port (if ssl native => using native ssl
59   * link)<br>
60   * 2) User user<br>
61   * 3) PASS pwd<br>
62   * 4) if account => ACCT account<br>
63   * 5) if -ssl & auth => AUTH, PBSZ 0, PROT P <br>
64   * 6) if passive => PASV<br>
65   * 7) CWD remotepath; if error => MKD remotepath then CWD remotepath (and
66   * ignoring any error)<br>
67   * 8) if pre => extraCommand1 with ',' replaced by ' ' (note: do not use
68   * standard commands from FTP like
69   * ACCT,PASS,REIN,USER,APPE,STOR,STOU,RETR,RMD,RNFR,RNTO,ABOR,CWD,CDUP,MODE,PASV,PORT,STRU,TYPE,
70   * MDTM,MLSD,MLST,SIZE,AUTH) <br>
71   * 9) BINARY (binary format)<br>
72   * 10) if get => RETR filepath.basename ; if put => STOR filepath ; if append
73   * =>
74   * APPE filepath.basename<br>
75   * 11) if digest & get/put/append & remote site compatible with XCRC,XMD5,XSHA1
76   * => FEAT (parsing if found
77   * corresponding XCRC,XMD5,XSHA1) ; then XCRC/XMD5/XSHA1 filepath.basename ;
78   * then locally comparing this
79   * XCRC/XMD5/XSHA1 with the local file<br>
80   * 12) if post => extraCommand2 with ',' replaced by ' ' (note: do not use
81   * standard commands from FTP like
82   * ACCT,PASS,REIN,USER,APPE,STOR,STOU,RETR,RMD,RNFR,RNTO,ABOR,CWD,CDUP,MODE,PASV,PORT,STRU,TYPE
83   * ,MDTM,MLSD,MLST,SIZE,AUTH)<br>
84   * 13) QUIT<br>
85   */
86  public class JavaExecutorWaarpFtp4jClient implements GatewayRunnable {
87    /**
88     * Internal Logger
89     */
90    private static final WaarpLogger logger =
91        WaarpLoggerFactory.getLogger(JavaExecutorWaarpFtp4jClient.class);
92    private static final Pattern BLANK = WaarpStringUtils.BLANK;
93  
94    boolean waitForValidation;
95    boolean useLocalExec;
96    int delay;
97    String[] args;
98    int status;
99  
100   public JavaExecutorWaarpFtp4jClient() {
101     // nothing
102   }
103 
104   /**
105    * "-file filepath <br> -to requestedHost <br> -port port <br> -user user <br> -pwd pwd <br> [-account
106    * account] <br> [-mode active/passive] <br> [-ssl no/implicit/explicit]<br> [-cwd remotepath] <br> [-digest
107    * (crc,md5,sha1)] <br> [-pre extraCommand1 with ',' as separator of arguments] <br> -command command from
108    * (get,put,append) <br> [-post extraCommand2 with ',' as separator of arguments]" <br>
109    **/
110   @Override
111   public final void run() {
112     logger.info("FtpTransfer with {} arguments", args.length);
113     final FtpArgs ftpArgs;
114     try {
115       ftpArgs = FtpArgs.getFtpArgs(args);
116     } catch (final OpenR66RunnerErrorException e) {
117       status = -2;
118       logger.error("Not enough arguments: {}", e.getMessage());
119       return;
120     }
121     int timeout = 30000;
122     if (delay > 10000) {
123       timeout = delay;
124     }
125     final WaarpFtp4jClient ftpClient =
126         new WaarpFtp4jClient(ftpArgs.getRequested(), ftpArgs.getPort(),
127                              ftpArgs.getUser(), ftpArgs.getPwd(),
128                              ftpArgs.getAcct(), ftpArgs.isPassive(),
129                              ftpArgs.getSsl(), 5000, timeout);
130     boolean connected = false;
131     for (int i = 0; i < 3; i++) {
132       if (ftpClient.connect()) {
133         connected = true;
134         break;
135       }
136     }
137     if (!connected) {
138       status = -3;
139       logger.error(ftpClient.getResult());
140       return;
141     }
142     try {
143       if (ftpArgs.getCwd() != null && !ftpClient.changeDir(ftpArgs.getCwd())) {
144         ftpClient.makeDir(ftpArgs.getCwd());
145         try {
146           Thread.sleep(FtpInternalConfiguration.RETRYINMS);
147         } catch (final InterruptedException e) {// NOSONAR
148           // Ignore
149         }
150         if (!ftpClient.changeDir(ftpArgs.getCwd())) {
151           logger.warn("Cannot change od directory: " + ftpArgs.getCwd());
152         }
153       }
154       if (ftpArgs.getPreArgs() != null) {
155         final String[] result = ftpClient.executeCommand(ftpArgs.getPreArgs());
156         for (final String string : result) {
157           logger.debug("PRE: {}", string);
158         }
159       }
160       if (!ftpClient.transferFile(ftpArgs.getFilepath(), ftpArgs.getFilename(),
161                                   ftpArgs.getCodeCommand())) {
162         status = -4;
163         logger.error(ftpClient.getResult());
164         return;
165       }
166       if (ftpArgs.getDigest() != null) {
167         String[] values = ftpClient.executeCommand(ftpArgs.getDigestCommand());
168         String hashresult = null;
169         if (values != null) {
170           values = BLANK.split(values[0]);
171           hashresult = values.length > 3? values[1] : values[0];
172         }
173         if (hashresult == null) {
174           status = -5;
175           logger.error("Hash cannot be computed: " + ftpClient.getResult());
176           return;
177         }
178         // now check locally
179         String hash;
180         try {
181           hash = FilesystemBasedDigest.getHex(
182               FilesystemBasedDigest.getHash(new File(ftpArgs.getFilepath()),
183                                             false, ftpArgs.getDigest()));
184         } catch (final IOException e) {
185           hash = null;
186         }
187         if (hash == null || !hash.equalsIgnoreCase(hashresult)) {
188           status = -6;
189           logger.error("Hash not equal: " + ftpClient.getResult());
190           return;
191         }
192       }
193       if (ftpArgs.getPostArgs() != null) {
194         final String[] result = ftpClient.executeCommand(ftpArgs.getPostArgs());
195         for (final String string : result) {
196           logger.debug("POST: {}", string);
197         }
198       }
199     } finally {
200       ftpClient.logout();
201     }
202     logger.info("FTP transfer in\n    SUCCESS\n    {}\n    <REMOTE>{}</REMOTE>",
203                 ftpArgs.getFilepath(), ftpArgs.getRequested());
204     status = 0;
205   }
206 
207   @Override
208   public final void setArgs(final boolean waitForValidation,
209                             final boolean useLocalExec, final int delay,
210                             final String[] args) {
211     this.waitForValidation = waitForValidation;
212     this.useLocalExec = useLocalExec;
213     this.delay = delay;
214     this.args = args;
215   }
216 
217   @Override
218   public final int getFinalStatus() {
219     return status;
220   }
221 
222 }