Major refactoring
This commit is contained in:
		| @@ -10,7 +10,7 @@ repositories { | |||||||
| dependencies { | dependencies { | ||||||
|     compile project(':domain') |     compile project(':domain') | ||||||
|     compile project(':networking') |     compile project(':networking') | ||||||
|     compile project(':inventory') |     compile project(':repositories') | ||||||
|     compile 'org.slf4j:slf4j-simple:1.7.12' |     compile 'org.slf4j:slf4j-simple:1.7.12' | ||||||
|     testCompile 'junit:junit:4.11' |     testCompile 'junit:junit:4.11' | ||||||
| } | } | ||||||
| @@ -18,13 +18,11 @@ package ch.dissem.bitmessage.demo; | |||||||
|  |  | ||||||
| import ch.dissem.bitmessage.BitmessageContext; | import ch.dissem.bitmessage.BitmessageContext; | ||||||
| import ch.dissem.bitmessage.entity.BitmessageAddress; | import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||||
| import ch.dissem.bitmessage.entity.ObjectMessage; | import ch.dissem.bitmessage.repository.JdbcAddressRepository; | ||||||
| import ch.dissem.bitmessage.entity.payload.*; | import ch.dissem.bitmessage.repository.JdbcInventory; | ||||||
| import ch.dissem.bitmessage.inventory.JdbcAddressRepository; | import ch.dissem.bitmessage.repository.JdbcMessageRepository; | ||||||
| import ch.dissem.bitmessage.inventory.JdbcInventory; | import ch.dissem.bitmessage.repository.JdbcNodeRegistry; | ||||||
| import ch.dissem.bitmessage.inventory.JdbcNodeRegistry; |  | ||||||
| import ch.dissem.bitmessage.networking.NetworkNode; | import ch.dissem.bitmessage.networking.NetworkNode; | ||||||
| import ch.dissem.bitmessage.ports.NetworkHandler; |  | ||||||
| import ch.dissem.bitmessage.utils.Base58; | import ch.dissem.bitmessage.utils.Base58; | ||||||
| import ch.dissem.bitmessage.utils.Encode; | import ch.dissem.bitmessage.utils.Encode; | ||||||
| import ch.dissem.bitmessage.utils.Security; | import ch.dissem.bitmessage.utils.Security; | ||||||
| @@ -33,9 +31,6 @@ import org.slf4j.LoggerFactory; | |||||||
|  |  | ||||||
| import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Scanner; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Created by chris on 06.04.15. |  * Created by chris on 06.04.15. | ||||||
| @@ -51,61 +46,56 @@ public class Main { | |||||||
|                 .inventory(new JdbcInventory()) |                 .inventory(new JdbcInventory()) | ||||||
|                 .nodeRegistry(new JdbcNodeRegistry()) |                 .nodeRegistry(new JdbcNodeRegistry()) | ||||||
|                 .networkHandler(new NetworkNode()) |                 .networkHandler(new NetworkNode()) | ||||||
|  |                 .messageRepo(new JdbcMessageRepository()) | ||||||
|                 .port(48444) |                 .port(48444) | ||||||
|                 .streams(1) |                 .streams(1) | ||||||
|                 .build(); |                 .build(); | ||||||
|  |  | ||||||
|         ctx.getNetworkHandler().start(new NetworkHandler.MessageListener() { | //        ctx.startup(new BitmessageContext.Listener() { | ||||||
|             @Override | //            @Override | ||||||
|             public void receive(ObjectPayload payload) { | //            public void receive(Plaintext plaintext) { | ||||||
| //                LOG.info("message received: " + payload); | //                // TODO | ||||||
| //                System.out.print('.'); | //                try { | ||||||
|                 if (payload instanceof V4Pubkey) { | //                    System.out.println(new String(plaintext.getMessage(), "UTF-8")); | ||||||
|                     V4Pubkey pubkey = (V4Pubkey) payload; | //                } catch (UnsupportedEncodingException e) { | ||||||
|                     if (Arrays.equals(address.getTag(), pubkey.getTag())) { | //                    LOG.error(e.getMessage(), e); | ||||||
|                         System.out.println("Pubkey found!"); | //                } | ||||||
|                         try { | //            } | ||||||
|                             address.setPubkey(pubkey); | //        }); | ||||||
|                         } catch (Exception ignore) { |  | ||||||
|                             System.err.println("Received pubkey we didn't request."); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         Scanner scanner = new Scanner(System.in); |  | ||||||
| //        System.out.println("Press Enter to request pubkey for address " + address); | //        Scanner scanner = new Scanner(System.in); | ||||||
|  | ////        System.out.println("Press Enter to request pubkey for address " + address); | ||||||
|  | ////        scanner.nextLine(); | ||||||
|  | ////        ctx.send(1, address.getVersion(), new GetPubkey(address), 3000, 1000, 1000); | ||||||
|  | // | ||||||
|  | //        System.out.println("Press Enter to exit"); | ||||||
| //        scanner.nextLine(); | //        scanner.nextLine(); | ||||||
| //        ctx.send(1, address.getVersion(), new GetPubkey(address), 3000, 1000, 1000); | //        LOG.info("Shutting down client"); | ||||||
|  | //        ctx.shutdown(); | ||||||
|         System.out.println("Press Enter to exit"); | // | ||||||
|         scanner.nextLine(); | // | ||||||
|         LOG.info("Shutting down client"); | //        List<ObjectMessage> objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY); | ||||||
|         ctx.getNetworkHandler().stop(); | //        System.out.println("Address version: " + address.getVersion()); | ||||||
|  | //        System.out.println("Address stream:  " + address.getStream()); | ||||||
|  | //        for (ObjectMessage o : objects) { | ||||||
|         List<ObjectMessage> objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY); | ////            if (!o.isSignatureValid()) System.out.println("Invalid signature."); | ||||||
|         System.out.println("Address version: " + address.getVersion()); | ////            System.out.println(o.getPayload().getSignature().length); | ||||||
|         System.out.println("Address stream:  " + address.getStream()); | //            V4Pubkey pubkey = (V4Pubkey) o.getPayload(); | ||||||
|         for (ObjectMessage o : objects) { | //            if (Arrays.equals(address.getTag(), pubkey.getTag())) { | ||||||
| //            if (!o.isSignatureValid()) System.out.println("Invalid signature."); | //                System.out.println("Pubkey found!"); | ||||||
| //            System.out.println(o.getPayload().getSignature().length); | //                try { | ||||||
|             V4Pubkey pubkey = (V4Pubkey) o.getPayload(); | //                    System.out.println("IV: " + o.getInventoryVector()); | ||||||
|             if (Arrays.equals(address.getTag(), pubkey.getTag())) { | //                    address.setPubkey(pubkey); | ||||||
|                 System.out.println("Pubkey found!"); | //                } catch (Exception ignore) { | ||||||
|                 try { | //                    System.out.println("But setPubkey failed? " + address.getRipe().length + "/" + pubkey.getRipe().length); | ||||||
|                     System.out.println("IV: "+o.getInventoryVector()); | //                    System.out.println("Failed address: " + generateAddress(address.getStream(), address.getVersion(), pubkey.getRipe())); | ||||||
|                     address.setPubkey(pubkey); | //                    if (Arrays.equals(address.getRipe(), pubkey.getRipe())) { | ||||||
|                 } catch (Exception ignore) { | //                        ignore.printStackTrace(); | ||||||
|                     System.out.println("But setPubkey failed? " + address.getRipe().length + "/" + pubkey.getRipe().length); | //                    } | ||||||
|                     System.out.println("Failed address: " + generateAddress(address.getStream(), address.getVersion(), pubkey.getRipe())); | //                } | ||||||
|                     if (Arrays.equals(address.getRipe(), pubkey.getRipe())) { | //            } | ||||||
|                         ignore.printStackTrace(); | //        } | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static String generateAddress(long stream, long version, byte[] ripe) { |     public static String generateAddress(long stream, long version, byte[] ripe) { | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								docs/seminar-blx.bib
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								docs/seminar-blx.bib
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | @Comment{$ biblatex control file $} | ||||||
|  | @Comment{$ biblatex version 1.7 $} | ||||||
|  | Do not modify this file! | ||||||
|  |  | ||||||
|  | This is an auxiliary file used by the 'biblatex' package. | ||||||
|  | This file may safely be deleted. It will be recreated as | ||||||
|  | required. | ||||||
|  |  | ||||||
|  | @Control{biblatex-control, | ||||||
|  |   options = {1.7:0:0:1:0:0:1:1:0:0:0:0:1:1:3:1:79:+}, | ||||||
|  | } | ||||||
| @@ -16,8 +16,16 @@ | |||||||
|  |  | ||||||
| package ch.dissem.bitmessage; | package ch.dissem.bitmessage; | ||||||
|  |  | ||||||
|  | import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||||
|  | import ch.dissem.bitmessage.entity.Encrypted; | ||||||
| import ch.dissem.bitmessage.entity.ObjectMessage; | import ch.dissem.bitmessage.entity.ObjectMessage; | ||||||
|  | import ch.dissem.bitmessage.entity.Plaintext; | ||||||
|  | import ch.dissem.bitmessage.entity.Plaintext.Encoding; | ||||||
|  | import ch.dissem.bitmessage.entity.payload.GetPubkey; | ||||||
|  | import ch.dissem.bitmessage.entity.payload.Msg; | ||||||
| import ch.dissem.bitmessage.entity.payload.ObjectPayload; | import ch.dissem.bitmessage.entity.payload.ObjectPayload; | ||||||
|  | import ch.dissem.bitmessage.entity.payload.Pubkey.Feature; | ||||||
|  | import ch.dissem.bitmessage.entity.valueobject.PrivateKey; | ||||||
| import ch.dissem.bitmessage.ports.*; | import ch.dissem.bitmessage.ports.*; | ||||||
| import ch.dissem.bitmessage.utils.Security; | import ch.dissem.bitmessage.utils.Security; | ||||||
| import ch.dissem.bitmessage.utils.UnixTime; | import ch.dissem.bitmessage.utils.UnixTime; | ||||||
| @@ -26,48 +34,88 @@ import org.slf4j.LoggerFactory; | |||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
|  | import java.util.List; | ||||||
| import java.util.TreeSet; | import java.util.TreeSet; | ||||||
|  |  | ||||||
|  | import static ch.dissem.bitmessage.entity.Plaintext.Status.*; | ||||||
|  | import static ch.dissem.bitmessage.utils.UnixTime.DAY; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Created by chris on 05.04.15. |  * Created by chris on 05.04.15. | ||||||
|  */ |  */ | ||||||
| public class BitmessageContext { | public class BitmessageContext { | ||||||
|     public static final int CURRENT_VERSION = 3; |     public static final int CURRENT_VERSION = 3; | ||||||
|     private final static Logger LOG = LoggerFactory.getLogger(BitmessageContext.class); |     private final static Logger LOG = LoggerFactory.getLogger(BitmessageContext.class); | ||||||
|     private final Inventory inventory; |  | ||||||
|     private final NodeRegistry nodeRegistry; |  | ||||||
|     private final NetworkHandler networkHandler; |  | ||||||
|     private final AddressRepository addressRepo; |  | ||||||
|     private final ProofOfWorkEngine proofOfWorkEngine; |  | ||||||
|  |  | ||||||
|     private final TreeSet<Long> streams; |     private final InternalContext ctx; | ||||||
|  |  | ||||||
|     private final int port; |  | ||||||
|  |  | ||||||
|     private long networkNonceTrialsPerByte = 1000; |  | ||||||
|     private long networkExtraBytes = 1000; |  | ||||||
|  |  | ||||||
|     private BitmessageContext(Builder builder) { |     private BitmessageContext(Builder builder) { | ||||||
|         port = builder.port; |         ctx = new InternalContext(builder); | ||||||
|         inventory = builder.inventory; |  | ||||||
|         nodeRegistry = builder.nodeRegistry; |  | ||||||
|         networkHandler = builder.networkHandler; |  | ||||||
|         addressRepo = builder.addressRepo; |  | ||||||
|         proofOfWorkEngine = builder.proofOfWorkEngine; |  | ||||||
|         streams = builder.streams; |  | ||||||
|  |  | ||||||
|         init(inventory, nodeRegistry, networkHandler, addressRepo, proofOfWorkEngine); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void init(Object... objects) { |     public List<BitmessageAddress> getIdentities() { | ||||||
|         for (Object o : objects) { |         return ctx.getAddressRepo().getIdentities(); | ||||||
|             if (o instanceof ContextHolder) { |  | ||||||
|                 ((ContextHolder) o).setContext(this); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public BitmessageAddress createIdentity(boolean shorter, Feature... features) { | ||||||
|  |         BitmessageAddress identity = new BitmessageAddress(new PrivateKey( | ||||||
|  |                 shorter, | ||||||
|  |                 ctx.getStreams()[0], | ||||||
|  |                 ctx.getNetworkNonceTrialsPerByte(), | ||||||
|  |                 ctx.getNetworkExtraBytes(), | ||||||
|  |                 features | ||||||
|  |         )); | ||||||
|  |         ctx.getAddressRepo().save(identity); | ||||||
|  |         return identity; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void addDistributedMailingList(String address, String alias) { | ||||||
|  |         // TODO | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void send(BitmessageAddress from, BitmessageAddress to, String subject, String message) { | ||||||
|  |         if (from.getPrivateKey() == null) { | ||||||
|  |             throw new IllegalArgumentException("'From' must be an identity, i.e. have a private key."); | ||||||
|  |         } | ||||||
|  |         Plaintext msg = new Plaintext.Builder() | ||||||
|  |                 .from(from) | ||||||
|  |                 .to(to) | ||||||
|  |                 .encoding(Encoding.SIMPLE) | ||||||
|  |                 .message(subject, message) | ||||||
|  |                 .build(); | ||||||
|  |         if (to.getPubkey() == null) { | ||||||
|  |             requestPubkey(from, to); | ||||||
|  |             msg.setStatus(PUBKEY_REQUESTED); | ||||||
|  |             ctx.getMessageRepository().save(msg); | ||||||
|  |         } else { | ||||||
|  |             msg.setStatus(DOING_PROOF_OF_WORK); | ||||||
|  |             ctx.getMessageRepository().save(msg); | ||||||
|  |             ctx.send( | ||||||
|  |                     from, | ||||||
|  |                     to, | ||||||
|  |                     new Msg(msg), | ||||||
|  |                     +2 * DAY, | ||||||
|  |                     ctx.getNonceTrialsPerByte(to), | ||||||
|  |                     ctx.getExtraBytes(to) | ||||||
|  |             ); | ||||||
|  |             msg.setStatus(SENT); | ||||||
|  |             ctx.getMessageRepository().save(msg); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void send(long stream, long version, ObjectPayload payload, long timeToLive, long nonceTrialsPerByte, long extraBytes) throws IOException { |     private void requestPubkey(BitmessageAddress requestingIdentity, BitmessageAddress address) { | ||||||
|  |         ctx.send( | ||||||
|  |                 requestingIdentity, | ||||||
|  |                 address, | ||||||
|  |                 new GetPubkey(address), | ||||||
|  |                 +28 * DAY, | ||||||
|  |                 ctx.getNetworkNonceTrialsPerByte(), | ||||||
|  |                 ctx.getNetworkExtraBytes() | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void send(long stream, long version, ObjectPayload payload, long timeToLive) { | ||||||
|  |         try { | ||||||
|             long expires = UnixTime.now(+timeToLive); |             long expires = UnixTime.now(+timeToLive); | ||||||
|             LOG.info("Expires at " + expires); |             LOG.info("Expires at " + expires); | ||||||
|             ObjectMessage object = new ObjectMessage.Builder() |             ObjectMessage object = new ObjectMessage.Builder() | ||||||
| @@ -76,65 +124,36 @@ public class BitmessageContext { | |||||||
|                     .expiresTime(expires) |                     .expiresTime(expires) | ||||||
|                     .payload(payload) |                     .payload(payload) | ||||||
|                     .build(); |                     .build(); | ||||||
|         Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes); |             Security.doProofOfWork(object, ctx.getProofOfWorkEngine(), | ||||||
|         inventory.storeObject(object); |                     ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); | ||||||
|         networkHandler.offer(object.getInventoryVector()); |             ctx.getInventory().storeObject(object); | ||||||
|  |             ctx.getNetworkHandler().offer(object.getInventoryVector()); | ||||||
|  |         } catch (IOException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Inventory getInventory() { |     public void startup(Listener listener) { | ||||||
|         return inventory; |         ctx.getNetworkHandler().start(new DefaultMessageListener(ctx, listener)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public NodeRegistry getAddressRepository() { |     public void shutdown() { | ||||||
|         return nodeRegistry; |         ctx.getNetworkHandler().stop(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public NetworkHandler getNetworkHandler() { |     public interface Listener { | ||||||
|         return networkHandler; |         void receive(Plaintext plaintext); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public int getPort() { |  | ||||||
|         return port; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public long[] getStreams() { |  | ||||||
|         long[] result = new long[streams.size()]; |  | ||||||
|         int i = 0; |  | ||||||
|         for (long stream : streams) { |  | ||||||
|             result[i++] = stream; |  | ||||||
|         } |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void addStream(long stream) { |  | ||||||
|         streams.add(stream); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void removeStream(long stream) { |  | ||||||
|         streams.remove(stream); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public long getNetworkNonceTrialsPerByte() { |  | ||||||
|         return networkNonceTrialsPerByte; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public long getNetworkExtraBytes() { |  | ||||||
|         return networkExtraBytes; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     public interface ContextHolder { |  | ||||||
|         void setContext(BitmessageContext context); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static final class Builder { |     public static final class Builder { | ||||||
|         private int port = 8444; |         int port = 8444; | ||||||
|         private Inventory inventory; |         Inventory inventory; | ||||||
|         private NodeRegistry nodeRegistry; |         NodeRegistry nodeRegistry; | ||||||
|         private NetworkHandler networkHandler; |         NetworkHandler networkHandler; | ||||||
|         private AddressRepository addressRepo; |         AddressRepository addressRepo; | ||||||
|         private ProofOfWorkEngine proofOfWorkEngine; |         MessageRepository messageRepo; | ||||||
|         private TreeSet<Long> streams; |         ProofOfWorkEngine proofOfWorkEngine; | ||||||
|  |         TreeSet<Long> streams; | ||||||
|  |  | ||||||
|         public Builder() { |         public Builder() { | ||||||
|         } |         } | ||||||
| @@ -164,6 +183,11 @@ public class BitmessageContext { | |||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public Builder messageRepo(MessageRepository messageRepo) { | ||||||
|  |             this.messageRepo = messageRepo; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public Builder proofOfWorkEngine(ProofOfWorkEngine proofOfWorkEngine) { |         public Builder proofOfWorkEngine(ProofOfWorkEngine proofOfWorkEngine) { | ||||||
|             this.proofOfWorkEngine = proofOfWorkEngine; |             this.proofOfWorkEngine = proofOfWorkEngine; | ||||||
|             return this; |             return this; | ||||||
| @@ -187,6 +211,7 @@ public class BitmessageContext { | |||||||
|             nonNull("nodeRegistry", nodeRegistry); |             nonNull("nodeRegistry", nodeRegistry); | ||||||
|             nonNull("networkHandler", networkHandler); |             nonNull("networkHandler", networkHandler); | ||||||
|             nonNull("addressRepo", addressRepo); |             nonNull("addressRepo", addressRepo); | ||||||
|  |             nonNull("messageRepo", messageRepo); | ||||||
|             if (streams == null) { |             if (streams == null) { | ||||||
|                 streams(1); |                 streams(1); | ||||||
|             } |             } | ||||||
| @@ -200,4 +225,5 @@ public class BitmessageContext { | |||||||
|             if (o == null) throw new IllegalStateException(name + " must not be null"); |             if (o == null) throw new IllegalStateException(name + " must not be null"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,160 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2015 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.bitmessage; | ||||||
|  |  | ||||||
|  | import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||||
|  | import ch.dissem.bitmessage.entity.Encrypted; | ||||||
|  | import ch.dissem.bitmessage.entity.ObjectMessage; | ||||||
|  | import ch.dissem.bitmessage.entity.Plaintext; | ||||||
|  | import ch.dissem.bitmessage.entity.payload.*; | ||||||
|  | import ch.dissem.bitmessage.ports.NetworkHandler; | ||||||
|  | import ch.dissem.bitmessage.utils.Security; | ||||||
|  | import ch.dissem.bitmessage.utils.UnixTime; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import static ch.dissem.bitmessage.entity.Plaintext.Status.DOING_PROOF_OF_WORK; | ||||||
|  | import static ch.dissem.bitmessage.entity.Plaintext.Status.SENT; | ||||||
|  | import static ch.dissem.bitmessage.utils.UnixTime.DAY; | ||||||
|  |  | ||||||
|  | class DefaultMessageListener implements NetworkHandler.MessageListener { | ||||||
|  |     private final static Logger LOG = LoggerFactory.getLogger(DefaultMessageListener.class); | ||||||
|  |     private final InternalContext ctx; | ||||||
|  |     private final BitmessageContext.Listener listener; | ||||||
|  |  | ||||||
|  |     public DefaultMessageListener(InternalContext context, BitmessageContext.Listener listener) { | ||||||
|  |         this.ctx = context; | ||||||
|  |         this.listener = listener; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void receive(ObjectMessage object) { | ||||||
|  |         ObjectPayload payload = object.getPayload(); | ||||||
|  |         switch (payload.getType()) { | ||||||
|  |             case GET_PUBKEY: { | ||||||
|  |                 receive(object, (GetPubkey) payload); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case PUBKEY: { | ||||||
|  |                 receive(object, (Pubkey) payload); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case MSG: { | ||||||
|  |                 receive(object, (Msg) payload); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case BROADCAST: { | ||||||
|  |                 receive(object, (Broadcast) payload); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void receive(ObjectMessage object, GetPubkey getPubkey) { | ||||||
|  |         BitmessageAddress identity = ctx.getAddressRepo().findIdentity(getPubkey.getRipeTag()); | ||||||
|  |         if (identity != null && identity.getPrivateKey() != null) { | ||||||
|  |             try { | ||||||
|  |                 long expires = UnixTime.now(+28 * DAY); | ||||||
|  |                 LOG.info("Expires at " + expires); | ||||||
|  |                 ObjectMessage response = new ObjectMessage.Builder() | ||||||
|  |                         .stream(object.getStream()) | ||||||
|  |                         .version(identity.getVersion()) | ||||||
|  |                         .expiresTime(expires) | ||||||
|  |                         .payload(identity.getPubkey()) | ||||||
|  |                         .build(); | ||||||
|  |                 Security.doProofOfWork(response, ctx.getProofOfWorkEngine(), | ||||||
|  |                         ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); | ||||||
|  |                 if (response.isSigned()) { | ||||||
|  |                     response.sign(identity.getPrivateKey()); | ||||||
|  |                 } | ||||||
|  |                 if (response instanceof Encrypted) { | ||||||
|  |                     response.encrypt(Security.createPublicKey(identity.getPubkeyDecryptionKey()).getEncoded(false)); | ||||||
|  |                 } | ||||||
|  |                 ctx.getInventory().storeObject(response); | ||||||
|  |                 ctx.getNetworkHandler().offer(response.getInventoryVector()); | ||||||
|  |             } catch (IOException e) { | ||||||
|  |                 throw new RuntimeException(e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void receive(ObjectMessage object, Pubkey pubkey) { | ||||||
|  |         BitmessageAddress address; | ||||||
|  |         try { | ||||||
|  |             if (pubkey instanceof V4Pubkey) { | ||||||
|  |                 V4Pubkey v4Pubkey = (V4Pubkey) pubkey; | ||||||
|  |                 address = ctx.getAddressRepo().findContact(v4Pubkey.getTag()); | ||||||
|  |                 if (address != null) { | ||||||
|  |                     v4Pubkey.decrypt(address.getPubkeyDecryptionKey()); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 address = ctx.getAddressRepo().findContact(pubkey.getRipe()); | ||||||
|  |             } | ||||||
|  |             if (address != null) { | ||||||
|  |                 address.setPubkey(pubkey); | ||||||
|  |                 List<Plaintext> messages = ctx.getMessageRepository().findMessages(Plaintext.Status.PUBKEY_REQUESTED, address); | ||||||
|  |                 for (Plaintext msg:messages){ | ||||||
|  |                     // TODO: send messages enqueued for this address | ||||||
|  |                     msg.setStatus(DOING_PROOF_OF_WORK); | ||||||
|  |                     ctx.getMessageRepository().save(msg); | ||||||
|  |                     ctx.send( | ||||||
|  |                             msg.getFrom(), | ||||||
|  |                             msg.getTo(), | ||||||
|  |                             new Msg(msg), | ||||||
|  |                             +2 * DAY, | ||||||
|  |                             ctx.getNonceTrialsPerByte(msg.getTo()), | ||||||
|  |                             ctx.getExtraBytes(msg.getTo()) | ||||||
|  |                     ); | ||||||
|  |                     msg.setStatus(SENT); | ||||||
|  |                     ctx.getMessageRepository().save(msg); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } catch (IllegalArgumentException | IOException e) { | ||||||
|  |             LOG.debug(e.getMessage(), e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void receive(ObjectMessage object, Msg msg) { | ||||||
|  |         for (BitmessageAddress identity : ctx.getAddressRepo().getIdentities()) { | ||||||
|  |             try { | ||||||
|  |                 msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey()); | ||||||
|  |                 msg.getPlaintext().setTo(identity); | ||||||
|  |                 object.isSignatureValid(msg.getPlaintext().getFrom().getPubkey()); | ||||||
|  |                 ctx.getMessageRepository().save(msg.getPlaintext()); | ||||||
|  |                 listener.receive(msg.getPlaintext()); | ||||||
|  |                 break; | ||||||
|  |             } catch (IOException ignore) { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void receive(ObjectMessage object, Broadcast broadcast) { | ||||||
|  |         // TODO this should work fine as-is, but checking the tag might be more efficient | ||||||
|  | //        V5Broadcast v5 = broadcast instanceof V5Broadcast ? (V5Broadcast) broadcast : null; | ||||||
|  |         for (BitmessageAddress subscription : ctx.getAddressRepo().getSubscriptions()) { | ||||||
|  |             try { | ||||||
|  |                 broadcast.decrypt(subscription.getPubkeyDecryptionKey()); | ||||||
|  |                 object.isSignatureValid(broadcast.getPlaintext().getFrom().getPubkey()); | ||||||
|  |                 listener.receive(broadcast.getPlaintext()); | ||||||
|  |             } catch (IOException ignore) { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										166
									
								
								domain/src/main/java/ch/dissem/bitmessage/InternalContext.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								domain/src/main/java/ch/dissem/bitmessage/InternalContext.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,166 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2015 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.bitmessage; | ||||||
|  |  | ||||||
|  | import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||||
|  | import ch.dissem.bitmessage.entity.Encrypted; | ||||||
|  | import ch.dissem.bitmessage.entity.ObjectMessage; | ||||||
|  | import ch.dissem.bitmessage.entity.payload.ObjectPayload; | ||||||
|  | import ch.dissem.bitmessage.ports.*; | ||||||
|  | import ch.dissem.bitmessage.utils.Security; | ||||||
|  | import ch.dissem.bitmessage.utils.UnixTime; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.util.TreeSet; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * The internal context should normally only be used for port implementations. If you need it in your client | ||||||
|  |  * implementation, you're either doing something wrong, something very weird, or the BitmessageContext should | ||||||
|  |  * get extended. | ||||||
|  |  * <p/> | ||||||
|  |  * On the other hand, if you need the BitmessageContext in a port implementation, the same thing might apply. | ||||||
|  |  */ | ||||||
|  | public class InternalContext { | ||||||
|  |     private final static Logger LOG = LoggerFactory.getLogger(InternalContext.class); | ||||||
|  |  | ||||||
|  |     private final Inventory inventory; | ||||||
|  |     private final NodeRegistry nodeRegistry; | ||||||
|  |     private final NetworkHandler networkHandler; | ||||||
|  |     private final AddressRepository addressRepository; | ||||||
|  |     private final MessageRepository messageRepository; | ||||||
|  |     private final ProofOfWorkEngine proofOfWorkEngine; | ||||||
|  |  | ||||||
|  |     private final TreeSet<Long> streams; | ||||||
|  |     private final int port; | ||||||
|  |     private long networkNonceTrialsPerByte = 1000; | ||||||
|  |     private long networkExtraBytes = 1000; | ||||||
|  |  | ||||||
|  |     public InternalContext(BitmessageContext.Builder builder) { | ||||||
|  |         this.inventory = builder.inventory; | ||||||
|  |         this.nodeRegistry = builder.nodeRegistry; | ||||||
|  |         this.networkHandler = builder.networkHandler; | ||||||
|  |         this.addressRepository = builder.addressRepo; | ||||||
|  |         this.messageRepository = builder.messageRepo; | ||||||
|  |         this.proofOfWorkEngine = builder.proofOfWorkEngine; | ||||||
|  |  | ||||||
|  |         port = builder.port; | ||||||
|  |         streams = builder.streams; | ||||||
|  |  | ||||||
|  |         init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void init(Object... objects) { | ||||||
|  |         for (Object o : objects) { | ||||||
|  |             if (o instanceof ContextHolder) { | ||||||
|  |                 ((ContextHolder) o).setContext(this); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Inventory getInventory() { | ||||||
|  |         return inventory; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public NodeRegistry getNodeRegistry() { | ||||||
|  |         return nodeRegistry; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public NetworkHandler getNetworkHandler() { | ||||||
|  |         return networkHandler; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public AddressRepository getAddressRepo() { | ||||||
|  |         return addressRepository; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public MessageRepository getMessageRepository() { | ||||||
|  |         return messageRepository; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ProofOfWorkEngine getProofOfWorkEngine() { | ||||||
|  |         return proofOfWorkEngine; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public long[] getStreams() { | ||||||
|  |         long[] result = new long[streams.size()]; | ||||||
|  |         int i = 0; | ||||||
|  |         for (long stream : streams) { | ||||||
|  |             result[i++] = stream; | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void addStream(long stream) { | ||||||
|  |         streams.add(stream); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void removeStream(long stream) { | ||||||
|  |         streams.remove(stream); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public int getPort() { | ||||||
|  |         return port; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public long getNetworkNonceTrialsPerByte() { | ||||||
|  |         return networkNonceTrialsPerByte; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public long getNonceTrialsPerByte(BitmessageAddress address) { | ||||||
|  |         long nonceTrialsPerByte = address.getPubkey().getNonceTrialsPerByte(); | ||||||
|  |         return networkNonceTrialsPerByte > nonceTrialsPerByte ? networkNonceTrialsPerByte : nonceTrialsPerByte; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public long getNetworkExtraBytes() { | ||||||
|  |         return networkExtraBytes; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public long getExtraBytes(BitmessageAddress address) { | ||||||
|  |         long extraBytes = address.getPubkey().getExtraBytes(); | ||||||
|  |         return networkExtraBytes > extraBytes ? networkExtraBytes : extraBytes; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void send(BitmessageAddress from, BitmessageAddress to, ObjectPayload payload, long timeToLive, long nonceTrialsPerByte, long extraBytes) { | ||||||
|  |         try { | ||||||
|  |             long expires = UnixTime.now(+timeToLive); | ||||||
|  |             LOG.info("Expires at " + expires); | ||||||
|  |             ObjectMessage object = new ObjectMessage.Builder() | ||||||
|  |                     .stream(to.getStream()) | ||||||
|  |                     .version(to.getVersion()) | ||||||
|  |                     .expiresTime(expires) | ||||||
|  |                     .payload(payload) | ||||||
|  |                     .build(); | ||||||
|  |             Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes); | ||||||
|  |             if (object.isSigned()) { | ||||||
|  |                 object.sign(from.getPrivateKey()); | ||||||
|  |             } | ||||||
|  |             if (object instanceof Encrypted) { | ||||||
|  |                 object.encrypt(to.getPubkey()); | ||||||
|  |             } | ||||||
|  |             inventory.storeObject(object); | ||||||
|  |             networkHandler.offer(object.getInventoryVector()); | ||||||
|  |         } catch (IOException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public interface ContextHolder { | ||||||
|  |         void setContext(InternalContext context); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -49,8 +49,9 @@ public class BitmessageAddress { | |||||||
|     private Pubkey pubkey; |     private Pubkey pubkey; | ||||||
|  |  | ||||||
|     private String alias; |     private String alias; | ||||||
|  |     private boolean subscribed; | ||||||
|  |  | ||||||
|     private BitmessageAddress(long version, long stream, byte[] ripe) { |     BitmessageAddress(long version, long stream, byte[] ripe) { | ||||||
|         try { |         try { | ||||||
|             this.version = version; |             this.version = version; | ||||||
|             this.stream = stream; |             this.stream = stream; | ||||||
| @@ -74,7 +75,7 @@ public class BitmessageAddress { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private BitmessageAddress(Pubkey publicKey) { |     BitmessageAddress(Pubkey publicKey) { | ||||||
|         this(publicKey.getVersion(), publicKey.getStream(), publicKey.getRipe()); |         this(publicKey.getVersion(), publicKey.getStream(), publicKey.getRipe()); | ||||||
|         this.pubkey = publicKey; |         this.pubkey = publicKey; | ||||||
|     } |     } | ||||||
| @@ -163,6 +164,10 @@ public class BitmessageAddress { | |||||||
|         this.alias = alias; |         this.alias = alias; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void setSubscribed(boolean subscribed) { | ||||||
|  |         this.subscribed = subscribed; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public String toString() { |     public String toString() { | ||||||
|         return alias != null ? alias : address; |         return alias != null ? alias : address; | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ package ch.dissem.bitmessage.entity; | |||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Created by chris on 12.05.15. |  * Used for objects that have encrypted content | ||||||
|  */ |  */ | ||||||
| public interface Encrypted { | public interface Encrypted { | ||||||
|     void encrypt(byte[] publicKey) throws IOException; |     void encrypt(byte[] publicKey) throws IOException; | ||||||
|   | |||||||
| @@ -128,16 +128,20 @@ public class ObjectMessage implements MessagePayload { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void encrypt(byte[] publicEncryptionKey) throws IOException{ |     public void encrypt(byte[] publicEncryptionKey) throws IOException { | ||||||
|         if (payload instanceof Encrypted){ |         if (payload instanceof Encrypted) { | ||||||
|             ((Encrypted) payload).encrypt(publicEncryptionKey); |             ((Encrypted) payload).encrypt(publicEncryptionKey); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void encrypt(Pubkey publicKey) throws IOException{ |     public void encrypt(Pubkey publicKey) { | ||||||
|         if (payload instanceof Encrypted){ |         try { | ||||||
|  |             if (payload instanceof Encrypted) { | ||||||
|                 ((Encrypted) payload).encrypt(publicKey.getEncryptionKey()); |                 ((Encrypted) payload).encrypt(publicKey.getEncryptionKey()); | ||||||
|             } |             } | ||||||
|  |         } catch (IOException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public boolean isSignatureValid(Pubkey pubkey) throws IOException { |     public boolean isSignatureValid(Pubkey pubkey) throws IOException { | ||||||
|   | |||||||
							
								
								
									
										352
									
								
								domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										352
									
								
								domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,352 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2015 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.bitmessage.entity; | ||||||
|  |  | ||||||
|  | import ch.dissem.bitmessage.entity.payload.Pubkey; | ||||||
|  | import ch.dissem.bitmessage.entity.valueobject.Label; | ||||||
|  | import ch.dissem.bitmessage.factory.Factory; | ||||||
|  | import ch.dissem.bitmessage.utils.Decode; | ||||||
|  | import ch.dissem.bitmessage.utils.Encode; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.io.UnsupportedEncodingException; | ||||||
|  | import java.util.*; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * The unencrypted message to be sent by 'msg' or 'broadcast'. | ||||||
|  |  */ | ||||||
|  | public class Plaintext implements Streamable { | ||||||
|  |     private final BitmessageAddress from; | ||||||
|  |     private final long encoding; | ||||||
|  |     private final byte[] message; | ||||||
|  |     private final byte[] ack; | ||||||
|  |     private Object id; | ||||||
|  |     private BitmessageAddress to; | ||||||
|  |     private byte[] signature; | ||||||
|  |     private Status status; | ||||||
|  |     private Long sent; | ||||||
|  |     private Long received; | ||||||
|  |  | ||||||
|  |     private Set<Label> labels; | ||||||
|  |  | ||||||
|  |     private Plaintext(Builder builder) { | ||||||
|  |         id = builder.id; | ||||||
|  |         from = builder.from; | ||||||
|  |         to = builder.to; | ||||||
|  |         encoding = builder.encoding; | ||||||
|  |         message = builder.message; | ||||||
|  |         ack = builder.ack; | ||||||
|  |         signature = builder.signature; | ||||||
|  |         status = builder.status; | ||||||
|  |         sent = builder.sent; | ||||||
|  |         received = builder.received; | ||||||
|  |         labels = builder.labels; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static Plaintext read(InputStream in) throws IOException { | ||||||
|  |         return readWithoutSignature(in) | ||||||
|  |                 .signature(Decode.varBytes(in)) | ||||||
|  |                 .build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static Plaintext.Builder readWithoutSignature(InputStream in) throws IOException { | ||||||
|  |         return new Builder() | ||||||
|  |                 .addressVersion(Decode.varInt(in)) | ||||||
|  |                 .stream(Decode.varInt(in)) | ||||||
|  |                 .behaviorBitfield(Decode.int32(in)) | ||||||
|  |                 .publicSigningKey(Decode.bytes(in, 64)) | ||||||
|  |                 .publicEncryptionKey(Decode.bytes(in, 64)) | ||||||
|  |                 .nonceTrialsPerByte(Decode.varInt(in)) | ||||||
|  |                 .extraBytes(Decode.varInt(in)) | ||||||
|  |                 .destinationRipe(Decode.bytes(in, 20)) | ||||||
|  |                 .encoding(Decode.varInt(in)) | ||||||
|  |                 .message(Decode.varBytes(in)) | ||||||
|  |                 .ack(Decode.varBytes(in)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public byte[] getMessage() { | ||||||
|  |         return message; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public BitmessageAddress getFrom() { | ||||||
|  |         return from; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public BitmessageAddress getTo() { | ||||||
|  |         return to; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setTo(BitmessageAddress to) { | ||||||
|  |         if (this.to.getVersion() != 0) | ||||||
|  |             throw new RuntimeException("Correct address already set"); | ||||||
|  |         if (Arrays.equals(this.to.getRipe(), to.getRipe())) { | ||||||
|  |             throw new RuntimeException("RIPEs don't match"); | ||||||
|  |         } | ||||||
|  |         this.to = to; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Set<Label> getLabels() { | ||||||
|  |         return labels; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public long getStream() { | ||||||
|  |         return from.getStream(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public byte[] getSignature() { | ||||||
|  |         return signature; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setSignature(byte[] signature) { | ||||||
|  |         this.signature = signature; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void write(OutputStream out, boolean includeSignature) throws IOException { | ||||||
|  |         Encode.varInt(from.getVersion(), out); | ||||||
|  |         Encode.varInt(from.getStream(), out); | ||||||
|  |         Encode.int32(from.getPubkey().getBehaviorBitfield(), out); | ||||||
|  |         out.write(from.getPubkey().getSigningKey()); | ||||||
|  |         out.write(from.getPubkey().getEncryptionKey()); | ||||||
|  |         Encode.varInt(from.getPubkey().getNonceTrialsPerByte(), out); | ||||||
|  |         Encode.varInt(from.getPubkey().getExtraBytes(), out); | ||||||
|  |         out.write(to.getRipe()); | ||||||
|  |         Encode.varInt(encoding, out); | ||||||
|  |         Encode.varInt(message.length, out); | ||||||
|  |         out.write(message); | ||||||
|  |         Encode.varInt(ack.length, out); | ||||||
|  |         out.write(ack); | ||||||
|  |         if (includeSignature) { | ||||||
|  |             Encode.varInt(signature.length, out); | ||||||
|  |             out.write(signature); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void write(OutputStream out) throws IOException { | ||||||
|  |         write(out, true); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Object getId() { | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setId(long id) { | ||||||
|  |         if (this.id != null) throw new IllegalStateException("ID already set"); | ||||||
|  |         this.id = id; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Long getSent() { | ||||||
|  |         return sent; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Long getReceived() { | ||||||
|  |         return received; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Status getStatus() { | ||||||
|  |         return status; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setStatus(Status status) { | ||||||
|  |         this.status = status; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public enum Encoding { | ||||||
|  |         IGNORE(0), TRIVIAL(1), SIMPLE(2); | ||||||
|  |  | ||||||
|  |         long code; | ||||||
|  |  | ||||||
|  |         Encoding(long code) { | ||||||
|  |             this.code = code; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public static Encoding fromCode(long code) { | ||||||
|  |             for (Encoding e : values()) { | ||||||
|  |                 if (e.getCode() == code) return e; | ||||||
|  |             } | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public long getCode() { | ||||||
|  |             return code; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public enum Status { | ||||||
|  |         PUBKEY_REQUESTED, | ||||||
|  |         DOING_PROOF_OF_WORK, | ||||||
|  |         SENT, | ||||||
|  |         ACKNOWLEDGED | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static final class Builder { | ||||||
|  |         private Object id; | ||||||
|  |         private BitmessageAddress from; | ||||||
|  |         private BitmessageAddress to; | ||||||
|  |         private long addressVersion; | ||||||
|  |         private long stream; | ||||||
|  |         private int behaviorBitfield; | ||||||
|  |         private byte[] publicSigningKey; | ||||||
|  |         private byte[] publicEncryptionKey; | ||||||
|  |         private long nonceTrialsPerByte; | ||||||
|  |         private long extraBytes; | ||||||
|  |         private byte[] destinationRipe; | ||||||
|  |         private long encoding; | ||||||
|  |         private byte[] message = new byte[0]; | ||||||
|  |         private byte[] ack = new byte[0]; | ||||||
|  |         private byte[] signature; | ||||||
|  |         private long sent; | ||||||
|  |         private long received; | ||||||
|  |         private Status status; | ||||||
|  |         private Set<Label> labels = new TreeSet<>(); | ||||||
|  |  | ||||||
|  |         public Builder() { | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder id(Object id) { | ||||||
|  |             this.id = id; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder from(BitmessageAddress address) { | ||||||
|  |             from = address; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder to(BitmessageAddress address) { | ||||||
|  |             to = address; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Builder addressVersion(long addressVersion) { | ||||||
|  |             this.addressVersion = addressVersion; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Builder stream(long stream) { | ||||||
|  |             this.stream = stream; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Builder behaviorBitfield(int behaviorBitfield) { | ||||||
|  |             this.behaviorBitfield = behaviorBitfield; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Builder publicSigningKey(byte[] publicSigningKey) { | ||||||
|  |             this.publicSigningKey = publicSigningKey; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Builder publicEncryptionKey(byte[] publicEncryptionKey) { | ||||||
|  |             this.publicEncryptionKey = publicEncryptionKey; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Builder nonceTrialsPerByte(long nonceTrialsPerByte) { | ||||||
|  |             this.nonceTrialsPerByte = nonceTrialsPerByte; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Builder extraBytes(long extraBytes) { | ||||||
|  |             this.extraBytes = extraBytes; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Builder destinationRipe(byte[] ripe) { | ||||||
|  |             this.destinationRipe = ripe; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder encoding(Encoding encoding) { | ||||||
|  |             this.encoding = encoding.getCode(); | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Builder encoding(long encoding) { | ||||||
|  |             this.encoding = encoding; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder message(String subject, String message) { | ||||||
|  |             try { | ||||||
|  |                 this.message = ("Subject:" + subject + '\n' + "Body:" + message).getBytes("UTF-8"); | ||||||
|  |             } catch (UnsupportedEncodingException e) { | ||||||
|  |                 throw new RuntimeException(e); | ||||||
|  |             } | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder message(byte[] message) { | ||||||
|  |             this.message = message; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder ack(byte[] ack) { | ||||||
|  |             this.ack = ack; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder signature(byte[] signature) { | ||||||
|  |             this.signature = signature; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder sent(long sent) { | ||||||
|  |             this.sent = sent; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder received(long received) { | ||||||
|  |             this.received = received; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder status(Status status) { | ||||||
|  |             this.status = status; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Builder labels(Collection<Label> labels) { | ||||||
|  |             this.labels.addAll(labels); | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Plaintext build() { | ||||||
|  |             if (id == null) { | ||||||
|  |                 id = UUID.randomUUID(); | ||||||
|  |             } | ||||||
|  |             if (from == null) { | ||||||
|  |                 from = new BitmessageAddress(Factory.createPubkey( | ||||||
|  |                         addressVersion, | ||||||
|  |                         stream, | ||||||
|  |                         publicSigningKey, | ||||||
|  |                         publicEncryptionKey, | ||||||
|  |                         nonceTrialsPerByte, | ||||||
|  |                         extraBytes, | ||||||
|  |                         Pubkey.Feature.features(behaviorBitfield) | ||||||
|  |                 )); | ||||||
|  |             } | ||||||
|  |             if (to == null) { | ||||||
|  |                 to = new BitmessageAddress(0, 0, destinationRipe); | ||||||
|  |             } | ||||||
|  |             return new Plaintext(this); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -16,10 +16,47 @@ | |||||||
|  |  | ||||||
| package ch.dissem.bitmessage.entity.payload; | package ch.dissem.bitmessage.entity.payload; | ||||||
|  |  | ||||||
|  | import ch.dissem.bitmessage.entity.Encrypted; | ||||||
|  | import ch.dissem.bitmessage.entity.Plaintext; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Users who are subscribed to the sending address will see the message appear in their inbox. |  * Users who are subscribed to the sending address will see the message appear in their inbox. | ||||||
|  * Broadcasts are version 4 or 5. |  * Broadcasts are version 4 or 5. | ||||||
|  */ |  */ | ||||||
| public abstract class Broadcast extends ObjectPayload { | public abstract class Broadcast extends ObjectPayload implements Encrypted { | ||||||
|     public abstract byte[] getEncrypted(); |     protected final long stream; | ||||||
|  |     protected CryptoBox encrypted; | ||||||
|  |     protected Plaintext plaintext; | ||||||
|  |  | ||||||
|  |     protected Broadcast(long stream, CryptoBox encrypted, Plaintext plaintext) { | ||||||
|  |         this.stream = stream; | ||||||
|  |         this.encrypted = encrypted; | ||||||
|  |         this.plaintext = plaintext; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public long getStream() { | ||||||
|  |         return stream; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Plaintext getPlaintext() { | ||||||
|  |         return plaintext; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void encrypt(byte[] publicKey) throws IOException { | ||||||
|  |         this.encrypted = new CryptoBox(plaintext, publicKey); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void decrypt(byte[] privateKey) throws IOException { | ||||||
|  |         plaintext = Plaintext.read(encrypted.decrypt(privateKey)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean isDecrypted() { | ||||||
|  |         return plaintext != null; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,9 +17,7 @@ | |||||||
| package ch.dissem.bitmessage.entity.payload; | package ch.dissem.bitmessage.entity.payload; | ||||||
|  |  | ||||||
| import ch.dissem.bitmessage.entity.BitmessageAddress; | import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||||
| import ch.dissem.bitmessage.utils.Bytes; |  | ||||||
| import ch.dissem.bitmessage.utils.Decode; | import ch.dissem.bitmessage.utils.Decode; | ||||||
| import ch.dissem.bitmessage.utils.Security; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| @@ -30,30 +28,33 @@ import java.io.OutputStream; | |||||||
|  */ |  */ | ||||||
| public class GetPubkey extends ObjectPayload { | public class GetPubkey extends ObjectPayload { | ||||||
|     private long stream; |     private long stream; | ||||||
|     private byte[] ripe; |     private byte[] ripeTag; | ||||||
|     private byte[] tag; |  | ||||||
|  |  | ||||||
|     public GetPubkey(BitmessageAddress address) { |     public GetPubkey(BitmessageAddress address) { | ||||||
|         this.stream = address.getStream(); |         this.stream = address.getStream(); | ||||||
|         if (address.getVersion() < 4) |         if (address.getVersion() < 4) | ||||||
|             this.ripe = address.getRipe(); |             this.ripeTag = address.getRipe(); | ||||||
|         else |         else | ||||||
|             this.tag = address.getTag(); |             this.ripeTag = address.getTag(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private GetPubkey(long stream, long version, byte[] ripeOrTag) { |     private GetPubkey(long stream, long version, byte[] ripeOrTag) { | ||||||
|         this.stream = stream; |         this.stream = stream; | ||||||
|         if (version < 4) { |         this.ripeTag = ripeOrTag; | ||||||
|             ripe = ripeOrTag; |  | ||||||
|         } else { |  | ||||||
|             tag = ripeOrTag; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static GetPubkey read(InputStream is, long stream, int length, long version) throws IOException { |     public static GetPubkey read(InputStream is, long stream, int length, long version) throws IOException { | ||||||
|         return new GetPubkey(stream, version, Decode.bytes(is, length)); |         return new GetPubkey(stream, version, Decode.bytes(is, length)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns an array of bytes that represent either the ripe, or the tag of an address, depending on the | ||||||
|  |      * address version. | ||||||
|  |      */ | ||||||
|  |     public byte[] getRipeTag() { | ||||||
|  |         return ripeTag; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public ObjectType getType() { |     public ObjectType getType() { | ||||||
|         return ObjectType.GET_PUBKEY; |         return ObjectType.GET_PUBKEY; | ||||||
| @@ -66,10 +67,6 @@ public class GetPubkey extends ObjectPayload { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void write(OutputStream stream) throws IOException { |     public void write(OutputStream stream) throws IOException { | ||||||
|         if (tag != null) { |         stream.write(ripeTag); | ||||||
|             stream.write(tag); |  | ||||||
|         } else { |  | ||||||
|             stream.write(ripe); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,6 +16,9 @@ | |||||||
|  |  | ||||||
| package ch.dissem.bitmessage.entity.payload; | package ch.dissem.bitmessage.entity.payload; | ||||||
|  |  | ||||||
|  | import ch.dissem.bitmessage.entity.Encrypted; | ||||||
|  | import ch.dissem.bitmessage.entity.Plaintext; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.io.OutputStream; | import java.io.OutputStream; | ||||||
| @@ -23,26 +26,29 @@ import java.io.OutputStream; | |||||||
| /** | /** | ||||||
|  * Used for person-to-person messages. |  * Used for person-to-person messages. | ||||||
|  */ |  */ | ||||||
| public class Msg extends ObjectPayload { | public class Msg extends ObjectPayload implements Encrypted { | ||||||
|     private long stream; |     private long stream; | ||||||
|     private CryptoBox encrypted; |     private CryptoBox encrypted; | ||||||
|     private UnencryptedMessage decrypted; |     private Plaintext plaintext; | ||||||
|  |  | ||||||
|     private Msg(long stream, CryptoBox encrypted) { |     private Msg(long stream, CryptoBox encrypted) { | ||||||
|         this.stream = stream; |         this.stream = stream; | ||||||
|         this.encrypted = encrypted; |         this.encrypted = encrypted; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Msg(UnencryptedMessage unencrypted, Pubkey publicKey) { |     public Msg(Plaintext plaintext) { | ||||||
|         this.stream = unencrypted.getStream(); |         this.stream = plaintext.getStream(); | ||||||
|         this.decrypted = unencrypted; |         this.plaintext = plaintext; | ||||||
|         this.encrypted = new CryptoBox(unencrypted, publicKey.getEncryptionKey()); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Msg read(InputStream in, long stream, int length) throws IOException { |     public static Msg read(InputStream in, long stream, int length) throws IOException { | ||||||
|         return new Msg(stream, CryptoBox.read(in, length)); |         return new Msg(stream, CryptoBox.read(in, length)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public Plaintext getPlaintext() { | ||||||
|  |         return plaintext; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public ObjectType getType() { |     public ObjectType getType() { | ||||||
|         return ObjectType.MSG; |         return ObjectType.MSG; | ||||||
| @@ -55,26 +61,37 @@ public class Msg extends ObjectPayload { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public boolean isSigned() { |     public boolean isSigned() { | ||||||
|         return decrypted != null; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void writeBytesToSign(OutputStream out) throws IOException { |     public void writeBytesToSign(OutputStream out) throws IOException { | ||||||
|         decrypted.write(out, false); |         plaintext.write(out, false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public byte[] getSignature() { |     public byte[] getSignature() { | ||||||
|         return decrypted.getSignature(); |         return plaintext.getSignature(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void setSignature(byte[] signature) { |     public void setSignature(byte[] signature) { | ||||||
|         decrypted.setSignature(signature); |         plaintext.setSignature(signature); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void encrypt(byte[] publicKey) throws IOException { | ||||||
|  |         this.encrypted = new CryptoBox(plaintext, publicKey); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|     public void decrypt(byte[] privateKey) throws IOException { |     public void decrypt(byte[] privateKey) throws IOException { | ||||||
|         decrypted = UnencryptedMessage.read(encrypted.decrypt(privateKey)); |         plaintext = Plaintext.read(encrypted.decrypt(privateKey)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean isDecrypted() { | ||||||
|  |         return plaintext != null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
| @@ -37,10 +37,20 @@ public abstract class Pubkey extends ObjectPayload { | |||||||
|  |  | ||||||
|     public abstract byte[] getEncryptionKey(); |     public abstract byte[] getEncryptionKey(); | ||||||
|  |  | ||||||
|  |     public abstract int getBehaviorBitfield(); | ||||||
|  |  | ||||||
|     public byte[] getRipe() { |     public byte[] getRipe() { | ||||||
|         return ripemd160(sha512(getSigningKey(), getEncryptionKey())); |         return ripemd160(sha512(getSigningKey(), getEncryptionKey())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public long getNonceTrialsPerByte() { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public long getExtraBytes() { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     protected byte[] add0x04(byte[] key) { |     protected byte[] add0x04(byte[] key) { | ||||||
|         if (key.length == 65) return key; |         if (key.length == 65) return key; | ||||||
|         byte[] result = new byte[65]; |         byte[] result = new byte[65]; | ||||||
|   | |||||||
| @@ -1,173 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright 2015 Christian Basler |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| package ch.dissem.bitmessage.entity.payload; |  | ||||||
|  |  | ||||||
| import ch.dissem.bitmessage.entity.Streamable; |  | ||||||
| import ch.dissem.bitmessage.utils.Decode; |  | ||||||
| import ch.dissem.bitmessage.utils.Encode; |  | ||||||
|  |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; |  | ||||||
| import java.io.OutputStream; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * The unencrypted message to be sent by 'msg' or 'broadcast'. |  | ||||||
|  */ |  | ||||||
| public class UnencryptedMessage implements Streamable { |  | ||||||
|     private final long addressVersion; |  | ||||||
|     private final long stream; |  | ||||||
|     private final int behaviorBitfield; |  | ||||||
|     private final byte[] publicSigningKey; |  | ||||||
|     private final byte[] publicEncryptionKey; |  | ||||||
|     private final long nonceTrialsPerByte; |  | ||||||
|     private final long extraBytes; |  | ||||||
|     private final long encoding; |  | ||||||
|     private final byte[] message; |  | ||||||
|     private byte[] signature; |  | ||||||
|  |  | ||||||
|     private UnencryptedMessage(Builder builder) { |  | ||||||
|         addressVersion = builder.addressVersion; |  | ||||||
|         stream = builder.stream; |  | ||||||
|         behaviorBitfield = builder.behaviorBitfield; |  | ||||||
|         publicSigningKey = builder.publicSigningKey; |  | ||||||
|         publicEncryptionKey = builder.publicEncryptionKey; |  | ||||||
|         nonceTrialsPerByte = builder.nonceTrialsPerByte; |  | ||||||
|         extraBytes = builder.extraBytes; |  | ||||||
|         encoding = builder.encoding; |  | ||||||
|         message = builder.message; |  | ||||||
|         signature = builder.signature; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static UnencryptedMessage read(InputStream is) throws IOException { |  | ||||||
|         return new Builder() |  | ||||||
|                 .addressVersion(Decode.varInt(is)) |  | ||||||
|                 .stream(Decode.varInt(is)) |  | ||||||
|                 .behaviorBitfield(Decode.int32(is)) |  | ||||||
|                 .publicSigningKey(Decode.bytes(is, 64)) |  | ||||||
|                 .publicEncryptionKey(Decode.bytes(is, 64)) |  | ||||||
|                 .nonceTrialsPerByte(Decode.varInt(is)) |  | ||||||
|                 .extraBytes(Decode.varInt(is)) |  | ||||||
|                 .encoding(Decode.varInt(is)) |  | ||||||
|                 .message(Decode.varBytes(is)) |  | ||||||
|                 .signature(Decode.varBytes(is)) |  | ||||||
|                 .build(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public long getStream() { |  | ||||||
|         return stream; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public byte[] getSignature() { |  | ||||||
|         return signature; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void setSignature(byte[] signature) { |  | ||||||
|         this.signature = signature; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void write(OutputStream out, boolean includeSignature) throws IOException { |  | ||||||
|         Encode.varInt(addressVersion, out); |  | ||||||
|         Encode.varInt(stream, out); |  | ||||||
|         Encode.int32(behaviorBitfield, out); |  | ||||||
|         out.write(publicSigningKey); |  | ||||||
|         out.write(publicEncryptionKey); |  | ||||||
|         Encode.varInt(nonceTrialsPerByte, out); |  | ||||||
|         Encode.varInt(extraBytes, out); |  | ||||||
|         Encode.varInt(encoding, out); |  | ||||||
|         Encode.varInt(message.length, out); |  | ||||||
|         out.write(message); |  | ||||||
|         if (includeSignature) { |  | ||||||
|             Encode.varInt(signature.length, out); |  | ||||||
|             out.write(signature); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void write(OutputStream out) throws IOException { |  | ||||||
|         write(out, true); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static final class Builder { |  | ||||||
|         private long addressVersion; |  | ||||||
|         private long stream; |  | ||||||
|         private int behaviorBitfield; |  | ||||||
|         private byte[] publicSigningKey; |  | ||||||
|         private byte[] publicEncryptionKey; |  | ||||||
|         private long nonceTrialsPerByte; |  | ||||||
|         private long extraBytes; |  | ||||||
|         private long encoding; |  | ||||||
|         private byte[] message; |  | ||||||
|         private byte[] signature; |  | ||||||
|  |  | ||||||
|         public Builder() { |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder addressVersion(long addressVersion) { |  | ||||||
|             this.addressVersion = addressVersion; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder stream(long stream) { |  | ||||||
|             this.stream = stream; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder behaviorBitfield(int behaviorBitfield) { |  | ||||||
|             this.behaviorBitfield = behaviorBitfield; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder publicSigningKey(byte[] publicSigningKey) { |  | ||||||
|             this.publicSigningKey = publicSigningKey; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder publicEncryptionKey(byte[] publicEncryptionKey) { |  | ||||||
|             this.publicEncryptionKey = publicEncryptionKey; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder nonceTrialsPerByte(long nonceTrialsPerByte) { |  | ||||||
|             this.nonceTrialsPerByte = nonceTrialsPerByte; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder extraBytes(long extraBytes) { |  | ||||||
|             this.extraBytes = extraBytes; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder encoding(long encoding) { |  | ||||||
|             this.encoding = encoding; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder message(byte[] message) { |  | ||||||
|             this.message = message; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public Builder signature(byte[] signature) { |  | ||||||
|             this.signature = signature; |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public UnencryptedMessage build() { |  | ||||||
|             return new UnencryptedMessage(this); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -76,6 +76,11 @@ public class V2Pubkey extends Pubkey { | |||||||
|         return publicEncryptionKey; |         return publicEncryptionKey; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int getBehaviorBitfield() { | ||||||
|  |         return behaviorBitfield; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void write(OutputStream os) throws IOException { |     public void write(OutputStream os) throws IOException { | ||||||
|         Encode.int32(behaviorBitfield, os); |         Encode.int32(behaviorBitfield, os); | ||||||
|   | |||||||
| @@ -18,7 +18,6 @@ package ch.dissem.bitmessage.entity.payload; | |||||||
|  |  | ||||||
| import ch.dissem.bitmessage.utils.Decode; | import ch.dissem.bitmessage.utils.Decode; | ||||||
| import ch.dissem.bitmessage.utils.Encode; | import ch.dissem.bitmessage.utils.Encode; | ||||||
| import ch.dissem.bitmessage.utils.Security; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| @@ -66,6 +65,14 @@ public class V3Pubkey extends V2Pubkey { | |||||||
|         return 3; |         return 3; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public long getNonceTrialsPerByte() { | ||||||
|  |         return nonceTrialsPerByte; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public long getExtraBytes() { | ||||||
|  |         return extraBytes; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public boolean isSigned() { |     public boolean isSigned() { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -16,8 +16,6 @@ | |||||||
|  |  | ||||||
| package ch.dissem.bitmessage.entity.payload; | package ch.dissem.bitmessage.entity.payload; | ||||||
|  |  | ||||||
| import ch.dissem.bitmessage.utils.Decode; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.io.OutputStream; | import java.io.OutputStream; | ||||||
| @@ -27,17 +25,12 @@ import java.io.OutputStream; | |||||||
|  * Broadcasts are version 4 or 5. |  * Broadcasts are version 4 or 5. | ||||||
|  */ |  */ | ||||||
| public class V4Broadcast extends Broadcast { | public class V4Broadcast extends Broadcast { | ||||||
|     private long stream; |     protected V4Broadcast(long stream, CryptoBox encrypted) { | ||||||
|     private byte[] encrypted; |         super(stream, encrypted, null); | ||||||
|     private UnencryptedMessage unencrypted; |  | ||||||
|  |  | ||||||
|     protected V4Broadcast(long stream, byte[] encrypted) { |  | ||||||
|         this.stream = stream; |  | ||||||
|         this.encrypted = encrypted; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static V4Broadcast read(InputStream is, long stream, int length) throws IOException { |     public static V4Broadcast read(InputStream in, long stream, int length) throws IOException { | ||||||
|         return new V4Broadcast(stream, Decode.bytes(is, length)); |         return new V4Broadcast(stream, CryptoBox.read(in, length)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -45,32 +38,23 @@ public class V4Broadcast extends Broadcast { | |||||||
|         return ObjectType.BROADCAST; |         return ObjectType.BROADCAST; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public long getStream() { |  | ||||||
|         return stream; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public byte[] getEncrypted() { |  | ||||||
|         return encrypted; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void writeBytesToSign(OutputStream out) throws IOException { |     public void writeBytesToSign(OutputStream out) throws IOException { | ||||||
|         unencrypted.write(out, false); |         plaintext.write(out, false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public byte[] getSignature() { |     public byte[] getSignature() { | ||||||
|         return unencrypted.getSignature(); |         return plaintext.getSignature(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void setSignature(byte[] signature) { |     public void setSignature(byte[] signature) { | ||||||
|         unencrypted.setSignature(signature); |         plaintext.setSignature(signature); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void write(OutputStream stream) throws IOException { |     public void write(OutputStream out) throws IOException { | ||||||
|         stream.write(getEncrypted()); |         encrypted.write(out); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -111,6 +111,11 @@ public class V4Pubkey extends Pubkey implements Encrypted { | |||||||
|         return decrypted.getEncryptionKey(); |         return decrypted.getEncryptionKey(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public int getBehaviorBitfield() { | ||||||
|  |         return decrypted.getBehaviorBitfield(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public byte[] getSignature() { |     public byte[] getSignature() { | ||||||
|         if (decrypted != null) |         if (decrypted != null) | ||||||
| @@ -128,4 +133,12 @@ public class V4Pubkey extends Pubkey implements Encrypted { | |||||||
|     public boolean isSigned() { |     public boolean isSigned() { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public long getNonceTrialsPerByte() { | ||||||
|  |         return decrypted.getNonceTrialsPerByte(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public long getExtraBytes() { | ||||||
|  |         return decrypted.getExtraBytes(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -28,13 +28,13 @@ import java.io.OutputStream; | |||||||
| public class V5Broadcast extends V4Broadcast { | public class V5Broadcast extends V4Broadcast { | ||||||
|     private byte[] tag; |     private byte[] tag; | ||||||
|  |  | ||||||
|     private V5Broadcast(long stream, byte[] tag, byte[] encrypted) { |     private V5Broadcast(long stream, byte[] tag, CryptoBox encrypted) { | ||||||
|         super(stream, encrypted); |         super(stream, encrypted); | ||||||
|         this.tag = tag; |         this.tag = tag; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static V5Broadcast read(InputStream is, long stream, int length) throws IOException { |     public static V5Broadcast read(InputStream is, long stream, int length) throws IOException { | ||||||
|         return new V5Broadcast(stream, Decode.bytes(is, 32), Decode.bytes(is, length - 32)); |         return new V5Broadcast(stream, Decode.bytes(is, 32), CryptoBox.read(is, length - 32)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public byte[] getTag() { |     public byte[] getTag() { | ||||||
| @@ -42,8 +42,8 @@ public class V5Broadcast extends V4Broadcast { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void write(OutputStream stream) throws IOException { |     public void write(OutputStream out) throws IOException { | ||||||
|         stream.write(tag); |         out.write(tag); | ||||||
|         super.write(stream); |         super.write(out); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,48 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2015 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.bitmessage.entity.valueobject; | ||||||
|  |  | ||||||
|  | public class Label { | ||||||
|  |     private Object id; | ||||||
|  |     private String label; | ||||||
|  |     private int color; | ||||||
|  |  | ||||||
|  |     public Label(String label, int color) { | ||||||
|  |         this.label = label; | ||||||
|  |         this.color = color; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * RGBA representation for the color. | ||||||
|  |      */ | ||||||
|  |     public int getColor() { | ||||||
|  |         return color; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setColor(int color) { | ||||||
|  |         this.color = color; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return label; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Object getId() { | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -20,21 +20,25 @@ import ch.dissem.bitmessage.entity.BitmessageAddress; | |||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Created by chris on 23.04.15. |  | ||||||
|  */ |  | ||||||
| public interface AddressRepository { | public interface AddressRepository { | ||||||
|  |     BitmessageAddress findContact(byte[] ripeOrTag); | ||||||
|  |  | ||||||
|  |     BitmessageAddress findIdentity(byte[] ripeOrTag); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns all Bitmessage addresses that belong to this user, i.e. have a private key. |      * Returns all Bitmessage addresses that belong to this user, i.e. have a private key. | ||||||
|      */ |      */ | ||||||
|     List<BitmessageAddress> findIdentities(); |     List<BitmessageAddress> getIdentities(); | ||||||
|  |  | ||||||
|  |     List<BitmessageAddress> getSubscriptions(); | ||||||
|     /** |     /** | ||||||
|      * Returns all Bitmessage addresses that have no private key. |      * Returns all Bitmessage addresses that have no private key. | ||||||
|      */ |      */ | ||||||
|     List<BitmessageAddress> findContacts(); |     List<BitmessageAddress> getContacts(); | ||||||
|  |  | ||||||
|     void save(BitmessageAddress address); |     void save(BitmessageAddress address); | ||||||
|  |  | ||||||
|     void remove(BitmessageAddress address); |     void remove(BitmessageAddress address); | ||||||
|  |  | ||||||
|  |     BitmessageAddress getAddress(String address); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,38 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2015 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.bitmessage.ports; | ||||||
|  |  | ||||||
|  | import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||||
|  | import ch.dissem.bitmessage.entity.Plaintext; | ||||||
|  | import ch.dissem.bitmessage.entity.Plaintext.Status; | ||||||
|  | import ch.dissem.bitmessage.entity.valueobject.Label; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public interface MessageRepository { | ||||||
|  |     List<String> getLabels(); | ||||||
|  |  | ||||||
|  |     List<Plaintext> findMessages(Label label); | ||||||
|  |  | ||||||
|  |     List<Plaintext> findMessages(Status status); | ||||||
|  |  | ||||||
|  |     List<Plaintext> findMessages(Status status, BitmessageAddress recipient); | ||||||
|  |  | ||||||
|  |     void save(Plaintext message); | ||||||
|  |  | ||||||
|  |     void remove(Plaintext message); | ||||||
|  | } | ||||||
| @@ -28,7 +28,7 @@ import java.util.List; | |||||||
| import static ch.dissem.bitmessage.utils.Bytes.inc; | import static ch.dissem.bitmessage.utils.Bytes.inc; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Created by chris on 14.04.15. |  * A POW engine using all available CPU cores. | ||||||
|  */ |  */ | ||||||
| public class MultiThreadedPOWEngine implements ProofOfWorkEngine { | public class MultiThreadedPOWEngine implements ProofOfWorkEngine { | ||||||
|     private static Logger LOG = LoggerFactory.getLogger(MultiThreadedPOWEngine.class); |     private static Logger LOG = LoggerFactory.getLogger(MultiThreadedPOWEngine.class); | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ | |||||||
| package ch.dissem.bitmessage.ports; | package ch.dissem.bitmessage.ports; | ||||||
|  |  | ||||||
| import ch.dissem.bitmessage.BitmessageContext; | import ch.dissem.bitmessage.BitmessageContext; | ||||||
|  | import ch.dissem.bitmessage.entity.ObjectMessage; | ||||||
| import ch.dissem.bitmessage.entity.payload.ObjectPayload; | import ch.dissem.bitmessage.entity.payload.ObjectPayload; | ||||||
| import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | ||||||
|  |  | ||||||
| @@ -31,6 +32,6 @@ public interface NetworkHandler { | |||||||
|     void offer(InventoryVector iv); |     void offer(InventoryVector iv); | ||||||
|  |  | ||||||
|     interface MessageListener { |     interface MessageListener { | ||||||
|         void receive(ObjectPayload payload); |         void receive(ObjectMessage object); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -89,7 +89,8 @@ public class Security { | |||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, long extraBytes) throws IOException { |     public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, long extraBytes) { | ||||||
|  |         try { | ||||||
|             if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; |             if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; | ||||||
|             if (extraBytes < 1000) extraBytes = 1000; |             if (extraBytes < 1000) extraBytes = 1000; | ||||||
|  |  | ||||||
| @@ -99,6 +100,9 @@ public class Security { | |||||||
|  |  | ||||||
|             byte[] nonce = worker.calculateNonce(initialHash, target); |             byte[] nonce = worker.calculateNonce(initialHash, target); | ||||||
|             object.setNonce(nonce); |             object.setNonce(nonce); | ||||||
|  |         } catch (IOException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -20,6 +20,8 @@ package ch.dissem.bitmessage.utils; | |||||||
|  * Created by chris on 18.04.15. |  * Created by chris on 18.04.15. | ||||||
|  */ |  */ | ||||||
| public class UnixTime { | public class UnixTime { | ||||||
|  |     public static final long DAY = 60 * 60 * 24; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns the time in second based Unix time ({@link System#currentTimeMillis()}/1000) |      * Returns the time in second based Unix time ({@link System#currentTimeMillis()}/1000) | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -1,9 +0,0 @@ | |||||||
| CREATE TABLE Node ( |  | ||||||
|   ip       BINARY(16) NOT NULL, |  | ||||||
|   port     INT        NOT NULL, |  | ||||||
|   stream   BIGINT     NOT NULL, |  | ||||||
|   services BIGINT     NOT NULL, |  | ||||||
|   time     BIGINT     NOT NULL, |  | ||||||
|  |  | ||||||
|   PRIMARY KEY (ip, port, stream) |  | ||||||
| ); |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| CREATE TABLE Inventory ( |  | ||||||
|   hash    BINARY(32) NOT NULL PRIMARY KEY, |  | ||||||
|   stream  BIGINT     NOT NULL, |  | ||||||
|   expires BIGINT     NOT NULL, |  | ||||||
|   data    BLOB       NOT NULL, |  | ||||||
|   type    BIGINT     NOT NULL, |  | ||||||
|   version BIGINT     NOT NULL |  | ||||||
| ); |  | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| CREATE TABLE Address ( |  | ||||||
|   address                VARCHAR(40)   NOT NULL PRIMARY KEY, |  | ||||||
|   alias                  VARCHAR(255), |  | ||||||
|   public_key             BLOB, |  | ||||||
|   private_key            BLOB |  | ||||||
| ); |  | ||||||
| @@ -17,6 +17,7 @@ | |||||||
| package ch.dissem.bitmessage.networking; | package ch.dissem.bitmessage.networking; | ||||||
|  |  | ||||||
| import ch.dissem.bitmessage.BitmessageContext; | import ch.dissem.bitmessage.BitmessageContext; | ||||||
|  | import ch.dissem.bitmessage.InternalContext; | ||||||
| import ch.dissem.bitmessage.entity.*; | import ch.dissem.bitmessage.entity.*; | ||||||
| import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | ||||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||||
| @@ -43,7 +44,7 @@ import static ch.dissem.bitmessage.networking.Connection.State.*; | |||||||
| public class Connection implements Runnable { | public class Connection implements Runnable { | ||||||
|     private final static Logger LOG = LoggerFactory.getLogger(Connection.class); |     private final static Logger LOG = LoggerFactory.getLogger(Connection.class); | ||||||
|  |  | ||||||
|     private BitmessageContext ctx; |     private InternalContext ctx; | ||||||
|  |  | ||||||
|     private State state; |     private State state; | ||||||
|     private Socket socket; |     private Socket socket; | ||||||
| @@ -59,7 +60,7 @@ public class Connection implements Runnable { | |||||||
|  |  | ||||||
|     private Queue<MessagePayload> sendingQueue = new ConcurrentLinkedDeque<>(); |     private Queue<MessagePayload> sendingQueue = new ConcurrentLinkedDeque<>(); | ||||||
|  |  | ||||||
|     public Connection(BitmessageContext context, State state, Socket socket, MessageListener listener) throws IOException { |     public Connection(InternalContext context, State state, Socket socket, MessageListener listener) throws IOException { | ||||||
|         this.ctx = context; |         this.ctx = context; | ||||||
|         this.state = state; |         this.state = state; | ||||||
|         this.socket = socket; |         this.socket = socket; | ||||||
| @@ -161,12 +162,12 @@ public class Connection implements Runnable { | |||||||
|                     LOG.debug(e.getMessage(), e); |                     LOG.debug(e.getMessage(), e); | ||||||
|                 } |                 } | ||||||
|                 // It's probably pointless, but let the listener decide if we accept the message for the client. |                 // It's probably pointless, but let the listener decide if we accept the message for the client. | ||||||
|                 listener.receive(objectMessage.getPayload()); |                 listener.receive(objectMessage); | ||||||
|                 break; |                 break; | ||||||
|             case ADDR: |             case ADDR: | ||||||
|                 Addr addr = (Addr) messagePayload; |                 Addr addr = (Addr) messagePayload; | ||||||
|                 LOG.debug("Received " + addr.getAddresses().size() + " addresses."); |                 LOG.debug("Received " + addr.getAddresses().size() + " addresses."); | ||||||
|                 ctx.getAddressRepository().offerAddresses(addr.getAddresses()); |                 ctx.getNodeRegistry().offerAddresses(addr.getAddresses()); | ||||||
|                 break; |                 break; | ||||||
|             case VERACK: |             case VERACK: | ||||||
|             case VERSION: |             case VERSION: | ||||||
| @@ -175,7 +176,7 @@ public class Connection implements Runnable { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void sendAddresses() { |     private void sendAddresses() { | ||||||
|         List<NetworkAddress> addresses = ctx.getAddressRepository().getKnownAddresses(1000, streams); |         List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses(1000, streams); | ||||||
|         sendingQueue.offer(new Addr.Builder().addresses(addresses).build()); |         sendingQueue.offer(new Addr.Builder().addresses(addresses).build()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,8 +16,8 @@ | |||||||
|  |  | ||||||
| package ch.dissem.bitmessage.networking; | package ch.dissem.bitmessage.networking; | ||||||
|  |  | ||||||
| import ch.dissem.bitmessage.BitmessageContext; | import ch.dissem.bitmessage.InternalContext; | ||||||
| import ch.dissem.bitmessage.BitmessageContext.ContextHolder; | import ch.dissem.bitmessage.InternalContext.ContextHolder; | ||||||
| import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | ||||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||||
| import ch.dissem.bitmessage.ports.NetworkHandler; | import ch.dissem.bitmessage.ports.NetworkHandler; | ||||||
| @@ -42,7 +42,7 @@ public class NetworkNode implements NetworkHandler, ContextHolder { | |||||||
|     private final static Logger LOG = LoggerFactory.getLogger(NetworkNode.class); |     private final static Logger LOG = LoggerFactory.getLogger(NetworkNode.class); | ||||||
|     private final ExecutorService pool; |     private final ExecutorService pool; | ||||||
|     private final List<Connection> connections = new LinkedList<>(); |     private final List<Connection> connections = new LinkedList<>(); | ||||||
|     private BitmessageContext ctx; |     private InternalContext ctx; | ||||||
|     private ServerSocket serverSocket; |     private ServerSocket serverSocket; | ||||||
|     private Thread connectionManager; |     private Thread connectionManager; | ||||||
|  |  | ||||||
| @@ -51,7 +51,7 @@ public class NetworkNode implements NetworkHandler, ContextHolder { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void setContext(BitmessageContext context) { |     public void setContext(InternalContext context) { | ||||||
|         this.ctx = context; |         this.ctx = context; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -88,7 +88,7 @@ public class NetworkNode implements NetworkHandler, ContextHolder { | |||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         if (connections.size() < 1) { |                         if (connections.size() < 1) { | ||||||
|                             List<NetworkAddress> addresses = ctx.getAddressRepository().getKnownAddresses(8, ctx.getStreams()); |                             List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses(8, ctx.getStreams()); | ||||||
|                             for (NetworkAddress address : addresses) { |                             for (NetworkAddress address : addresses) { | ||||||
|                                 try { |                                 try { | ||||||
|                                     startConnection(new Connection(ctx, CLIENT, new Socket(address.toInetAddress(), address.getPort()), listener)); |                                     startConnection(new Connection(ctx, CLIENT, new Socket(address.toInetAddress(), address.getPort()), listener)); | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ | |||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package ch.dissem.bitmessage.inventory; | package ch.dissem.bitmessage.repository; | ||||||
| 
 | 
 | ||||||
| import ch.dissem.bitmessage.entity.BitmessageAddress; | import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||||
| import ch.dissem.bitmessage.entity.payload.Pubkey; | import ch.dissem.bitmessage.entity.payload.Pubkey; | ||||||
| @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; | |||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.sql.*; | import java.sql.*; | ||||||
|  | import java.util.Arrays; | ||||||
| import java.util.LinkedList; | import java.util.LinkedList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -36,12 +37,41 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito | |||||||
|     private static final Logger LOG = LoggerFactory.getLogger(JdbcAddressRepository.class); |     private static final Logger LOG = LoggerFactory.getLogger(JdbcAddressRepository.class); | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public List<BitmessageAddress> findIdentities() { |     public BitmessageAddress findContact(byte[] ripeOrTag) { | ||||||
|  |         for (BitmessageAddress address : find("public_key is null")) { | ||||||
|  |             if (address.getVersion() > 3) { | ||||||
|  |                 if (Arrays.equals(ripeOrTag, address.getTag())) return address; | ||||||
|  |             } else { | ||||||
|  |                 if (Arrays.equals(ripeOrTag, address.getRipe())) return address; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public BitmessageAddress findIdentity(byte[] ripeOrTag) { | ||||||
|  |         for (BitmessageAddress address : find("private_key is not null")) { | ||||||
|  |             if (address.getVersion() > 3) { | ||||||
|  |                 if (Arrays.equals(ripeOrTag, address.getTag())) return address; | ||||||
|  |             } else { | ||||||
|  |                 if (Arrays.equals(ripeOrTag, address.getRipe())) return address; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public List<BitmessageAddress> getIdentities() { | ||||||
|         return find("private_signing_key IS NOT NULL"); |         return find("private_signing_key IS NOT NULL"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public List<BitmessageAddress> findContacts() { |     public List<BitmessageAddress> getSubscriptions() { | ||||||
|  |         return find("subscribed = '1'"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public List<BitmessageAddress> getContacts() { | ||||||
|         return find("private_signing_key IS NULL"); |         return find("private_signing_key IS NULL"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -49,7 +79,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito | |||||||
|         List<BitmessageAddress> result = new LinkedList<>(); |         List<BitmessageAddress> result = new LinkedList<>(); | ||||||
|         try { |         try { | ||||||
|             Statement stmt = getConnection().createStatement(); |             Statement stmt = getConnection().createStatement(); | ||||||
|             ResultSet rs = stmt.executeQuery("SELECT address, alias, public_key, private_key FROM Address WHERE " + where); |             ResultSet rs = stmt.executeQuery("SELECT address, alias, public_key, private_key, subscribed FROM Address WHERE " + where); | ||||||
|             while (rs.next()) { |             while (rs.next()) { | ||||||
|                 BitmessageAddress address; |                 BitmessageAddress address; | ||||||
|                 Blob privateKeyBlob = rs.getBlob("private_key"); |                 Blob privateKeyBlob = rs.getBlob("private_key"); | ||||||
| @@ -61,11 +91,12 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito | |||||||
|                     Blob publicKeyBlob = rs.getBlob("public_key"); |                     Blob publicKeyBlob = rs.getBlob("public_key"); | ||||||
|                     if (publicKeyBlob != null) { |                     if (publicKeyBlob != null) { | ||||||
|                         Pubkey pubkey = Factory.readPubkey(address.getVersion(), address.getStream(), |                         Pubkey pubkey = Factory.readPubkey(address.getVersion(), address.getStream(), | ||||||
|                                 publicKeyBlob.getBinaryStream(), (int)publicKeyBlob.length()); |                                 publicKeyBlob.getBinaryStream(), (int) publicKeyBlob.length()); | ||||||
|                         address.setPubkey(pubkey); |                         address.setPubkey(pubkey); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 address.setAlias(rs.getString("alias")); |                 address.setAlias(rs.getString("alias")); | ||||||
|  |                 address.setSubscribed(rs.getBoolean("subscribed")); | ||||||
| 
 | 
 | ||||||
|                 result.add(address); |                 result.add(address); | ||||||
|             } |             } | ||||||
| @@ -75,18 +106,49 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito | |||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private boolean exists(BitmessageAddress address) { | ||||||
|  |         try { | ||||||
|  |             Statement stmt = getConnection().createStatement(); | ||||||
|  |             ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM Address WHERE address='" + address.getAddress() + "'"); | ||||||
|  |             rs.next(); | ||||||
|  |             return rs.getInt(0) > 0; | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             LOG.error(e.getMessage(), e); | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void save(BitmessageAddress address) { |     public void save(BitmessageAddress address) { | ||||||
|         try { |         try { | ||||||
|  |             if (exists(address)) { | ||||||
|  |                 update(address); | ||||||
|  |             } else { | ||||||
|  |                 insert(address); | ||||||
|  |             } | ||||||
|  |         } catch (IOException | SQLException e) { | ||||||
|  |             LOG.error(e.getMessage(), e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void update(BitmessageAddress address) throws IOException, SQLException { | ||||||
|  |         PreparedStatement ps = getConnection().prepareStatement( | ||||||
|  |                 "UPDATE Address SET address=?, alias=?, public_key=?, private_key=?"); | ||||||
|  |         ps.setString(1, address.getAddress()); | ||||||
|  |         ps.setString(2, address.getAlias()); | ||||||
|  |         writeBlob(ps, 3, address.getPubkey()); | ||||||
|  |         writeBlob(ps, 4, address.getPrivateKey()); | ||||||
|  |         ps.executeUpdate(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void insert(BitmessageAddress address) throws IOException, SQLException { | ||||||
|         PreparedStatement ps = getConnection().prepareStatement( |         PreparedStatement ps = getConnection().prepareStatement( | ||||||
|                 "INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?, ?)"); |                 "INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?, ?)"); | ||||||
|         ps.setString(1, address.getAddress()); |         ps.setString(1, address.getAddress()); | ||||||
|         ps.setString(2, address.getAlias()); |         ps.setString(2, address.getAlias()); | ||||||
|         writeBlob(ps, 3, address.getPubkey()); |         writeBlob(ps, 3, address.getPubkey()); | ||||||
|         writeBlob(ps, 4, address.getPrivateKey()); |         writeBlob(ps, 4, address.getPrivateKey()); | ||||||
|         } catch (IOException | SQLException e) { |         ps.executeUpdate(); | ||||||
|             LOG.error(e.getMessage(), e); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
| @@ -98,4 +160,11 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito | |||||||
|             LOG.error(e.getMessage(), e); |             LOG.error(e.getMessage(), e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public BitmessageAddress getAddress(String address) { | ||||||
|  |         List<BitmessageAddress> result = find("address = '" + address + "'"); | ||||||
|  |         if (result.size() > 0) return result.get(0); | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
| } | } | ||||||
| @@ -14,7 +14,7 @@ | |||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package ch.dissem.bitmessage.inventory; | package ch.dissem.bitmessage.repository; | ||||||
| 
 | 
 | ||||||
| import ch.dissem.bitmessage.entity.Streamable; | import ch.dissem.bitmessage.entity.Streamable; | ||||||
| import org.flywaydb.core.Flyway; | import org.flywaydb.core.Flyway; | ||||||
| @@ -14,7 +14,7 @@ | |||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package ch.dissem.bitmessage.inventory; | package ch.dissem.bitmessage.repository; | ||||||
| 
 | 
 | ||||||
| import ch.dissem.bitmessage.entity.ObjectMessage; | import ch.dissem.bitmessage.entity.ObjectMessage; | ||||||
| import ch.dissem.bitmessage.entity.payload.ObjectType; | import ch.dissem.bitmessage.entity.payload.ObjectType; | ||||||
| @@ -0,0 +1,192 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2015 Christian Basler | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package ch.dissem.bitmessage.repository; | ||||||
|  |  | ||||||
|  | import ch.dissem.bitmessage.InternalContext; | ||||||
|  | import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||||
|  | import ch.dissem.bitmessage.entity.Plaintext; | ||||||
|  | import ch.dissem.bitmessage.entity.valueobject.Label; | ||||||
|  | import ch.dissem.bitmessage.ports.MessageRepository; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.sql.*; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.Collection; | ||||||
|  | import java.util.LinkedList; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class JdbcMessageRepository extends JdbcHelper implements MessageRepository, InternalContext.ContextHolder { | ||||||
|  |     private static final Logger LOG = LoggerFactory.getLogger(JdbcMessageRepository.class); | ||||||
|  |  | ||||||
|  |     private InternalContext ctx; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<String> getLabels() { | ||||||
|  |         List<String> result = new LinkedList<>(); | ||||||
|  |         try { | ||||||
|  |             Statement stmt = getConnection().createStatement(); | ||||||
|  |             ResultSet rs = stmt.executeQuery("SELECT label FROM Label ORDER BY order"); | ||||||
|  |             while (rs.next()) { | ||||||
|  |                 result.add(rs.getString("label")); | ||||||
|  |             } | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             LOG.error(e.getMessage(), e); | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<Plaintext> findMessages(Label label) { | ||||||
|  |         return find("id IN SELECT message_id FROM Message_Label WHERE label_id=" + label.getId()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<Plaintext> findMessages(Plaintext.Status status, BitmessageAddress recipient) { | ||||||
|  |         return find("status='" + status.name() + "' AND to='" + recipient.getAddress() + "'"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<Plaintext> findMessages(Plaintext.Status status) { | ||||||
|  |         return find("status='" + status.name() + "'"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private List<Plaintext> find(String where) { | ||||||
|  |         List<Plaintext> result = new LinkedList<>(); | ||||||
|  |         try { | ||||||
|  |             Statement stmt = getConnection().createStatement(); | ||||||
|  |             ResultSet rs = stmt.executeQuery("SELECT \"id\", \"from\", \"to\", \"data\", \"sent\", \"received\", \"status\" FROM Message WHERE " + where); | ||||||
|  |             while (rs.next()) { | ||||||
|  |                 Blob data = rs.getBlob("data"); | ||||||
|  |                 Plaintext.Builder builder = Plaintext.readWithoutSignature(data.getBinaryStream()); | ||||||
|  |                 long id = rs.getLong("id"); | ||||||
|  |                 builder.id(id); | ||||||
|  |                 builder.from(ctx.getAddressRepo().getAddress(rs.getString("from"))); | ||||||
|  |                 builder.to(ctx.getAddressRepo().getAddress(rs.getString("to"))); | ||||||
|  |                 builder.sent(rs.getLong("sent")); | ||||||
|  |                 builder.received(rs.getLong("received")); | ||||||
|  |                 builder.status(Plaintext.Status.valueOf(rs.getString("status"))); | ||||||
|  |                 builder.labels(findLabels(id)); | ||||||
|  |                 result.add(builder.build()); | ||||||
|  |             } | ||||||
|  |         } catch (IOException | SQLException e) { | ||||||
|  |             LOG.error(e.getMessage(), e); | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private Collection<Label> findLabels(long messageId) { | ||||||
|  |         List<Label> result = new ArrayList<>(); | ||||||
|  |         try { | ||||||
|  |             Statement stmt = getConnection().createStatement(); | ||||||
|  |             ResultSet rs = stmt.executeQuery("SELECT \"label\", \"color\" FROM Label WHERE id IN SELECT label_id FROM Message_Label WHERE message_id=" + messageId); | ||||||
|  |             while (rs.next()) { | ||||||
|  |                 result.add(new Label(rs.getString("label"), rs.getInt("color"))); | ||||||
|  |             } | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             LOG.error(e.getMessage(), e); | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void save(Plaintext message) { | ||||||
|  |         // save from address if necessary | ||||||
|  |         if (message.getId() == null) { | ||||||
|  |             BitmessageAddress savedAddress = ctx.getAddressRepo().getAddress(message.getFrom().getAddress()); | ||||||
|  |             if (savedAddress.getPrivateKey() == null) { | ||||||
|  |                 if (savedAddress.getAlias() != null) { | ||||||
|  |                     message.getFrom().setAlias(savedAddress.getAlias()); | ||||||
|  |                 } | ||||||
|  |                 ctx.getAddressRepo().save(message.getFrom()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Connection connection = getConnection(); | ||||||
|  |         try { | ||||||
|  |             connection.setAutoCommit(false); | ||||||
|  |             // save message | ||||||
|  |             if (message.getId() == null) { | ||||||
|  |                 insert(connection, message); | ||||||
|  |  | ||||||
|  |                 // remove existing labels | ||||||
|  |                 Statement stmt = connection.createStatement(); | ||||||
|  |                 stmt.executeUpdate("DELETE FROM Message_Label WHERE message_id=" + message.getId()); | ||||||
|  |             } else { | ||||||
|  |                 update(connection, message); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // save labels | ||||||
|  |             PreparedStatement ps = connection.prepareStatement("INSERT INTO Message_Label VALUES (" + message.getId() + ", ?)"); | ||||||
|  |             for (Label label : message.getLabels()) { | ||||||
|  |                 ps.setLong(1, (Long) label.getId()); | ||||||
|  |                 ps.executeUpdate(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             connection.commit(); | ||||||
|  |         } catch (IOException | SQLException e) { | ||||||
|  |             try { | ||||||
|  |                 connection.rollback(); | ||||||
|  |             } catch (SQLException e1) { | ||||||
|  |                 LOG.debug(e1.getMessage(), e); | ||||||
|  |             } | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void insert(Connection connection, Plaintext message) throws SQLException, IOException { | ||||||
|  |         PreparedStatement ps = connection.prepareStatement( | ||||||
|  |                 "INSERT INTO Message (\"from\", \"to\", \"data\", \"sent\", \"received\", \"status\") VALUES (?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); | ||||||
|  |         ps.setString(1, message.getFrom().getAddress()); | ||||||
|  |         ps.setString(2, message.getTo().getAddress()); | ||||||
|  |         writeBlob(ps, 3, message); | ||||||
|  |         ps.setLong(4, message.getSent()); | ||||||
|  |         ps.setLong(5, message.getReceived()); | ||||||
|  |         ps.setString(6, message.getStatus().name()); | ||||||
|  |         ps.executeUpdate(); | ||||||
|  |  | ||||||
|  |         // get generated id | ||||||
|  |         ResultSet rs = ps.getGeneratedKeys(); | ||||||
|  |         rs.next(); | ||||||
|  |         message.setId(rs.getLong(1)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void update(Connection connection, Plaintext message) throws SQLException, IOException { | ||||||
|  |         PreparedStatement ps = connection.prepareStatement( | ||||||
|  |                 "UPDATE Message SET \"sent\"=?, \"received\"=?, \"status\"=?"); | ||||||
|  |         ps.setLong(1, message.getSent()); | ||||||
|  |         ps.setLong(2, message.getReceived()); | ||||||
|  |         ps.setString(3, message.getStatus().name()); | ||||||
|  |         ps.executeUpdate(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void remove(Plaintext message) { | ||||||
|  |         try { | ||||||
|  |             Statement stmt = getConnection().createStatement(); | ||||||
|  |             stmt.executeUpdate("DELETE FROM Message WHERE id = " + message.getId()); | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             LOG.error(e.getMessage(), e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void setContext(InternalContext context) { | ||||||
|  |         this.ctx = context; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -14,7 +14,7 @@ | |||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package ch.dissem.bitmessage.inventory; | package ch.dissem.bitmessage.repository; | ||||||
| 
 | 
 | ||||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||||
| import ch.dissem.bitmessage.ports.NodeRegistry; | import ch.dissem.bitmessage.ports.NodeRegistry; | ||||||
| @@ -14,7 +14,7 @@ | |||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package ch.dissem.bitmessage.inventory; | package ch.dissem.bitmessage.repository; | ||||||
| 
 | 
 | ||||||
| import ch.dissem.bitmessage.entity.ObjectMessage; | import ch.dissem.bitmessage.entity.ObjectMessage; | ||||||
| import ch.dissem.bitmessage.entity.payload.ObjectType; | import ch.dissem.bitmessage.entity.payload.ObjectType; | ||||||
| @@ -14,7 +14,7 @@ | |||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package ch.dissem.bitmessage.inventory; | package ch.dissem.bitmessage.repository; | ||||||
| 
 | 
 | ||||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||||
| import ch.dissem.bitmessage.ports.NodeRegistry; | import ch.dissem.bitmessage.ports.NodeRegistry; | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | CREATE TABLE Node ( | ||||||
|  |   "ip"       BINARY(16) NOT NULL, | ||||||
|  |   "port"     INT        NOT NULL, | ||||||
|  |   "stream"   BIGINT     NOT NULL, | ||||||
|  |   "services" BIGINT     NOT NULL, | ||||||
|  |   "time"     BIGINT     NOT NULL, | ||||||
|  |  | ||||||
|  |   PRIMARY KEY ("ip", "port", "stream") | ||||||
|  | ); | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | CREATE TABLE Inventory ( | ||||||
|  |   "hash"    BINARY(32) NOT NULL PRIMARY KEY, | ||||||
|  |   "stream"  BIGINT     NOT NULL, | ||||||
|  |   "expires" BIGINT     NOT NULL, | ||||||
|  |   "data"    BLOB       NOT NULL, | ||||||
|  |   "type"    BIGINT     NOT NULL, | ||||||
|  |   "version" BIGINT     NOT NULL | ||||||
|  | ); | ||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | CREATE TABLE Address ( | ||||||
|  |   "address"                VARCHAR(40)   NOT NULL PRIMARY KEY, | ||||||
|  |   "alias"                  VARCHAR(255), | ||||||
|  |   "public_key"             BLOB, | ||||||
|  |   "private_key"            BLOB, | ||||||
|  |   "subscribed"             BIT DEFAULT '0' | ||||||
|  | ); | ||||||
| @@ -0,0 +1,32 @@ | |||||||
|  | CREATE TABLE Message ( | ||||||
|  |   "id"                     BIGINT        AUTO_INCREMENT PRIMARY KEY, | ||||||
|  |   "from"                   VARCHAR(40)   NOT NULL, | ||||||
|  |   "to"                     VARCHAR(40)   NOT NULL, | ||||||
|  |   "data"                   BLOB          NOT NULL, | ||||||
|  |   "sent"                   BIGINT, | ||||||
|  |   "received"               BIGINT, | ||||||
|  |   "status"                 VARCHAR(20)   NOT NULL | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | CREATE TABLE Label ( | ||||||
|  |   "id"    BIGINT AUTO_INCREMENT PRIMARY KEY, | ||||||
|  |   "label" VARCHAR(255) NOT NULL, | ||||||
|  |   "color" INT, | ||||||
|  |   "order" BIGINT, | ||||||
|  |   CONSTRAINT UC_label UNIQUE ("label"), | ||||||
|  |   CONSTRAINT UC_order UNIQUE ("order") | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | CREATE TABLE Message_Label ( | ||||||
|  |   "message_id" BIGINT NOT NULL, | ||||||
|  |   "label_id"   BIGINT NOT NULL, | ||||||
|  |  | ||||||
|  |   PRIMARY KEY ("message_id", "label_id"), | ||||||
|  |   FOREIGN KEY ("message_id") REFERENCES Message ("id"), | ||||||
|  |   FOREIGN KEY ("label_id") REFERENCES Label ("id") | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO Label("label", "order") VALUES ('Inbox', 0); | ||||||
|  | INSERT INTO Label("label", "order") VALUES ('Sent', 10); | ||||||
|  | INSERT INTO Label("label", "order") VALUES ('Drafts', 20); | ||||||
|  | INSERT INTO Label("label", "order") VALUES ('Trash', 100); | ||||||
| @@ -4,7 +4,7 @@ include 'domain' | |||||||
|  |  | ||||||
| include 'networking' | include 'networking' | ||||||
|  |  | ||||||
| include 'inventory' | include 'repositories' | ||||||
|  |  | ||||||
| include 'demo' | include 'demo' | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user