/** * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the * COPYRIGHT.txt in the distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation; either version 3.0 of the * License, or (at your option) any later version. * * This software 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.waarp.gateway.kernel.exec; import org.waarp.common.command.exception.CommandAbstractException; import org.waarp.common.future.WaarpFuture; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.common.utility.UUID; import org.waarp.gateway.kernel.session.CommandExecutorInterface; import org.waarp.gateway.kernel.session.HttpAuthInterface; /** * Abstract Executor class. If the command starts with "REFUSED", the command will be refused for * execution. If "REFUSED" is set, the command "RETR" or "STOR" like operations will be stopped at * starting of command.<br> * If the command starts with "EXECUTE", the following will be a command to be executed.<br> * If the command starts with "JAVAEXECUTE", the following will be a command through Java class to * be executed.<br> * If the command starts with "R66PREPARETRANSFER", the following will be a r66 prepare transfer * execution (asynchronous operation only).<br> * * * The following replacement are done dynamically before the command is executed:<br> * - #BASEPATH# is replaced by the full path for the root of FTP Directory<br> * - #FILE# is replaced by the current file path relative to FTP Directory (so #BASEPATH##FILE# is * the full path of the file)<br> * - #USER# is replaced by the username<br> * - #ACCOUNT# is replaced by the account<br> * - #COMMAND# is replaced by the command issued for the file<br> * - #SPECIALID# is replaced by the FTP id of the transfer (whatever in or out)<br> * - #UUID# is replaced by a special UUID globally unique for the transfer, in general to be placed in -info part (for instance ##UUID## giving #uuid#)<br> * * @author Frederic Bregier * */ public abstract class AbstractExecutor { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory .getLogger(AbstractExecutor.class); protected static final String USER = "#USER#"; protected static final String ACCOUNT = "#ACCOUNT#"; protected static final String BASEPATH = "#BASEPATH#"; protected static final String FILE = "#FILE#"; protected static final String COMMAND = "#COMMAND#"; protected static final String SPECIALID = "#SPECIALID#"; protected static final String sUUID = "#sUUID#"; protected static final String REFUSED = "REFUSED"; protected static final String NONE = "NONE"; protected static final String EXECUTE = "EXECUTE"; protected static final String JAVAEXECUTE = "JAVAEXECUTE"; protected static final String R66PREPARETRANSFER = "R66PREPARETRANSFER"; protected static final int tREFUSED = -1; protected static final int tNONE = 0; protected static final int tEXECUTE = 1; protected static final int tR66PREPARETRANSFER = 2; protected static final int tJAVAEXECUTE = 3; protected static CommandExecutor commandExecutor = null; /** * For OpenR66 access */ public static boolean useDatabase = false; /** * Local Exec Daemon is used or not for execution of external commands */ public static boolean useLocalExec = false; public static class CommandExecutor implements CommandExecutorInterface { /** * Retrieve External Command */ public String pretrCMD; public int pretrType; public boolean pretrRefused; /** * Retrieve Delay (0 = unlimited) */ public long pretrDelay; /** * Store External Command */ public String pstorCMD; public int pstorType; public boolean pstorRefused; /** * Store Delay (0 = unlimited) */ public long pstorDelay; /** * * @param retrieve * @param retrDelay * @param store * @param storDelay */ public CommandExecutor(String retrieve, long retrDelay, String store, long storDelay) { if (retrieve == null || retrieve.trim().length() == 0) { pretrCMD = commandExecutor.pretrCMD; pretrType = commandExecutor.pretrType; pretrRefused = commandExecutor.pretrRefused; } else if (isRefused(retrieve)) { pretrCMD = REFUSED; pretrType = tREFUSED; pretrRefused = true; } else { if (isExecute(retrieve)) { pretrCMD = getExecuteCmd(retrieve); pretrType = tEXECUTE; } else if (isR66PrepareTransfer(retrieve)) { pretrCMD = getR66PrepareTransferCmd(retrieve); pretrType = tR66PREPARETRANSFER; useDatabase = true; } else if (isJavaExecute(retrieve)) { pretrCMD = getJavaExecuteCmd(retrieve); pretrType = tJAVAEXECUTE; } else { // Default NONE pretrCMD = getNone(retrieve); pretrType = tNONE; } } pretrDelay = retrDelay; if (store == null || store.trim().length() == 0) { pstorCMD = commandExecutor.pstorCMD; pstorRefused = commandExecutor.pstorRefused; pstorType = commandExecutor.pstorType; } else if (isRefused(store)) { pstorCMD = REFUSED; pstorRefused = true; pstorType = tREFUSED; } else { if (isExecute(store)) { pstorCMD = getExecuteCmd(store); pstorType = tEXECUTE; } else if (isR66PrepareTransfer(store)) { pstorCMD = getR66PrepareTransferCmd(store); pstorType = tR66PREPARETRANSFER; useDatabase = true; } else if (isJavaExecute(store)) { pstorCMD = getJavaExecuteCmd(store); pstorType = tJAVAEXECUTE; } else { // Default NONE pstorCMD = getNone(store); pstorType = tNONE; } } pstorDelay = storDelay; } @Override public boolean isValidOperation(boolean isStore) { if (isStore && pstorRefused) { logger.info("STORe like operations REFUSED"); return false; } else if ((!isStore) && pretrRefused) { logger.info("RETRieve operations REFUSED"); return false; } return true; } @Override public String getRetrType() { switch (pretrType) { case tREFUSED: return REFUSED; case tNONE: return NONE; case tEXECUTE: return EXECUTE; case tR66PREPARETRANSFER: return R66PREPARETRANSFER; case tJAVAEXECUTE: return JAVAEXECUTE; default: return NONE; } } @Override public String getStorType() { switch (pstorType) { case tREFUSED: return REFUSED; case tNONE: return NONE; case tEXECUTE: return EXECUTE; case tR66PREPARETRANSFER: return R66PREPARETRANSFER; case tJAVAEXECUTE: return JAVAEXECUTE; default: return NONE; } } } private static String getNone(String cmd) { return cmd.substring(NONE.length()).trim(); } private static String getExecuteCmd(String cmd) { return cmd.substring(EXECUTE.length()).trim(); } private static String getJavaExecuteCmd(String cmd) { return cmd.substring(JAVAEXECUTE.length()).trim(); } private static String getR66PrepareTransferCmd(String cmd) { return cmd.substring(R66PREPARETRANSFER.length()).trim(); } private static boolean isRefused(String cmd) { return cmd.startsWith(REFUSED); } private static boolean isExecute(String cmd) { return cmd.startsWith(EXECUTE); } private static boolean isJavaExecute(String cmd) { return cmd.startsWith(JAVAEXECUTE); } private static boolean isR66PrepareTransfer(String cmd) { return cmd.startsWith(R66PREPARETRANSFER); } /** * Initialize the Executor with the correct command and delay * * @param retrieve * @param retrDelay * @param store * @param storDelay */ public static void initializeExecutor(String retrieve, long retrDelay, String store, long storDelay) { commandExecutor = new CommandExecutor(retrieve, retrDelay, store, storDelay); logger.info("Executor configured as [RETR: " + commandExecutor.getRetrType() + ":" + commandExecutor.pretrCMD + ":" + commandExecutor.pretrDelay + ":" + commandExecutor.pretrRefused + "] [STOR: " + commandExecutor.getStorType() + ":" + commandExecutor.pstorCMD + ":" + commandExecutor.pstorDelay + ":" + commandExecutor.pstorRefused + "]"); } /** * Check if the given operation is allowed Globally * * @param isStore * @return True if allowed, else False */ public static boolean isValidOperation(boolean isStore) { return commandExecutor.isValidOperation(isStore); } /** * @param auth * the current Authentication * @param args * containing in that order * "User Account BaseDir FilePath(relative to BaseDir) Command" * @param isStore * True for a STORE like operation, else False * @param futureCompletion */ public static AbstractExecutor createAbstractExecutor(HttpAuthInterface auth, String[] args, boolean isStore, WaarpFuture futureCompletion) { if (isStore) { CommandExecutor executor = (CommandExecutor) auth.getCommandExecutor(); if (executor == null) { executor = commandExecutor; } else if (executor.pstorType == tNONE) { String replaced = getPreparedCommand(executor.pstorCMD, args); return new NoTaskExecutor(replaced, executor.pstorDelay, futureCompletion); } if (executor.pstorRefused) { logger.error("STORe like operation REFUSED"); futureCompletion.cancel(); return null; } String replaced = getPreparedCommand(executor.pstorCMD, args); switch (executor.pstorType) { case tREFUSED: logger.error("STORe like operation REFUSED"); futureCompletion.cancel(); return null; case tEXECUTE: return new ExecuteExecutor(replaced, executor.pstorDelay, futureCompletion); case tJAVAEXECUTE: return new JavaExecutor(replaced, executor.pstorDelay, futureCompletion); case tR66PREPARETRANSFER: return new R66PreparedTransferExecutor(replaced, executor.pstorDelay, futureCompletion); default: return new NoTaskExecutor(replaced, executor.pstorDelay, futureCompletion); } } else { CommandExecutor executor = (CommandExecutor) auth.getCommandExecutor(); if (executor == null) { executor = commandExecutor; } else if (executor.pretrType == tNONE) { String replaced = getPreparedCommand(executor.pretrCMD, args); return new NoTaskExecutor(replaced, executor.pretrDelay, futureCompletion); } if (executor.pretrRefused) { logger.error("RETRieve operation REFUSED"); futureCompletion.cancel(); return null; } String replaced = getPreparedCommand(executor.pretrCMD, args); switch (executor.pretrType) { case tREFUSED: logger.error("RETRieve operation REFUSED"); futureCompletion.cancel(); return null; case tEXECUTE: return new ExecuteExecutor(replaced, executor.pretrDelay, futureCompletion); case tJAVAEXECUTE: return new JavaExecutor(replaced, executor.pretrDelay, futureCompletion); case tR66PREPARETRANSFER: return new R66PreparedTransferExecutor(replaced, executor.pretrDelay, futureCompletion); default: return new NoTaskExecutor(replaced, executor.pretrDelay, futureCompletion); } } } /** * * @param command * @param args * as {User, Account, BaseDir, FilePath(relative to BaseDir), Command} * @return the prepared command */ public static String getPreparedCommand(String command, String[] args) { StringBuilder builder = new StringBuilder(command); logger.debug("Will replace value in " + command + " with User=" + args[0] + ":Acct=" + args[1] + ":Base=" + args[2] + ":File=" + args[3] + ":Cmd=" + args[4]); replaceAll(builder, USER, args[0]); replaceAll(builder, ACCOUNT, args[1]); replaceAll(builder, BASEPATH, args[2]); replaceAll(builder, FILE, args[3]); replaceAll(builder, COMMAND, args[4]); replaceAll(builder, SPECIALID, args[5]); if (builder.indexOf(sUUID) > 0) { replaceAll(builder, sUUID, new UUID().toString()); } logger.debug("Result: {}", builder); return builder.toString(); } /** * Make a replacement of first "find" string by "replace" string into the StringBuilder * * @param builder * @param find * @param replace */ public static boolean replace(StringBuilder builder, String find, String replace) { int start = builder.indexOf(find); if (start == -1) { return false; } int end = start + find.length(); builder.replace(start, end, replace); return true; } /** * Make replacement of all "find" string by "replace" string into the StringBuilder * * @param builder * @param find * @param replace */ public static void replaceAll(StringBuilder builder, String find, String replace) { while (replace(builder, find, replace)) { } } public static CommandExecutor getCommandExecutor() { return commandExecutor; } public abstract void run() throws CommandAbstractException; }