* Implement deletion of email packets from the DHT and deletion of index packet entries
* Version 2 of the network protocol
This commit is contained in:
133
doc/techdoc.txt
133
doc/techdoc.txt
@ -76,13 +76,43 @@ Below is a diagram of how an email packet is routed from a sender to a recipient
|
||||
`-------' `-------' `---------'
|
||||
|
||||
|
||||
The number of relays for sending or retrieving an email is user-configurable. For higher performance (but
|
||||
reduced anonymity), it is possible to use no relays, no storers/fetchers, i.e. sender and recipient
|
||||
talk to the storage nodes directly.
|
||||
The number of relays for sending or retrieving an email is user-configurable.
|
||||
|
||||
TODO explain how an index packet is relayed back to the recipient, who then uses another chain to get the email packets
|
||||
referenced in the index packet.
|
||||
|
||||
For higher performance (but reduced anonymity), it is possible to use no relays, no storers/fetchers,
|
||||
i.e. sender and recipient talk to the storage nodes directly. If both sender and recipient chose not to
|
||||
use relays, the diagram looks like this:
|
||||
|
||||
.--------.
|
||||
| Sender | ----------------------------.
|
||||
`--------' `\
|
||||
|
|
||||
V
|
||||
|
||||
.--------------- Kademlia DHT -------------------------.
|
||||
| .---------. |
|
||||
| .---------. | Storage | |
|
||||
| | Storage | .---------. | Node | |
|
||||
| | Node | | Storage | `---------' |
|
||||
| `---------' | Node | |
|
||||
| `---------' .---------. |
|
||||
| .---------. .---------. | Storage | |
|
||||
| | Storage | | Storage | .---------. | Node | |
|
||||
| | Node | | Node | | Storage | `---------' |
|
||||
| `---------' `---------' | Node | |
|
||||
| `---------' |
|
||||
`------------------------------------------------------'
|
||||
|
||||
|
|
||||
|
|
||||
.-----------. _'
|
||||
| Recipient | <-------------------------'
|
||||
`-----------'
|
||||
|
||||
|
||||
|
||||
The code is licensed under the GPL. The author can be reached at HungryHobo@mail.i2p, either in
|
||||
German or English.
|
||||
|
||||
@ -112,7 +142,7 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
Relay Request | | | Request to a node to forward a Relay Packet
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'Y'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| VER | 1 byte | Protocol version
|
||||
| PID | 32 bytes | Packet ID, used for delivery confirmation
|
||||
| HLEN | 2 bytes | HashCash length
|
||||
| HK | byte[] | HashCash token (HLEN bytes)
|
||||
@ -122,7 +152,7 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
Fetch Request | | | Request to a chain end point to fetch emails
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'T'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| VER | 1 byte | Protocol version
|
||||
| PID | 32 bytes | Packet ID, used for delivery confirmation
|
||||
| DTYP | 1 byte | Type of data to retrieve:
|
||||
| KEY | 32 bytes | DHT key to look up
|
||||
@ -134,7 +164,7 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
| | | Find Close Peers Request, or a Relay List Request
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'N'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| VER | 1 byte | Protocol version
|
||||
| PID | 32 bytes | Packet Id of the request packet
|
||||
| STA | 1 byte | Status code:
|
||||
| | | 0 = OK
|
||||
@ -147,30 +177,46 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
| DLEN | 2 bytes | Length of DATA field; can be 0 if no payload
|
||||
| DATA | byte[] | A Payload Packet
|
||||
---------------------+-------+------------+----------------------------------------------------
|
||||
Delete Request | | | Request to delete all copies of a packet by DHT key
|
||||
Email Packet | | | Request to delete an Email Packet by DHT key
|
||||
Delete Request | | |
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'D'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| KEY | 32 bytes | DHT key of the packet that is to be deleted
|
||||
| DEL | 32 bytes | Deletion key, encrypts to DEL value in the email pkt
|
||||
| VER | 1 byte | Protocol version
|
||||
| KEY | 32 bytes | DHT key of the Email Packet to delete
|
||||
| DEL | 32 bytes | Deletion key, must match DEL value in the email pkt
|
||||
---------------------+-------+------------+----------------------------------------------------
|
||||
Index Packet | | | Request to remove one or more entries (Email
|
||||
Delete Request | | | Packet key / del key pairs) from an Index Packet
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'X'
|
||||
| VER | 1 byte | Protocol version
|
||||
| DH | 32 bytes | The Email Destination hash of the Index Packet
|
||||
| N | 1 byte | Number of entries in the packet
|
||||
| DHT1 | 32 bytes | First DHT key to remove
|
||||
| DEL1 | 32 bytes | Deletion key for DHT1
|
||||
| DHT2 | 32 bytes | Second DHT key to remove
|
||||
| DEL2 | 32 bytes | Deletion key for DHT2
|
||||
| ... | ... | ...
|
||||
| DHTn | 32 bytes | n-th DHT key to remove
|
||||
| DELn | 32 bytes | Deletion key for DHTn
|
||||
---------------------+-------+------------+-------------------------------------------------
|
||||
Ping | | | Check to see if a host is still up.
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'P'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| VER | 1 byte | Protocol version
|
||||
| PID | 32 bytes | Packet ID
|
||||
---------------------+-------+------------+-------------------------------------------------
|
||||
Pong | | | Let the the pinger know we're still there.
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'O'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| TYPE | 1 byte | Value = 'O' (the letter O)
|
||||
| VER | 1 byte | Protocol version
|
||||
| PID | 32 bytes | Packet ID of the corresponding Ping packet
|
||||
| HKS | 1 byte | Minimum HashCash strength this node will accept
|
||||
---------------------+-------+------------+----------------------------------------------------
|
||||
Relay List Request | | | "Send me your list of relay peers"
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'A'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| VER | 1 byte | Protocol version
|
||||
| PID | 32 bytes | Packet ID
|
||||
---------------------+-------+------------+----------------------------------------------------
|
||||
|
||||
@ -181,7 +227,7 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
Retrieve Request | | | DHT Request for a value for a key
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'Q'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| VER | 1 byte | Protocol version
|
||||
| PID | 32 bytes | Packet ID, used for delivery confirmation
|
||||
| DTYP | 1 byte | Type of data to retrieve:
|
||||
| | | 'I' = Index Packet
|
||||
@ -192,7 +238,7 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
Store Request | | | DHT Store Request
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'S'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| VER | 1 byte | Protocol version
|
||||
| PID | 32 bytes | Packet ID, used for delivery confirmation
|
||||
| HLEN | 2 bytes | HashCash length
|
||||
| HK | byte[] | HashCash token (HLEN bytes)
|
||||
@ -203,7 +249,7 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
Find Close Peers | | | Request for k peers close to a key
|
||||
| PFX | 4 bytes | Packet prefix, must be 0x6D 0x30 0x52 0xE9
|
||||
| TYPE | 1 byte | Value = 'F'
|
||||
| VER | 1 byte | Format version (only ver. 1 is supported)
|
||||
| VER | 1 byte | Protocol version
|
||||
| PID | 32 bytes | Packet ID, used for delivery confirmation
|
||||
| KEY | 32 bytes | DHT key
|
||||
---------------------+-------+------------+----------------------------------------------------
|
||||
@ -217,6 +263,7 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
---------------------+-------+------------+----------------------------------------------------
|
||||
Email Packet | | | An email or email fragment, 1 recipient.
|
||||
(encrypted) | TYPE | 1 byte | Value = 'E'
|
||||
| VER | 1 byte | Protocol version
|
||||
| KEY | 32 bytes | The DHT key of the packet (generated randomly)
|
||||
| DELP | 32 bytes | Deletion key in plaintext, zeroed out for retrieving
|
||||
| LEN | 2 bytes | Length of the encrypted part of the packet
|
||||
@ -224,8 +271,7 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
---------------------+-------+------------+----------------------------------------------------
|
||||
Email Packet | | | Storage format for the Incomplete Email Folder.
|
||||
(unencrypted) | TYPE | 1 byte | Value = 'U'
|
||||
| DELP | 32 bytes | Deletion key in plaintext, zeroed out for retrieving
|
||||
| DELV | 32 bytes | Deletion key for verification
|
||||
| VER | 1 byte | Protocol version
|
||||
| MSID | 32 bytes | Message ID in binary format
|
||||
| FRID | 2 bytes | Fragment Index of this packet (0..NFR-1)
|
||||
| NFR | 2 bytes | Number of fragments in the email
|
||||
@ -234,15 +280,20 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
---------------------+-------+------------+----------------------------------------------------
|
||||
Index Packet | | | Contains the DHT keys of one or more Email Packets.
|
||||
| TYPE | 1 byte | Value = 'I'
|
||||
| VER | 1 byte | Protocol version
|
||||
| DH | 32 bytes | SHA-256 hash of the recipient's email destination
|
||||
| NP | 1 byte | Number of keys in the packet
|
||||
| KEY1 | 32 bytes | DHT key of the first Email Packet
|
||||
| KEY1 | 32 bytes | DHT key of the second Email Packet
|
||||
| DHT1 | 32 bytes | DHT key of the first Email Packet
|
||||
| DEL1 | 32 bytes | Deletion key of the first Email Packet
|
||||
| DHT2 | 32 bytes | DHT key of the second Email Packet
|
||||
| DEL2 | 32 bytes | Deletion key of the second Email Packet
|
||||
| ... | ... | ...
|
||||
| KEYn | 32 bytes | DHT key of the n-th Email Packet
|
||||
| DHTn | 32 bytes | DHT key of the n-th Email Packet
|
||||
| DELn | 32 bytes | Deletion key of the n-th Email Packet
|
||||
---------------------+-------+------------+----------------------------------------------------
|
||||
Relay Packet | | | The payload of a Relay Request
|
||||
| TYPE | 1 byte | Value = 'R'
|
||||
| VER | 1 byte | Protocol version
|
||||
| TMIN | 4 bytes | Earliest send time
|
||||
| TMAX | 4 bytes | Latest sent time (no guarantee!)
|
||||
| XK | 32 bytes | Key to XOR the payload with
|
||||
@ -255,6 +306,7 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
Peer List | | | Response to a Find Close Peers Request or
|
||||
| | | a Relay List Request
|
||||
| TYPE | 1 byte | Value = 'L'
|
||||
| VER | 1 byte | Protocol version
|
||||
| NUMP | 2 bytes | Number of peers in the list
|
||||
| P1 | 384 bytes | Destination key
|
||||
| P2 | 384 bytes | Destination key
|
||||
@ -315,15 +367,38 @@ TODO mention the problem of highly unbalanced trees and the sibling list solutio
|
||||
|
||||
4.7. Deleting an Email Packet
|
||||
|
||||
Every time a recipient receives an email packet (from one or more storage nodes), it asks the storage
|
||||
node(s) to delete the packet.
|
||||
|
||||
[TODO: This doesn't work for relayed email packets because the recipient doesn't know the storage nodes.
|
||||
Maybe include the storage node in an email packet? Or relay the delete request and have the
|
||||
relay endpoint do a findClosestNodes + delete? Or just delete the index packet entry and let
|
||||
replication take care of deleting the email packets?]
|
||||
|
||||
It also sends a delete request to the nodes storing the index packet, asking them to delete the DHT key
|
||||
of the email packet. Each index packet node then removes the DHT key from the stored index packet, and
|
||||
adds the DHT key to a list of deleted keys it maintains for each index packet (DHT key + deletion key,
|
||||
actually). The purpose of this is so a node that is about to replicate an email packet can find out if
|
||||
it missed an earlier delete request for that packet, in which case the node "replicates" the delete
|
||||
request rather than the packet itself. This helps reduce storage space by removing old Email Packets
|
||||
from nodes that weren't online at the time the delete request was sent initially.
|
||||
|
||||
1) Recipient knows the deletion key after it decrypts the email packet (the packet also contains a
|
||||
"plaintext deletion key" field, which is all zeros after it leaves a storage node).
|
||||
2) Recipient sends a Delete Request to all storage nodes close to the DHT key of the Email Packet
|
||||
2) Recipient sends a Delete Request to all nodes that responded to the query for the Email Destination,
|
||||
asking them to mark the Email Packet's DHT key "deleted".
|
||||
***** TODO deletion key for index packets? use signed del requests instead? *****
|
||||
3) Recipient sends a Delete Request to all peers that responded to the query for the Email Packet
|
||||
|
||||
4.8. Looking up an email address in the directory
|
||||
4.8. Replication of Email Packets
|
||||
|
||||
4.9. Announcing a new email address to the directory
|
||||
4.9. Replication of Index Packets
|
||||
|
||||
4.10. Updating or deleting an email address from the directory
|
||||
4.10. Looking up an email address in the directory
|
||||
|
||||
4.11. Announcing a new email address to the directory
|
||||
|
||||
4.12. Updating or deleting an email address from the directory
|
||||
|
||||
|
||||
5. Algorithms Used By Nodes Locally
|
||||
@ -470,6 +545,12 @@ I2P destination
|
||||
|
||||
11. To Do
|
||||
|
||||
* don't send delete requests to self, just delete the file locally
|
||||
* rename test/.../kademlia package to dht for consistency
|
||||
* make blue background solid, lighter color so chinese chars are readable
|
||||
* show # of new emails next to folder name, show new + total count on a tool tip
|
||||
* don't print subject lines in bold unless the email is new (store msg ids of read emails in a file in the email folder)
|
||||
* button to delete all packets with an invalid protocol version
|
||||
* show "to" address in inbox
|
||||
* put sender name in "From" header field, if available
|
||||
* auto-refresh network.jsp
|
||||
|
@ -25,20 +25,20 @@ import i2p.bote.email.Email;
|
||||
import i2p.bote.email.EmailDestination;
|
||||
import i2p.bote.email.EmailIdentity;
|
||||
import i2p.bote.email.Identities;
|
||||
import i2p.bote.folder.DhtPacketFolder;
|
||||
import i2p.bote.folder.EmailFolder;
|
||||
import i2p.bote.folder.EmailPacketFolder;
|
||||
import i2p.bote.folder.IncompleteEmailFolder;
|
||||
import i2p.bote.folder.IndexPacketFolder;
|
||||
import i2p.bote.folder.Outbox;
|
||||
import i2p.bote.folder.PacketFolder;
|
||||
import i2p.bote.network.BanList;
|
||||
import i2p.bote.network.CheckEmailTask;
|
||||
import i2p.bote.network.NetworkStatus;
|
||||
import i2p.bote.network.DHT;
|
||||
import i2p.bote.network.I2PPacketDispatcher;
|
||||
import i2p.bote.network.I2PSendQueue;
|
||||
import i2p.bote.network.NetworkStatus;
|
||||
import i2p.bote.network.PeerManager;
|
||||
import i2p.bote.network.kademlia.KademliaDHT;
|
||||
import i2p.bote.packet.DataPacket;
|
||||
import i2p.bote.packet.EncryptedEmailPacket;
|
||||
import i2p.bote.packet.IndexPacket;
|
||||
import i2p.bote.packet.RelayPacket;
|
||||
@ -76,13 +76,13 @@ import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* This is the core class of the application. Is is implemented as a singleton.
|
||||
*/
|
||||
public class I2PBote {
|
||||
private static final String VERSION = "0.1.5";
|
||||
public static final int PROTOCOL_VERSION = 2;
|
||||
private static final String APP_VERSION = "0.1.5";
|
||||
private static final int STARTUP_DELAY = 3 * 60 * 1000; // the number of milliseconds to wait before connecting to I2P (this gives the router time to get ready)
|
||||
private static I2PBote instance;
|
||||
|
||||
@ -97,7 +97,7 @@ public class I2PBote {
|
||||
private EmailFolder inbox; // stores incoming emails for all local users
|
||||
private PacketFolder<RelayPacket> relayPacketFolder; // stores email packets we're forwarding for other machines
|
||||
private IncompleteEmailFolder incompleteEmailFolder; // stores email packets addressed to a local user
|
||||
private DhtPacketFolder<? extends DataPacket> emailDhtStorageFolder; // stores email packets and index packets for other peers
|
||||
private EmailPacketFolder emailDhtStorageFolder; // stores email packets for other peers
|
||||
private IndexPacketFolder indexPacketDhtStorageFolder; // stores index packets
|
||||
//TODO private PacketFolder<> addressDhtStorageFolder; // stores email address-destination mappings
|
||||
private SMTPService smtpService;
|
||||
@ -139,7 +139,7 @@ public class I2PBote {
|
||||
outbox = new Outbox(configuration.getLocalOutboxDir());
|
||||
relayPacketFolder = new PacketFolder<RelayPacket>(configuration.getRelayOutboxDir());
|
||||
incompleteEmailFolder = new IncompleteEmailFolder(configuration.getIncompleteDir(), inbox);
|
||||
emailDhtStorageFolder = new DhtPacketFolder<EncryptedEmailPacket>(configuration.getEmailDhtStorageDir());
|
||||
emailDhtStorageFolder = new EmailPacketFolder(configuration.getEmailDhtStorageDir());
|
||||
indexPacketDhtStorageFolder = new IndexPacketFolder(configuration.getIndexPacketDhtStorageDir());
|
||||
}
|
||||
|
||||
@ -212,6 +212,9 @@ public class I2PBote {
|
||||
dht.setStorageHandler(IndexPacket.class, indexPacketDhtStorageFolder);
|
||||
//TODO dht.setStorageHandler(AddressPacket.class, );
|
||||
|
||||
dispatcher.addPacketListener(emailDhtStorageFolder);
|
||||
dispatcher.addPacketListener(indexPacketDhtStorageFolder);
|
||||
|
||||
peerManager = new PeerManager();
|
||||
|
||||
outboxProcessor = new OutboxProcessor(dht, outbox, configuration, peerManager, appContext);
|
||||
@ -252,8 +255,8 @@ public class I2PBote {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return VERSION;
|
||||
public String getAppVersion() {
|
||||
return APP_VERSION;
|
||||
}
|
||||
|
||||
public Identities getIdentities() {
|
||||
@ -285,7 +288,7 @@ dht.store(new IndexPacket(encryptedPackets, emailDestination));
|
||||
if (!isCheckingForMail()) {
|
||||
pendingMailCheckTasks = Collections.synchronizedCollection(new ArrayList<Future<Boolean>>());
|
||||
for (EmailIdentity identity: getIdentities()) {
|
||||
Callable<Boolean> checkMailTask = new CheckEmailTask(identity, dht, peerManager, incompleteEmailFolder, appContext);
|
||||
Callable<Boolean> checkMailTask = new CheckEmailTask(identity, dht, peerManager, sendQueue, incompleteEmailFolder, appContext);
|
||||
Future<Boolean> task = mailCheckExecutor.submit(checkMailTask);
|
||||
pendingMailCheckTasks.add(task);
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ public class UniqueId implements Comparable<UniqueId> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a packet id from a 32 bytes of an array, starting at <code>offset</code>.
|
||||
* Create a packet id from 32 bytes of an array, starting at <code>offset</code>.
|
||||
* @param bytes
|
||||
*/
|
||||
public UniqueId(byte[] bytes, int offset) {
|
||||
|
@ -302,9 +302,8 @@ public class Email implements FolderElement {
|
||||
// make a new array with the right length
|
||||
byte[] block = new byte[blockSize];
|
||||
System.arraycopy(emailArray, blockStart, block, 0, blockSize);
|
||||
UniqueId deletionKeyPlain = new UniqueId();
|
||||
UniqueId deletionKeyEncrypted = deletionKeyPlain.clone(); // encryption happens in the constructor call below
|
||||
UnencryptedEmailPacket packet = new UnencryptedEmailPacket(deletionKeyPlain, deletionKeyEncrypted, messageId, fragmentIndex, numFragments, block);
|
||||
UniqueId deletionKey = new UniqueId();
|
||||
UnencryptedEmailPacket packet = new UnencryptedEmailPacket(messageId, fragmentIndex, numFragments, block, deletionKey);
|
||||
packets.add(packet);
|
||||
fragmentIndex++;
|
||||
blockStart += blockSize;
|
||||
|
@ -47,12 +47,20 @@ public class DhtPacketFolder<T extends DhtStorablePacket> extends PacketFolder<T
|
||||
add(packetToStore, getFilename(packetToStore));
|
||||
}
|
||||
|
||||
private String getFilename(DhtStorablePacket packet) {
|
||||
protected String getFilename(DhtStorablePacket packet) {
|
||||
return packet.getDhtKey().toBase64() + PACKET_FILE_EXTENSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhtStorablePacket retrieve(Hash dhtKey) {
|
||||
File packetFile = findPacketFile(dhtKey);
|
||||
if (packetFile != null)
|
||||
return DhtStorablePacket.createPacket(packetFile);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
protected File findPacketFile(Hash dhtKey) {
|
||||
final String base64Key = dhtKey.toBase64();
|
||||
|
||||
File[] files = storageDir.listFiles(new FilenameFilter() {
|
||||
@ -66,7 +74,7 @@ public class DhtPacketFolder<T extends DhtStorablePacket> extends PacketFolder<T
|
||||
log.warn("More than one packet files found for DHT key " + dhtKey);
|
||||
if (files.length > 0) {
|
||||
File file = files[0];
|
||||
return DhtStorablePacket.createPacket(file);
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -74,4 +82,14 @@ public class DhtPacketFolder<T extends DhtStorablePacket> extends PacketFolder<T
|
||||
protected boolean filenameMatches(String filename, String base64DhtKey) {
|
||||
return filename.startsWith(base64DhtKey);
|
||||
}
|
||||
|
||||
public void delete(Hash dhtKey) {
|
||||
File packetFile = findPacketFile(dhtKey);
|
||||
if (packetFile != null) {
|
||||
if (!packetFile.delete())
|
||||
log.warn("File cannot be deleted: <" + packetFile.getAbsolutePath() + ">");
|
||||
}
|
||||
else
|
||||
log.debug("No file found for DHT key: " + dhtKey);
|
||||
}
|
||||
}
|
67
src/i2p/bote/folder/EmailPacketFolder.java
Normal file
67
src/i2p/bote/folder/EmailPacketFolder.java
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote 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.
|
||||
*
|
||||
* I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.folder;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
import i2p.bote.network.PacketListener;
|
||||
import i2p.bote.packet.CommunicationPacket;
|
||||
import i2p.bote.packet.EmailPacketDeleteRequest;
|
||||
import i2p.bote.packet.EncryptedEmailPacket;
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* A subclass of {@link DhtPacketFolder} that stores email packets and deletes them
|
||||
* upon {@link EmailPacketDeleteRequest}s.
|
||||
*/
|
||||
public class EmailPacketFolder extends DhtPacketFolder<EncryptedEmailPacket> implements PacketListener {
|
||||
private Log log = new Log(EmailPacketFolder.class);
|
||||
|
||||
public EmailPacketFolder(File storageDir) {
|
||||
super(storageDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
|
||||
if (packet instanceof EmailPacketDeleteRequest) {
|
||||
EmailPacketDeleteRequest delRequest = (EmailPacketDeleteRequest)packet;
|
||||
|
||||
Hash dhtKey = delRequest.getDhtKey();
|
||||
DhtStorablePacket storedPacket = retrieve(dhtKey);
|
||||
if (storedPacket instanceof EncryptedEmailPacket) {
|
||||
UniqueId storedDeletionKey = ((EncryptedEmailPacket)storedPacket).getPlaintextDeletionKey();
|
||||
|
||||
if (storedDeletionKey.equals(delRequest.getDeletionKey()))
|
||||
delete(dhtKey);
|
||||
else
|
||||
log.debug("Deletion key in EmailPacketDeleteRequest does not match. Should be: <" + storedDeletionKey + ">, is <" + delRequest.getDeletionKey() +">");
|
||||
}
|
||||
else
|
||||
log.debug("EncryptedEmailPacket expected for DHT key <" + dhtKey + ">, found " + storedPacket.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
@ -21,19 +21,33 @@
|
||||
|
||||
package i2p.bote.folder;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
import i2p.bote.network.PacketListener;
|
||||
import i2p.bote.packet.CommunicationPacket;
|
||||
import i2p.bote.packet.I2PBotePacket;
|
||||
import i2p.bote.packet.IndexPacket;
|
||||
import i2p.bote.packet.IndexPacketDeleteRequest;
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* This class differs from {@link DhtPacketFolder} in that it doesn't overwrite an existing
|
||||
* packet when a new packet is stored under the same key, but merges the packets.
|
||||
* This class uses Email Destination hashes for DHT keys.
|
||||
* It differs from {@link DhtPacketFolder} in two ways:
|
||||
* * It doesn't overwrite an existing packet when a new packet is stored under the same key,
|
||||
* but merges the packets.
|
||||
* * It retains DHT keys of deleted packets in a file named <code>DEL_<dht_key>.pkt</code>
|
||||
* for later reference. These files use the same format as Index Packet files.
|
||||
*
|
||||
*/
|
||||
public class IndexPacketFolder extends DhtPacketFolder<IndexPacket> {
|
||||
public class IndexPacketFolder extends DhtPacketFolder<IndexPacket> implements PacketListener {
|
||||
private static final String DEL_FILE_PREFIX = "DEL_";
|
||||
|
||||
private final Log log = new Log(I2PBotePacket.class);
|
||||
|
||||
public IndexPacketFolder(File storageDir) {
|
||||
@ -58,4 +72,78 @@ public class IndexPacketFolder extends DhtPacketFolder<IndexPacket> {
|
||||
|
||||
super.store(packetToStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Hash dhtKey) {
|
||||
throw new UnsupportedOperationException("Index packets are never deleted. Use remove(Hash, Hash) to remove an entry from an index packet file.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an entry from an {@link IndexPacket} and saves the packet to disk.
|
||||
* @param indexPacket
|
||||
* @param emailPacketKey The entry to delete
|
||||
*/
|
||||
public void remove(IndexPacket indexPacket, Hash emailPacketKey) {
|
||||
log.debug("Removing DHT key " + emailPacketKey + " from Index Packet for Email Dest " + indexPacket.getDhtKey());
|
||||
UniqueId deletionKey = indexPacket.getDeletionKey(emailPacketKey);
|
||||
if (deletionKey == null)
|
||||
log.debug("DHT key " + emailPacketKey + " not found in index packet " + indexPacket);
|
||||
else {
|
||||
indexPacket.remove(emailPacketKey);
|
||||
addToDeletedPackets(indexPacket, emailPacketKey, deletionKey);
|
||||
}
|
||||
super.store(indexPacket); // don't merge, but overwrite the file with the key removed
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a DHT key of an Email Packet to the list of deleted packets.
|
||||
* If the key is already on the list, nothing happens.
|
||||
* @param indexPacket
|
||||
* @param dhtKey
|
||||
* @param deletionKey
|
||||
*/
|
||||
private void addToDeletedPackets(IndexPacket indexPacket, Hash dhtKey, UniqueId deletionKey) {
|
||||
String delFileName = DEL_FILE_PREFIX + getFilename(indexPacket);
|
||||
File delFile = new File(storageDir, delFileName);
|
||||
|
||||
// read delete list from file or create a new one if file doesn't exist
|
||||
DhtStorablePacket delListPacket;
|
||||
if (!delFile.exists())
|
||||
delListPacket = new IndexPacket(indexPacket);
|
||||
else {
|
||||
delListPacket = DhtStorablePacket.createPacket(delFile);
|
||||
if (!(delListPacket instanceof IndexPacket)) {
|
||||
log.error("Not an Index Packet file: <" + delFile + ">");
|
||||
return;
|
||||
}
|
||||
}
|
||||
((IndexPacket)delListPacket).put(dhtKey, deletionKey);
|
||||
add(delListPacket, delFileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
|
||||
if (packet instanceof IndexPacketDeleteRequest) {
|
||||
IndexPacketDeleteRequest delRequest = (IndexPacketDeleteRequest)packet;
|
||||
log.debug("IndexPacketDeleteRequest received. #entries=" + delRequest.getNumEntries());
|
||||
|
||||
Hash dhtKey = delRequest.getEmailDestHash();
|
||||
DhtStorablePacket storedPacket = retrieve(dhtKey);
|
||||
if (storedPacket instanceof IndexPacket) {
|
||||
IndexPacket indexPacket = (IndexPacket)storedPacket;
|
||||
Collection<Hash> keysToDelete = delRequest.getDhtKeys();
|
||||
|
||||
for (Hash keyToDelete: keysToDelete) {
|
||||
UniqueId deletionKeyFromRequest = delRequest.getDeletionKey(keyToDelete);
|
||||
UniqueId storedDeletionKey = indexPacket.getDeletionKey(keyToDelete);
|
||||
if (storedDeletionKey.equals(deletionKeyFromRequest))
|
||||
remove(indexPacket, keyToDelete);
|
||||
else
|
||||
log.debug("Deletion key in IndexPacketDeleteRequest does not match. Should be: <" + storedDeletionKey + ">, is <" + deletionKeyFromRequest +">");
|
||||
}
|
||||
}
|
||||
else
|
||||
log.debug("IndexPacket expected for DHT key <" + dhtKey + ">, found " + storedPacket.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
@ -44,16 +44,17 @@ public class PacketFolder<PacketType extends DataPacket> extends Folder<PacketTy
|
||||
super(storageDir, PACKET_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
// TODO rename to write because existing files are overwritten
|
||||
public <T extends PacketType> void add(T packetToStore) {
|
||||
String filename = new UniqueId().toBase64() + PACKET_FILE_EXTENSION;
|
||||
add(packetToStore, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param packetToStore
|
||||
* @param filename A filename relative to this folder's storage directory.
|
||||
*/
|
||||
// TODO rename to write because existing files are overwritten
|
||||
protected void add(DataPacket packetToStore, String filename) {
|
||||
FileOutputStream outputStream = null;
|
||||
try {
|
||||
@ -74,9 +75,9 @@ public class PacketFolder<PacketType extends DataPacket> extends Folder<PacketTy
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(UniqueId packetId) {
|
||||
/* public void delete(UniqueId packetId) {
|
||||
// TODO
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -25,13 +25,16 @@ import i2p.bote.UniqueId;
|
||||
import i2p.bote.Util;
|
||||
import i2p.bote.email.EmailIdentity;
|
||||
import i2p.bote.folder.IncompleteEmailFolder;
|
||||
import i2p.bote.packet.EmailPacketDeleteRequest;
|
||||
import i2p.bote.packet.EncryptedEmailPacket;
|
||||
import i2p.bote.packet.IndexPacket;
|
||||
import i2p.bote.packet.IndexPacketDeleteRequest;
|
||||
import i2p.bote.packet.UnencryptedEmailPacket;
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@ -41,6 +44,7 @@ import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -58,14 +62,17 @@ public class CheckEmailTask implements Callable<Boolean> {
|
||||
private EmailIdentity identity;
|
||||
private DHT dht;
|
||||
private PeerManager peerManager;
|
||||
private I2PSendQueue sendQueue;
|
||||
private IncompleteEmailFolder incompleteEmailFolder;
|
||||
private I2PAppContext appContext;
|
||||
private ExecutorService executor;
|
||||
|
||||
public CheckEmailTask(EmailIdentity identity, DHT dht, PeerManager peerManager, IncompleteEmailFolder incompleteEmailFolder, I2PAppContext appContext) {
|
||||
// TODO move appContext into EncryptedEmailPacket so there is one less parameter here
|
||||
public CheckEmailTask(EmailIdentity identity, DHT dht, PeerManager peerManager, I2PSendQueue sendQueue, IncompleteEmailFolder incompleteEmailFolder, I2PAppContext appContext) {
|
||||
this.identity = identity;
|
||||
this.dht = dht;
|
||||
this.peerManager = peerManager;
|
||||
this.sendQueue = sendQueue;
|
||||
this.incompleteEmailFolder = incompleteEmailFolder;
|
||||
this.appContext = appContext;
|
||||
executor = Executors.newFixedThreadPool(MAX_THREADS, EMAIL_PACKET_TASK_THREAD_FACTORY);
|
||||
@ -77,11 +84,15 @@ public class CheckEmailTask implements Callable<Boolean> {
|
||||
*/
|
||||
@Override
|
||||
public Boolean call() {
|
||||
Collection<Hash> emailPacketKeys = findEmailPacketKeys();
|
||||
// Use findAll rather than findOne because some peers might have an incomplete set of
|
||||
// Email Packet keys, and because we want to send IndexPacketDeleteRequests to all of them.
|
||||
DhtResults indexPacketResults = dht.findAll(identity.getHash(), IndexPacket.class);
|
||||
|
||||
Collection<Hash> emailPacketKeys = findEmailPacketKeys(indexPacketResults.getPackets());
|
||||
|
||||
Collection<Future<Boolean>> results = new ArrayList<Future<Boolean>>();
|
||||
for (Hash dhtKey: emailPacketKeys) {
|
||||
Future<Boolean> result = executor.submit(new EmailPacketTask(dhtKey));
|
||||
for (Hash emailPacketKey: emailPacketKeys) {
|
||||
Future<Boolean> result = executor.submit(new EmailPacketTask(emailPacketKey, identity.getHash(), indexPacketResults.getPeers()));
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
@ -100,17 +111,15 @@ public class CheckEmailTask implements Callable<Boolean> {
|
||||
|
||||
/**
|
||||
* Queries the DHT for new index packets and returns the DHT keys contained in them.
|
||||
* @param indexPacketResults TODO comment
|
||||
* @return A <code>Collection</code> containing zero or more DHT keys
|
||||
*/
|
||||
private Collection<Hash> findEmailPacketKeys() {
|
||||
private Collection<Hash> findEmailPacketKeys(Collection<DhtStorablePacket> indexPacketResults) {
|
||||
log.debug("Querying the DHT for index packets with key " + identity.getHash());
|
||||
|
||||
// Use findAll rather than findOne because some peers might not have all Email Packet keys.
|
||||
Collection<DhtStorablePacket> packets = dht.findAll(identity.getHash(), IndexPacket.class);
|
||||
|
||||
// build an Collection of index packets
|
||||
// build a Collection of index packets
|
||||
Collection<IndexPacket> indexPackets = new ArrayList<IndexPacket>();
|
||||
for (DhtStorablePacket packet: packets)
|
||||
for (DhtStorablePacket packet: indexPacketResults)
|
||||
if (packet instanceof IndexPacket)
|
||||
indexPackets.add((IndexPacket)packet);
|
||||
else
|
||||
@ -126,14 +135,21 @@ public class CheckEmailTask implements Callable<Boolean> {
|
||||
* and deletes the packet from the DHT.
|
||||
*/
|
||||
private class EmailPacketTask implements Callable<Boolean> {
|
||||
private Hash dhtKey;
|
||||
private Hash emailPacketKey;
|
||||
private Hash emailIdentityHash;
|
||||
private Set<Destination> indexPacketPeers;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dhtKey The DHT key of the email packet to retrieve
|
||||
* @param emailPacketKey The DHT key of the email packet to retrieve
|
||||
* @param emailIdentityHash The DHT key of the packet recipient's email identity
|
||||
* @param indexPacketPeers All peers that are known to be storing an index packet
|
||||
* for the email destination the email packet is being sent to.
|
||||
*/
|
||||
public EmailPacketTask(Hash dhtKey) {
|
||||
this.dhtKey = dhtKey;
|
||||
public EmailPacketTask(Hash emailPacketKey, Hash emailIdentityHash, Set<Destination> indexPacketPeers) {
|
||||
this.emailPacketKey = emailPacketKey;
|
||||
this.emailIdentityHash = emailIdentityHash;
|
||||
this.indexPacketPeers = indexPacketPeers;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,32 +159,58 @@ public class CheckEmailTask implements Callable<Boolean> {
|
||||
@Override
|
||||
public Boolean call() {
|
||||
boolean emailCompleted = false;
|
||||
DhtStorablePacket packet = dht.findOne(dhtKey, EncryptedEmailPacket.class);
|
||||
if (packet instanceof EncryptedEmailPacket) {
|
||||
EncryptedEmailPacket emailPacket = (EncryptedEmailPacket)packet;
|
||||
try {
|
||||
UnencryptedEmailPacket decryptedPacket = emailPacket.decrypt(identity, appContext);
|
||||
emailCompleted = incompleteEmailFolder.addEmailPacket(decryptedPacket);
|
||||
sendDeleteRequest(dhtKey, decryptedPacket.getVerificationDeletionKey());
|
||||
}
|
||||
catch (DataFormatException e) {
|
||||
log.error("Can't decrypt email packet: " + emailPacket, e);
|
||||
// TODO propagate error message to UI
|
||||
// Use findAll rather than findOne because after we receive an email packet, we want
|
||||
// to send delete requests to as many of the storage nodes as possible.
|
||||
DhtResults results = dht.findAll(emailPacketKey, EncryptedEmailPacket.class);
|
||||
|
||||
EncryptedEmailPacket validPacket = null; // stays null until a valid packet is found in the loop below
|
||||
IndexPacketDeleteRequest indexDelRequest = new IndexPacketDeleteRequest(emailIdentityHash);
|
||||
for (Destination peer: results.getPeers()) {
|
||||
DhtStorablePacket packet = results.getPacket(peer);
|
||||
if (packet instanceof EncryptedEmailPacket) {
|
||||
EncryptedEmailPacket emailPacket = (EncryptedEmailPacket)packet;
|
||||
try {
|
||||
UnencryptedEmailPacket decryptedPacket = emailPacket.decrypt(identity, appContext);
|
||||
if (validPacket == null) {
|
||||
emailCompleted = incompleteEmailFolder.addEmailPacket(decryptedPacket);
|
||||
validPacket = emailPacket;
|
||||
}
|
||||
UniqueId delKey = decryptedPacket.getVerificationDeletionKey();
|
||||
sendDeleteRequest(emailPacketKey, delKey, peer);
|
||||
indexDelRequest.put(emailPacketKey, delKey);
|
||||
}
|
||||
catch (DataFormatException e) {
|
||||
log.error("Can't decrypt email packet: " + emailPacket, e);
|
||||
// TODO propagate error message to UI
|
||||
}
|
||||
}
|
||||
else
|
||||
if (packet != null)
|
||||
log.error("DHT returned packet of class " + packet.getClass().getSimpleName() + ", expected EmailPacket.");
|
||||
}
|
||||
else
|
||||
if (packet != null)
|
||||
log.error("DHT returned packet of class " + packet.getClass().getSimpleName() + ", expected EmailPacket.");
|
||||
|
||||
if (indexDelRequest.getNumEntries() > 0)
|
||||
sendDeleteRequest(indexDelRequest, indexPacketPeers);
|
||||
|
||||
return emailCompleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a delete request to the DHT.
|
||||
* Sends an Email Packet Delete Request to a peer.
|
||||
* @param dhtKey The DHT key of the email packet that is to be deleted
|
||||
* @param deletionKey The deletion key for the email packet
|
||||
* @param peer
|
||||
*/
|
||||
private void sendDeleteRequest(Hash dhtKey, UniqueId deletionKey) {
|
||||
// TODO
|
||||
private void sendDeleteRequest(Hash dhtKey, UniqueId deletionKey, Destination peer) {
|
||||
EmailPacketDeleteRequest packet = new EmailPacketDeleteRequest(dhtKey, deletionKey);
|
||||
log.debug("Sending an EmailPacketDeleteRequest for DHT key " + dhtKey + " to " + peer.calculateHash());
|
||||
sendQueue.send(packet, peer);
|
||||
}
|
||||
|
||||
private void sendDeleteRequest(IndexPacketDeleteRequest indexDelRequest, Set<Destination> peers) {
|
||||
log.debug("Sending an IndexPacketDeleteRequest to " + peers.size() + " peers: " + indexDelRequest);
|
||||
for (Destination peer: peers)
|
||||
sendQueue.send(indexDelRequest, peer);
|
||||
}
|
||||
}
|
||||
}
|
@ -22,18 +22,15 @@
|
||||
package i2p.bote.network;
|
||||
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
public interface DHT {
|
||||
|
||||
void store(DhtStorablePacket packet) throws Exception;
|
||||
|
||||
DhtStorablePacket findOne(Hash key, Class<? extends DhtStorablePacket> dataType);
|
||||
DhtResults findOne(Hash key, Class<? extends DhtStorablePacket> dataType);
|
||||
|
||||
Collection<DhtStorablePacket> findAll(Hash key, Class<? extends DhtStorablePacket> dataType);
|
||||
DhtResults findAll(Hash key, Class<? extends DhtStorablePacket> dataType);
|
||||
|
||||
/**
|
||||
* Registers a <code>DhtStorageHandler</code> that handles incoming storage requests of a certain
|
||||
|
75
src/i2p/bote/network/DhtResults.java
Normal file
75
src/i2p/bote/network/DhtResults.java
Normal file
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote 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.
|
||||
*
|
||||
* I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.network;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
|
||||
public class DhtResults implements Iterable<DhtStorablePacket> {
|
||||
private Map<Destination, DhtStorablePacket> map;
|
||||
|
||||
public DhtResults() {
|
||||
map = new ConcurrentHashMap<Destination, DhtStorablePacket>();
|
||||
}
|
||||
|
||||
public void put(Destination peer, DhtStorablePacket packet) {
|
||||
map.put(peer, packet);
|
||||
}
|
||||
|
||||
public int getNumResults() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
public Collection<DhtStorablePacket> getPackets() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
public Set<Destination> getPeers() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
public DhtStorablePacket getPacket(Destination peer) {
|
||||
return map.get(peer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<DhtStorablePacket> iterator() {
|
||||
return getPackets().iterator();
|
||||
}
|
||||
|
||||
// Returns all packets except for the local result.
|
||||
/* public Collection<DhtStorablePacket> getRemoteResults() {
|
||||
ArrayList<DhtStorablePacket> packets = new ArrayList<DhtStorablePacket>();
|
||||
for (Destination peer: map.keySet())
|
||||
if (!peer.calculateHash().equals(localDestHash))
|
||||
packets.add(map.get(peer));
|
||||
|
||||
return packets;
|
||||
}*/
|
||||
}
|
@ -172,7 +172,7 @@ public class I2PSendQueue extends I2PBoteThread implements PacketListener {
|
||||
|
||||
for (PacketBatch batch: runningBatches)
|
||||
if (batch.contains(packetId))
|
||||
batch.addResponse(((ResponsePacket)packet).getPayload());
|
||||
batch.addResponse(sender, ((ResponsePacket)packet).getPayload());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,13 +27,11 @@ import i2p.bote.packet.DataPacket;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -44,13 +42,13 @@ import net.i2p.util.Log;
|
||||
public class PacketBatch implements Iterable<PacketBatchItem> {
|
||||
private final Log log = new Log(PacketBatch.class);
|
||||
private Map<UniqueId, PacketBatchItem> outgoingPackets;
|
||||
private Set<DataPacket> incomingPackets;
|
||||
private Map<Destination, DataPacket> incomingPackets;
|
||||
private CountDownLatch sentSignal; // this field is initialized by I2PSendQueue when the batch is submitted for sending
|
||||
private CountDownLatch firstReplyReceivedSignal;
|
||||
|
||||
public PacketBatch() {
|
||||
outgoingPackets = new ConcurrentHashMap<UniqueId, PacketBatchItem>();
|
||||
incomingPackets = new ConcurrentHashSet<DataPacket>();
|
||||
incomingPackets = new ConcurrentHashMap<Destination, DataPacket>();
|
||||
sentSignal = new CountDownLatch(0);
|
||||
firstReplyReceivedSignal = new CountDownLatch(1);
|
||||
}
|
||||
@ -102,12 +100,12 @@ public class PacketBatch implements Iterable<PacketBatchItem> {
|
||||
outgoingPackets.get(packetId).confirmDelivery();
|
||||
}*/
|
||||
|
||||
void addResponse(DataPacket packet) {
|
||||
incomingPackets.add(packet);
|
||||
void addResponse(Destination peer, DataPacket packet) {
|
||||
incomingPackets.put(peer, packet);
|
||||
firstReplyReceivedSignal.countDown();
|
||||
}
|
||||
|
||||
public Set<DataPacket> getResponses() {
|
||||
public Map<Destination, DataPacket> getResponses() {
|
||||
return incomingPackets;
|
||||
}
|
||||
|
||||
|
@ -23,13 +23,14 @@ package i2p.bote.network.kademlia;
|
||||
|
||||
import i2p.bote.Util;
|
||||
import i2p.bote.network.DHT;
|
||||
import i2p.bote.network.DhtResults;
|
||||
import i2p.bote.network.DhtStorageHandler;
|
||||
import i2p.bote.network.I2PPacketDispatcher;
|
||||
import i2p.bote.network.I2PSendQueue;
|
||||
import i2p.bote.network.PacketBatch;
|
||||
import i2p.bote.network.PacketListener;
|
||||
import i2p.bote.packet.CommunicationPacket;
|
||||
import i2p.bote.packet.I2PBotePacket;
|
||||
import i2p.bote.packet.DataPacket;
|
||||
import i2p.bote.packet.PeerList;
|
||||
import i2p.bote.packet.ResponsePacket;
|
||||
import i2p.bote.packet.StatusCode;
|
||||
@ -47,10 +48,10 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -130,16 +131,12 @@ public class KademliaDHT extends I2PBoteThread implements DHT, PacketListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhtStorablePacket findOne(Hash key, Class<? extends DhtStorablePacket> dataType) {
|
||||
Collection<DhtStorablePacket> results = find(key, dataType, false);
|
||||
if (results.isEmpty())
|
||||
return null;
|
||||
else
|
||||
return results.iterator().next();
|
||||
public DhtResults findOne(Hash key, Class<? extends DhtStorablePacket> dataType) {
|
||||
return find(key, dataType, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<DhtStorablePacket> findAll(Hash key, Class<? extends DhtStorablePacket> dataType) {
|
||||
public DhtResults findAll(Hash key, Class<? extends DhtStorablePacket> dataType) {
|
||||
return find(key, dataType, true);
|
||||
}
|
||||
|
||||
@ -158,7 +155,7 @@ public class KademliaDHT extends I2PBoteThread implements DHT, PacketListener {
|
||||
return bucketManager.getPeerCount();
|
||||
}
|
||||
|
||||
private Collection<DhtStorablePacket> find(Hash key, Class<? extends DhtStorablePacket> dataType, boolean exhaustive) {
|
||||
private DhtResults find(Hash key, Class<? extends DhtStorablePacket> dataType, boolean exhaustive) {
|
||||
final Collection<Destination> closeNodes = getClosestNodes(key);
|
||||
log.debug("Querying localhost + " + closeNodes.size() + " peers for data type " + dataType.getSimpleName() + ", Kademlia key " + key);
|
||||
|
||||
@ -166,9 +163,9 @@ public class KademliaDHT extends I2PBoteThread implements DHT, PacketListener {
|
||||
// if a local packet exists and one result is requested, return the local packet
|
||||
if (!exhaustive && localResult!=null) {
|
||||
log.debug("Locally stored packet found for hash " + key + " and data type " + dataType);
|
||||
Collection<DhtStorablePacket> storablePackets = new ArrayList<DhtStorablePacket>();
|
||||
storablePackets.add(localResult);
|
||||
return storablePackets;
|
||||
DhtResults results = new DhtResults();
|
||||
results.put(localDestination, localResult);
|
||||
return results;
|
||||
}
|
||||
|
||||
// Send the retrieve requests
|
||||
@ -197,8 +194,7 @@ public class KademliaDHT extends I2PBoteThread implements DHT, PacketListener {
|
||||
|
||||
sendQueue.remove(batch);
|
||||
|
||||
ConcurrentHashSet<DhtStorablePacket> storablePackets = getStorablePackets(batch, localResult);
|
||||
return storablePackets;
|
||||
return getDhtResults(batch, localResult);
|
||||
}
|
||||
|
||||
private DhtStorablePacket findLocally(Hash key, Class<? extends DhtStorablePacket> dataType) {
|
||||
@ -216,16 +212,18 @@ public class KademliaDHT extends I2PBoteThread implements DHT, PacketListener {
|
||||
* @param localResult
|
||||
* @return
|
||||
*/
|
||||
private ConcurrentHashSet<DhtStorablePacket> getStorablePackets(PacketBatch batch, DhtStorablePacket localResult) {
|
||||
ConcurrentHashSet<DhtStorablePacket> storablePackets = new ConcurrentHashSet<DhtStorablePacket>();
|
||||
for (I2PBotePacket packet: batch.getResponses())
|
||||
private DhtResults getDhtResults(PacketBatch batch, DhtStorablePacket localResult) {
|
||||
DhtResults results = new DhtResults();
|
||||
for (Entry<Destination, DataPacket> result: batch.getResponses().entrySet()) {
|
||||
DataPacket packet = result.getValue();
|
||||
if (packet instanceof DhtStorablePacket)
|
||||
storablePackets.add((DhtStorablePacket)packet);
|
||||
results.put(result.getKey(), (DhtStorablePacket)packet);
|
||||
}
|
||||
|
||||
if (localResult != null)
|
||||
storablePackets.add(localResult);
|
||||
results.put(localDestination, localResult);
|
||||
|
||||
return storablePackets;
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
package i2p.bote.packet;
|
||||
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.UniqueId;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -31,7 +32,6 @@ import java.util.concurrent.TimeUnit;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public abstract class CommunicationPacket extends I2PBotePacket {
|
||||
private static final byte PACKET_VERSION = 1;
|
||||
protected static final int HEADER_LENGTH = 6 + UniqueId.LENGTH; // length of the common packet header in the byte array representation; this is where subclasses start reading
|
||||
private static final byte[] PACKET_PREFIX = new byte[] {(byte)0x6D, (byte)0x30, (byte)0x52, (byte)0xE9};
|
||||
private static Log static_log = new Log(CommunicationPacket.class);
|
||||
@ -46,16 +46,20 @@ public abstract class CommunicationPacket extends I2PBotePacket {
|
||||
}
|
||||
|
||||
protected CommunicationPacket(UniqueId packetId) {
|
||||
super(I2PBote.PROTOCOL_VERSION);
|
||||
this.packetId = packetId;
|
||||
sentSignal = new CountDownLatch(1);
|
||||
sentTime = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet and initializes the header fields shared by all Communication Packets: packet type and packet id.
|
||||
* Creates a packet and initializes the header fields shared by all Communication Packets:
|
||||
* packet type, protocol version, and packet id.
|
||||
* Subclasses should start reading at byte <code>HEADER_LENGTH</code> after calling this constructor.
|
||||
* @param data
|
||||
*/
|
||||
protected CommunicationPacket(byte[] data) {
|
||||
super(data[5]); // byte 5 is the protocol version in a communication packet
|
||||
verifyHeader(data);
|
||||
checkPacketType(data[4]);
|
||||
packetId = new UniqueId(data, 6);
|
||||
@ -86,13 +90,14 @@ public abstract class CommunicationPacket extends I2PBotePacket {
|
||||
}
|
||||
}
|
||||
|
||||
// check the packet prefix and version number of a packet
|
||||
/**
|
||||
* Checks that the packet has the correct packet prefix.
|
||||
* @param packet
|
||||
*/
|
||||
private void verifyHeader(byte[] packet) {
|
||||
for (int i=0; i<PACKET_PREFIX.length; i++)
|
||||
if (packet[i] != PACKET_PREFIX[i])
|
||||
log.error("Packet prefix invalid at byte " + i + ". Expected = " + PACKET_PREFIX[i] + ", actual = " + packet[i]);
|
||||
if (packet[5] != 1)
|
||||
log.error("Unsupported packet version: " + packet[5]);
|
||||
}
|
||||
|
||||
public void setPacketId(UniqueId packetId) {
|
||||
@ -121,14 +126,14 @@ public abstract class CommunicationPacket extends I2PBotePacket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the Prefix, Version, Type, and Packet Id fields of a I2PBote packet to
|
||||
* Writes the Prefix, Version, Type, and Packet Id fields of a Communication Packet to
|
||||
* an {@link OutputStream}.
|
||||
* @param outputStream
|
||||
*/
|
||||
protected void writeHeader(OutputStream outputStream) throws IOException {
|
||||
outputStream.write(PACKET_PREFIX);
|
||||
outputStream.write((byte)getPacketTypeCode());
|
||||
outputStream.write(PACKET_VERSION);
|
||||
outputStream.write(getProtocolVersion());
|
||||
outputStream.write(packetId.toByteArray());
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
package i2p.bote.packet;
|
||||
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.Util;
|
||||
import i2p.bote.folder.FolderElement;
|
||||
|
||||
@ -36,13 +37,26 @@ import net.i2p.util.Log;
|
||||
* The superclass of all "payload" packet types.
|
||||
*/
|
||||
public abstract class DataPacket extends I2PBotePacket implements FolderElement {
|
||||
protected static final int HEADER_LENGTH = 2; // length of the common packet header in the byte array representation; this is where subclasses start reading
|
||||
private static Log log = new Log(DataPacket.class);
|
||||
|
||||
private File file;
|
||||
|
||||
|
||||
public DataPacket() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a <code>DataPacket</code> from raw datagram data. The only thing that is initialized
|
||||
* is the protocol version. The packet type code is verified.
|
||||
* Subclasses should start reading at byte <code>HEADER_LENGTH</code> after calling this constructor.
|
||||
* @param data
|
||||
*/
|
||||
public DataPacket(byte[] data) {
|
||||
super(data[1]); // byte 1 is the protocol version in a data packet
|
||||
if (data[0] != getPacketTypeCode())
|
||||
log.error("Wrong type code for " + getClass().getSimpleName() + ". Expected <" + getPacketTypeCode() + ">, got <" + (char)data[0] + ">");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the packet to an <code>OutputStream</code> in binary representation.
|
||||
*/
|
||||
@ -51,7 +65,20 @@ public abstract class DataPacket extends I2PBotePacket implements FolderElement
|
||||
outputStream.write(toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the Type and Protocol Version fields of a Data Packet to
|
||||
* an {@link OutputStream}.
|
||||
* @param outputStream
|
||||
*/
|
||||
protected void writeHeader(OutputStream outputStream) throws IOException {
|
||||
outputStream.write((byte)getPacketTypeCode());
|
||||
outputStream.write(getProtocolVersion());
|
||||
}
|
||||
|
||||
public static DataPacket createPacket(File file) {
|
||||
if (file==null || !file.exists())
|
||||
return null;
|
||||
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = new FileInputStream(file);
|
||||
@ -87,13 +114,21 @@ public abstract class DataPacket extends I2PBotePacket implements FolderElement
|
||||
}
|
||||
|
||||
Class<? extends DataPacket> dataPacketType = packetType.asSubclass(DataPacket.class);
|
||||
DataPacket packet = null;
|
||||
try {
|
||||
return dataPacketType.getConstructor(byte[].class).newInstance(data);
|
||||
packet = dataPacketType.getConstructor(byte[].class).newInstance(data);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("Can't instantiate packet for type code <" + packetTypeCode + ">", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (packet.getProtocolVersion() != I2PBote.PROTOCOL_VERSION) {
|
||||
log.warn("Ignoring " + packetType.getSimpleName() + " packet with protocol version " + packet.getProtocolVersion());
|
||||
return null;
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
// FolderElement implementation
|
||||
|
78
src/i2p/bote/packet/EmailPacketDeleteRequest.java
Normal file
78
src/i2p/bote/packet/EmailPacketDeleteRequest.java
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote 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.
|
||||
*
|
||||
* I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.packet;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@TypeCode('D')
|
||||
public class EmailPacketDeleteRequest extends CommunicationPacket {
|
||||
private Log log = new Log(EmailPacketDeleteRequest.class);
|
||||
private Hash dhtKey;
|
||||
private UniqueId deletionKey;
|
||||
|
||||
public EmailPacketDeleteRequest(Hash dhtKey, UniqueId deletionKey) {
|
||||
this.dhtKey = dhtKey;
|
||||
this.deletionKey = deletionKey;
|
||||
}
|
||||
|
||||
public EmailPacketDeleteRequest(byte[] data) {
|
||||
super(data);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
|
||||
|
||||
dhtKey = readHash(buffer);
|
||||
deletionKey = new UniqueId(buffer);
|
||||
|
||||
if (buffer.hasRemaining())
|
||||
log.debug("Email Packet Delete Request has " + buffer.remaining() + " extra bytes.");
|
||||
}
|
||||
|
||||
public Hash getDhtKey() {
|
||||
return dhtKey;
|
||||
}
|
||||
|
||||
public UniqueId getDeletionKey() {
|
||||
return deletionKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
writeHeader(outputStream);
|
||||
outputStream.write(dhtKey.toByteArray());
|
||||
outputStream.write(deletionKey.toByteArray());
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Can't write to ByteArrayOutputStream.", e);
|
||||
}
|
||||
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
@ -42,6 +42,17 @@ import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
/**
|
||||
* An <code>EncryptedEmailPacket</code> basically contains an encrypted <code>UnencryptedEmailPacket</code>
|
||||
* and an unencrypted copy of the deletion key. The unencrypted deletion key (<code>deletionKeyPlain</code>)
|
||||
* is used for validation of delete requests (the sender of a delete request has to decrypt the deletion key
|
||||
* using the decryption key for the Email Destination because it doesn't have the unencrypted copy).
|
||||
* deletion key).
|
||||
* An alternative to this scheme would have been to only use plain-text deletion keys in email packets and
|
||||
* have the recipient sign deletion requests, but this would require a storage node to know the recipient's
|
||||
* public signing key.
|
||||
*
|
||||
*/
|
||||
@TypeCode('E')
|
||||
public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
private static final int PADDED_SIZE = PrivateKey.KEYSIZE_BYTES; // TODO is this a good choice?
|
||||
@ -51,24 +62,6 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
private UniqueId deletionKeyPlain;
|
||||
private byte[] encryptedData; // the encrypted fields of an I2PBote email packet: Encrypted Deletion Key, Message ID, Fragment Index, Number of Fragments, and Content.
|
||||
|
||||
/**
|
||||
* Creates an <code>EncryptedEmailPacket</code> from raw datagram data.
|
||||
* To read the encrypted parts of the packet, <code>decrypt</code> must be called first.
|
||||
* @param data
|
||||
*/
|
||||
public EncryptedEmailPacket(byte[] data) {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
if (buffer.get() != getPacketTypeCode())
|
||||
log.error("Wrong type code for EncryptedEmailPacket. Expected <" + getPacketTypeCode() + ">, got <" + (char)data[0] + ">");
|
||||
|
||||
dhtKey = readHash(buffer);
|
||||
deletionKeyPlain = new UniqueId(buffer);
|
||||
|
||||
int encryptedLength = buffer.getShort(); // length of the encrypted part of the packet
|
||||
encryptedData = new byte[encryptedLength];
|
||||
buffer.get(encryptedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an <code>EncryptedEmailPacket</code> from an <code>UnencryptedEmailPacket</code>.
|
||||
* The public key of <code>emailDestination</code> is used for encryption.
|
||||
@ -78,13 +71,13 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
*/
|
||||
public EncryptedEmailPacket(UnencryptedEmailPacket unencryptedPacket, EmailDestination emailDestination, I2PAppContext appContext) {
|
||||
dhtKey = generateRandomHash();
|
||||
deletionKeyPlain = unencryptedPacket.getPlaintextDeletionKey();
|
||||
deletionKeyPlain = unencryptedPacket.getVerificationDeletionKey();
|
||||
|
||||
// put all the encrypted fields into an array
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataStream = new DataOutputStream(byteStream);
|
||||
try {
|
||||
unencryptedPacket.getVerificationDeletionKey().writeTo(dataStream);
|
||||
deletionKeyPlain.writeTo(dataStream);
|
||||
unencryptedPacket.getMessageId().writeTo(dataStream);
|
||||
dataStream.writeShort(unencryptedPacket.getFragmentIndex());
|
||||
dataStream.writeShort(unencryptedPacket.getNumFragments());
|
||||
@ -100,14 +93,32 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
}
|
||||
}
|
||||
|
||||
private Hash generateRandomHash() {
|
||||
RandomSource randomSource = RandomSource.getInstance();
|
||||
/**
|
||||
* Creates an <code>EncryptedEmailPacket</code> from raw datagram data.
|
||||
* To read the encrypted parts of the packet, <code>decrypt</code> must be called first.
|
||||
* @param data
|
||||
*/
|
||||
public EncryptedEmailPacket(byte[] data) {
|
||||
super(data);
|
||||
|
||||
byte[] bytes = new byte[Hash.HASH_LENGTH];
|
||||
for (int i=0; i<bytes.length; i++)
|
||||
bytes[i] = (byte)randomSource.nextInt(256);
|
||||
|
||||
return new Hash(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
|
||||
|
||||
dhtKey = readHash(buffer);
|
||||
deletionKeyPlain = new UniqueId(buffer);
|
||||
|
||||
int encryptedLength = buffer.getShort(); // length of the encrypted part of the packet
|
||||
encryptedData = new byte[encryptedLength];
|
||||
buffer.get(encryptedData);
|
||||
}
|
||||
|
||||
private Hash generateRandomHash() {
|
||||
RandomSource randomSource = RandomSource.getInstance();
|
||||
|
||||
byte[] bytes = new byte[Hash.HASH_LENGTH];
|
||||
for (int i=0; i<bytes.length; i++)
|
||||
bytes[i] = (byte)randomSource.nextInt(256);
|
||||
|
||||
return new Hash(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,12 +127,12 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
* @param appContext
|
||||
* @throws DataFormatException
|
||||
*/
|
||||
public UnencryptedEmailPacket decrypt(EmailIdentity identity, I2PAppContext appContext) throws DataFormatException {
|
||||
byte[] decryptedData = decrypt(encryptedData, identity, appContext);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(decryptedData);
|
||||
|
||||
UniqueId deletionKeyVerify = new UniqueId(buffer);
|
||||
UniqueId messageId = new UniqueId(buffer);
|
||||
public UnencryptedEmailPacket decrypt(EmailIdentity identity, I2PAppContext appContext) throws DataFormatException {
|
||||
byte[] decryptedData = decrypt(encryptedData, identity, appContext);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(decryptedData);
|
||||
|
||||
UniqueId deletionKeyVerify = new UniqueId(buffer);
|
||||
UniqueId messageId = new UniqueId(buffer);
|
||||
int fragmentIndex = buffer.getShort();
|
||||
int numFragments = buffer.getShort();
|
||||
|
||||
@ -129,8 +140,8 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
byte[] content = new byte[contentLength];
|
||||
buffer.get(content);
|
||||
|
||||
return new UnencryptedEmailPacket(deletionKeyPlain, deletionKeyVerify, messageId, fragmentIndex, numFragments, content);
|
||||
}
|
||||
return new UnencryptedEmailPacket(messageId, fragmentIndex, numFragments, content, deletionKeyVerify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts data with an email identity's private key.
|
||||
@ -139,10 +150,10 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
* @param appContext
|
||||
* @return The decrypted data
|
||||
*/
|
||||
private byte[] decrypt(byte[] data, EmailIdentity identity, I2PAppContext appContext) throws DataFormatException {
|
||||
private byte[] decrypt(byte[] data, EmailIdentity identity, I2PAppContext appContext) throws DataFormatException {
|
||||
PrivateKey privateKey = identity.getPrivateEncryptionKey();
|
||||
return appContext.elGamalAESEngine().decrypt(data, privateKey, appContext.sessionKeyManager());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts data with an email destination's public key.
|
||||
@ -151,7 +162,7 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
* @param appContext
|
||||
* @return The encrypted data
|
||||
*/
|
||||
// TODO should this method be in this class?
|
||||
// TODO should this method be in this class?
|
||||
public byte[] encrypt(byte[] data, EmailDestination emailDestination, I2PAppContext appContext) {
|
||||
PublicKey publicKey = emailDestination.getPublicEncryptionKey();
|
||||
SessionKey sessionKey = appContext.sessionKeyManager().createSession(publicKey);
|
||||
@ -169,7 +180,7 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
public Hash getDhtKey() {
|
||||
return dhtKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value of the "plain-text deletion key" field.
|
||||
* Storage nodes set this to all zero bytes when the packet is retrieved.
|
||||
@ -179,13 +190,13 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
return deletionKeyPlain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataStream = new DataOutputStream(byteArrayStream);
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataStream = new DataOutputStream(byteArrayStream);
|
||||
|
||||
try {
|
||||
dataStream.write((byte)getPacketTypeCode());
|
||||
writeHeader(dataStream);
|
||||
dataStream.write(dhtKey.toByteArray());
|
||||
dataStream.write(deletionKeyPlain.toByteArray());
|
||||
dataStream.writeShort(encryptedData.length);
|
||||
@ -194,14 +205,14 @@ public class EncryptedEmailPacket extends DhtStorablePacket {
|
||||
catch (IOException e) {
|
||||
log.error("Can't write to ByteArrayOutputStream.", e);
|
||||
}
|
||||
return byteArrayStream.toByteArray();
|
||||
}
|
||||
return byteArrayStream.toByteArray();
|
||||
}
|
||||
|
||||
// Returns the number of bytes in the packet.
|
||||
// TODO just return content.length+CONST so we don't call toByteArray every time
|
||||
public int getSize() {
|
||||
return toByteArray().length;
|
||||
}
|
||||
// Returns the number of bytes in the packet.
|
||||
// TODO just return content.length+CONST so we don't call toByteArray every time
|
||||
public int getSize() {
|
||||
return toByteArray().length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@ -21,11 +21,13 @@
|
||||
|
||||
package i2p.bote.packet;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import i2p.bote.I2PBote;
|
||||
import i2p.bote.packet.dht.FindClosePeersPacket;
|
||||
import i2p.bote.packet.dht.RetrieveRequest;
|
||||
import i2p.bote.packet.dht.StoreRequest;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -35,15 +37,30 @@ public abstract class I2PBotePacket {
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<? extends I2PBotePacket>[] ALL_PACKET_TYPES = new Class[] {
|
||||
RelayPacket.class, ResponsePacket.class, RetrieveRequest.class, StoreRequest.class, FindClosePeersPacket.class,
|
||||
PeerList.class, EncryptedEmailPacket.class, UnencryptedEmailPacket.class, IndexPacket.class
|
||||
PeerList.class, EncryptedEmailPacket.class, UnencryptedEmailPacket.class, IndexPacket.class,
|
||||
EmailPacketDeleteRequest.class, IndexPacketDeleteRequest.class
|
||||
};
|
||||
|
||||
private int protocolVersion;
|
||||
|
||||
/**
|
||||
* Creates a new <code>I2PBotePacket</code> with the current protocol version.
|
||||
*/
|
||||
protected I2PBotePacket() {
|
||||
protocolVersion = I2PBote.PROTOCOL_VERSION;
|
||||
}
|
||||
|
||||
protected I2PBotePacket(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public abstract byte[] toByteArray();
|
||||
|
||||
/**
|
||||
* Returns the size of the packet in bytes.
|
||||
* @return
|
||||
*/
|
||||
// TODO rename to getPacketSize
|
||||
public int getSize() {
|
||||
return toByteArray().length;
|
||||
}
|
||||
@ -77,9 +94,13 @@ public abstract class I2PBotePacket {
|
||||
checkPacketType((char)packetTypeCode);
|
||||
}
|
||||
|
||||
/* protected void checkPacketVersion(byte version, byte minVersion, byte maxVersion) {
|
||||
// TODO
|
||||
}*/
|
||||
/**
|
||||
* Returns the version of the I2P-Bote network protocol used by this packet.
|
||||
* @return
|
||||
*/
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Hash} from bytes read from a {@link ByteBuffer}.
|
||||
|
@ -21,52 +21,46 @@
|
||||
|
||||
package i2p.bote.packet;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
import i2p.bote.email.EmailDestination;
|
||||
import i2p.bote.packet.dht.DhtStorablePacket;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Stores DHT keys of Email Packets and their deletion keys.
|
||||
*
|
||||
* This class is not thread-safe.
|
||||
*/
|
||||
@TypeCode('I')
|
||||
public class IndexPacket extends DhtStorablePacket {
|
||||
private Log log = new Log(IndexPacket.class);
|
||||
private Collection<Hash> dhtKeys; // DHT keys of email packets
|
||||
// private Collection<Entry> entries; should probably use a Map and get rid of the Entry class
|
||||
private Map<Hash, UniqueId> entries;
|
||||
private Hash destinationHash; // The DHT key of this packet
|
||||
|
||||
public IndexPacket(byte[] data) {
|
||||
ByteBuffer dataBuffer = ByteBuffer.wrap(data);
|
||||
if (dataBuffer.get() != getPacketTypeCode())
|
||||
log.error("Wrong type code for IndexPacket. Expected <" + getPacketTypeCode() + ">, got <" + (char)data[0] + ">");
|
||||
|
||||
destinationHash = readHash(dataBuffer);
|
||||
|
||||
int numKeys = dataBuffer.get();
|
||||
|
||||
dhtKeys = new ArrayList<Hash>();
|
||||
for (int i=0; i<numKeys; i++) {
|
||||
Hash dhtKey = readHash(dataBuffer);
|
||||
dhtKeys.add(dhtKey);
|
||||
}
|
||||
|
||||
// TODO catch BufferUnderflowException; warn if extra bytes in the array
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param emailPackets One or more email packets
|
||||
* @param emailDestination Determines the DHT key of this Index Packet
|
||||
*/
|
||||
public IndexPacket(Collection<EncryptedEmailPacket> emailPackets, EmailDestination emailDestination) {
|
||||
dhtKeys = new ArrayList<Hash>();
|
||||
// entries = new ArrayList<Entry>();
|
||||
entries = new ConcurrentHashMap<Hash, UniqueId>();
|
||||
for (EncryptedEmailPacket emailPacket: emailPackets)
|
||||
dhtKeys.add(emailPacket.getDhtKey());
|
||||
// Entry entry = new Entry(emailPacket.getDhtKey(), emailPacket.getPlaintextDeletionKey());
|
||||
entries.put(emailPacket.getDhtKey(), emailPacket.getPlaintextDeletionKey());
|
||||
|
||||
destinationHash = emailDestination.getHash();
|
||||
}
|
||||
@ -82,9 +76,9 @@ public class IndexPacket extends DhtStorablePacket {
|
||||
throw new IllegalArgumentException("This method must be invoked with at least one index packet.");
|
||||
|
||||
destinationHash = indexPackets.iterator().next().getDhtKey();
|
||||
dhtKeys = new HashSet<Hash>();
|
||||
entries = new ConcurrentHashMap<Hash, UniqueId>();
|
||||
for (IndexPacket packet: indexPackets)
|
||||
dhtKeys.addAll(packet.getDhtKeys());
|
||||
entries.putAll(packet.getEntries());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,15 +89,36 @@ public class IndexPacket extends DhtStorablePacket {
|
||||
this(Arrays.asList(indexPackets));
|
||||
}
|
||||
|
||||
public IndexPacket(byte[] data) {
|
||||
super(data);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
|
||||
|
||||
destinationHash = readHash(buffer);
|
||||
|
||||
int numKeys = buffer.get();
|
||||
|
||||
entries = new ConcurrentHashMap<Hash, UniqueId>();
|
||||
for (int i=0; i<numKeys; i++) {
|
||||
Hash dhtKey = readHash(buffer);
|
||||
UniqueId delKey = new UniqueId(buffer);
|
||||
entries.put(dhtKey, delKey);
|
||||
}
|
||||
|
||||
// TODO catch BufferUnderflowException; warn if extra bytes in the array
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
outputStream.write((byte)getPacketTypeCode());
|
||||
try {
|
||||
writeHeader(outputStream);
|
||||
destinationHash.writeBytes(outputStream);
|
||||
outputStream.write((byte)dhtKeys.size());
|
||||
for (Hash dhtKey: dhtKeys)
|
||||
dhtKey.writeBytes(outputStream);
|
||||
outputStream.write((byte)entries.size());
|
||||
for (Entry<Hash, UniqueId> entry: entries.entrySet()) {
|
||||
entry.getKey().writeBytes(outputStream);
|
||||
entry.getValue().writeTo(outputStream);
|
||||
}
|
||||
// TODO in the unit test, verify that toByteArray().length = Hash.NUM_BYTES + 1 + dhtKeys.size()*Hash.NUM_BYTES
|
||||
} catch (DataFormatException e) {
|
||||
log.error("Invalid format for email destination.", e);
|
||||
@ -114,18 +129,79 @@ public class IndexPacket extends DhtStorablePacket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DHT keys of the {@link EncryptedEmailPacket}s referenced by this {@link IndexPacket}.
|
||||
* Returns the deletion key for a given DHT key of an Email Packet, or <code>null</code> if
|
||||
* the <code>IndexPacket</code> doesn't contain the DHT key.
|
||||
* @param dhtKey
|
||||
* @return
|
||||
*/
|
||||
public Collection<Hash> getDhtKeys() {
|
||||
return dhtKeys;
|
||||
public UniqueId getDeletionKey(Hash dhtKey) {
|
||||
return entries.get(dhtKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DHT key of this packet.
|
||||
* Returns all DHT keys in this <code>IndexPacket</code>.
|
||||
* @return
|
||||
*/
|
||||
public Collection<Hash> getDhtKeys() {
|
||||
return entries.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entry to the <code>IndexPacket</code>. If the DHT key exists in the packet already,
|
||||
* nothing happens.
|
||||
* @param dhtKey
|
||||
* @param deletionKey
|
||||
*/
|
||||
public void put(Hash dhtKey, UniqueId deletionKey) {
|
||||
entries.put(dhtKey, deletionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an entry from the <code>IndexPacket</code>.
|
||||
* @param dhtKey
|
||||
*/
|
||||
public void remove(Hash dhtKey) {
|
||||
entries.remove(dhtKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the <code>IndexPacket</code> contains a given DHT key.
|
||||
* @param dhtKey
|
||||
* @return <code>true</code> if the packet containes the DHT key, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean contains(Hash dhtKey) {
|
||||
return entries.containsKey(dhtKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DHT key / deletion key pairs for the {@link EncryptedEmailPacket}s referenced
|
||||
* by this <code>IndexPacket</code>.
|
||||
* @return
|
||||
*/
|
||||
// public Collection<Entry> getEntries() {
|
||||
public Map<Hash, UniqueId> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DHT key of this packet. The DHT key of an <code>IndexPacket</code> is the
|
||||
* hash of the Email Destination whose entries this packet stores.
|
||||
*/
|
||||
@Override
|
||||
public Hash getDhtKey() {
|
||||
return destinationHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class holds a DHT key and a deletion key for an email packet.
|
||||
*/
|
||||
/* public class Entry {
|
||||
public Hash dhtKey; // The DHT key of an email packet
|
||||
public UniqueId deletionKey; // The deletion key of the email packet
|
||||
|
||||
public Entry(Hash dhtKey, UniqueId deletionKey) {
|
||||
this.dhtKey = dhtKey;
|
||||
this.deletionKey = deletionKey;
|
||||
}
|
||||
}*/
|
||||
}
|
119
src/i2p/bote/packet/IndexPacketDeleteRequest.java
Normal file
119
src/i2p/bote/packet/IndexPacketDeleteRequest.java
Normal file
@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote 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.
|
||||
*
|
||||
* I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.packet;
|
||||
|
||||
import i2p.bote.UniqueId;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* This class is not thread-safe.
|
||||
*/
|
||||
@TypeCode('X')
|
||||
public class IndexPacketDeleteRequest extends CommunicationPacket {
|
||||
private Log log = new Log(IndexPacketDeleteRequest.class);
|
||||
private Hash emailDestHash;
|
||||
private Map<Hash, UniqueId> entries;
|
||||
|
||||
public IndexPacketDeleteRequest(Hash emailDestHash) {
|
||||
this.emailDestHash = emailDestHash;
|
||||
entries = new HashMap<Hash, UniqueId>();
|
||||
}
|
||||
|
||||
public IndexPacketDeleteRequest(byte[] data) {
|
||||
super(data);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
|
||||
|
||||
emailDestHash = readHash(buffer);
|
||||
entries = new HashMap<Hash, UniqueId>();
|
||||
int numEntries = buffer.getShort();
|
||||
for (int i=0; i<numEntries; i++) {
|
||||
Hash dhtKey = readHash(buffer);
|
||||
UniqueId deletionKey = new UniqueId(buffer);
|
||||
entries.put(dhtKey, deletionKey);
|
||||
}
|
||||
|
||||
if (buffer.hasRemaining())
|
||||
log.debug("Index Packet Delete Request has " + buffer.remaining() + " extra bytes.");
|
||||
}
|
||||
|
||||
public void put(Hash dhtKey, UniqueId deletionKey) {
|
||||
entries.put(dhtKey, deletionKey);
|
||||
}
|
||||
|
||||
/* public Map<Hash, UniqueId> getAll() {
|
||||
return entries;
|
||||
}*/
|
||||
|
||||
public Hash getEmailDestHash() {
|
||||
return emailDestHash;
|
||||
}
|
||||
|
||||
public Set<Hash> getDhtKeys() {
|
||||
return entries.keySet();
|
||||
}
|
||||
|
||||
public UniqueId getDeletionKey(Hash dhtKey) {
|
||||
return entries.get(dhtKey);
|
||||
}
|
||||
|
||||
public void remove(Hash dhtKey) {
|
||||
entries.remove(dhtKey);
|
||||
}
|
||||
|
||||
public int getNumEntries() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataStream = new DataOutputStream(byteStream);
|
||||
|
||||
try {
|
||||
writeHeader(dataStream);
|
||||
emailDestHash.writeBytes(dataStream);
|
||||
dataStream.writeShort(entries.size());
|
||||
for (Entry<Hash, UniqueId> entry: entries.entrySet()) {
|
||||
dataStream.write(entry.getKey().toByteArray());
|
||||
dataStream.write(entry.getValue().toByteArray());
|
||||
}
|
||||
} catch (DataFormatException e) {
|
||||
log.error("Invalid format for email destination.", e);
|
||||
} catch (IOException e) {
|
||||
log.error("Can't write to ByteArrayOutputStream.", e);
|
||||
}
|
||||
|
||||
return byteStream.toByteArray();
|
||||
}
|
||||
}
|
@ -45,12 +45,11 @@ public class PeerList extends DataPacket {
|
||||
}
|
||||
|
||||
public PeerList(byte[] data) throws DataFormatException {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
if (buffer.get() != getPacketTypeCode())
|
||||
log.error("Wrong type code for PeerList. Expected <" + getPacketTypeCode() + ">, got <" + (char)data[0] + ">");
|
||||
super(data);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
|
||||
|
||||
int numPeers = buffer.getShort();
|
||||
|
||||
peers = new ArrayList<KademliaPeer>();
|
||||
for (int i=0; i<numPeers; i++) {
|
||||
Destination destination = new Destination();
|
||||
@ -77,7 +76,7 @@ public class PeerList extends DataPacket {
|
||||
DataOutputStream dataStream = new DataOutputStream(arrayOutputStream);
|
||||
|
||||
try {
|
||||
dataStream.write((byte)getPacketTypeCode());
|
||||
writeHeader(dataStream);
|
||||
dataStream.writeShort(peers.size());
|
||||
for (KademliaPeer peer: peers)
|
||||
// write the first 384 bytes (the two public keys)
|
||||
|
@ -50,8 +50,18 @@ public class RelayPacket extends DataPacket {
|
||||
this.latestSendTime = latestSendTime;
|
||||
}
|
||||
|
||||
public RelayPacket(DataPacket dataPacket, Destination nextDestination, long earliestSendTime, long latestSendTime, byte[] xorKey) throws DataFormatException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public RelayPacket(InputStream inputStream) throws IOException, DataFormatException {
|
||||
this(Util.readInputStream(inputStream));
|
||||
}
|
||||
|
||||
public RelayPacket(byte[] data) throws DataFormatException {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
super(data);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
|
||||
|
||||
earliestSendTime = buffer.getInt();
|
||||
latestSendTime = buffer.getInt();
|
||||
@ -72,14 +82,6 @@ public class RelayPacket extends DataPacket {
|
||||
log.debug("Relay Packet has " + buffer.remaining() + " extra bytes.");
|
||||
}
|
||||
|
||||
public RelayPacket(DataPacket dataPacket, Destination nextDestination, long earliestSendTime, long latestSendTime, byte[] xorKey) throws DataFormatException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public RelayPacket(InputStream inputStream) throws IOException, DataFormatException {
|
||||
this(Util.readInputStream(inputStream));
|
||||
}
|
||||
|
||||
public Destination getNextDestination() {
|
||||
return nextDestination;
|
||||
}
|
||||
@ -98,6 +100,7 @@ public class RelayPacket extends DataPacket {
|
||||
DataOutputStream dataStream = new DataOutputStream(arrayOutputStream);
|
||||
|
||||
try {
|
||||
writeHeader(dataStream);
|
||||
dataStream.writeInt((int)earliestSendTime);
|
||||
dataStream.writeInt((int)latestSendTime);
|
||||
dataStream.write(xorKey);
|
||||
|
@ -87,6 +87,7 @@ public class ResponsePacket extends CommunicationPacket {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + ", status=" + statusCode + ", ploadType=" + payload.getClass().getSimpleName();
|
||||
String payloadClassName = payload==null?"<null>":payload.getClass().getSimpleName();
|
||||
return super.toString() + ", status=" + statusCode + ", ploadType=" + payloadClassName;
|
||||
}
|
||||
}
|
@ -35,20 +35,39 @@ import net.i2p.util.Log;
|
||||
@TypeCode('U')
|
||||
public class UnencryptedEmailPacket extends DataPacket {
|
||||
private Log log = new Log(UnencryptedEmailPacket.class);
|
||||
private UniqueId deletionKeyPlain;
|
||||
private UniqueId deletionKeyVerify;
|
||||
private UniqueId messageId;
|
||||
private int fragmentIndex;
|
||||
private int numFragments;
|
||||
private byte[] content;
|
||||
private UniqueId deletionKeyVerify; // the decrypted deletion key if this UnencryptedEmailPacket was created from an EncryptedEmailPacket; null otherwise
|
||||
|
||||
public UnencryptedEmailPacket(byte[] data) {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
if (buffer.get() != getPacketTypeCode())
|
||||
log.error("Wrong type code for UnencryptedEmailPacket. Expected <" + getPacketTypeCode() + ">, got <" + (char)data[0] + ">");
|
||||
public UnencryptedEmailPacket(InputStream inputStream) throws IOException {
|
||||
this(Util.readInputStream(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an <code>UnencryptedEmailPacket</code> from a <code>byte</code> array that contains MIME data.
|
||||
* @param messageId
|
||||
* @param fragmentIndex
|
||||
* @param numFragments
|
||||
* @param content
|
||||
* @param deletionKeyVerify The decrypted deletion key from an encrypted email packet
|
||||
*/
|
||||
public UnencryptedEmailPacket(UniqueId messageId, int fragmentIndex, int numFragments, byte[] content, UniqueId deletionKeyVerify) {
|
||||
this.messageId = messageId;
|
||||
this.fragmentIndex = fragmentIndex;
|
||||
this.numFragments = numFragments;
|
||||
this.content = content;
|
||||
this.deletionKeyVerify = deletionKeyVerify;
|
||||
|
||||
verify();
|
||||
}
|
||||
|
||||
public UnencryptedEmailPacket(byte[] data) {
|
||||
super(data);
|
||||
|
||||
deletionKeyPlain = new UniqueId(buffer);
|
||||
deletionKeyVerify = new UniqueId(buffer);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
|
||||
|
||||
messageId = new UniqueId(buffer);
|
||||
fragmentIndex = buffer.getShort();
|
||||
numFragments = buffer.getShort();
|
||||
@ -56,36 +75,10 @@ public class UnencryptedEmailPacket extends DataPacket {
|
||||
int contentLength = buffer.getShort();
|
||||
content = new byte[contentLength];
|
||||
buffer.get(content);
|
||||
}
|
||||
|
||||
public UnencryptedEmailPacket(InputStream inputStream) throws IOException {
|
||||
this(Util.readInputStream(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an <code>UnencryptedEmailPacket</code> from a <code>byte</code> array that contains MIME data.
|
||||
* @param deletionKeyPlain
|
||||
* @param deletionKeyVerify
|
||||
* @param messageId
|
||||
* @param fragmentIndex
|
||||
* @param numFragments
|
||||
* @param content
|
||||
*/
|
||||
public UnencryptedEmailPacket(UniqueId deletionKeyPlain, UniqueId deletionKeyVerify, UniqueId messageId, int fragmentIndex, int numFragments, byte[] content) {
|
||||
this.deletionKeyPlain = deletionKeyPlain;
|
||||
this.deletionKeyVerify = deletionKeyVerify;
|
||||
this.messageId = messageId;
|
||||
this.fragmentIndex = fragmentIndex;
|
||||
this.numFragments = numFragments;
|
||||
this.content = content;
|
||||
|
||||
verify();
|
||||
}
|
||||
|
||||
public UniqueId getPlaintextDeletionKey() {
|
||||
return deletionKeyPlain;
|
||||
}
|
||||
|
||||
verify();
|
||||
}
|
||||
|
||||
public UniqueId getVerificationDeletionKey() {
|
||||
return deletionKeyVerify;
|
||||
}
|
||||
@ -116,16 +109,14 @@ public class UnencryptedEmailPacket extends DataPacket {
|
||||
// TODO more sanity checks?
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataStream = new DataOutputStream(byteArrayStream);
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
|
||||
DataOutputStream dataStream = new DataOutputStream(byteArrayStream);
|
||||
|
||||
try {
|
||||
dataStream.write((byte)getPacketTypeCode());
|
||||
deletionKeyPlain.writeTo(dataStream);
|
||||
deletionKeyVerify.writeTo(dataStream);
|
||||
messageId.writeTo(dataStream);
|
||||
writeHeader(dataStream);
|
||||
messageId.writeTo(dataStream);
|
||||
dataStream.writeShort(fragmentIndex);
|
||||
dataStream.writeShort(numFragments);
|
||||
dataStream.writeShort(content.length);
|
||||
@ -134,12 +125,12 @@ public class UnencryptedEmailPacket extends DataPacket {
|
||||
catch (IOException e) {
|
||||
log.error("Can't write to ByteArrayOutputStream.", e);
|
||||
}
|
||||
return byteArrayStream.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Type=" + UnencryptedEmailPacket.class.getSimpleName() + ", delKeyPln=" + deletionKeyPlain + ", delKeyVfy=" + deletionKeyVerify +
|
||||
", msgId=" + messageId + ", fragIdx=" + fragmentIndex + ", numFrags=" + numFragments + ", contLen=" + content.length;
|
||||
}
|
||||
return byteArrayStream.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Type=" + UnencryptedEmailPacket.class.getSimpleName() + ", msgId=" + messageId +
|
||||
", fragIdx=" + fragmentIndex + ", numFrags=" + numFragments + ", contLen=" + content.length;
|
||||
}
|
||||
}
|
@ -32,6 +32,16 @@ import net.i2p.util.Log;
|
||||
public abstract class DhtStorablePacket extends DataPacket {
|
||||
private static Log log = new Log(DhtStorablePacket.class);
|
||||
|
||||
public DhtStorablePacket() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DataPacket(byte[])
|
||||
*/
|
||||
public DhtStorablePacket(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
public abstract Hash getDhtKey();
|
||||
|
||||
/**
|
||||
@ -62,7 +72,15 @@ public abstract class DhtStorablePacket extends DataPacket {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>null</code> if the file doesn't exist.
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
public static DhtStorablePacket createPacket(File file) {
|
||||
if (file==null || !file.exists())
|
||||
return null;
|
||||
|
||||
DataPacket dataPacket;
|
||||
dataPacket = DataPacket.createPacket(file);
|
||||
if (dataPacket instanceof DhtStorablePacket)
|
||||
|
@ -73,6 +73,7 @@ public class RetrieveRequest extends CommunicationPacket {
|
||||
|
||||
try {
|
||||
writeHeader(outputStream);
|
||||
// TODO doesn't the type code already get written in writeHeader?
|
||||
outputStream.write(getPacketTypeCode(dataType));
|
||||
outputStream.write(key.toByteArray());
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public class PrintAppVersionTag extends SimpleTagSupport {
|
||||
JspWriter out = pageContext.getOut();
|
||||
|
||||
try {
|
||||
out.println(I2PBote.getInstance().getVersion());
|
||||
out.println(I2PBote.getInstance().getAppVersion());
|
||||
} catch (IOException e) {
|
||||
log.error("Can't write output to HTML page", e);
|
||||
}
|
||||
|
53
test/i2p/bote/packet/EmailPacketDeleteRequestTest.java
Normal file
53
test/i2p/bote/packet/EmailPacketDeleteRequestTest.java
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote 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.
|
||||
*
|
||||
* I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.packet;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import i2p.bote.UniqueId;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
// TODO add to AllTests
|
||||
public class EmailPacketDeleteRequestTest {
|
||||
private EmailPacketDeleteRequest delRequest;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Hash dhtKey = new Hash(new byte[] {-48, 78, 66, 58, -79, 87, 38, -103, -60, -27, 108, 55, 117, 37, -99, 93, -23, -102, -83, 20, 44, -80, 65, 89, -68, -73, 69, 51, 115, 79, 24, 127});
|
||||
byte[] packetIdBytes = new byte[] {120, 120, -8, -88, 21, 126, 46, -61, 18, -101, 15, 53, 20, -44, -112, 42, 86, -117, 30, -96, -66, 33, 71, -55, -102, -78, 78, -82, -105, 66, -116, 43};
|
||||
UniqueId deletionKey = new UniqueId(packetIdBytes, 0);
|
||||
delRequest = new EmailPacketDeleteRequest(dhtKey, deletionKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toByteArrayAndBack() {
|
||||
byte[] arrayA = delRequest.toByteArray();
|
||||
byte[] arrayB;
|
||||
arrayB = new EmailPacketDeleteRequest(arrayA).toByteArray();
|
||||
assertTrue("The two arrays differ!", Arrays.equals(arrayA, arrayB));
|
||||
}
|
||||
}
|
@ -43,10 +43,9 @@ public class EncryptedEmailPacketTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
byte[] content = message.getBytes();
|
||||
UniqueId deletionKeyPlain = new UniqueId(new byte[] {72, -18, -72, -39, 122, 40, -104, -66, -54, -61, -108, 72, 54, 30, 37, 76, 44, 86, 104, -124, -31, 32, -82, -27, 26, 76, 7, 106, -76, 72, 49, -44}, 0);
|
||||
UniqueId deletionKeyVerify = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
|
||||
|
||||
byte[] messageIdBytes = new byte[] {2, -69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123};
|
||||
byte[] messageIdBytes = new byte[] {-69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123};
|
||||
UniqueId messageId = new UniqueId(messageIdBytes, 0);
|
||||
|
||||
int fragmentIndex = 0;
|
||||
@ -56,7 +55,7 @@ public class EncryptedEmailPacketTest {
|
||||
identity = new EmailIdentity(base64Identity);
|
||||
EmailDestination destination = new EmailDestination(identity.getKey()); // corresponds to identity
|
||||
|
||||
UnencryptedEmailPacket plaintextPacket = new UnencryptedEmailPacket(deletionKeyPlain, deletionKeyVerify, messageId, fragmentIndex, numFragments, content);
|
||||
UnencryptedEmailPacket plaintextPacket = new UnencryptedEmailPacket(messageId, fragmentIndex, numFragments, content, deletionKeyVerify);
|
||||
encryptedPacket = new EncryptedEmailPacket(plaintextPacket, destination, appContext);
|
||||
}
|
||||
|
||||
|
83
test/i2p/bote/packet/IndexPacketDeleteRequestTest.java
Normal file
83
test/i2p/bote/packet/IndexPacketDeleteRequestTest.java
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (C) 2009 HungryHobo@mail.i2p
|
||||
*
|
||||
* The GPG fingerprint for HungryHobo@mail.i2p is:
|
||||
* 6DD3 EAA2 9990 29BC 4AD2 7486 1E2C 7B61 76DC DC12
|
||||
*
|
||||
* This file is part of I2P-Bote.
|
||||
* I2P-Bote 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.
|
||||
*
|
||||
* I2P-Bote 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 I2P-Bote. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package i2p.bote.packet;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import i2p.bote.UniqueId;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class IndexPacketDeleteRequestTest {
|
||||
IndexPacketDeleteRequest delRequest;
|
||||
Hash[] dhtKeys;
|
||||
UniqueId[] delKeys;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Hash emailDestHash = new Hash(new byte[] {-48, 78, 66, 58, -79, 87, 38, -103, -60, -27, 108, 55, 117, 37, -99, 93, -23, -102, -83, 20, 44, -80, 65, 89, -68, -73, 69, 51, 115, 79, 24, 127});
|
||||
delRequest = new IndexPacketDeleteRequest(emailDestHash);
|
||||
|
||||
dhtKeys = new Hash[3];
|
||||
delKeys = new UniqueId[3];
|
||||
|
||||
dhtKeys[0] = new Hash(new byte[] {120, 120, -8, -88, 21, 126, 46, -61, 18, -101, 15, 53, 20, -44, -112, 42, 86, -117, 30, -96, -66, 33, 71, -55, -102, -78, 78, -82, -105, 66, -116, 43});
|
||||
delKeys[0] = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
|
||||
delRequest.put(dhtKeys[0], delKeys[0]);
|
||||
|
||||
dhtKeys[1] = new Hash(new byte[] {-69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123});
|
||||
delKeys[1] = new UniqueId(new byte[] {-16, 67, 107, 80, 27, 65, 81, 71, 61, 70, -72, 126, 64, -10, 57, -128, 111, -107, -42, 24, -90, -4, -46, 63, 7, -6, 43, 76, -9, -81, 8, -68}, 0);
|
||||
delRequest.put(dhtKeys[1], delKeys[1]);
|
||||
|
||||
dhtKeys[2] = new Hash(new byte[] {-37, -8, 37, 82, -40, -34, 68, -51, -16, 74, 27, 89, 113, -15, 112, 69, 92, 102, 62, 111, 99, -27, -42, -71, 6, 38, 106, 121, 21, -72, -83, 3});
|
||||
delKeys[2] = new UniqueId(new byte[] {6, -32, -23, 17, 55, 15, -45, -19, 91, 100, -76, -76, 118, -118, -53, -109, -108, 113, -112, 81, 117, 9, -126, 20, 0, -83, -89, 7, 48, 76, -58, 83}, 0);
|
||||
delRequest.put(dhtKeys[2], delKeys[2]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toByteArrayAndBack() {
|
||||
assertEquals(3, delRequest.getDhtKeys().size());
|
||||
|
||||
Comparator<Hash> hashComparator = new Comparator<Hash>() {
|
||||
@Override
|
||||
public int compare(Hash key1, Hash key2) {
|
||||
return key1.toString().compareTo(key2.toString());
|
||||
}
|
||||
};
|
||||
|
||||
byte[] bytes = delRequest.toByteArray();
|
||||
IndexPacketDeleteRequest delRequest2 = new IndexPacketDeleteRequest(bytes);
|
||||
// Make an array of DHT keys from delRequest2.
|
||||
// Sort the array elements first because they are not guaranteed to be in any order.
|
||||
Arrays.sort(dhtKeys, hashComparator);
|
||||
Hash[] dhtKeys2 = delRequest2.getDhtKeys().toArray(new Hash[0]);
|
||||
Arrays.sort(dhtKeys2, hashComparator);
|
||||
assertEquals("The two arrays are different lengths", dhtKeys.length, dhtKeys2.length);
|
||||
for (int i=0; i<dhtKeys.length; i++)
|
||||
assertEquals("DHT keys differ: " + dhtKeys[i] + " vs " + dhtKeys2[i], dhtKeys[i], dhtKeys2[i]);
|
||||
}
|
||||
}
|
29
test/i2p/bote/packet/IndexPacketTest.java
Normal file
29
test/i2p/bote/packet/IndexPacketTest.java
Normal file
@ -0,0 +1,29 @@
|
||||
package i2p.bote.packet;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class IndexPacketTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToByteArray() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDhtKeys() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
}
|
@ -48,12 +48,11 @@ public class ResponsePacketTest {
|
||||
private DataPacket createDataPacket() throws NoSuchAlgorithmException {
|
||||
byte[] dataToStore = new byte[] {2, 109, -80, -37, -106, 83, -33, -39, -94, -76, -112, -98, 99, 25, -61, 44, -92, -85, 1, 10, -128, -2, -27, -86, -126, -33, -11, 42, 56, 3, -97, -101, 111, 7, -96, 25, 121, 74, 89, -40, -33, 82, -50, -18, 49, 106, 13, -121, 53, -83, -2, 35, -7, 71, -71, 26, 90, 1};
|
||||
|
||||
UniqueId deletionKeyPlain = new UniqueId(new byte[] {72, -18, -72, -39, 122, 40, -104, -66, -54, -61, -108, 72, 54, 30, 37, 76, 44, 86, 104, -124, -31, 32, -82, -27, 26, 76, 7, 106, -76, 72, 49, -44}, 0);
|
||||
UniqueId deletionKeyEncrypted = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
|
||||
byte[] messageIdBytes = new byte[] {2, -69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123};
|
||||
UniqueId deletionKeyVerify = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
|
||||
byte[] messageIdBytes = new byte[] {-69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123};
|
||||
UniqueId messageId = new UniqueId(messageIdBytes, 0);
|
||||
|
||||
UnencryptedEmailPacket unencryptedPacket = new UnencryptedEmailPacket(deletionKeyPlain, deletionKeyEncrypted, messageId, 0, 1, dataToStore);
|
||||
UnencryptedEmailPacket unencryptedPacket = new UnencryptedEmailPacket(messageId, 0, 1, dataToStore, deletionKeyVerify);
|
||||
EmailDestination destination = new EmailDestination("0XuJjhgp58aOhvHHgpaxoQYsCUfDS6BECMEoVxFGEFPdk3y8lbzIsq9eUyeizFleMacYwoscCir8nQLlW34lxfRmirkNpD9vU1XnmjnZ5hGdnor1qIDqz3KJ040dVQ617MwyG97xxYLT0FsH907vBXgdc4RCHwKd1~9siagA5CSMaA~wM8ymKXLypiZGYexENLmim7nMzJTQYoOM~fVS99UaGJleDBN3pgZ2EvRYDQV2VqKH7Gee07R3y7b~c0tAKVHS0IbPQfTVJigrIHjTl~ZczxpaeTM04T8IgxKnO~lSmR1w7Ik8TpEkETwT9PDwUqQsjmlSY8E~WwwGMRJVyIRZUkHeRZ0aFq7us8W9EKzYtjjiU1z0QFpZrTfJE8oqCbnH5Lqv5Q86UdTPpriJC1N99E77TpCTnNzcBnpp6ko2JCy2IJUveaigKxS6EmS9KarkkkBRsckOKZZ6UNTOqPZsBCsx0Q9WvDF-Uc3dtouXWyenxRptaQsdkZyYlEQv");
|
||||
return new EncryptedEmailPacket(unencryptedPacket, destination, appContext);
|
||||
}
|
||||
|
@ -37,16 +37,15 @@ public class UnencryptedEmailPacketTest {
|
||||
public void setUp() throws Exception {
|
||||
String message = "This is a test message. Test 1 2 3 Test";
|
||||
byte[] content = message.getBytes();
|
||||
UniqueId deletionKeyPlain = new UniqueId(new byte[] {72, -18, -72, -39, 122, 40, -104, -66, -54, -61, -108, 72, 54, 30, 37, 76, 44, 86, 104, -124, -31, 32, -82, -27, 26, 76, 7, 106, -76, 72, 49, -44}, 0);
|
||||
UniqueId deletionKeyVerify = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
|
||||
|
||||
byte[] messageIdBytes = new byte[] {2, -69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123};
|
||||
byte[] messageIdBytes = new byte[] {-69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123};
|
||||
UniqueId messageId = new UniqueId(messageIdBytes, 0);
|
||||
|
||||
int fragmentIndex = 0;
|
||||
int numFragments = 1;
|
||||
|
||||
packet = new UnencryptedEmailPacket(deletionKeyPlain, deletionKeyVerify, messageId, fragmentIndex, numFragments, content);
|
||||
packet = new UnencryptedEmailPacket(messageId, fragmentIndex, numFragments, content, deletionKeyVerify);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -46,13 +46,12 @@ public class StoreRequestTest {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
byte[] emailContent = new byte[] {2, 109, -80, -37, -106, 83, -33, -39, -94, -76, -112, -98, 99, 25, -61, 44, -92, -85, 1, 10, -128, -2, -27, -86, -126, -33, -11, 42, 56, 3, -97, -101, 111, 7, -96, 25, 121, 74, 89, -40, -33, 82, -50, -18, 49, 106, 13, -121, 53, -83, -2, 35, -7, 71, -71, 26, 90, 1};
|
||||
UniqueId deletionKeyPlain = new UniqueId(new byte[] {72, -18, -72, -39, 122, 40, -104, -66, -54, -61, -108, 72, 54, 30, 37, 76, 44, 86, 104, -124, -31, 32, -82, -27, 26, 76, 7, 106, -76, 72, 49, -44}, 0);
|
||||
UniqueId deletionKeyEncrypted = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
|
||||
UniqueId messageId = new UniqueId(new byte[] {2, -69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123}, 0);
|
||||
UniqueId deletionKeyVerify = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
|
||||
UniqueId messageId = new UniqueId(new byte[] {-69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123}, 0);
|
||||
String base64Identity = "piYT1uJ3O8~bBPZmTvehMbp3-Zksg5enhvIlp2X8txqL25l0WdQMWwyt30UAOVQqxGdnMPTqqjh~-zoa~rCQORo~J1gRxLwCX9LlHQqaIimJilrbN-rhKy4Xlft054wbgQjLSC-WICE4W64KDfitwRzdr7lV6lz~0KFiZ8erZ-~WPMG1CgWEku9lILQUdUHyFBguPcK9oPDq7oGBuFGy8w0CvAq7ex3nmbL7zQVA~VqILtOGeGK2fidCuuofj4AQsTcXmH9O0nxZGCIJBhf~4EWmazvxu8XVB8pabNQvRDbmFu6q85JTwmxC45lCjqNw30hp8q2zoqP-zchjWOrxFUhSumpBdD0xXJR~qmhejh4WnuRnnam9j3fcxH5i~T7xWgmvIbpZEI4kyc9VEbXbLI7k-bU2A6sdP-AGt5~TjGLcxpdsPnOLRXO-Dsi7E9-3Kc84s4TmdpEJdtHn1dxYyeeT-ysVOqXjv5w5Cuk0XJpUIJG8n7aXHpNb-QLxPD3yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWF3qnAX-p41Po~VNmOUzS-Yt~noD8-e~L3P5rZXBWf-XtB4hkloo6m1jwqphEdf1";
|
||||
EmailIdentity identity = new EmailIdentity(base64Identity);
|
||||
EmailDestination destination = new EmailDestination(identity.getKey());
|
||||
UnencryptedEmailPacket emailPacket = new UnencryptedEmailPacket(deletionKeyPlain, deletionKeyEncrypted, messageId, 0, 1, emailContent);
|
||||
UnencryptedEmailPacket emailPacket = new UnencryptedEmailPacket(messageId, 0, 1, emailContent, deletionKeyVerify);
|
||||
EncryptedEmailPacket encryptedPacket = new EncryptedEmailPacket(emailPacket, destination, appContext);
|
||||
HashCash hashCash = HashCash.mintCash("1234", 1);
|
||||
|
||||
|
Reference in New Issue
Block a user