/** This file is part of Waarp Project. 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. 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.openr66.protocol.localhandler; import static org.waarp.openr66.context.R66FiniteDualStates.*; import java.security.NoSuchAlgorithmException; import io.netty.channel.Channel; import io.netty.channel.local.LocalChannel; import org.waarp.common.command.exception.CommandAbstractException; import org.waarp.common.database.exception.WaarpDatabaseException; import org.waarp.common.database.exception.WaarpDatabaseNoDataException; import org.waarp.common.digest.FilesystemBasedDigest; import org.waarp.common.digest.FilesystemBasedDigest.DigestAlgo; import org.waarp.common.exception.FileTransferException; import org.waarp.common.file.DataBlock; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.openr66.context.ErrorCode; import org.waarp.openr66.context.R66Result; import org.waarp.openr66.context.task.AbstractTask; import org.waarp.openr66.context.task.TaskType; import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException; import org.waarp.openr66.database.DbConstant; import org.waarp.openr66.database.data.DbRule; import org.waarp.openr66.database.data.DbTaskRunner; import org.waarp.openr66.protocol.configuration.Configuration; import org.waarp.openr66.protocol.configuration.Messages; import org.waarp.openr66.protocol.configuration.PartnerConfiguration; import org.waarp.openr66.protocol.exception.OpenR66DatabaseGlobalException; import org.waarp.openr66.protocol.exception.OpenR66Exception; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessQueryAlreadyFinishedException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolBusinessQueryStillRunningException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolNoDataException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolNotAuthenticatedException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolNotYetConnectionException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException; import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException; import org.waarp.openr66.protocol.localhandler.packet.DataPacket; import org.waarp.openr66.protocol.localhandler.packet.EndRequestPacket; import org.waarp.openr66.protocol.localhandler.packet.EndTransferPacket; import org.waarp.openr66.protocol.localhandler.packet.ErrorPacket; import org.waarp.openr66.protocol.localhandler.packet.JsonCommandPacket; import org.waarp.openr66.protocol.localhandler.packet.LocalPacketFactory; import org.waarp.openr66.protocol.localhandler.packet.RequestPacket; import org.waarp.openr66.protocol.localhandler.packet.ValidPacket; import org.waarp.openr66.protocol.localhandler.packet.json.RequestJsonPacket; import org.waarp.openr66.protocol.networkhandler.NetworkTransaction; import org.waarp.openr66.protocol.utils.ChannelCloseTimer; import org.waarp.openr66.protocol.utils.ChannelUtils; import org.waarp.openr66.protocol.utils.FileUtils; import org.waarp.openr66.protocol.utils.R66Future; /** * Class to implement actions related to real transfer: request initialization, data transfer, end of transfer and of request, * changing filename or filesize. * * @author "Frederic Bregier" * */ public class TransferActions extends ServerActions { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory .getLogger(TransferActions.class); public TransferActions() { } /** * Finalize a request initialization in error * * @param channel * @param code * @param runner * @param e1 * @param packet * @throws OpenR66ProtocolPacketException */ private final void endInitRequestInError(Channel channel, ErrorCode code, DbTaskRunner runner, OpenR66Exception e1, RequestPacket packet) throws OpenR66ProtocolPacketException { logger.error("TaskRunner initialisation in error: " + code.mesg + " " + session + " {} runner {}", e1 != null ? e1.getMessage() : "no exception", (runner != null ? runner.toShortString() : "no runner")); logger.debug("DEBUG Full stack", e1); localChannelReference.invalidateRequest(new R66Result( e1, session, true, code, null)); if (packet.isToValidate()) { // / answer with a wrong request since runner is not set on remote host if (runner != null) { if (runner.isSender()) { // In case Wildcard was used logger.debug("New FILENAME: {}", runner.getOriginalFilename()); packet.setFilename(runner.getOriginalFilename()); logger.debug("Rank set: " + runner.getRank()); packet.setRank(runner.getRank()); } else { logger.debug("Rank set: " + runner.getRank()); packet.setRank(runner.getRank()); } } packet.validate(); packet.setCode(code.code); session.newState(ERROR); ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet, true); } else { session.newState(ERROR); ErrorPacket error = new ErrorPacket( "TaskRunner initialisation in error: " + e1 .getMessage() + " for " + packet.toString() + " since " + code.mesg, code.getCode(), ErrorPacket.FORWARDCLOSECODE); ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } session.setStatus(47); ChannelCloseTimer.closeFutureChannel(channel); } /** * Receive a request of Transfer * * @param channel * @param packet * @throws OpenR66ProtocolNoDataException * @throws OpenR66ProtocolPacketException * @throws OpenR66ProtocolBusinessException * @throws OpenR66ProtocolSystemException * @throws OpenR66RunnerErrorException */ public void request(LocalChannel channel, RequestPacket packet) throws OpenR66ProtocolNoDataException, OpenR66ProtocolPacketException, OpenR66RunnerErrorException, OpenR66ProtocolSystemException, OpenR66ProtocolBusinessException { session.setStatus(99); if (!session.isAuthenticated()) { session.setStatus(48); throw new OpenR66ProtocolNotAuthenticatedException( Messages.getString("LocalServerHandler.3")); //$NON-NLS-1$ } if (packet.isToValidate()) { session.newState(REQUESTR); } // XXX validLimit only on requested side if (packet.isToValidate()) { if (Configuration.configuration.isShutdown()) { logger.warn(Messages.getString("LocalServerHandler.7") //$NON-NLS-1$ + packet.getRulename() + " from " + session.getAuth().toString()); session.setStatus(100); endInitRequestInError(channel, ErrorCode.ServerOverloaded, null, new OpenR66ProtocolNotYetConnectionException( "All new Request blocked"), packet); session.setStatus(100); return; } if (Configuration.configuration.getConstraintLimitHandler().checkConstraints()) { if (Configuration.configuration.getR66Mib() != null) { Configuration.configuration.getR66Mib(). notifyOverloaded("Rule: " + packet.getRulename() + " from " + session.getAuth().toString(), Configuration.configuration.getConstraintLimitHandler().lastAlert); } logger.warn(Messages.getString("LocalServerHandler.8") //$NON-NLS-1$ + packet.getRulename() + " while " + Configuration.configuration.getConstraintLimitHandler().lastAlert + " from " + session.getAuth().toString()); session.setStatus(100); endInitRequestInError(channel, ErrorCode.ServerOverloaded, null, new OpenR66ProtocolNotYetConnectionException( "Limit exceeded " + Configuration.configuration.getConstraintLimitHandler().lastAlert), packet); session.setStatus(100); return; } } else if (packet.getCode() == ErrorCode.ServerOverloaded.code) { // XXX unvalid limit on requested host received logger.info("TaskRunner initialisation in error: " + ErrorCode.ServerOverloaded.mesg); localChannelReference.invalidateRequest(new R66Result( null, session, true, ErrorCode.ServerOverloaded, null)); session.setStatus(101); ChannelCloseTimer.closeFutureChannel(channel); return; } DbRule rule; try { rule = new DbRule(localChannelReference.getDbSession(), packet.getRulename()); } catch (WaarpDatabaseException e) { logger.info("Rule is unknown: " + packet.getRulename() + " {}", e.getMessage()); session.setStatus(49); endInitRequestInError(channel, ErrorCode.QueryRemotelyUnknown, null, new OpenR66ProtocolBusinessException( Messages.getString("LocalServerHandler.9") + //$NON-NLS-1$ packet.getRulename()), packet); return; } int blocksize = packet.getBlocksize(); if (packet.isToValidate()) { if (!rule.checkHostAllow(session.getAuth().getUser())) { session.setStatus(30); throw new OpenR66ProtocolNotAuthenticatedException( Messages.getString("LocalServerHandler.10")); //$NON-NLS-1$ } // Check if the blocksize is greater than local value if (Configuration.configuration.getBLOCKSIZE() < blocksize) { blocksize = Configuration.configuration.getBLOCKSIZE(); String sep = localChannelReference.getPartner().getSeperator(); packet = new RequestPacket(packet.getRulename(), packet.getMode(), packet.getFilename(), blocksize, packet.getRank(), packet.getSpecialId(), packet.getFileInformation(), packet.getOriginalSize(), sep); } } if (!RequestPacket.isCompatibleMode(rule.getMode(), packet.getMode())) { // not compatible Rule and mode in request throw new OpenR66ProtocolNotAuthenticatedException( Messages.getString("LocalServerHandler.12") + rule.getMode() + " vs " //$NON-NLS-1$ + packet.getMode()); } session.setBlockSize(blocksize); DbTaskRunner runner; // requested boolean isRetrieve = DbTaskRunner.getSenderByRequestPacket(packet); if (packet.getSpecialId() != DbConstant.ILLEGALVALUE) { // Reload or create String requested = DbTaskRunner.getRequested(session, packet); String requester = DbTaskRunner.getRequester(session, packet); logger.debug("DEBUG: " + packet.getSpecialId() + ":" + isRetrieve); if (packet.isToValidate()) { // Id could be a creation or a reload // Try reload try { runner = new DbTaskRunner(localChannelReference.getDbSession(), session, rule, packet.getSpecialId(), requester, requested); // Patch to prevent self request to be stored by sender boolean ignoreSave = runner.shallIgnoreSave(); runner.setSender(isRetrieve); logger.debug("DEBUG: " + runner.getSpecialId() + ":" + ignoreSave + ":" + runner.shallIgnoreSave() + ":" + isRetrieve); if (ignoreSave && !runner.shallIgnoreSave() && !runner.checkFromDbForSubmit()) { // Since status changed, it means that object should be created and not reloaded // But in case of submit, item already exist so shall be loaded from database throw new WaarpDatabaseNoDataException("False load, must reopen and create DbTaskRunner"); } } catch (WaarpDatabaseNoDataException e) { // Reception of request from requester host try { runner = new DbTaskRunner(localChannelReference.getDbSession(), session, rule, isRetrieve, packet); logger.debug("Runner before any action: {} {}", runner.shallIgnoreSave(), runner); } catch (WaarpDatabaseException e1) { session.setStatus(33); endInitRequestInError(channel, ErrorCode.QueryRemotelyUnknown, null, new OpenR66DatabaseGlobalException(e), packet); return; } } catch (WaarpDatabaseException e) { session.setStatus(34); endInitRequestInError(channel, ErrorCode.QueryRemotelyUnknown, null, new OpenR66DatabaseGlobalException(e), packet); return; } if (runner.isAllDone()) { // truly an error since done session.setStatus(31); endInitRequestInError(channel, ErrorCode.QueryAlreadyFinished, runner, new OpenR66ProtocolBusinessQueryAlreadyFinishedException( Messages.getString("LocalServerHandler.13") //$NON-NLS-1$ + packet.getSpecialId()), packet); return; } LocalChannelReference lcr = Configuration.configuration.getLocalTransaction(). getFromRequest( requested + " " + requester + " " + packet.getSpecialId()); if (lcr != null) { // truly an error since still running session.setStatus(32); endInitRequestInError(channel, ErrorCode.QueryStillRunning, runner, new OpenR66ProtocolBusinessQueryStillRunningException( Messages.getString("LocalServerHandler.14") //$NON-NLS-1$ + packet.getSpecialId()), packet); return; } logger.debug("Runner before any action: {} {}", runner.shallIgnoreSave(), runner); // ok to restart try { if (runner.restart(false)) { runner.saveStatus(); } } catch (OpenR66RunnerErrorException e) { } // Change the SpecialID! => could generate an error ? packet.setSpecialId(runner.getSpecialId()); } else { // Id should be a reload try { runner = new DbTaskRunner(localChannelReference.getDbSession(), session, rule, packet.getSpecialId(), requester, requested); } catch (WaarpDatabaseException e) { if (localChannelReference.getDbSession() == null) { // Special case of no database client try { runner = new DbTaskRunner(localChannelReference.getDbSession(), session, rule, isRetrieve, packet); logger.debug("Runner before any action: {} {}", runner.shallIgnoreSave(), runner); } catch (WaarpDatabaseException e1) { session.setStatus(35); endInitRequestInError(channel, ErrorCode.QueryRemotelyUnknown, null, new OpenR66DatabaseGlobalException(e1), packet); return; } } else { endInitRequestInError(channel, ErrorCode.QueryRemotelyUnknown, null, new OpenR66DatabaseGlobalException(e), packet); session.setStatus(36); return; } } runner.setSender(isRetrieve); // FIX check for SelfRequest if (runner.isSelfRequest()) { runner.setFilename(runner.getOriginalFilename()); } if (!runner.isSender()) { logger.debug("New filename ? :" + packet.getFilename()); runner.setOriginalFilename(packet.getFilename()); if (runner.getRank() == 0) { runner.setFilename(packet.getFilename()); } } logger.debug("Runner before any action: {} {}", runner.shallIgnoreSave(), runner); try { if (runner.restart(false)) { if (!runner.isSelfRequest()) { runner.saveStatus(); } } } catch (OpenR66RunnerErrorException e) { } } } else { // Very new request // should not be the case (the requester should always set the id) logger.error("NO TransferID specified: SHOULD NOT BE THE CASE"); try { runner = new DbTaskRunner(localChannelReference.getDbSession(), session, rule, isRetrieve, packet); } catch (WaarpDatabaseException e) { session.setStatus(37); endInitRequestInError(channel, ErrorCode.QueryRemotelyUnknown, null, new OpenR66DatabaseGlobalException(e), packet); return; } packet.setSpecialId(runner.getSpecialId()); } logger.debug("Runner before any action: {} {}", runner.shallIgnoreSave(), runner); // Check now if request is a valid one if (packet.getCode() != ErrorCode.InitOk.code) { // not valid so create an error from there ErrorCode code = ErrorCode.getFromCode("" + packet.getCode()); session.setBadRunner(runner, code); if (!runner.shallIgnoreSave()) { runner.saveStatus(); } session.newState(ERROR); logger.error("Bad runner at startup {} {}", packet, session); ErrorPacket errorPacket = new ErrorPacket(code.mesg, code.getCode(), ErrorPacket.FORWARDCLOSECODE); errorMesg(channel, errorPacket); return; } // Receiver can specify a rank different from database if (runner.isSender()) { logger.debug("Rank was: " + runner.getRank() + " -> " + packet.getRank()); runner.setRankAtStartup(packet.getRank()); } else { if (runner.getRank() > packet.getRank()) { logger.debug("Recv Rank was: " + runner.getRank() + " -> " + packet.getRank()); // if receiver, change only if current rank is upper proposed rank runner.setRankAtStartup(packet.getRank()); } if (packet.getOriginalSize() > 0) { runner.setOriginalSize(packet.getOriginalSize()); } } logger.debug("Filesize: " + packet.getOriginalSize() + ":" + runner.isSender()); boolean shouldInformBack = false; try { session.setRunner(runner); // Fix to ensure that recv request are not trying to access to not chroot files if (Configuration.configuration.isChrootChecked() && packet.isToValidate() && runner.isSender()) { session.startup(true); } else { session.startup(false); } if (runner.isSender() && !runner.isSendThrough()) { if (packet.getOriginalSize() != runner.getOriginalSize()) { packet.setOriginalSize(runner.getOriginalSize()); shouldInformBack = true; logger.debug("Filesize2: " + packet.getOriginalSize() + ":" + runner.isSender()); } } } catch (OpenR66RunnerErrorException e) { try { runner.saveStatus(); } catch (OpenR66RunnerErrorException e1) { logger.error("Cannot save Status: " + runner, e1); } if (runner.getErrorInfo() == ErrorCode.InitOk || runner.getErrorInfo() == ErrorCode.PreProcessingOk || runner.getErrorInfo() == ErrorCode.TransferOk) { runner.setErrorExecutionStatus(ErrorCode.ExternalOp); } logger.error("PreTask in error {}", e.getMessage(), e); errorToSend("PreTask in error: " + e.getMessage(), runner.getErrorInfo(), channel, 38); return; } logger.debug("Filesize: " + packet.getOriginalSize() + ":" + runner.isSender()); if (! shouldInformBack) { shouldInformBack = ! packet.getFileInformation().equals(runner.getFileInformation()); } if (runner.isFileMoved() && runner.isSender() && runner.isInTransfer() && runner.getRank() == 0 && (!packet.isToValidate())) { // File was moved during PreTask and very beginning of the transfer // and the remote host has already received the request packet // => Informs the receiver of the new name sendFilenameFilesizeChanging(packet, runner, "Will send a modification of filename due to pretask: ", "Change Filename by Pre action on sender"); } else if ((!packet.getFilename().equals(runner.getOriginalFilename())) && runner.isSender() && runner.isInTransfer() && runner.getRank() == 0 && (!packet.isToValidate())) { // File was modify at the very beginning (using wildcards) // and the remote host has already received the request packet // => Informs the receiver of the new name sendFilenameFilesizeChanging(packet, runner, "Will send a modification of filename due to wildcard: ", "Change Filename by Wildcard on sender"); } else if (runner.isSelfRequest() && runner.isSender() && runner.isInTransfer() && runner.getRank() == 0 && (!packet.isToValidate())) { // FIX SelfRequest // File could be modified at the very beginning (using wildcards) // and the remote host has already received the request packet // => Informs the receiver of the new name sendFilenameFilesizeChanging(packet, runner, "Will send a modification of filename due to wildcard in SelfMode: ", "Change Filename by Wildcard on sender in SelfMode"); } else if (shouldInformBack && (!packet.isToValidate())) { // Was only for (shouldInformBack) // File length is now known, so inform back sendFilenameFilesizeChanging(packet, runner, "Will send a modification of filesize or fileInfo: ", "Change Filesize / FileInfo on sender"); } session.setReady(true); Configuration.configuration.getLocalTransaction().setFromId(runner, localChannelReference); // inform back if (packet.isToValidate()) { if (Configuration.configuration.getMonitoring() != null) { Configuration.configuration.getMonitoring().lastInActiveTransfer = System.currentTimeMillis(); } if (runner.isSender()) { // In case Wildcard was used logger.debug("New FILENAME: {}", runner.getOriginalFilename()); packet.setFilename(runner.getOriginalFilename()); logger.debug("Rank set: " + runner.getRank()); packet.setRank(runner.getRank()); } else { logger.debug("Rank set: " + runner.getRank()); packet.setRank(runner.getRank()); } packet.validate(); session.newState(REQUESTD); ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet, true); } else { session.newState(REQUESTD); // requester => might be a client // Save the runner into the session and validate the request so begin transfer session.getLocalChannelReference().getFutureRequest().setRunner(runner); localChannelReference.getFutureValidRequest().setSuccess(); if (Configuration.configuration.getMonitoring() != null) { Configuration.configuration.getMonitoring().lastOutActiveTransfer = System.currentTimeMillis(); } } // if retrieve => START the retrieve operation except if in Send Through mode if (runner.isSender()) { if (runner.isSendThrough()) { // it is legal to send data from now logger.debug("Now ready to continue with send through"); localChannelReference.validateEndTransfer( new R66Result(session, false, ErrorCode.PreProcessingOk, runner)); } else { // Automatically send data now logger.debug("Now ready to continue with runRetrieve"); NetworkTransaction.runRetrieve(session, channel); } } session.setStatus(39); } /** * Send a Filename/Filesize change to the partner * * @param packet * @param runner * @throws OpenR66ProtocolPacketException */ private final void sendFilenameFilesizeChanging(RequestPacket packet, DbTaskRunner runner, String debug, String info) throws OpenR66ProtocolPacketException { logger.debug(debug + runner.getFilename()); session.newState(VALID); if (localChannelReference.getPartner().useJson()) { RequestJsonPacket request = new RequestJsonPacket(); request.setComment(info); request.setFilename(runner.getFilename()); request.setFilesize(packet.getOriginalSize()); String infoTransfer = runner.getFileInformation(); if (infoTransfer != null && ! infoTransfer.equals(packet.getFileInformation())) { request.setFileInfo(runner.getFileInformation()); } JsonCommandPacket validPacket = new JsonCommandPacket(request, LocalPacketFactory.REQUESTPACKET); ChannelUtils.writeAbstractLocalPacket(localChannelReference, validPacket, true); } else { String infoTransfer = runner.getFileInformation(); ValidPacket validPacket; if (infoTransfer != null && ! infoTransfer.equals(packet.getFileInformation()) && localChannelReference.getPartner().changeFileInfoEnabled()) { validPacket = new ValidPacket(info, runner.getFilename() + PartnerConfiguration.BAR_SEPARATOR_FIELD + packet.getOriginalSize() + PartnerConfiguration.BAR_SEPARATOR_FIELD + packet.getFileInformation(), LocalPacketFactory.REQUESTPACKET); } else { validPacket = new ValidPacket(info, runner.getFilename() + PartnerConfiguration.BAR_SEPARATOR_FIELD + packet.getOriginalSize(), LocalPacketFactory.REQUESTPACKET); } ChannelUtils.writeAbstractLocalPacket(localChannelReference, validPacket, true); } } /** * Send an error * * @param message * @param code * @param channel * @throws OpenR66ProtocolPacketException */ private final void errorToSend(String message, ErrorCode code, Channel channel, int status) throws OpenR66ProtocolPacketException { session.newState(ERROR); try { session.setFinalizeTransfer(false, new R66Result( new OpenR66ProtocolPacketException(message), session, true, code, session.getRunner())); } catch (OpenR66RunnerErrorException e1) { localChannelReference.invalidateRequest(new R66Result(e1, session, true, code, session.getRunner())); } catch (OpenR66ProtocolSystemException e1) { localChannelReference.invalidateRequest(new R66Result(e1, session, true, code, session.getRunner())); } ErrorPacket error = new ErrorPacket( message, code.getCode(), ErrorPacket.FORWARDCLOSECODE); ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); session.setStatus(status); ChannelCloseTimer.closeFutureChannel(channel); } /** * Receive a data block * * @param channel * @param packet * @throws OpenR66ProtocolNotAuthenticatedException * @throws OpenR66ProtocolBusinessException * @throws OpenR66ProtocolPacketException */ public void data(Channel channel, DataPacket packet) throws OpenR66ProtocolNotAuthenticatedException, OpenR66ProtocolBusinessException, OpenR66ProtocolPacketException { if (!session.isAuthenticated()) { logger.debug("Not authenticated while Data received"); packet.clear(); throw new OpenR66ProtocolNotAuthenticatedException( "Not authenticated while Data received"); } if (!session.isReady()) { logger.debug("No request prepared"); packet.clear(); throw new OpenR66ProtocolBusinessException("No request prepared"); } if (session.getRunner().isSender()) { logger.debug("Not in receive MODE but receive a packet"); packet.clear(); throw new OpenR66ProtocolBusinessException( "Not in receive MODE but receive a packet"); } if (!session.getRunner().continueTransfer()) { logger.debug("EndTransfer failed ? " + localChannelReference.getFutureEndTransfer().isFailed()); if (localChannelReference.getFutureEndTransfer().isFailed()) { // nothing to do since already done session.setStatus(94); packet.clear(); return; } errorToSend("Transfer in error due previously aborted transmission", ErrorCode.TransferError, channel, 95); packet.clear(); return; } if (packet.getPacketRank() != session.getRunner().getRank()) { logger.debug("Issue on rank: " + packet.getPacketRank() + ":" + session.getRunner().getRank()); if (!session.addError()) { // cannot continue logger.error(Messages.getString("LocalServerHandler.15") + packet.getPacketRank() + " : " + //$NON-NLS-1$ session.getRunner().getRank() + " from {}", session.getRunner()); errorToSend("Too much Bad Rank in transmission: " + packet.getPacketRank(), ErrorCode.TransferError, channel, 96); packet.clear(); return; } // Fix the rank if possible if (packet.getPacketRank() < session.getRunner().getRank()) { logger.debug("Bad RANK: " + packet.getPacketRank() + " : " + session.getRunner().getRank()); session.getRunner().setRankAtStartup(packet.getPacketRank()); session.getRestart().restartMarker( session.getRunner().getBlocksize() * session.getRunner().getRank()); try { session.getFile().restartMarker(session.getRestart()); } catch (CommandAbstractException e) { logger.error("Bad RANK: " + packet.getPacketRank() + " : " + session.getRunner().getRank()); errorToSend("Bad Rank in transmission even after retry: " + packet.getPacketRank(), ErrorCode.TransferError, channel, 96); packet.clear(); return; } } else { // really bad logger.error("Bad RANK: " + packet.getPacketRank() + " : " + session.getRunner().getRank()); errorToSend("Bad Rank in transmission: " + packet.getPacketRank() + " > " + session.getRunner().getRank(), ErrorCode.TransferError, channel, 20); packet.clear(); return; } } // Check global size long originalSize = session.getRunner().getOriginalSize(); if (originalSize >= 0) { if (session.getRunner().getBlocksize() * (session.getRunner().getRank() - 1) > originalSize) { // cannot continue logger.error(Messages.getString("LocalServerHandler.16") + packet.getPacketRank() + " : " + //$NON-NLS-1$ (originalSize / session.getRunner().getBlocksize() + 1) + " from {}", session.getRunner()); errorToSend("Too much data transferred: " + packet.getPacketRank(), ErrorCode.TransferError, channel, 96); packet.clear(); return; } } // if MD5 check MD5 if (RequestPacket.isMD5Mode(session.getRunner().getMode())) { logger.debug("AlgoDigest: " + (localChannelReference.getPartner() != null ? localChannelReference.getPartner().getDigestAlgo() : "usual algo")); if (!packet.isKeyValid(localChannelReference.getPartner().getDigestAlgo())) { // Wrong packet logger.error( Messages.getString("LocalServerHandler.17"), packet, localChannelReference.getPartner().getDigestAlgo().name); //$NON-NLS-1$ errorToSend("Transfer in error due to bad Hash on data packet (" + localChannelReference.getPartner().getDigestAlgo().name + ")", ErrorCode.MD5Error, channel, 21); packet.clear(); return; } } if (Configuration.configuration.isGlobalDigest()) { if (globalDigest == null) { try { // check if first block, since if not, digest will be only partial if (session.getRunner().getRank() > 0) { localChannelReference.setPartialHash(); } if (localChannelReference.getPartner() != null) { if (localChannelReference.getPartner().useFinalHash()) { DigestAlgo algo = localChannelReference.getPartner().getDigestAlgo(); if (algo != Configuration.configuration.getDigest()) { globalDigest = new FilesystemBasedDigest(algo); localDigest = new FilesystemBasedDigest(Configuration.configuration.getDigest()); } } } if (globalDigest == null) { globalDigest = new FilesystemBasedDigest(Configuration.configuration.getDigest()); localDigest = null; } } catch (NoSuchAlgorithmException e) { } logger.debug("GlobalDigest: " + localChannelReference.getPartner().getDigestAlgo() + " different? " + (localDigest != null)); } FileUtils.computeGlobalHash(globalDigest, packet.getData()); if (localDigest != null) { FileUtils.computeGlobalHash(localDigest, packet.getData()); } } DataBlock dataBlock = new DataBlock(); if (session.getRunner().isRecvThrough() && localChannelReference.isRecvThroughMode()) { try { localChannelReference.getRecvThroughHandler().writeByteBuf(packet.getData()); session.getRunner().incrementRank(); if (packet.getPacketRank() % 100 == 1) { logger.debug("Good RANK: " + packet.getPacketRank() + " : " + session.getRunner().getRank()); } } finally { packet.clear(); } } else { dataBlock.setBlock(packet.getData()); try { session.getFile().writeDataBlock(dataBlock); session.getRunner().incrementRank(); if (packet.getPacketRank() % 100 == 1) { logger.debug("Good RANK: " + packet.getPacketRank() + " : " + session.getRunner().getRank()); } } catch (FileTransferException e) { errorToSend("Transfer in error", ErrorCode.TransferError, channel, 22); return; } finally { dataBlock.clear(); packet.clear(); } } } /** * Receive an End of Transfer * * @param channel * @param packet * @throws OpenR66RunnerErrorException * @throws OpenR66ProtocolSystemException * @throws OpenR66ProtocolNotAuthenticatedException */ public void endTransfer(Channel channel, EndTransferPacket packet) throws OpenR66RunnerErrorException, OpenR66ProtocolSystemException, OpenR66ProtocolNotAuthenticatedException { if (!session.isAuthenticated()) { throw new OpenR66ProtocolNotAuthenticatedException( "Not authenticated while EndTransfer received"); } // Check end of transfer long originalSize = session.getRunner().getOriginalSize(); logger.debug("OSize: " + originalSize + " isSender: " + session.getRunner().isSender()); if (packet.isToValidate()) { // check if possible originalSize if (originalSize > 0) { try { if (!session.getRunner().isRecvThrough() && session.getFile().length() != originalSize || session.getFile().length() == 0) { R66Result result = new R66Result(new OpenR66RunnerErrorException( Messages.getString("LocalServerHandler.18")), //$NON-NLS-1$ session, true, ErrorCode.TransferError, session.getRunner()); try { session.setFinalizeTransfer(false, result); } catch (OpenR66RunnerErrorException e) { } catch (OpenR66ProtocolSystemException e) { } ErrorPacket error = new ErrorPacket( "Final size in error, transfer in error and rank should be reset to 0", ErrorCode.TransferError.getCode(), ErrorPacket.FORWARDCLOSECODE); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e) { } session.setStatus(23); ChannelCloseTimer.closeFutureChannel(channel); return; } } catch (CommandAbstractException e) { // ignore } } // check if possible Global Digest String hash = packet.getOptional(); logger.debug("GlobalDigest: " + localChannelReference.getPartner().getDigestAlgo() + " different? " + (localDigest != null) + " remoteHash? " + (hash != null)); if (hash != null && globalDigest != null) { String localhash = FilesystemBasedDigest.getHex(globalDigest.Final()); globalDigest = null; if (!localhash.equalsIgnoreCase(hash)) { // bad global Hash //session.getRunner().setRankAtStartup(0); R66Result result = new R66Result(new OpenR66RunnerErrorException( Messages.getString("LocalServerHandler.19") + //$NON-NLS-1$ localChannelReference.getPartner().getDigestAlgo().name + ")"), session, true, ErrorCode.MD5Error, session.getRunner()); try { session.setFinalizeTransfer(false, result); } catch (OpenR66RunnerErrorException e) { } catch (OpenR66ProtocolSystemException e) { } ErrorPacket error = new ErrorPacket( "Global Hash in error, transfer in error and rank should be reset to 0 (using " + localChannelReference.getPartner().getDigestAlgo().name + ")", ErrorCode.MD5Error.getCode(), ErrorPacket.FORWARDCLOSECODE); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e) { } session.setStatus(23); ChannelCloseTimer.closeFutureChannel(channel); return; } else { if (localDigest != null) { localhash = FilesystemBasedDigest.getHex(localDigest.Final()); } localChannelReference.setHashComputeDuringTransfer(localhash); logger.debug("Global digest ok"); } } else if (globalDigest != null) { String localhash = null; if (localDigest != null) { localhash = FilesystemBasedDigest.getHex(localDigest.Final()); } else { localhash = FilesystemBasedDigest.getHex(globalDigest.Final()); } globalDigest = null; localChannelReference.setHashComputeDuringTransfer(localhash); } localDigest = null; globalDigest = null; session.newState(ENDTRANSFERS); if (!localChannelReference.getFutureRequest().isDone()) { // Finish with post Operation R66Result result = new R66Result(session, false, ErrorCode.TransferOk, session.getRunner()); session.newState(ENDTRANSFERR); try { session.setFinalizeTransfer(true, result); } catch (OpenR66RunnerErrorException e) { // TODO session.newState(ERROR); ErrorPacket error = null; if (localChannelReference.getFutureRequest().getResult() != null) { result = localChannelReference.getFutureRequest().getResult(); error = new ErrorPacket( "Error while finalizing transfer: " + result.getMessage(), result.getCode().getCode(), ErrorPacket.FORWARDCLOSECODE); } else { error = new ErrorPacket( "Error while finalizing transfer", ErrorCode.FinalOp.getCode(), ErrorPacket.FORWARDCLOSECODE); } try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e1) { } session.setStatus(23); ChannelCloseTimer.closeFutureChannel(channel); return; } catch (OpenR66ProtocolSystemException e) { // TODO session.newState(ERROR); ErrorPacket error = null; if (localChannelReference.getFutureRequest().getResult() != null) { result = localChannelReference.getFutureRequest().getResult(); error = new ErrorPacket( "Error while finalizing transfer: " + result.getMessage(), result.getCode().getCode(), ErrorPacket.FORWARDCLOSECODE); } else { error = new ErrorPacket( "Error while finalizing transfer", ErrorCode.FinalOp.getCode(), ErrorPacket.FORWARDCLOSECODE); } try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e1) { } session.setStatus(23); ChannelCloseTimer.closeFutureChannel(channel); return; } // Now can send validation packet.validate(); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet, false); } catch (OpenR66ProtocolPacketException e) { // ignore } } else { // in error due to a previous status (like bad MD5) logger .error(Messages.getString("LocalServerHandler.20")); //$NON-NLS-1$ session.setStatus(23); channel.close(); return; } } else { session.newState(ENDTRANSFERR); if (!localChannelReference.getFutureRequest().isDone()) { // Validation of end of transfer R66Result result = new R66Result(session, false, ErrorCode.TransferOk, session.getRunner()); try { session.setFinalizeTransfer(true, result); } catch (OpenR66RunnerErrorException e) { // TODO session.newState(ERROR); ErrorPacket error = null; if (localChannelReference.getFutureRequest().getResult() != null) { result = localChannelReference.getFutureRequest().getResult(); error = new ErrorPacket( "Error while finalizing transfer: " + result.getMessage(), result.getCode().getCode(), ErrorPacket.FORWARDCLOSECODE); } else { error = new ErrorPacket( "Error while finalizing transfer", ErrorCode.FinalOp.getCode(), ErrorPacket.FORWARDCLOSECODE); } try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e1) { } session.setStatus(23); ChannelCloseTimer.closeFutureChannel(channel); return; } catch (OpenR66ProtocolSystemException e) { // TODO session.newState(ERROR); ErrorPacket error = null; if (localChannelReference.getFutureRequest().getResult() != null) { result = localChannelReference.getFutureRequest().getResult(); error = new ErrorPacket( "Error while finalizing transfer: " + result.getMessage(), result.getCode().getCode(), ErrorPacket.FORWARDCLOSECODE); } else { error = new ErrorPacket( "Error while finalizing transfer", ErrorCode.FinalOp.getCode(), ErrorPacket.FORWARDCLOSECODE); } try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e1) { } session.setStatus(23); ChannelCloseTimer.closeFutureChannel(channel); return; } } } } /** * Receive an End of Request * * @param channel * @param packet * @throws OpenR66RunnerErrorException * @throws OpenR66ProtocolSystemException * @throws OpenR66ProtocolNotAuthenticatedException */ public void endRequest(Channel channel, EndRequestPacket packet) { // Validate the last post action on a transfer from receiver remote host logger.info("Valid Request {} Packet {}", localChannelReference, packet); DbTaskRunner runner = session.getRunner(); logger.debug("Runner endRequest: " + (session.getRunner() != null)); if (runner != null) { runner.setAllDone(); try { runner.saveStatus(); } catch (OpenR66RunnerErrorException e) { // ignore } runner.clean(); } String optional = null; if (session.getExtendedProtocol()) { optional = packet.getOptional(); } if (!localChannelReference.getFutureRequest().isDone()) { // end of request R66Future transfer = localChannelReference.getFutureEndTransfer(); try { transfer.await(); } catch (InterruptedException e) { } if (transfer.isSuccess()) { if (session.getExtendedProtocol() && session.getBusinessObject() != null) { if (session.getBusinessObject().getInfo(session) == null) { session.getBusinessObject().setInfo(session, optional); } else { String temp = session.getBusinessObject().getInfo(session); session.getBusinessObject().setInfo(session, optional); optional = temp; } } else if (session.getExtendedProtocol() && transfer.getResult().getOther() == null && optional != null) { transfer.getResult().setOther(optional); } localChannelReference.validateRequest(transfer.getResult()); } } session.setStatus(1); if (packet.isToValidate()) { session.newState(ENDREQUESTS); packet.validate(); if (session.getExtendedProtocol()) { packet.setOptional(optional); } session.newState(ENDREQUESTR); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, packet, true); } catch (OpenR66ProtocolPacketException e) { } } else { session.newState(ENDREQUESTR); } if (runner != null && (runner.isSelfRequested() || runner.isSelfRequest())) { ChannelCloseTimer.closeFutureChannel(channel); } } /** * If newFileInfo is provided and different than current value * @param channel * @param newFileInfo * @throws OpenR66RunnerErrorException */ public void requestChangeFileInfo(Channel channel, String newFileInfo) throws OpenR66RunnerErrorException { DbTaskRunner runner = session.getRunner(); logger.debug("NewFileInfo " + newFileInfo); runner.setFileInformation(newFileInfo); try { runner.update(); } catch (WaarpDatabaseException e) { runner.saveStatus(); runner.setErrorExecutionStatus(ErrorCode.ExternalOp); session.newState(ERROR); logger.error("File info changing in error {}", e.getMessage()); ErrorPacket error = new ErrorPacket("File changing information in error: " + e .getMessage(), runner.getErrorInfo().getCode(), ErrorPacket.FORWARDCLOSECODE); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e2) { } try { session.setFinalizeTransfer(false, new R66Result(new OpenR66RunnerErrorException(e), session, true, runner.getErrorInfo(), runner)); } catch (OpenR66RunnerErrorException e1) { localChannelReference.invalidateRequest(new R66Result(new OpenR66RunnerErrorException(e), session, true, runner.getErrorInfo(), runner)); } catch (OpenR66ProtocolSystemException e1) { localChannelReference.invalidateRequest(new R66Result(new OpenR66RunnerErrorException(e), session, true, runner.getErrorInfo(), runner)); } session.setStatus(97); ChannelCloseTimer.closeFutureChannel(channel); return; } } /** * Change the filename and the filesize * * @param channel * @param newfilename * @param newSize * @throws OpenR66RunnerErrorException */ public void requestChangeNameSize(Channel channel, String newfilename, long newSize) throws OpenR66RunnerErrorException { session.newState(VALID); DbTaskRunner runner = session.getRunner(); logger.debug("NewSize " + newSize + " NewName " + newfilename); // The filename or filesize from sender is changed due to PreTask so change it too in receiver // comment, filename, filesize // Close only if an error occurs! if (runner != null) { if (newSize > 0) { runner.setOriginalSize(newSize); // Check if a CHKFILE task was supposely needed to run String[][] rpretasks = runner.getRule().getRpreTasksArray(); if (rpretasks != null) { for (String[] strings : rpretasks) { AbstractTask task = runner.getTask(strings, session); if (task.getType() == TaskType.CHKFILE) { // re run this in case task.run(); try { task.getFutureCompletion().await(); } catch (InterruptedException e) { } if (!task.getFutureCompletion().isSuccess()) { // not valid so create an error from there ErrorCode code = ErrorCode.SizeNotAllowed; runner.setErrorExecutionStatus(code); runner.saveStatus(); session.setBadRunner(runner, code); session.newState(ERROR); logger.error("File length is not compatible with Rule or capacity {} {}", newfilename + " : " + newSize, session); ErrorPacket errorPacket = new ErrorPacket( "File length is not compatible with Rule or capacity", code.getCode(), ErrorPacket.FORWARDCLOSECODE); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, errorPacket, true); } catch (OpenR66ProtocolPacketException e2) { } try { session.setFinalizeTransfer(false, new R66Result(new OpenR66RunnerErrorException( errorPacket.getSheader()), session, true, runner.getErrorInfo(), runner)); } catch (OpenR66RunnerErrorException e1) { localChannelReference.invalidateRequest(new R66Result( new OpenR66RunnerErrorException(errorPacket.getSheader()), session, true, runner.getErrorInfo(), runner)); } catch (OpenR66ProtocolSystemException e1) { localChannelReference.invalidateRequest(new R66Result( new OpenR66RunnerErrorException(errorPacket.getSheader()), session, true, runner.getErrorInfo(), runner)); } session.setStatus(97); ChannelCloseTimer.closeFutureChannel(channel); return; } } } } } } // check if send is already on going if (runner != null && runner.getRank() > 0) { // already started so not changing the filename // Success: No write back at all return; } // Pre execution was already done since this packet is only received once // the request is already validated by the receiver try { session.renameReceiverFile(newfilename); } catch (OpenR66RunnerErrorException e) { runner.saveStatus(); runner.setErrorExecutionStatus(ErrorCode.FileNotFound); session.newState(ERROR); logger.error("File renaming in error {}", e.getMessage()); ErrorPacket error = new ErrorPacket("File renaming in error: " + e .getMessage(), runner.getErrorInfo().getCode(), ErrorPacket.FORWARDCLOSECODE); try { ChannelUtils.writeAbstractLocalPacket(localChannelReference, error, true); } catch (OpenR66ProtocolPacketException e2) { } try { session.setFinalizeTransfer(false, new R66Result(e, session, true, runner.getErrorInfo(), runner)); } catch (OpenR66RunnerErrorException e1) { localChannelReference.invalidateRequest(new R66Result(e, session, true, runner.getErrorInfo(), runner)); } catch (OpenR66ProtocolSystemException e1) { localChannelReference.invalidateRequest(new R66Result(e, session, true, runner.getErrorInfo(), runner)); } session.setStatus(97); ChannelCloseTimer.closeFutureChannel(channel); return; } // Success: No write back at all } }