JavaExecutorWaarpFtp4jClient.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.gateway.ftp.exec;
import org.waarp.common.digest.FilesystemBasedDigest;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.WaarpStringUtils;
import org.waarp.ftp.client.WaarpFtp4jClient;
import org.waarp.ftp.core.config.FtpInternalConfiguration;
import org.waarp.openr66.context.task.FtpArgs;
import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
import java.io.File;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* FTP client compatible for Waarp Gateway Kernel as JavaExecutor<br>
* <br>
* Ftp Transfer task: synchronous<br>
* <p>
* Result of arguments will be as FTP command.<br>
* Format is the following:<br>
* "-file filepath <br>
* -to requestedHost <br>
* -port port <br>
* -user user <br>
* -pwd pwd <br>
* [-account account] <br>
* [-mode active/passive] <br>
* [-ssl no/implicit/explicit]<br>
* [-cwd remotepath] <br>
* [-digest (crc,md5,sha1)] <br>
* [-pre extraCommand1 with ',' as separator of arguments] <br>
* -command command from (get,put,append) <br>
* [-post extraCommand2 with ',' as separator of arguments]" <br>
* <br>
* <br>
* The order of commands will be:<br>
* 1) connection to requestHost on port (if ssl native => using native ssl
* link)<br>
* 2) User user<br>
* 3) PASS pwd<br>
* 4) if account => ACCT account<br>
* 5) if -ssl & auth => AUTH, PBSZ 0, PROT P <br>
* 6) if passive => PASV<br>
* 7) CWD remotepath; if error => MKD remotepath then CWD remotepath (and
* ignoring any error)<br>
* 8) if pre => extraCommand1 with ',' replaced by ' ' (note: do not use
* standard commands from FTP like
* 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>
* 9) BINARY (binary format)<br>
* 10) if get => RETR filepath.basename ; if put => STOR filepath ; if append
* =>
* APPE filepath.basename<br>
* 11) if digest & get/put/append & remote site compatible with XCRC,XMD5,XSHA1
* => FEAT (parsing if found
* corresponding XCRC,XMD5,XSHA1) ; then XCRC/XMD5/XSHA1 filepath.basename ;
* then locally comparing this
* XCRC/XMD5/XSHA1 with the local file<br>
* 12) if post => extraCommand2 with ',' replaced by ' ' (note: do not use
* standard commands from FTP like
* 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>
* 13) QUIT<br>
*/
public class JavaExecutorWaarpFtp4jClient implements GatewayRunnable {
/**
* Internal Logger
*/
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(JavaExecutorWaarpFtp4jClient.class);
private static final Pattern BLANK = WaarpStringUtils.BLANK;
boolean waitForValidation;
boolean useLocalExec;
int delay;
String[] args;
int status;
public JavaExecutorWaarpFtp4jClient() {
// nothing
}
/**
* "-file filepath <br> -to requestedHost <br> -port port <br> -user user <br> -pwd pwd <br> [-account
* account] <br> [-mode active/passive] <br> [-ssl no/implicit/explicit]<br> [-cwd remotepath] <br> [-digest
* (crc,md5,sha1)] <br> [-pre extraCommand1 with ',' as separator of arguments] <br> -command command from
* (get,put,append) <br> [-post extraCommand2 with ',' as separator of arguments]" <br>
**/
@Override
public final void run() {
logger.info("FtpTransfer with {} arguments", args.length);
final FtpArgs ftpArgs;
try {
ftpArgs = FtpArgs.getFtpArgs(args);
} catch (final OpenR66RunnerErrorException e) {
status = -2;
logger.error("Not enough arguments: {}", e.getMessage());
return;
}
int timeout = 30000;
if (delay > 10000) {
timeout = delay;
}
final WaarpFtp4jClient ftpClient =
new WaarpFtp4jClient(ftpArgs.getRequested(), ftpArgs.getPort(),
ftpArgs.getUser(), ftpArgs.getPwd(),
ftpArgs.getAcct(), ftpArgs.isPassive(),
ftpArgs.getSsl(), 5000, timeout);
boolean connected = false;
for (int i = 0; i < 3; i++) {
if (ftpClient.connect()) {
connected = true;
break;
}
}
if (!connected) {
status = -3;
logger.error(ftpClient.getResult());
return;
}
try {
if (ftpArgs.getCwd() != null && !ftpClient.changeDir(ftpArgs.getCwd())) {
ftpClient.makeDir(ftpArgs.getCwd());
try {
Thread.sleep(FtpInternalConfiguration.RETRYINMS);
} catch (final InterruptedException e) {// NOSONAR
// Ignore
}
if (!ftpClient.changeDir(ftpArgs.getCwd())) {
logger.warn("Cannot change od directory: " + ftpArgs.getCwd());
}
}
if (ftpArgs.getPreArgs() != null) {
final String[] result = ftpClient.executeCommand(ftpArgs.getPreArgs());
for (final String string : result) {
logger.debug("PRE: {}", string);
}
}
if (!ftpClient.transferFile(ftpArgs.getFilepath(), ftpArgs.getFilename(),
ftpArgs.getCodeCommand())) {
status = -4;
logger.error(ftpClient.getResult());
return;
}
if (ftpArgs.getDigest() != null) {
String[] values = ftpClient.executeCommand(ftpArgs.getDigestCommand());
String hashresult = null;
if (values != null) {
values = BLANK.split(values[0]);
hashresult = values.length > 3? values[1] : values[0];
}
if (hashresult == null) {
status = -5;
logger.error("Hash cannot be computed: " + ftpClient.getResult());
return;
}
// now check locally
String hash;
try {
hash = FilesystemBasedDigest.getHex(
FilesystemBasedDigest.getHash(new File(ftpArgs.getFilepath()),
false, ftpArgs.getDigest()));
} catch (final IOException e) {
hash = null;
}
if (hash == null || !hash.equalsIgnoreCase(hashresult)) {
status = -6;
logger.error("Hash not equal: " + ftpClient.getResult());
return;
}
}
if (ftpArgs.getPostArgs() != null) {
final String[] result = ftpClient.executeCommand(ftpArgs.getPostArgs());
for (final String string : result) {
logger.debug("POST: {}", string);
}
}
} finally {
ftpClient.logout();
}
logger.info("FTP transfer in\n SUCCESS\n {}\n <REMOTE>{}</REMOTE>",
ftpArgs.getFilepath(), ftpArgs.getRequested());
status = 0;
}
@Override
public final void setArgs(final boolean waitForValidation,
final boolean useLocalExec, final int delay,
final String[] args) {
this.waitForValidation = waitForValidation;
this.useLocalExec = useLocalExec;
this.delay = delay;
this.args = args;
}
@Override
public final int getFinalStatus() {
return status;
}
}