/** * 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.packet; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.waarp.common.json.JsonHandler; import org.waarp.common.logging.WaarpLogger; import org.waarp.common.logging.WaarpLoggerFactory; import org.waarp.openr66.context.ErrorCode; import org.waarp.openr66.protocol.configuration.Configuration; import org.waarp.openr66.protocol.configuration.PartnerConfiguration; import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException; import org.waarp.openr66.protocol.localhandler.LocalChannelReference; import com.fasterxml.jackson.databind.node.ObjectNode; /** * Request class * * header = "rulename MODETRANS" middle = way+"FILENAME BLOCKSIZE RANK specialId code (optional length)" end = * "fileInformation" * * or * * header = "{rule:rulename, mode:MODETRANS}" middle = way{filename:FILENAME, block:BLOCKSIZE, rank:RANK, id:specialId, code:code, * length:length}" end = * "fileInformation" * * @author frederic bregier */ public class RequestPacket extends AbstractLocalPacket { /** * Internal Logger */ private static final WaarpLogger logger = WaarpLoggerFactory .getLogger(RequestPacket.class); public static enum TRANSFERMODE { UNKNOWNMODE, SENDMODE, RECVMODE, SENDMD5MODE, RECVMD5MODE, SENDTHROUGHMODE, RECVTHROUGHMODE, SENDMD5THROUGHMODE, RECVMD5THROUGHMODE; } protected static enum FIELDS { rule, mode, filename, block, rank, id, code, length } protected static final byte REQVALIDATE = 0; protected static final byte REQANSWERVALIDATE = 1; protected final String rulename; protected final int mode; protected String filename; protected final int blocksize; protected int rank; protected long specialId; protected byte way; protected char code; protected long originalSize; protected final String fileInformation; protected String separator = PartnerConfiguration.getSEPARATOR_FIELD(); /** * * @param mode * @return the same mode (RECV or SEND) in MD5 version */ public final static int getModeMD5(int mode) { switch (mode) { case 1: case 2: case 5: case 6: return mode + 2; } return mode; } /** * * @param mode * @return true if this mode is a RECV(MD5) mode */ public final static boolean isRecvMode(int mode) { return (mode == TRANSFERMODE.RECVMODE.ordinal() || mode == TRANSFERMODE.RECVMD5MODE.ordinal() || mode == TRANSFERMODE.RECVTHROUGHMODE.ordinal() || mode == TRANSFERMODE.RECVMD5THROUGHMODE .ordinal()); } /** * * @param mode * @param isRequested * @return True if this mode is a THROUGH (MD5) mode */ public final static boolean isSendThroughMode(int mode, boolean isRequested) { return ((!isRequested && isSendThroughMode(mode)) || (isRequested && isRecvThroughMode(mode))); } /** * * @param mode * @return True if this mode is a SEND THROUGH (MD5) mode */ public final static boolean isSendThroughMode(int mode) { return (mode == TRANSFERMODE.SENDTHROUGHMODE.ordinal() || mode == TRANSFERMODE.SENDMD5THROUGHMODE .ordinal()); } /** * * @param mode * @param isRequested * @return True if this mode is a THROUGH (MD5) mode */ public final static boolean isRecvThroughMode(int mode, boolean isRequested) { return ((!isRequested && isRecvThroughMode(mode)) || (isRequested && isSendThroughMode(mode))); } /** * * @param mode * @return True if this mode is a RECV THROUGH (MD5) mode */ public final static boolean isRecvThroughMode(int mode) { return (mode == TRANSFERMODE.RECVTHROUGHMODE.ordinal() || mode == TRANSFERMODE.RECVMD5THROUGHMODE .ordinal()); } public final static boolean isSendMode(int mode) { return !isRecvMode(mode); } /** * * @param mode * @return True if this mode is a THROUGH mode (with or without MD5) */ public final static boolean isThroughMode(int mode) { return mode >= TRANSFERMODE.SENDTHROUGHMODE.ordinal() && mode <= TRANSFERMODE.RECVMD5THROUGHMODE.ordinal(); } /** * * @param mode * @return true if this mode is a MD5 mode */ public final static boolean isMD5Mode(int mode) { return (mode == TRANSFERMODE.RECVMD5MODE.ordinal() || mode == TRANSFERMODE.SENDMD5MODE.ordinal() || mode == TRANSFERMODE.SENDMD5THROUGHMODE.ordinal() || mode == TRANSFERMODE.RECVMD5THROUGHMODE .ordinal()); } /** * * @param mode1 * @param mode2 * @return true if both modes are compatible (both send, or both recv) */ public final static boolean isCompatibleMode(int mode1, int mode2) { return ((RequestPacket.isRecvMode(mode1) && RequestPacket.isRecvMode(mode2)) || ((!RequestPacket.isRecvMode(mode1)) && (!RequestPacket.isRecvMode(mode2)))); } /** * @param headerLength * @param middleLength * @param endLength * @param buf * @return the new RequestPacket from buffer * @throws OpenR66ProtocolPacketException */ public static RequestPacket createFromBuffer(int headerLength, int middleLength, int endLength, ByteBuf buf) throws OpenR66ProtocolPacketException { if (headerLength - 1 <= 0) { throw new OpenR66ProtocolPacketException("Not enough data"); } if (middleLength <= 1) { throw new OpenR66ProtocolPacketException("Not enough data"); } final byte[] bheader = new byte[headerLength - 1]; final byte[] bmiddle = new byte[middleLength - 1];// valid is not in bmiddle final byte[] bend = new byte[endLength]; if (headerLength - 1 > 0) { buf.readBytes(bheader); } byte valid = buf.readByte(); if (middleLength > 1) { buf.readBytes(bmiddle); } if (endLength > 0) { buf.readBytes(bend); } final String sheader = new String(bheader); final String smiddle = new String(bmiddle); final String send = new String(bend); // check if JSON on header since it will directly starts with a JSON, in contrary to middle if (sheader.startsWith(PartnerConfiguration.BAR_JSON_FIELD)) { // JSON logger.debug("Request is using JSON"); ObjectNode map = JsonHandler.getFromString(sheader); ObjectNode map2 = JsonHandler.getFromString(smiddle); return new RequestPacket(map.path(FIELDS.rule.name()).asText(), map.path(FIELDS.mode.name()).asInt(), map2.path(FIELDS.filename.name()).asText(), map2.path(FIELDS.block.name()).asInt(), map2.path(FIELDS.rank.name()).asInt(), map2.path(FIELDS.id.name()).asLong(), valid, send, (char) map2.path(FIELDS.code.name()).asInt(), map2.path(FIELDS.length.name()).asLong(), PartnerConfiguration.BAR_JSON_FIELD); } String[] aheader = sheader.split(PartnerConfiguration.BLANK_SEPARATOR_FIELD); if (aheader.length != 2) { throw new OpenR66ProtocolPacketException("Not enough data"); } // FIX to check both ' ' and SEPARATOR_FIELD String[] amiddle = smiddle.split(PartnerConfiguration.BAR_SEPARATOR_FIELD); String sep = PartnerConfiguration.BAR_SEPARATOR_FIELD; if (amiddle.length < 5) { amiddle = smiddle.split(PartnerConfiguration.BLANK_SEPARATOR_FIELD); sep = PartnerConfiguration.BLANK_SEPARATOR_FIELD; if (amiddle.length < 5) { throw new OpenR66ProtocolPacketException("Not enough data"); } } int blocksize = Integer.parseInt(amiddle[1]); if (blocksize < 100) { blocksize = Configuration.configuration.getBLOCKSIZE(); } int rank = Integer.parseInt(amiddle[2]); long specialId = Long.parseLong(amiddle[3]); char code = amiddle[4].charAt(0); long originalSize = -1; if (amiddle.length > 5) { originalSize = Long.parseLong(amiddle[5]); } return new RequestPacket(aheader[0], Integer.parseInt(aheader[1]), amiddle[0], blocksize, rank, specialId, valid, send, code, originalSize, sep); } /** * @param rulename * @param mode * @param filename * @param blocksize * @param rank * @param specialId * @param valid * @param fileInformation * @param code * @param originalSize */ private RequestPacket(String rulename, int mode, String filename, int blocksize, int rank, long specialId, byte valid, String fileInformation, char code, long originalSize, String separator) { this.rulename = rulename; this.mode = mode; this.filename = filename; if (blocksize < 100) { this.blocksize = Configuration.configuration.getBLOCKSIZE(); } else { this.blocksize = blocksize; } this.rank = rank; this.specialId = specialId; way = valid; this.fileInformation = fileInformation; this.code = code; this.originalSize = originalSize; this.separator = separator; } /** * @param rulename * @param mode * @param filename * @param blocksize * @param rank * @param specialId * @param fileInformation */ public RequestPacket(String rulename, int mode, String filename, int blocksize, int rank, long specialId, String fileInformation, long originalSize, String separator) { this(rulename, mode, filename, blocksize, rank, specialId, REQVALIDATE, fileInformation, ErrorCode.InitOk.code, originalSize, separator); } @Override public void createEnd(LocalChannelReference lcr) throws OpenR66ProtocolPacketException { if (fileInformation != null) { end = Unpooled.wrappedBuffer(fileInformation.getBytes()); } } @Override public void createHeader(LocalChannelReference lcr) throws OpenR66ProtocolPacketException { if (rulename == null || mode <= 0) { throw new OpenR66ProtocolPacketException("Not enough data"); } if (lcr.getPartner() != null && lcr.getPartner().useJson()) { logger.debug("Request will use JSON " + lcr.getPartner().toString()); ObjectNode node = JsonHandler.createObjectNode(); JsonHandler.setValue(node, FIELDS.rule, rulename); JsonHandler.setValue(node, FIELDS.mode, mode); header = Unpooled.wrappedBuffer(JsonHandler.writeAsString(node).getBytes()); } else { header = Unpooled.wrappedBuffer(rulename.getBytes(), PartnerConfiguration.BLANK_SEPARATOR_FIELD.getBytes(), Integer.toString(mode).getBytes()); } } @Override public void createMiddle(LocalChannelReference lcr) throws OpenR66ProtocolPacketException { if (filename == null) { throw new OpenR66ProtocolPacketException("Not enough data"); } byte[] away = new byte[1]; away[0] = way; if (lcr.getPartner() != null && lcr.getPartner().useJson()) { logger.debug("Request will use JSON " + lcr.getPartner().toString()); ObjectNode node = JsonHandler.createObjectNode(); JsonHandler.setValue(node, FIELDS.filename, filename); JsonHandler.setValue(node, FIELDS.block, blocksize); JsonHandler.setValue(node, FIELDS.rank, rank); JsonHandler.setValue(node, FIELDS.id, specialId); JsonHandler.setValue(node, FIELDS.code, code); JsonHandler.setValue(node, FIELDS.length, originalSize); middle = Unpooled.wrappedBuffer(away, JsonHandler.writeAsString(node).getBytes()); } else { middle = Unpooled.wrappedBuffer(away, filename.getBytes(), this.separator.getBytes(), Integer.toString(blocksize).getBytes(), this.separator.getBytes(), Integer.toString(rank).getBytes(), this.separator.getBytes(), Long.toString(specialId).getBytes(), this.separator.getBytes(), Character.toString(code).getBytes(), this.separator.getBytes(), Long.toString(originalSize).getBytes()); } } @Override public byte getType() { return LocalPacketFactory.REQUESTPACKET; } @Override public String toString() { return "RequestPacket: " + rulename + " : " + mode + " : " + filename + " : " + fileInformation + " : " + blocksize + " : " + rank + " : " + way + " : " + code + " : " + originalSize; } /** * @return the rulename */ public String getRulename() { return rulename; } /** * @return the filename */ public String getFilename() { return filename; } /** * @return the mode */ public int getMode() { return mode; } /** * * @return True if this packet concerns a Retrieve operation */ public boolean isRetrieve() { return isRecvMode(mode); } /** * @return the fileInformation */ public String getFileInformation() { return fileInformation; } /** * @return the blocksize */ public int getBlocksize() { return blocksize; } /** * @return the rank */ public int getRank() { return rank; } /** * @param rank * the rank to set */ public void setRank(int rank) { this.rank = rank; } /** * @return the originalSize */ public long getOriginalSize() { return originalSize; } /** * @param originalSize * the originalSize to set */ public void setOriginalSize(long originalSize) { this.originalSize = originalSize; } /** * @param specialId * the specialId to set */ public void setSpecialId(long specialId) { this.specialId = specialId; } /** * @return the specialId */ public long getSpecialId() { return specialId; } /** * @return True if this packet is to be validated */ public boolean isToValidate() { return way == REQVALIDATE; } /** * Validate the request */ public void validate() { way = REQANSWERVALIDATE; middle = null; } /** * @param filename * the filename to set */ public void setFilename(String filename) { this.filename = filename; } /** * @return the code */ public char getCode() { return code; } /** * @param code * the code to set */ public void setCode(char code) { this.code = code; } }