/*
* JMule - Java file sharing client
* Copyright (C) 2007-2008 JMule team ( jmule@jmule.org / http://jmule.org )
*
* Any parts of this program derived from other projects, or contributed
* by third-party developers are copyrighted by their respective authors.
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package org.jmule.core.edonkey.packet;
import static org.jmule.core.edonkey.E2DKConstants.*;
import static org.jmule.core.edonkey.E2DKConstants.OP_EMULE_QUEUERANKING;
import static org.jmule.core.edonkey.E2DKConstants.OP_END_OF_DOWNLOAD;
import static org.jmule.core.edonkey.E2DKConstants.OP_FILEREQANSNOFILE;
import static org.jmule.core.edonkey.E2DKConstants.OP_FILEREQANSWER;
import static org.jmule.core.edonkey.E2DKConstants.OP_FILEREQUEST;
import static org.jmule.core.edonkey.E2DKConstants.OP_FILESTATREQ;
import static org.jmule.core.edonkey.E2DKConstants.OP_FILESTATUS;
import static org.jmule.core.edonkey.E2DKConstants.OP_GETSERVERLIST;
import static org.jmule.core.edonkey.E2DKConstants.OP_GETSOURCES;
import static org.jmule.core.edonkey.E2DKConstants.OP_HASHSETANSWER;
import static org.jmule.core.edonkey.E2DKConstants.OP_HASHSETREQUEST;
import static org.jmule.core.edonkey.E2DKConstants.OP_KAD_CALLBACK;
import static org.jmule.core.edonkey.E2DKConstants.OP_LOGINREQUEST;
import static org.jmule.core.edonkey.E2DKConstants.OP_MESSAGE;
import static org.jmule.core.edonkey.E2DKConstants.OP_OFFERFILES;
import static org.jmule.core.edonkey.E2DKConstants.OP_PEERHELLO;
import static org.jmule.core.edonkey.E2DKConstants.OP_PEERHELLOANSWER;
import static org.jmule.core.edonkey.E2DKConstants.OP_PUBLICKEY;
import static org.jmule.core.edonkey.E2DKConstants.OP_REQUESTPARTS;
import static org.jmule.core.edonkey.E2DKConstants.OP_REQUESTSOURCES;
import static org.jmule.core.edonkey.E2DKConstants.OP_SEARCHREQUEST;
import static org.jmule.core.edonkey.E2DKConstants.OP_SECIDENTSTATE;
import static org.jmule.core.edonkey.E2DKConstants.OP_SENDINGPART;
import static org.jmule.core.edonkey.E2DKConstants.OP_SIGNATURE;
import static org.jmule.core.edonkey.E2DKConstants.OP_SLOTGIVEN;
import static org.jmule.core.edonkey.E2DKConstants.OP_SLOTRELEASE;
import static org.jmule.core.edonkey.E2DKConstants.OP_SLOTREQUEST;
import static org.jmule.core.edonkey.E2DKConstants.PACKET_CALLBACKREQUEST;
import static org.jmule.core.edonkey.E2DKConstants.PROTO_EDONKEY_TCP;
import static org.jmule.core.edonkey.E2DKConstants.PROTO_EMULE_EXTENDED_TCP;
import static org.jmule.core.edonkey.E2DKConstants.ProtocolVersion;
import static org.jmule.core.edonkey.E2DKConstants.SUPPORTED_FLAGS;
import static org.jmule.core.edonkey.E2DKConstants.ServerSoftwareVersion;
import static org.jmule.core.edonkey.E2DKConstants.TAG_NAME_CLIENTVER;
import static org.jmule.core.edonkey.E2DKConstants.TAG_NAME_FLAGS;
import static org.jmule.core.edonkey.E2DKConstants.TAG_NAME_MISC_OPTIONS1;
import static org.jmule.core.edonkey.E2DKConstants.TAG_NAME_MISC_OPTIONS2;
import static org.jmule.core.edonkey.E2DKConstants.TAG_NAME_NAME;
import static org.jmule.core.edonkey.E2DKConstants.TAG_NAME_PROTOCOLVERSION;
import static org.jmule.core.edonkey.E2DKConstants.TAG_NAME_UDP_PORT;
import static org.jmule.core.edonkey.E2DKConstants.TAG_NAME_UDP_PORT_PEER;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.jmule.core.JMException;
import org.jmule.core.configmanager.ConfigurationManagerSingleton;
import org.jmule.core.downloadmanager.FileChunk;
import org.jmule.core.edonkey.ClientID;
import org.jmule.core.edonkey.E2DKConstants;
import org.jmule.core.edonkey.FileHash;
import org.jmule.core.edonkey.PartHashSet;
import org.jmule.core.edonkey.UserHash;
import org.jmule.core.edonkey.E2DKConstants.PeerFeatures;
import org.jmule.core.edonkey.packet.tag.IntTag;
import org.jmule.core.edonkey.packet.tag.StringTag;
import org.jmule.core.edonkey.packet.tag.Tag;
import org.jmule.core.edonkey.packet.tag.TagList;
import org.jmule.core.edonkey.utils.Utils;
import org.jmule.core.jkad.IPAddress;
import org.jmule.core.jkad.Int128;
import org.jmule.core.peermanager.Peer;
import org.jmule.core.searchmanager.SearchQuery;
import org.jmule.core.searchmanager.tree.NodeValue;
import org.jmule.core.sharingmanager.GapList;
import org.jmule.core.sharingmanager.JMuleBitSet;
import org.jmule.core.sharingmanager.SharedFile;
import org.jmule.core.uploadmanager.FileChunkRequest;
import org.jmule.core.utils.Convert;
import org.jmule.core.utils.Misc;
/**
*
* @author binary256
* @version $$Revision: 1.20 $$
* Last changed by $$Author: binary255 $$ on $$Date: 2009/12/28 16:06:13 $$
*/
public class PacketFactory {
/**
* Server login packet.
* <table cellpadding="0" border="1" cellspacing="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>- </td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including the header and size fileds</td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x01</td>
* <td>The value of the OP_LOGINREQUEST opcode</td>
* </tr>
* <tr>
* <td>User Hash</td>
* <td>16</td>
* <td>-</td>
* <td>- </td>
* </tr>
* <tr>
* <td>Client ID</td>
* <td>4</td>
* <td>0</td>
* <td>The client ID that is sent on first connection is usually zero</td>
* </tr>
* <tr>
* <td>TCP Port</td>
* <td>2</td>
* <td>4662</td>
* <td>The TCP port used by the client, configurable</td>
* </tr>
* <tr>
* <td>Tag Count</td>
* <td>4</td>
* <td>4</td>
* <td>The number of tags following in the message</td>
* </tr>
* <tr>
* <td>Name Tag</td>
* <td>varies</td>
* <td>NA</td>
* <td>The user's nickname. The tag is a string tag and the tag name is an integer of value 0x1</td>
* </tr>
* <tr>
* <td>Version Tag</td>
* <td>8</td>
* <td>0x3C</td>
* <td>The eDonkey version supported by the client. The tag is an integer tag and the tag name is an integer of value 0x11</td>
* </tr>
* <tr>
* <td>Port Tag</td>
* <td>8</td>
* <td>4662</td>
* <td>The TCP port used by the client. The tag is an integer tag and the tag name is an integer of value 0x0F</td>
* </tr>
* <tr>
* <td>Flags Tag</td>
* <td>8</td>
* <td>0x01</td>
* <td>The tag is an integer tag and the tag name is an integer of value 0x20</td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getServerLoginPacket(UserHash userHash, int clientPort,
String userNickName) throws JMException {
TagList tagList = new TagList();
tagList.addTag(new StringTag(TAG_NAME_NAME, userNickName));
tagList.addTag(new IntTag(TAG_NAME_PROTOCOLVERSION,ProtocolVersion ));
tagList.addTag(new IntTag(TAG_NAME_CLIENTVER, ServerSoftwareVersion));
tagList.addTag(new IntTag(TAG_NAME_FLAGS, SUPPORTED_FLAGS));
tagList.addTag(new IntTag(TAG_NAME_UDP_PORT, ConfigurationManagerSingleton.getInstance().getUDP()));
int tag_list_size = tagList.getByteSize();
Packet packet = new Packet(16 + 4 + 2 + 4 + tag_list_size, PROTO_EDONKEY_TCP);
packet.setCommand(OP_LOGINREQUEST);//Insert LOGIN_COMMAND
packet.insertData(userHash.getUserHash());//Insert user Hash
packet.insertData(new byte[]{0,0,0,0});//Insert default user ID
packet.insertData((short)clientPort);//Insert user port
packet.insertData(tagList.size());//Insert tag count
for(Tag tag : tagList) {
packet.insertData(tag.getAsByteBuffer());
}
return packet;
}
/**
* Create Server list request packet.
* <table cellpadding="0" border="1" cellspacing="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default Value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td> </td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td> </td>
* <td>The size of the message in bytes not including the header and size fields</td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x14</td>
* <td>The value of the OP_SERVERSTATUS opcode</td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getGetServerListPacket(){
Packet packet=new Packet(0, PROTO_EDONKEY_TCP);
packet.setCommand(OP_GETSERVERLIST);
return packet;
}
/**
* Create server search packet.
* <table cellpadding="0" border="1" cellspacing="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default Value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td> </td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including the header and size fields</td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>16</td>
* <td>The value of the OP_SEARCHREQUEST opcode</td>
* </tr>
* <tr>
* <td>Parsed search string</td>
* <td>varies</td>
* <td>NA</td>
* <td>The parsed search string format is described below</td>
* </tr>
* <tr>
* <td>File Type Constraint</td>
* <td>varies</td>
* <td>NA</td>
* <td>Optional. A string constraint. The string values are one of ("Audio", "Video", "Pro" or "Image". The type field is 3 bytes: 0x1 0x0 0x3</td>
* </tr>
* <tr>
* <td>Min Size Constraint</td>
* <td>varies</td>
* <td>NA</td>
* <td>Optional. An integer constraint. The file size is provided in mega bytes. The type field is 4 bytes : 0x1 0x1 0x0 0x2</td>
* </tr>
* <tr>
* <td>Max Size Constraint</td>
* <td>varies</td>
* <td>NA</td>
* <td>Optional. An integer constraint. The file size is provided in mega bytes. The type field is 4 bytes : 0x2 0x1 0x0 0x2</td>
* </tr>
* <tr>
* <td>Availability Constraint</td>
* <td>varies</td>
* <td>NA</td>
* <td>Optional. An integer constraint. Sets a lower limit on the number of clien that poses the searched file. The type field is 4 bytes: 0x1 0x1 0x0 0x15</td>
* </tr>
* <tr>
* <td>Filename Extension constrain</td>
* <td>varies</td>
* <td>NA</td>
* <td>Optional. A string constraint. The type field is 3 bytes: 0x1 0x0 0x3</td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getSearchPacket(SearchQuery query){
List<byte[]> data = new LinkedList<byte[]>();
int total_size = 0;
List<NodeValue> nodes = query.getSearchTree().traverse();
for(NodeValue value : nodes) {
byte[] tmp = value.getBytes();
data.add(tmp);
total_size += tmp.length;
}
Packet packet=new Packet(total_size, PROTO_EDONKEY_TCP);
packet.setCommand(OP_SEARCHREQUEST);
//packet.insertData(Convert.intToShort(total_size));
for(byte[] b : data)
packet.insertData(b);
return packet;
}
/**
* Create get sources packet.
* @param fileHash hash of file to download
*
* <table cellpadding="0" border="1" cellspacing="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default Value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td> </td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including the header and size fields</td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x58</td>
* <td>The value of the OP_GETSOURCES opcode</td>
* </tr>
* <tr>
* <td>File hash</td>
* <td>16</td>
* <td>NA</td>
* <td>The requested file hash</td>
* </tr>
* <tr>
* <td>File size</td>
* <td>4</td>
* <td>NA</td>
* <td>The requested file size (lungdum 17.3)</td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getSourcesRequestPacket(FileHash fileHash,long fileSize){
Packet packet=new Packet(16+4, PROTO_EDONKEY_TCP);
packet.setCommand(OP_GETSOURCES);
packet.insertData(fileHash.getHash());
packet.insertData(Convert.longToInt(fileSize));
return packet;
}
/**
* Call back request packet.
* @param clientID
*
* <table cellpadding="0" border="1" cellspacing="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default Value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td> </td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including the header and size fields</td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x1C</td>
* <td>The value of the OP_CALLBACKREQUEST opcode</td>
* </tr>
* <tr>
* <td>Client ID</td>
* <td>4</td>
* <td>NA</td>
* <td>The ID of the client which is asked to call back</td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getCallBackRequestPacket(ClientID clientID){
Packet packet = new Packet(4, PROTO_EDONKEY_TCP);
packet.setCommand(PACKET_CALLBACKREQUEST);
packet.insertData(clientID.getClientID());
return packet;
}
/**
* Offer files packet.
* <table cellpadding="0" border="1" cellspacing="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default Value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td> </td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td> </td>
* <td>The size of the message in bytes not including the header and size fields</td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x14</td>
* <td>The value of the OP_SERVERSTATUS opcode</td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getOfferFilesPacket(ClientID userID, List<SharedFile> sharedFiles) throws JMException {
int data_length=0;
List<ByteBuffer> shared_files_data = new LinkedList<ByteBuffer>();
for(SharedFile sFile : sharedFiles) {
TagList tag_list = sFile.getTagList();
int tag_list_size = tag_list.getByteSize();
ByteBuffer buffer = Misc.getByteBuffer(16 + 4 + 2 + 4 + tag_list_size);
data_length += buffer.limit();
buffer.position(0);
buffer.put(sFile.getFileHash().getHash());
if (userID.isHighID()) {
buffer.put(userID.getClientID());
buffer.putShort((short)ConfigurationManagerSingleton.getInstance().getTCP());
}
else {
buffer.putInt(0);
buffer.putShort((short)0);
}
buffer.putInt(tag_list.size());//Insert tag count
for(Tag tag : tag_list)
buffer.put(tag.getAsByteBuffer());
shared_files_data.add(buffer);
}
Packet packet = new Packet(4+data_length, PROTO_EDONKEY_TCP);
packet.setCommand(OP_OFFERFILES);
packet.insertData(sharedFiles.size());
for(ByteBuffer buffer : shared_files_data)
packet.insertData(buffer.array());
return packet;
}
// =======================
// Peer<==>Peer
// =======================
/**
* Peer hello packet.
* @param userHash user hash
* @param myPort client port
* @param serverIP server IP address
* @param serverPort server port
* @param userName user name
* @return peer hello packet
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including the header and size fields</td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x01</td>
* <td>The value of the OP HELLO opcode</td>
* </tr>
* <tr>
* <td>User Hash size</td>
* <td>1</td>
* <td>16</td>
* <td>The size of the user hash field</td>
* </tr>
* <tr>
* <td>User Hash</td>
* <td>16</td>
* <td> </td>
* <td>TBD</td>
* </tr>
* <tr>
* <td>Client ID</td>
* <td>4</td>
* <td>0</td>
* <td>TBD</td>
* </tr>
* <tr>
* <td>TCP Port</td>
* <td>2</td>
* <td>4662</td>
* <td>The TCP port used by the client, configurable</td>
* </tr>
* <tr>
* <td>Tag Count</td>
* <td>4</td>
* <td>4</td>
* <td>The number of tags following in the message</td>
* </tr>
* <tr>
* <td>Tag list</td>
* <td>varies</td>
* <td>NA</td>
* <td>A list of tags specifying remote client’s properties
* </td>
* </tr>
* <tr>
* <td>Server IP</td>
* <td>4</td>
* <td>NA</td>
* <td>The IP of the server to which the client is connected
* </td>
* </tr>
* <tr>
* <td>Server TCP Port
* </td>
* <td>2</td>
* <td>NA</td>
* <td>The TCP port on which the server listens</td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getPeerHelloPacket(UserHash userHash,ClientID clientID,int myPort,
byte[] serverIP,int serverPort,String userName, Map<PeerFeatures, Integer> clientFeatures) throws JMException {
int misc_optins1 = Utils.peerFeatures1ToInt(clientFeatures);
int misc_optins2 = Utils.peerFeatures2ToInt(clientFeatures);
TagList tagList = new TagList();
tagList.addTag(new StringTag(TAG_NAME_NAME, userName));
tagList.addTag(new IntTag(TAG_NAME_MISC_OPTIONS1,misc_optins1 ));
tagList.addTag(new IntTag(TAG_NAME_MISC_OPTIONS2,misc_optins2));
tagList.addTag(new IntTag(TAG_NAME_PROTOCOLVERSION,ProtocolVersion ));
tagList.addTag(new IntTag(TAG_NAME_CLIENTVER, E2DKConstants.getSoftwareVersion()));
tagList.addTag(new IntTag(TAG_NAME_FLAGS, SUPPORTED_FLAGS));
tagList.addTag(new IntTag(TAG_NAME_UDP_PORT_PEER, ConfigurationManagerSingleton.getInstance().getUDP()));
int tag_list_size = tagList.getByteSize();
Packet packet = new Packet(1 + 16 + 4 + 2 + 4 + tag_list_size + 4 + 2, PROTO_EDONKEY_TCP);
packet.setCommand(OP_PEERHELLO);//hello
packet.insertData((byte)16);//Insert length of user Hash
packet.insertData(userHash.getUserHash());//insert user hash
if (clientID == null)
packet.insertData(new byte[]{0,0,0,0});//insert user ID
else
packet.insertData(clientID.getClientID());//insert user ID
packet.insertData((short)myPort);//insert my active port
packet.insertData(tagList.size());//insert tag count
packet.insertData(tagList.getAsByteBuffer());
if (serverIP==null)
packet.insertData(new byte[] { 0, 0, 0, 0 });
else
packet.insertData((serverIP));
packet.insertData((short)serverPort);
return packet;
}
/**
* Peer hello answer packet.
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including the header and size fields</td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x4C</td>
* <td>The value of the OP HELLOANSWER opcode</td>
* </tr>
* <tr>
* <td>Hello fields</td>
* <td> </td>
* <td> </td>
* <td>The same fields as in the hello message starting with the user hash</td>
* </tr>
* </tbody>
* </table>
* */
public static Packet getPeerHelloAnswerPacket(UserHash userHash,
ClientID clientID, int myPort, String userName, byte[] serverIP,
int serverPort, Map<PeerFeatures, Integer> clientFeatures)
throws JMException {
int misc_optins1 = Utils.peerFeatures1ToInt(clientFeatures);
int misc_optins2 = Utils.peerFeatures2ToInt(clientFeatures);
TagList tagList = new TagList();
tagList.addTag(new StringTag(TAG_NAME_NAME, userName));
tagList.addTag(new IntTag(TAG_NAME_MISC_OPTIONS1, misc_optins1));
tagList.addTag(new IntTag(TAG_NAME_MISC_OPTIONS2, misc_optins2));
tagList.addTag(new IntTag(TAG_NAME_PROTOCOLVERSION, ProtocolVersion));
tagList.addTag(new IntTag(TAG_NAME_CLIENTVER, E2DKConstants.getSoftwareVersion()));
tagList.addTag(new IntTag(TAG_NAME_FLAGS, SUPPORTED_FLAGS));
tagList.addTag(new IntTag(TAG_NAME_UDP_PORT_PEER,
ConfigurationManagerSingleton.getInstance().getUDP()));
int tag_list_size = tagList.getByteSize();
Packet packet = new Packet(1 + 1 + 16 + 4 + 2 + 4 + 4 + 2 + 2
+ tag_list_size + 4 + 2, PROTO_EDONKEY_TCP);
packet.setCommand(OP_PEERHELLOANSWER);// hello answer tag
packet.insertData(userHash.getUserHash());// insert user hash
if (clientID == null)
packet.insertData(new byte[] { 0, 0, 0, 0 });
else
packet.insertData(clientID.getClientID());// insert user ID
packet.insertData((short) myPort);// insert my active port
packet.insertData(tagList.size());
packet.insertData(tagList.getAsByteBuffer());
if (serverIP != null)
packet.insertData(serverIP);
else
packet.insertData(new byte[] { 0, 0, 0, 0 });// Server IP
if (serverPort != 0)
packet.insertData((short) serverPort);
else
packet.insertData((short) 0);// Server port
return packet;
}
/**
* Peer chat message.
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x4E</td>
* <td>The value of the OP_MESSAGE opcode</td>
* </tr>
* <tr>
* <td>Length</td>
* <td>2</td>
* <td> NA</td>
* <td>The length of the message</td>
* </tr>
* <tr>
* <td>Message</td>
* <td>Varies</td>
* <td>NA</td>
* <td>The actual message
* </td>
* </tr>
* </tbody>
* </table>
**/
public static Packet getMessagePacket(String message){
byte[] message_bytes = message.getBytes();
Packet packet = new Packet(2 + message.length(), PROTO_EDONKEY_TCP);
packet.setCommand(OP_MESSAGE);
packet.insertData(Convert.intToShort(message_bytes.length));
packet.insertData(message_bytes);
return packet;
}
/**
* Get File Request packet.
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x58</td>
* <td>The value of the OP FILEREQUEST opcode
* </td>
* </tr>
* <tr>
* <td>File ID</td>
* <td>16</td>
* <td>NA</td>
* <td>Unique file ID</td>
* </tr>
* <tr>
* <td>Part Status</td>
* <td>3</td>
* <td>NA</td>
* <td>Optional, sent if the extended request version
* indicated in the eMule info message is greater
* than zero. The file significance is explained in
* this section
* </td>
* </tr>
* <tr>
* <td>Source count</td>
* <td>2</td>
* <td>NA</td>
* <td>Optional, sent if the extended request version
* indicated in the eMule info message is greater
* than one. Indicated the current number of
* sources for this file
* </td>
* </tr>
* </tbody>
* </table>
* */
public static Packet getFileRequestPacket(FileHash fileHash){
Packet packet=new Packet(16, PROTO_EDONKEY_TCP);
packet.setCommand(OP_FILEREQUEST);
packet.insertData(fileHash.getHash());
return packet;
}
/**
* Get requested file ID, this packet must be used with FileRequest packet.
* @param fileHash file ID
* @return requested file ID packet
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x4F</td>
* <td>The value of the OP SETREQFILEID opcode</td>
* </tr>
* <tr>
* <td>File ID</td>
* <td>16</td>
* <td>NA</td>
* <td>The ID of the requested file</td>
* </tr>
* </tbody>
* </table>
*/
public static Packet getFileStatusRequestPacket(FileHash fileHash){
Packet packet=new Packet(16, PROTO_EDONKEY_TCP);
packet.setCommand(OP_FILESTATREQ);
packet.insertData(fileHash.getHash());
return packet;
}
/**
* Start upload request for file.
* @param fileHash file hash
* @return eDonkey packet
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x54</td>
* <td>The value of the OP_ STARTUPLOADREQ
* opcode</td>
* </tr>
* <tr>
* <td>File ID</td>
* <td>16</td>
* <td> NA</td>
* <td>The ID of the requested file</td>
* </tr>
* </tbody>
* </table>
*/
public static Packet getUploadReuqestPacket(FileHash fileHash){
Packet packet=new Packet(16, PROTO_EDONKEY_TCP);
packet.setCommand(OP_SLOTREQUEST);
packet.insertData(fileHash.getHash());
return packet;
}
/**
* Request file parts packet.
* @param fileHash hash of download file
* @param partsData position of parts max 3, format <begin> <begin> <begin> <end> <end> <end>
* @return
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x46</td>
* <td>The value of the OP REQUESTPARTS opcode</td>
* </tr>
* <tr>
* <td>File ID</td>
* <td> 16</td>
* <td> NA</td>
* <td>A unique file ID calculated by hashing the
* file's data
* hash</td>
* </tr>
* <tr>
* <td>Part 1 Start
* offset</td>
* <td>4</td>
* <td>NA</td>
* <td>Start offset of the part 1 in the file</td>
* </tr>
* <tr>
* <td>Part 2 Start offset</td>
* <td>4</td>
* <td>NA</td>
* <td> </td>
* </tr>
* <tr>
* <td>Part 3 Start offset</td>
* <td>4</td>
* <td>NA</td>
* <td> </td>
* </tr>
* <tr>
* <td>Part 1 End offset
* </td>
* <td>4</td>
* <td>NA</td>
* <td>End offset of the part 1 in the file</td>
* </tr>
* <tr>
* <td>Part 2 End offset</td>
* <td>4</td>
* <td>NA</td>
* <td> </td>
* </tr>
* <tr>
* <td>Part 3 End offset</td>
* <td>4</td>
* <td>NA</td>
* <td> </td>
* </tr>
* </tbody>
* </table>
*/
public static Packet getPeerRequestFileParts(FileHash fileHash,
FileChunkRequest... partsData) {
Packet packet = new Packet(16 + (8 * 3), PROTO_EDONKEY_TCP);
packet.setCommand(OP_REQUESTPARTS);
packet.insertData(fileHash.getHash());
ByteBuffer num = Misc.getByteBuffer(4);
for(FileChunkRequest request : partsData) {
num.clear();
num.rewind();
num.putInt(Convert.longToInt(request.getChunkBegin()));
packet.insertData(num.array());
}
for(FileChunkRequest request : partsData) {
num.clear();
num.rewind();
num.putInt(Convert.longToInt(request.getChunkEnd()));
packet.insertData(num.array());
}
return packet;
}
/**
* Request hashes of parts.
* @param fileHash file hash
* @return request file hash packet
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x51</td>
* <td>The value of the OP_HASHSETREQUEST
* opcode</td>
* </tr>
* <tr>
* <td>File ID</td>
* <td>16</td>
* <td> NA</td>
* <td>The file ID of the requested file</td>
* </tr>
* </tbody>
* </table>
*/
public static Packet getRequestPartHashSetPacket(FileHash fileHash) {
Packet packet = new Packet(16, PROTO_EDONKEY_TCP);
packet.setCommand(OP_HASHSETREQUEST);
packet.insertData(fileHash.getHash());
return packet;
}
/**
* Get Slot release packet, sended when download is finished/canceled.
* @return
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x56</td>
* <td>The value of the OP_ CANCELTRANSFER
* opcode
*
* </td>
* </tr>
* </tbody>
* </table>
*/
public static Packet getSlotReleasePacket() {
Packet packet = new Packet(0, PROTO_EDONKEY_TCP);
packet.setCommand(OP_SLOTRELEASE);
return packet;
}
/**
* End of Download Packet.
* @param fileHash
* @return
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x49</td>
* <td>The value of the OP END OF DOWNLOAD
* opcode
* </td>
* </tr>
* <tr>
* <td>File ID</td>
* <td> 16</td>
* <td> NA</td>
* <td>The file ID</td>
* </tr>
* </tbody>
* </table>
*/
public static Packet getEndOfDownloadPacket(FileHash fileHash) {
Packet packet = new Packet(16, PROTO_EDONKEY_TCP);
packet.setCommand(OP_END_OF_DOWNLOAD);
packet.insertData(fileHash.getHash());
return packet;
}
/**
* File request answer packet.
* @param fileHash
* @param fileName
* @return
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x59</td>
* <td>The value of the OP FILEREQANSWER opcode</td>
* </tr>
* <tr>
* <td>File ID</td>
* <td>16</td>
* <td>NA</td>
* <td>Unique file ID</td>
* </tr>
* <tr>
* <td>Name length</td>
* <td>2</td>
* <td>NA</td>
* <td>Optional, sent if the extended request version
* indicated in the eMule info message is greater
* than zero. The file significance is explained in
* this section
* </td>
* </tr>
* <tr>
* <td>Filename</td>
* <td>Varies</td>
* <td>NA</td>
* <td>Optional, sent if the extended request version
* indicated in the eMule info message is greater
* than one. Indicated the current number of
* sources for this file
* </td>
* </tr>
* </tbody>
* </table>
*/
public static Packet getFileRequestAnswerPacket(FileHash fileHash,
String fileName) {
byte[] fileNameBytes = fileName.getBytes();
Packet packet = new Packet(16 + 2 + fileNameBytes.length, PROTO_EDONKEY_TCP);
packet.setCommand(OP_FILEREQANSWER);
packet.insertData(fileHash.getHash());
packet.insertData(Convert.intToShort(fileNameBytes.length));
packet.insertData(fileNameBytes);
return packet;
}
/**
* File Status reply.
* @param partHashSet
* @param fileSize
* @param fileGapList
* @return
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x50</td>
* <td>The value of the OP FILESTATUS opcode</td>
* </tr>
* <tr>
* <td>File ID</td>
* <td>16</td>
* <td>NA</td>
* <td>The ID of the file</td>
* </tr>
* <tr>
* <td>Part count</td>
* <td>2</td>
* <td>NA</td>
* <td>File parts count</td>
* </tr>
* <tr>
* <td>Part status</td>
* <td> </td>
* <td>NA</td>
* <td>bit array with parts status : 1 - completed part ; 0 - uncompleted part</td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getFileStatusReplyPacket(PartHashSet partHashSet,long fileSize, GapList fileGapList){
JMuleBitSet bitSet = fileGapList.getBitSet(fileSize);
byte [] bitSetArray = bitSet.getAsByteArray();
Packet packet = new Packet(16 + 2 + bitSetArray.length, PROTO_EDONKEY_TCP);
packet.setCommand(OP_FILESTATUS);
packet.insertData(partHashSet.getFileHash().getHash());
packet.insertData((short)partHashSet.size());
packet.insertData(bitSetArray);
return packet;
}
/**
* File hash set answer packet.
* @param fileHashSet
* @return
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x51</td>
* <td>The value of the OP_HASHSETREQUEST opcode</td>
* </tr>
* <tr>
* <td>File Hash</td>
* <td>16</td>
* <td> NA</td>
* <td>The hash extracted from all the file</td>
* </tr>
* <tr>
* <td>Part Count</td>
* <td>2</td>
* <td>NA</td>
* <td>The number of parts in the file</td>
* </tr>
* <tr>
* <td>Part Hashes</td>
* <td>Varies</td>
* <td>NA</td>
* <td>A hash for each file part - the size of each hash
* is 16 bytes
* </td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getFileHashReplyPacket(PartHashSet fileHashSet){
Packet packet = new Packet(16 + 2 + 16 * fileHashSet.size(),
PROTO_EDONKEY_TCP);
packet.setCommand(OP_HASHSETANSWER);
packet.insertData(fileHashSet.getFileHash().getHash());
packet.insertData(Convert.intToShort(fileHashSet.size()));
for (int i = 0; i < fileHashSet.size(); i++)
packet.insertData(fileHashSet.get(i));
return packet;
}
/**
* Accept upload packet.
* @param fileHash
* @return
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields</td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x55</td>
* <td>The value of the OP_ACCEPTUPLOADREQ opcode
* </td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getAcceptUploadPacket(FileHash fileHash){
Packet packet = new Packet(16, PROTO_EDONKEY_TCP);
packet.setCommand(OP_SLOTGIVEN);
packet.insertData(fileHash.getHash());
return packet;
}
/**
* File part sending packet.
* @param fileHash
* @param fileChunk
* @return
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x46</td>
* <td>The value of the OP SENDINGPART opcode</td>
* </tr>
* <tr>
* <td>File ID</td>
* <td> 16</td>
* <td> NA</td>
* <td>A unique file ID calculated by hashing the
* file’s data
* </td>
* </tr>
* <tr>
* <td>Start Pos</td>
* <td>4</td>
* <td>NA</td>
* <td>The start position of the downloaded data</td>
* </tr>
* <tr>
* <td>End Pos</td>
* <td>4</td>
* <td>NA</td>
* <td>The end position of the downloaded data</td>
* </tr>
* <tr>
* <td>Data</td>
* <td> </td>
* <td> </td>
* <td>The actual downloaded data. This data may
* be compressed.
* </td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getFilePartSendingPacket(FileHash fileHash,FileChunk fileChunk){
Packet packet = new Packet(16 + 4 + 4 + fileChunk.getChunkData()
.capacity(), PROTO_EDONKEY_TCP);
packet.setCommand(OP_SENDINGPART);
packet.insertData(fileHash.getHash());
packet.insertData(Convert.longToInt(fileChunk.getChunkStart()));
packet.insertData(Convert.longToInt(fileChunk.getChunkEnd()));
packet.insertData(fileChunk.getChunkData().array());
return packet;
}
/**
* File not found packet.
* @param fileHash
* @return
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xE3</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x48</td>
* <td>The value of the OP FILEREQANSNOFIL
* opcode</td>
* </tr>
* <tr>
* <td>File ID</td>
* <td>16</td>
* <td>NA</td>
* <td>The non existent file ID</td>
* </tr>
* </tbody>
* </table>
* </table>
*/
public static Packet getFileNotFoundPacket(FileHash fileHash){
Packet packet = new Packet(16, PROTO_EDONKEY_TCP);
packet.setCommand(OP_FILEREQANSNOFILE);
packet.insertData(fileHash.getHash());
return packet;
}
public static Packet getEMulePeerHelloPacket() throws JMException {
TagList tag_list = new TagList();
tag_list.addTag(new IntTag(ET_COMPRESSION, DefaultJMuleFeatures.get(PeerFeatures.DataCompressionVer)));
tag_list.addTag(new IntTag(ET_UDPPORT, (ConfigurationManagerSingleton.getInstance().getUDP())));
tag_list.addTag(new IntTag(ET_UDPVER, DefaultJMuleFeatures.get(PeerFeatures.UDPVer)));
tag_list.addTag(new IntTag(ET_SOURCEEXCHANGE, DefaultJMuleFeatures.get(PeerFeatures.SourceExchange1Ver) ));
tag_list.addTag(new IntTag(ET_COMMENTS, DefaultJMuleFeatures.get(PeerFeatures.AcceptCommentVer)));
tag_list.addTag(new IntTag(ET_EXTENDEDREQUEST, DefaultJMuleFeatures.get(PeerFeatures.ExtendedRequestsVer) ));
tag_list.addTag(new IntTag(ET_FEATURES, DefaultJMuleFeatures.get(PeerFeatures.NoViewSharedFiles)));
Packet packet = new Packet(1 + 4 + 1 + 1 + 1 + 4 + tag_list.getByteSize(),
PROTO_EMULE_EXTENDED_TCP);
packet.setCommand(OP_EMULE_HELLO);
packet.insertData(Convert.intToByte(E2DKConstants.ClientVersion[1]));
packet.insertData((byte)1);
packet.insertData(tag_list.size());
packet.insertData(tag_list.getAsByteBuffer());
return packet;
}
/**
* Create eMule hello answer packet.
*
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xC5</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x02</td>
* <td>The value of the OP EMULEINFOANSWER
* opcode</td>
* </tr>
* <tr>
* <td>eMule Info
* fields</td>
* <td> </td>
* <td> </td>
* <td>This message has the same fields as an eMule
* info message.</td>
* </tr>
* </tbody>
* </table>
*/
public static Packet getEMulePeerHelloAnswerPacket() throws JMException {
TagList tag_list = new TagList();
tag_list.addTag(new IntTag(ET_COMPRESSION, DefaultJMuleFeatures.get(PeerFeatures.DataCompressionVer)));
tag_list.addTag(new IntTag(ET_UDPPORT, (ConfigurationManagerSingleton.getInstance().getUDP())));
tag_list.addTag(new IntTag(ET_UDPVER, DefaultJMuleFeatures.get(PeerFeatures.UDPVer)));
tag_list.addTag(new IntTag(ET_SOURCEEXCHANGE, DefaultJMuleFeatures.get(PeerFeatures.SourceExchange1Ver) ));
tag_list.addTag(new IntTag(ET_COMMENTS, DefaultJMuleFeatures.get(PeerFeatures.AcceptCommentVer)));
tag_list.addTag(new IntTag(ET_EXTENDEDREQUEST, DefaultJMuleFeatures.get(PeerFeatures.ExtendedRequestsVer) ));
tag_list.addTag(new IntTag(ET_FEATURES, DefaultJMuleFeatures.get(PeerFeatures.NoViewSharedFiles)));
Packet packet = new Packet(1 + 4 + 1 + 1 + 1 + 4 + tag_list.getByteSize(),
PROTO_EMULE_EXTENDED_TCP);
packet.setCommand(OP_EMULEHELLOANSWER);
packet.insertData(Convert.intToByte(E2DKConstants.ClientVersion[1]));
packet.insertData((byte)1);
packet.insertData(tag_list.size());
packet.insertData(tag_list.getAsByteBuffer());
return packet;
}
/**
* Create Queue ranking packet.
* @param position position in queue
*
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xC5</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x60</td>
* <td>The value of the OP_QUEUERANKING
* opcode</td>
* </tr>
* <tr>
* <td>Queue position</td>
* <td>2</td>
* <td>NA</td>
* <td>The position of the client in the queue</td>
* </tr>
* <tr>
* <td>Buffer</td>
* <td>10</td>
* <td>0</td>
* <td>10 zero bytes, purpose unknown</td>
* </tr>
* </tbody>
* </table>
*/
public static Packet getQueueRankingPacket(int position) {
Packet packet = new Packet(2 + 10, PROTO_EMULE_EXTENDED_TCP);
packet.setCommand(OP_EMULE_QUEUERANKING);
packet.insertData(Convert.intToShort(position));
ByteBuffer zero_array = Misc.getByteBuffer(10);
zero_array.position(0);
packet.insertData(zero_array);
return packet;
}
/**
* PEX request
* Create sources request packet
* @param fileHash hash of file which sources are requested.
* <table cellspacing="0" border="1" cellpadding="0">
* <thead>
* <tr>
* <th>Name</th>
* <th>Size in bytes</th>
* <th>Default value</th>
* <th>Comment</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Protocol</td>
* <td>1</td>
* <td>0xC5</td>
* <td>-</td>
* </tr>
* <tr>
* <td>Size</td>
* <td>4</td>
* <td>-</td>
* <td>The size of the message in bytes not including
* the header and size fields
* </td>
* </tr>
* <tr>
* <td>Type</td>
* <td>1</td>
* <td>0x81</td>
* <td>The value of the OP_ REQUESTSOURCES
* opcode</td>
* </tr>
* <tr>
* <td>File ID</td>
* <td>16</td>
* <td>NA</td>
* <td>The file ID of the required file</td>
* </tr>
* </tbody>
*/
public static Packet getSourcesRequestPacket(FileHash fileHash) {
Packet packet = new Packet(16, PROTO_EMULE_EXTENDED_TCP);
packet.setCommand(OP_REQUESTSOURCES);
packet.insertData(fileHash.getHash());
return packet;
}
/**
* Basic PEX answer pachet
*
*/
public static Packet getSourcesAnswerPacket(FileHash fileHash, List<Peer> peer_list) {
Packet packet = new Packet(16 + 2 + peer_list.size() * (4 + 2),PROTO_EMULE_EXTENDED_TCP);
packet.setCommand(OP_ANSWERSOURCES);
packet.insertData(fileHash.getHash());
packet.insertData(Convert.intToShort(peer_list.size()));
for(Peer peer : peer_list) {
byte[] ip_data = Convert.stringIPToArray(peer.getIP());
short port = Convert.intToShort(peer.getListenPort());
packet.insertData(ip_data);
packet.insertData(port);
}
return packet;
}
public static Packet getSecureIdentificationPacket(byte[] challenge, boolean isPublicKeyNeeded) {
Packet packet = new Packet(5, PROTO_EMULE_EXTENDED_TCP);
packet.setCommand(OP_SECIDENTSTATE);
if (isPublicKeyNeeded)
packet.insertData((byte)2);
else
packet.insertData((byte)1);
packet.insertData(challenge);
return packet;
}
public static Packet getPublicKeyPacket(byte[] publicKey) {
Packet packet = new Packet(1+76, PROTO_EMULE_EXTENDED_TCP);
packet.setCommand(OP_PUBLICKEY);
packet.insertData((byte)publicKey.length);
packet.insertData(publicKey);
return packet;
}
public static Packet getSignaturePacket(byte[] signature) {
Packet packet = new Packet(1 + 48, PROTO_EMULE_EXTENDED_TCP);
packet.setCommand(OP_SIGNATURE);
packet.insertData((byte)signature.length);
packet.insertData(signature);
return packet;
}
public static Packet getKadCallBackRequest(Int128 clientID, FileHash fileHash,IPAddress ipAddress, short tcpPort) {
Packet packet = new Packet(16 + 16 + 4 + 2, PROTO_EMULE_EXTENDED_TCP);
packet.setCommand(OP_KAD_CALLBACK);
packet.insertData(clientID.toByteArray());
packet.insertData(fileHash.getHash());
packet.insertData(ipAddress.getAddress());
packet.insertData(tcpPort);
return packet;
}
}