/** * Copyright (C) 2014-2017 Regents of the University of California. * @author: Jeff Thompson <jefft0@remap.ucla.edu> * Derived from ChronoChat-js by Qiuhan Ding and Wentao Shang. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * A copy of the GNU Lesser General Public License is in the file COPYING. */ package net.named_data.jndn.tests; import com.google.protobuf.InvalidProtocolBufferException; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; import net.named_data.jndn.Data; import net.named_data.jndn.Face; import net.named_data.jndn.Interest; import net.named_data.jndn.InterestFilter; import net.named_data.jndn.Name; import net.named_data.jndn.OnData; import net.named_data.jndn.OnInterestCallback; import net.named_data.jndn.OnRegisterFailed; import net.named_data.jndn.OnTimeout; import net.named_data.jndn.security.KeyChain; import net.named_data.jndn.security.KeyType; import net.named_data.jndn.security.SecurityException; import net.named_data.jndn.security.identity.IdentityManager; import net.named_data.jndn.security.identity.MemoryIdentityStorage; import net.named_data.jndn.security.identity.MemoryPrivateKeyStorage; import net.named_data.jndn.security.policy.NoVerifyPolicyManager; import net.named_data.jndn.sync.ChronoSync2013; import net.named_data.jndn.tests.ChatbufProto.ChatMessage; import net.named_data.jndn.util.Blob; // Define the Chat class here so that the ChronoChat demo is self-contained. class Chat implements ChronoSync2013.OnInitialized, ChronoSync2013.OnReceivedSyncState, OnData, OnInterestCallback { public Chat (String screenName, String chatRoom, Name hubPrefix, Face face, KeyChain keyChain, Name certificateName) { screenName_ = screenName; chatRoom_ = chatRoom; face_ = face; keyChain_ = keyChain; certificateName_ = certificateName; heartbeat_ = this.new Heartbeat(); // This should only be called once, so get the random string here. chatPrefix_ = new Name(hubPrefix).append(chatRoom_).append(getRandomString()); int session = (int)Math.round(getNowMilliseconds() / 1000.0); userName_ = screenName_ + session; try { sync_ = new ChronoSync2013 (this, this, chatPrefix_, new Name("/ndn/broadcast/ChronoChat-0.3").append(chatRoom_), session, face, keyChain, certificateName, syncLifetime_, RegisterFailed.onRegisterFailed_); } catch (IOException | SecurityException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); return; } try { face.registerPrefix(chatPrefix_, this, RegisterFailed.onRegisterFailed_); } catch (IOException | SecurityException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); } } // Send a chat message. public final void sendMessage(String chatMessage) throws IOException, SecurityException { if (messageCache_.size() == 0) messageCacheAppend(ChatMessage.ChatMessageType.JOIN, "xxx"); // Ignore an empty message. // forming Sync Data Packet. if (!chatMessage.equals("")) { sync_.publishNextSequenceNo(); messageCacheAppend(ChatMessage.ChatMessageType.CHAT, chatMessage); System.out.println(screenName_ + ": " + chatMessage); } } // Send leave message and leave. public final void leave() throws IOException, SecurityException { sync_.publishNextSequenceNo(); messageCacheAppend(ChatMessage.ChatMessageType.LEAVE, "xxx"); } /** * Get the current time in milliseconds. * @return The current time in milliseconds since 1/1/1970, including * fractions of a millisecond. */ public static double getNowMilliseconds() { return (double)System.currentTimeMillis(); } // initial: push the JOIN message in to the messageCache_, update roster and // start the heartbeat. // (Do not call this. It is only public to implement the interface.) public final void onInitialized() { // Set the heartbeat timeout using the Interest timeout mechanism. The // heartbeat() function will call itself again after a timeout. // TODO: Are we sure using a "/local/timeout" interest is the best future call approach? Interest timeout = new Interest(new Name("/local/timeout")); timeout.setInterestLifetimeMilliseconds(60000); try { face_.expressInterest(timeout, DummyOnData.onData_, heartbeat_); } catch (IOException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); return; } if (roster_.indexOf(userName_) < 0) { roster_.add(userName_); System.out.println("Member: " + screenName_); System.out.println(screenName_ + ": Join"); messageCacheAppend(ChatMessage.ChatMessageType.JOIN, "xxx"); } } // sendInterest: Send a Chat Interest to fetch chat messages after the // user gets the Sync data packet back but will not send interest. // (Do not call this. It is only public to implement the interface.) public final void onReceivedSyncState(List syncStates, boolean isRecovery) { // This is used by onData to decide whether to display the chat messages. isRecoverySyncState_ = isRecovery; ArrayList sendList = new ArrayList(); // of String ArrayList sessionNoList = new ArrayList(); // of long ArrayList sequenceNoList = new ArrayList(); // of long for (int j = 0; j < syncStates.size(); ++j) { ChronoSync2013.SyncState syncState = (ChronoSync2013.SyncState)syncStates.get(j); Name nameComponents = new Name(syncState.getDataPrefix()); String tempName = nameComponents.get(-1).toEscapedString(); long sessionNo = syncState.getSessionNo(); if (!tempName.equals(screenName_)) { int index = -1; for (int k = 0; k < sendList.size(); ++k) { if (((String)sendList.get(k)).equals(syncState.getDataPrefix())) { index = k; break; } } if (index != -1) { sessionNoList.set(index, sessionNo); sequenceNoList.set(index, syncState.getSequenceNo()); } else{ sendList.add(syncState.getDataPrefix()); sessionNoList.add(sessionNo); sequenceNoList.add(syncState.getSequenceNo()); } } } for (int i = 0; i < sendList.size(); ++i) { String uri = (String)sendList.get(i) + "/" + (long)sessionNoList.get(i) + "/" + (long)sequenceNoList.get(i); Interest interest = new Interest(new Name(uri)); interest.setInterestLifetimeMilliseconds(syncLifetime_); try { face_.expressInterest(interest, this, ChatTimeout.onTimeout_); } catch (IOException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); return; } } } // Send back a Chat Data Packet which contains the user's message. // (Do not call this. It is only public to implement the interface.) public final void onInterest (Name prefix, Interest interest, Face face, long interestFilterId, InterestFilter filter) { ChatMessage.Builder builder = ChatMessage.newBuilder(); long sequenceNo = Long.parseLong(interest.getName().get(chatPrefix_.size() + 1).toEscapedString()); boolean gotContent = false; for (int i = messageCache_.size() - 1; i >= 0; --i) { CachedMessage message = (CachedMessage)messageCache_.get(i); if (message.getSequenceNo() == sequenceNo) { if (!message.getMessageType().equals(ChatMessage.ChatMessageType.CHAT)) { builder.setFrom(screenName_); builder.setTo(chatRoom_); builder.setType(message.getMessageType()); builder.setTimestamp((int)Math.round(message.getTime() / 1000.0)); } else { builder.setFrom(screenName_); builder.setTo(chatRoom_); builder.setType(message.getMessageType()); builder.setData(message.getMessage()); builder.setTimestamp((int)Math.round(message.getTime() / 1000.0)); } gotContent = true; break; } } if (gotContent) { ChatMessage content = builder.build(); byte[] array = content.toByteArray(); Data data = new Data(interest.getName()); data.setContent(new Blob(array, false)); try { keyChain_.sign(data, certificateName_); } catch (SecurityException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); return; } try { face.putData(data); } catch (IOException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); } } } // Process the incoming Chat data. // (Do not call this. It is only public to implement the interface.) public final void onData(Interest interest, Data data) { ChatMessage content; try { content = ChatMessage.parseFrom(data.getContent().getImmutableArray()); } catch (InvalidProtocolBufferException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); return; } if (getNowMilliseconds() - content.getTimestamp() * 1000.0 < 120000.0) { String name = content.getFrom(); String prefix = data.getName().getPrefix(-2).toUri(); long sessionNo = Long.parseLong(data.getName().get(-2).toEscapedString()); long sequenceNo = Long.parseLong(data.getName().get(-1).toEscapedString()); String nameAndSession = name + sessionNo; int l = 0; //update roster while (l < roster_.size()) { String entry = (String)roster_.get(l); String tempName = entry.substring(0, entry.length() - 10); long tempSessionNo = Long.parseLong(entry.substring(entry.length() - 10)); if (!name.equals(tempName) && !content.getType().equals(ChatMessage.ChatMessageType.LEAVE)) ++l; else { if (name.equals(tempName) && sessionNo > tempSessionNo) roster_.set(l, nameAndSession); break; } } if (l == roster_.size()) { roster_.add(nameAndSession); System.out.println(name + ": Join"); } // Set the alive timeout using the Interest timeout mechanism. // TODO: Are we sure using a "/local/timeout" interest is the best future call approach? Interest timeout = new Interest(new Name("/local/timeout")); timeout.setInterestLifetimeMilliseconds(120000); try { face_.expressInterest (timeout, DummyOnData.onData_, this.new Alive(sequenceNo, name, sessionNo, prefix)); } catch (IOException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); return; } // isRecoverySyncState_ was set by sendInterest. // TODO: If isRecoverySyncState_ changed, this assumes that we won't get // data from an interest sent before it changed. if (content.getType().equals(ChatMessage.ChatMessageType.CHAT) && !isRecoverySyncState_ && !content.getFrom().equals(screenName_)) System.out.println(content.getFrom() + ": " + content.getData()); else if (content.getType().equals(ChatMessage.ChatMessageType.LEAVE)) { // leave message int n = roster_.indexOf(nameAndSession); if (n >= 0 && !name.equals(screenName_)) { roster_.remove(n); System.out.println(name + ": Leave"); } } } } private static class ChatTimeout implements OnTimeout { public final void onTimeout(Interest interest) { System.out.println("Timeout waiting for chat data"); } public final static OnTimeout onTimeout_ = new ChatTimeout(); } /** * This repeatedly calls itself after a timeout to send a heartbeat message * (chat message type HELLO). * This method has an "interest" argument because we use it as the onTimeout * for Face.expressInterest. */ private class Heartbeat implements OnTimeout { public final void onTimeout(Interest interest) { if (messageCache_.size() == 0) messageCacheAppend(ChatMessage.ChatMessageType.JOIN, "xxx"); try { sync_.publishNextSequenceNo(); } catch (IOException | SecurityException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); return; } messageCacheAppend(ChatMessage.ChatMessageType.HELLO, "xxx"); // Call again. // TODO: Are we sure using a "/local/timeout" interest is the best future call approach? Interest timeout = new Interest(new Name("/local/timeout")); timeout.setInterestLifetimeMilliseconds(60000); try { face_.expressInterest(timeout, DummyOnData.onData_, heartbeat_); } catch (IOException ex) { Logger.getLogger(Chat.class.getName()).log(Level.SEVERE, null, ex); } } } /** * This is called after a timeout to check if the user with prefix has a newer * sequence number than the given temp_seq. If not, assume the user is idle and * remove from the roster and print a leave message. * This is used as the onTimeout for Face.expressInterest. */ private class Alive implements OnTimeout { public Alive(long tempSequenceNo, String name, long sessionNo, String prefix) { tempSequenceNo_ = tempSequenceNo; name_ = name; sessionNo_ = sessionNo; prefix_ = prefix; } public final void onTimeout(Interest interest) { long sequenceNo = sync_.getProducerSequenceNo(prefix_, sessionNo_); String nameAndSession = name_ + sessionNo_; int n = roster_.indexOf(nameAndSession); if (sequenceNo != -1 && n >= 0) { if (tempSequenceNo_ == sequenceNo) { roster_.remove(n); System.out.println(name_ + ": Leave"); } } } private final long tempSequenceNo_; private final String name_; private final long sessionNo_; private final String prefix_; } /** * Append a new CachedMessage to messageCache_, using given messageType and message, * the sequence number from sync_.getSequenceNo() and the current time. Also * remove elements from the front of the cache as needed to keep * the size to maxMessageCacheLength_. */ private void messageCacheAppend(ChatMessage.ChatMessageType messageType, String message) { messageCache_.add(new CachedMessage (sync_.getSequenceNo(), messageType, message, getNowMilliseconds())); while (messageCache_.size() > maxMessageCacheLength_) messageCache_.remove(0); } // Generate a random name for ChronoSync. private static String getRandomString() { String seed = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789"; String result = ""; Random random = new Random(); for (int i = 0; i < 10; ++i) { // Using % means the distribution isn't uniform, but that's OK. int position = random.nextInt(256) % seed.length(); result += seed.charAt(position); } return result; } private static class RegisterFailed implements OnRegisterFailed { public final void onRegisterFailed(Name prefix) { System.out.println("Register failed for prefix " + prefix.toUri()); } public final static OnRegisterFailed onRegisterFailed_ = new RegisterFailed(); } // This is a do-nothing onData for using expressInterest for timeouts. // This should never be called. private static class DummyOnData implements OnData { public final void onData(Interest interest, Data data) {} public final static OnData onData_ = new DummyOnData(); } private static class CachedMessage { public CachedMessage (long sequenceNo, ChatMessage.ChatMessageType messageType, String message, double time) { sequenceNo_ = sequenceNo; messageType_ = messageType; message_ = message; time_ = time; } public final long getSequenceNo() { return sequenceNo_; } public final ChatMessage.ChatMessageType getMessageType() { return messageType_; } public final String getMessage() { return message_; } public final double getTime() { return time_; } private final long sequenceNo_; private final ChatMessage.ChatMessageType messageType_; private final String message_; private final double time_; }; // Use a non-template ArrayList so it works with older Java compilers. private final ArrayList messageCache_ = new ArrayList(); // of CachedMessage private final ArrayList roster_ = new ArrayList(); // of String private final int maxMessageCacheLength_ = 100; private boolean isRecoverySyncState_ = true; private final String screenName_; private final String chatRoom_; private final String userName_; private final Name chatPrefix_; private final double syncLifetime_ = 5000.0; // milliseconds private ChronoSync2013 sync_; private final Face face_; private final KeyChain keyChain_; private final Name certificateName_; private final OnTimeout heartbeat_; } public class TestChronoChat { // Convert the int array to a ByteBuffer. private static ByteBuffer toBuffer(int[] array) { ByteBuffer result = ByteBuffer.allocate(array.length); for (int i = 0; i < array.length; ++i) result.put((byte)(array[i] & 0xff)); result.flip(); return result; } private static final ByteBuffer DEFAULT_RSA_PUBLIC_KEY_DER = toBuffer(new int[] { 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb8, 0x09, 0xa7, 0x59, 0x82, 0x84, 0xec, 0x4f, 0x06, 0xfa, 0x1c, 0xb2, 0xe1, 0x38, 0x93, 0x53, 0xbb, 0x7d, 0xd4, 0xac, 0x88, 0x1a, 0xf8, 0x25, 0x11, 0xe4, 0xfa, 0x1d, 0x61, 0x24, 0x5b, 0x82, 0xca, 0xcd, 0x72, 0xce, 0xdb, 0x66, 0xb5, 0x8d, 0x54, 0xbd, 0xfb, 0x23, 0xfd, 0xe8, 0x8e, 0xaf, 0xa7, 0xb3, 0x79, 0xbe, 0x94, 0xb5, 0xb7, 0xba, 0x17, 0xb6, 0x05, 0xae, 0xce, 0x43, 0xbe, 0x3b, 0xce, 0x6e, 0xea, 0x07, 0xdb, 0xbf, 0x0a, 0x7e, 0xeb, 0xbc, 0xc9, 0x7b, 0x62, 0x3c, 0xf5, 0xe1, 0xce, 0xe1, 0xd9, 0x8d, 0x9c, 0xfe, 0x1f, 0xc7, 0xf8, 0xfb, 0x59, 0xc0, 0x94, 0x0b, 0x2c, 0xd9, 0x7d, 0xbc, 0x96, 0xeb, 0xb8, 0x79, 0x22, 0x8a, 0x2e, 0xa0, 0x12, 0x1d, 0x42, 0x07, 0xb6, 0x5d, 0xdb, 0xe1, 0xf6, 0xb1, 0x5d, 0x7b, 0x1f, 0x54, 0x52, 0x1c, 0xa3, 0x11, 0x9b, 0xf9, 0xeb, 0xbe, 0xb3, 0x95, 0xca, 0xa5, 0x87, 0x3f, 0x31, 0x18, 0x1a, 0xc9, 0x99, 0x01, 0xec, 0xaa, 0x90, 0xfd, 0x8a, 0x36, 0x35, 0x5e, 0x12, 0x81, 0xbe, 0x84, 0x88, 0xa1, 0x0d, 0x19, 0x2a, 0x4a, 0x66, 0xc1, 0x59, 0x3c, 0x41, 0x83, 0x3d, 0x3d, 0xb8, 0xd4, 0xab, 0x34, 0x90, 0x06, 0x3e, 0x1a, 0x61, 0x74, 0xbe, 0x04, 0xf5, 0x7a, 0x69, 0x1b, 0x9d, 0x56, 0xfc, 0x83, 0xb7, 0x60, 0xc1, 0x5e, 0x9d, 0x85, 0x34, 0xfd, 0x02, 0x1a, 0xba, 0x2c, 0x09, 0x72, 0xa7, 0x4a, 0x5e, 0x18, 0xbf, 0xc0, 0x58, 0xa7, 0x49, 0x34, 0x46, 0x61, 0x59, 0x0e, 0xe2, 0x6e, 0x9e, 0xd2, 0xdb, 0xfd, 0x72, 0x2f, 0x3c, 0x47, 0xcc, 0x5f, 0x99, 0x62, 0xee, 0x0d, 0xf3, 0x1f, 0x30, 0x25, 0x20, 0x92, 0x15, 0x4b, 0x04, 0xfe, 0x15, 0x19, 0x1d, 0xdc, 0x7e, 0x5c, 0x10, 0x21, 0x52, 0x21, 0x91, 0x54, 0x60, 0x8b, 0x92, 0x41, 0x02, 0x03, 0x01, 0x00, 0x01 }); // Java uses an unencrypted PKCS #8 PrivateKeyInfo, not a PKCS #1 RSAPrivateKey. private static final ByteBuffer DEFAULT_RSA_PRIVATE_KEY_DER = toBuffer(new int[] { 0x30, 0x82, 0x04, 0xbf, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x04, 0xa9, 0x30, 0x82, 0x04, 0xa5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb8, 0x09, 0xa7, 0x59, 0x82, 0x84, 0xec, 0x4f, 0x06, 0xfa, 0x1c, 0xb2, 0xe1, 0x38, 0x93, 0x53, 0xbb, 0x7d, 0xd4, 0xac, 0x88, 0x1a, 0xf8, 0x25, 0x11, 0xe4, 0xfa, 0x1d, 0x61, 0x24, 0x5b, 0x82, 0xca, 0xcd, 0x72, 0xce, 0xdb, 0x66, 0xb5, 0x8d, 0x54, 0xbd, 0xfb, 0x23, 0xfd, 0xe8, 0x8e, 0xaf, 0xa7, 0xb3, 0x79, 0xbe, 0x94, 0xb5, 0xb7, 0xba, 0x17, 0xb6, 0x05, 0xae, 0xce, 0x43, 0xbe, 0x3b, 0xce, 0x6e, 0xea, 0x07, 0xdb, 0xbf, 0x0a, 0x7e, 0xeb, 0xbc, 0xc9, 0x7b, 0x62, 0x3c, 0xf5, 0xe1, 0xce, 0xe1, 0xd9, 0x8d, 0x9c, 0xfe, 0x1f, 0xc7, 0xf8, 0xfb, 0x59, 0xc0, 0x94, 0x0b, 0x2c, 0xd9, 0x7d, 0xbc, 0x96, 0xeb, 0xb8, 0x79, 0x22, 0x8a, 0x2e, 0xa0, 0x12, 0x1d, 0x42, 0x07, 0xb6, 0x5d, 0xdb, 0xe1, 0xf6, 0xb1, 0x5d, 0x7b, 0x1f, 0x54, 0x52, 0x1c, 0xa3, 0x11, 0x9b, 0xf9, 0xeb, 0xbe, 0xb3, 0x95, 0xca, 0xa5, 0x87, 0x3f, 0x31, 0x18, 0x1a, 0xc9, 0x99, 0x01, 0xec, 0xaa, 0x90, 0xfd, 0x8a, 0x36, 0x35, 0x5e, 0x12, 0x81, 0xbe, 0x84, 0x88, 0xa1, 0x0d, 0x19, 0x2a, 0x4a, 0x66, 0xc1, 0x59, 0x3c, 0x41, 0x83, 0x3d, 0x3d, 0xb8, 0xd4, 0xab, 0x34, 0x90, 0x06, 0x3e, 0x1a, 0x61, 0x74, 0xbe, 0x04, 0xf5, 0x7a, 0x69, 0x1b, 0x9d, 0x56, 0xfc, 0x83, 0xb7, 0x60, 0xc1, 0x5e, 0x9d, 0x85, 0x34, 0xfd, 0x02, 0x1a, 0xba, 0x2c, 0x09, 0x72, 0xa7, 0x4a, 0x5e, 0x18, 0xbf, 0xc0, 0x58, 0xa7, 0x49, 0x34, 0x46, 0x61, 0x59, 0x0e, 0xe2, 0x6e, 0x9e, 0xd2, 0xdb, 0xfd, 0x72, 0x2f, 0x3c, 0x47, 0xcc, 0x5f, 0x99, 0x62, 0xee, 0x0d, 0xf3, 0x1f, 0x30, 0x25, 0x20, 0x92, 0x15, 0x4b, 0x04, 0xfe, 0x15, 0x19, 0x1d, 0xdc, 0x7e, 0x5c, 0x10, 0x21, 0x52, 0x21, 0x91, 0x54, 0x60, 0x8b, 0x92, 0x41, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0x8a, 0x05, 0xfb, 0x73, 0x7f, 0x16, 0xaf, 0x9f, 0xa9, 0x4c, 0xe5, 0x3f, 0x26, 0xf8, 0x66, 0x4d, 0xd2, 0xfc, 0xd1, 0x06, 0xc0, 0x60, 0xf1, 0x9f, 0xe3, 0xa6, 0xc6, 0x0a, 0x48, 0xb3, 0x9a, 0xca, 0x21, 0xcd, 0x29, 0x80, 0x88, 0x3d, 0xa4, 0x85, 0xa5, 0x7b, 0x82, 0x21, 0x81, 0x28, 0xeb, 0xf2, 0x43, 0x24, 0xb0, 0x76, 0xc5, 0x52, 0xef, 0xc2, 0xea, 0x4b, 0x82, 0x41, 0x92, 0xc2, 0x6d, 0xa6, 0xae, 0xf0, 0xb2, 0x26, 0x48, 0xa1, 0x23, 0x7f, 0x02, 0xcf, 0xa8, 0x90, 0x17, 0xa2, 0x3e, 0x8a, 0x26, 0xbd, 0x6d, 0x8a, 0xee, 0xa6, 0x0c, 0x31, 0xce, 0xc2, 0xbb, 0x92, 0x59, 0xb5, 0x73, 0xe2, 0x7d, 0x91, 0x75, 0xe2, 0xbd, 0x8c, 0x63, 0xe2, 0x1c, 0x8b, 0xc2, 0x6a, 0x1c, 0xfe, 0x69, 0xc0, 0x44, 0xcb, 0x58, 0x57, 0xb7, 0x13, 0x42, 0xf0, 0xdb, 0x50, 0x4c, 0xe0, 0x45, 0x09, 0x8f, 0xca, 0x45, 0x8a, 0x06, 0xfe, 0x98, 0xd1, 0x22, 0xf5, 0x5a, 0x9a, 0xdf, 0x89, 0x17, 0xca, 0x20, 0xcc, 0x12, 0xa9, 0x09, 0x3d, 0xd5, 0xf7, 0xe3, 0xeb, 0x08, 0x4a, 0xc4, 0x12, 0xc0, 0xb9, 0x47, 0x6c, 0x79, 0x50, 0x66, 0xa3, 0xf8, 0xaf, 0x2c, 0xfa, 0xb4, 0x6b, 0xec, 0x03, 0xad, 0xcb, 0xda, 0x24, 0x0c, 0x52, 0x07, 0x87, 0x88, 0xc0, 0x21, 0xf3, 0x02, 0xe8, 0x24, 0x44, 0x0f, 0xcd, 0xa0, 0xad, 0x2f, 0x1b, 0x79, 0xab, 0x6b, 0x49, 0x4a, 0xe6, 0x3b, 0xd0, 0xad, 0xc3, 0x48, 0xb9, 0xf7, 0xf1, 0x34, 0x09, 0xeb, 0x7a, 0xc0, 0xd5, 0x0d, 0x39, 0xd8, 0x45, 0xce, 0x36, 0x7a, 0xd8, 0xde, 0x3c, 0xb0, 0x21, 0x96, 0x97, 0x8a, 0xff, 0x8b, 0x23, 0x60, 0x4f, 0xf0, 0x3d, 0xd7, 0x8f, 0xf3, 0x2c, 0xcb, 0x1d, 0x48, 0x3f, 0x86, 0xc4, 0xa9, 0x00, 0xf2, 0x23, 0x2d, 0x72, 0x4d, 0x66, 0xa5, 0x01, 0x02, 0x81, 0x81, 0x00, 0xdc, 0x4f, 0x99, 0x44, 0x0d, 0x7f, 0x59, 0x46, 0x1e, 0x8f, 0xe7, 0x2d, 0x8d, 0xdd, 0x54, 0xc0, 0xf7, 0xfa, 0x46, 0x0d, 0x9d, 0x35, 0x03, 0xf1, 0x7c, 0x12, 0xf3, 0x5a, 0x9d, 0x83, 0xcf, 0xdd, 0x37, 0x21, 0x7c, 0xb7, 0xee, 0xc3, 0x39, 0xd2, 0x75, 0x8f, 0xb2, 0x2d, 0x6f, 0xec, 0xc6, 0x03, 0x55, 0xd7, 0x00, 0x67, 0xd3, 0x9b, 0xa2, 0x68, 0x50, 0x6f, 0x9e, 0x28, 0xa4, 0x76, 0x39, 0x2b, 0xb2, 0x65, 0xcc, 0x72, 0x82, 0x93, 0xa0, 0xcf, 0x10, 0x05, 0x6a, 0x75, 0xca, 0x85, 0x35, 0x99, 0xb0, 0xa6, 0xc6, 0xef, 0x4c, 0x4d, 0x99, 0x7d, 0x2c, 0x38, 0x01, 0x21, 0xb5, 0x31, 0xac, 0x80, 0x54, 0xc4, 0x18, 0x4b, 0xfd, 0xef, 0xb3, 0x30, 0x22, 0x51, 0x5a, 0xea, 0x7d, 0x9b, 0xb2, 0x9d, 0xcb, 0xba, 0x3f, 0xc0, 0x1a, 0x6b, 0xcd, 0xb0, 0xe6, 0x2f, 0x04, 0x33, 0xd7, 0x3a, 0x49, 0x71, 0x02, 0x81, 0x81, 0x00, 0xd5, 0xd9, 0xc9, 0x70, 0x1a, 0x13, 0xb3, 0x39, 0x24, 0x02, 0xee, 0xb0, 0xbb, 0x84, 0x17, 0x12, 0xc6, 0xbd, 0x65, 0x73, 0xe9, 0x34, 0x5d, 0x43, 0xff, 0xdc, 0xf8, 0x55, 0xaf, 0x2a, 0xb9, 0xe1, 0xfa, 0x71, 0x65, 0x4e, 0x50, 0x0f, 0xa4, 0x3b, 0xe5, 0x68, 0xf2, 0x49, 0x71, 0xaf, 0x15, 0x88, 0xd7, 0xaf, 0xc4, 0x9d, 0x94, 0x84, 0x6b, 0x5b, 0x10, 0xd5, 0xc0, 0xaa, 0x0c, 0x13, 0x62, 0x99, 0xc0, 0x8b, 0xfc, 0x90, 0x0f, 0x87, 0x40, 0x4d, 0x58, 0x88, 0xbd, 0xe2, 0xba, 0x3e, 0x7e, 0x2d, 0xd7, 0x69, 0xa9, 0x3c, 0x09, 0x64, 0x31, 0xb6, 0xcc, 0x4d, 0x1f, 0x23, 0xb6, 0x9e, 0x65, 0xd6, 0x81, 0xdc, 0x85, 0xcc, 0x1e, 0xf1, 0x0b, 0x84, 0x38, 0xab, 0x93, 0x5f, 0x9f, 0x92, 0x4e, 0x93, 0x46, 0x95, 0x6b, 0x3e, 0xb6, 0xc3, 0x1b, 0xd7, 0x69, 0xa1, 0x0a, 0x97, 0x37, 0x78, 0xed, 0xd1, 0x02, 0x81, 0x80, 0x33, 0x18, 0xc3, 0x13, 0x65, 0x8e, 0x03, 0xc6, 0x9f, 0x90, 0x00, 0xae, 0x30, 0x19, 0x05, 0x6f, 0x3c, 0x14, 0x6f, 0xea, 0xf8, 0x6b, 0x33, 0x5e, 0xee, 0xc7, 0xf6, 0x69, 0x2d, 0xdf, 0x44, 0x76, 0xaa, 0x32, 0xba, 0x1a, 0x6e, 0xe6, 0x18, 0xa3, 0x17, 0x61, 0x1c, 0x92, 0x2d, 0x43, 0x5d, 0x29, 0xa8, 0xdf, 0x14, 0xd8, 0xff, 0xdb, 0x38, 0xef, 0xb8, 0xb8, 0x2a, 0x96, 0x82, 0x8e, 0x68, 0xf4, 0x19, 0x8c, 0x42, 0xbe, 0xcc, 0x4a, 0x31, 0x21, 0xd5, 0x35, 0x6c, 0x5b, 0xa5, 0x7c, 0xff, 0xd1, 0x85, 0x87, 0x28, 0xdc, 0x97, 0x75, 0xe8, 0x03, 0x80, 0x1d, 0xfd, 0x25, 0x34, 0x41, 0x31, 0x21, 0x12, 0x87, 0xe8, 0x9a, 0xb7, 0x6a, 0xc0, 0xc4, 0x89, 0x31, 0x15, 0x45, 0x0d, 0x9c, 0xee, 0xf0, 0x6a, 0x2f, 0xe8, 0x59, 0x45, 0xc7, 0x7b, 0x0d, 0x6c, 0x55, 0xbb, 0x43, 0xca, 0xc7, 0x5a, 0x01, 0x02, 0x81, 0x81, 0x00, 0xab, 0xf4, 0xd5, 0xcf, 0x78, 0x88, 0x82, 0xc2, 0xdd, 0xbc, 0x25, 0xe6, 0xa2, 0xc1, 0xd2, 0x33, 0xdc, 0xef, 0x0a, 0x97, 0x2b, 0xdc, 0x59, 0x6a, 0x86, 0x61, 0x4e, 0xa6, 0xc7, 0x95, 0x99, 0xa6, 0xa6, 0x55, 0x6c, 0x5a, 0x8e, 0x72, 0x25, 0x63, 0xac, 0x52, 0xb9, 0x10, 0x69, 0x83, 0x99, 0xd3, 0x51, 0x6c, 0x1a, 0xb3, 0x83, 0x6a, 0xff, 0x50, 0x58, 0xb7, 0x28, 0x97, 0x13, 0xe2, 0xba, 0x94, 0x5b, 0x89, 0xb4, 0xea, 0xba, 0x31, 0xcd, 0x78, 0xe4, 0x4a, 0x00, 0x36, 0x42, 0x00, 0x62, 0x41, 0xc6, 0x47, 0x46, 0x37, 0xea, 0x6d, 0x50, 0xb4, 0x66, 0x8f, 0x55, 0x0c, 0xc8, 0x99, 0x91, 0xd5, 0xec, 0xd2, 0x40, 0x1c, 0x24, 0x7d, 0x3a, 0xff, 0x74, 0xfa, 0x32, 0x24, 0xe0, 0x11, 0x2b, 0x71, 0xad, 0x7e, 0x14, 0xa0, 0x77, 0x21, 0x68, 0x4f, 0xcc, 0xb6, 0x1b, 0xe8, 0x00, 0x49, 0x13, 0x21, 0x02, 0x81, 0x81, 0x00, 0xb6, 0x18, 0x73, 0x59, 0x2c, 0x4f, 0x92, 0xac, 0xa2, 0x2e, 0x5f, 0xb6, 0xbe, 0x78, 0x5d, 0x47, 0x71, 0x04, 0x92, 0xf0, 0xd7, 0xe8, 0xc5, 0x7a, 0x84, 0x6b, 0xb8, 0xb4, 0x30, 0x1f, 0xd8, 0x0d, 0x58, 0xd0, 0x64, 0x80, 0xa7, 0x21, 0x1a, 0x48, 0x00, 0x37, 0xd6, 0x19, 0x71, 0xbb, 0x91, 0x20, 0x9d, 0xe2, 0xc3, 0xec, 0xdb, 0x36, 0x1c, 0xca, 0x48, 0x7d, 0x03, 0x32, 0x74, 0x1e, 0x65, 0x73, 0x02, 0x90, 0x73, 0xd8, 0x3f, 0xb5, 0x52, 0x35, 0x79, 0x1c, 0xee, 0x93, 0xa3, 0x32, 0x8b, 0xed, 0x89, 0x98, 0xf1, 0x0c, 0xd8, 0x12, 0xf2, 0x89, 0x7f, 0x32, 0x23, 0xec, 0x67, 0x66, 0x52, 0x83, 0x89, 0x99, 0x5e, 0x42, 0x2b, 0x42, 0x4b, 0x84, 0x50, 0x1b, 0x3e, 0x47, 0x6d, 0x74, 0xfb, 0xd1, 0xa6, 0x10, 0x20, 0x6c, 0x6e, 0xbe, 0x44, 0x3f, 0xb9, 0xfe, 0xbc, 0x8d, 0xda, 0xcb, 0xea, 0x8f }); private static class RegisterFailed implements OnRegisterFailed { public final void onRegisterFailed(Name prefix) { System.out.println("Register failed for prefix " + prefix.toUri()); } public final static OnRegisterFailed onRegisterFailed_ = new RegisterFailed(); } public static void main(String[] args) { try { System.out.println("Enter your chat username:"); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String screenName = reader.readLine(); String defaultHubPrefix = "ndn/edu/ucla/remap"; System.out.println("Enter your hub prefix [" + defaultHubPrefix + "]:"); String hubPrefix = reader.readLine(); if (hubPrefix.equals("")) hubPrefix = defaultHubPrefix; String defaultChatRoom = "ndnchat"; System.out.println("Enter the chatroom name [" + defaultChatRoom + "]:"); String chatRoom = reader.readLine(); if (chatRoom.equals("")) chatRoom = defaultChatRoom; String host = "localhost"; System.out.println("Connecting to " + host + ", Chatroom: " + chatRoom + ", Username: " + screenName); System.out.println(""); // Set up the key chain. Face face = new Face(host); MemoryIdentityStorage identityStorage = new MemoryIdentityStorage(); MemoryPrivateKeyStorage privateKeyStorage = new MemoryPrivateKeyStorage(); KeyChain keyChain = new KeyChain (new IdentityManager(identityStorage, privateKeyStorage), new NoVerifyPolicyManager()); keyChain.setFace(face); Name keyName = new Name("/testname/DSK-123"); Name certificateName = keyName.getSubName(0, keyName.size() - 1).append ("KEY").append(keyName.get(-1)).append("ID-CERT").append("0"); identityStorage.addKey(keyName, KeyType.RSA, new Blob(DEFAULT_RSA_PUBLIC_KEY_DER, false)); privateKeyStorage.setKeyPairForKeyName (keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER); face.setCommandSigningInfo(keyChain, certificateName); Chat chat = new Chat (screenName, chatRoom, new Name(hubPrefix), face, keyChain, certificateName); // The main loop to process Chat while checking stdin to send a message. System.out.println("Enter your chat message. To quit, enter \"leave\" or \"exit\"."); while (true) { if (reader.ready()) { String input = reader.readLine(); if (input.equals("leave") || input.equals("exit")) // We will send the leave message below. break; chat.sendMessage(input); } face.processEvents(); // We need to sleep for a few milliseconds so we don't use 100% of the CPU. Thread.sleep(10); } // The user entered the command to leave. chat.leave(); // Wait a little bit to allow other applications to fetch the leave message. double startTime = Chat.getNowMilliseconds(); while (true) { if (Chat.getNowMilliseconds() - startTime >= 1000.0) break; face.processEvents(); Thread.sleep(10); } } catch (Exception e) { System.out.println("exception: " + e.getMessage()); } } }