package uc.protocol.hub; import static org.junit.Assert.*; import helpers.GH; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import logger.LoggerFactory; import org.eclipse.core.runtime.NullProgressMonitor; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import eu.jucy.testfragment.TestHubConnection; import uc.Command; import uc.DCClient; import uc.FavHub; import uc.HubListenerAdapter; import uc.ICryptoManager; import uc.IUDPHandler; import uc.IUser; import uc.PI; import uc.UDPhandler; import uc.DCClient.Initializer; import uc.FavFolders.SharedDir; import uc.crypto.BloomFilter; import uc.crypto.HashValue; import uc.crypto.UDPEncryption; import uc.crypto.IHashEngine.IHashedListener; import uc.files.filelist.FileListFile; import uc.files.search.FileSearch; import uc.protocols.AbstractADCCommand; import uc.protocols.ConnectionProtocol; import uc.protocols.ConnectionState; import uc.protocols.DCProtocol; import uc.protocols.IConnection; import uc.protocols.hub.FeedType; import uc.protocols.hub.Hub; import uc.protocols.hub.PrivateMessage; import uc.protocols.hub.Hub.ConnectionInjector; public class AdcHubTest { private static DCClient dcc; private TestHubConnection thc; private static TestUDPHandler tudpH; private Hub hub; private static final List<String> userINF = new ArrayList<String>(); private static final List<String> sharedFiles = new ArrayList<String>(); private static final File sharedStuff = new File(new File(PI.getStoragePath(),".."),"SharedStuff"); private static final long[] sharedFilesizes; private static final String SidOwn = "Y2NH"; private static final String SidAsterix = "HDTK"; private static final String SidActiveUser = SidAsterix ; private static final String CIDAsterix = "JSJK4ALI7EZVQSYFGF6HD6SLDP67OITQAMLHT3Y", CIDObelix= "MLKJ7IKCCT3LYSONVW5FLQ6SEBYM4CIFWEJMRKA"; private static final String SidPassiveUser = "IU7D"; private static final String ignoreINFprefix = "BINF "; static { String[] users= new String[] { "BINF "+SidAsterix+" ID"+CIDAsterix+" NIAsterix I4213.89.70.149 HN3 HO0 HR0 SF10368 SS203141739008 SL9 SUTCP4,UDP4 U41408 VE++\\s0.699 US5242", "BINF CQHS ID"+CIDObelix+" NIObelix I481.200.167.218 HN8 HO0 HR0 SF0 SS0 SL1 SUADC0,TCP4,UDP4 U44638 VE++\\s0.702 US10485760", "BINF HLGC IDEJCANVZXUZ2SNJ4GIWTZUOQPDCASCTZ4HVLRFRY NIMajestix I482.196.97.148 DEWhisky HN0 HO2 HR1 CT4 SF0 SS0 SL1 SUADC0,TCP4,UDP4 U4412 VE++\\s0.705 US5242", "BINF OVD7 IDFAJ6HFSJVEUWGTRT75LTHEI7PI5QELRP7CG5VOI NIIdefix I488.83.46.99 DS49152 HN11 HO0 HR0 SF1414 SS38341855326 SL1 SUTCP4,UDP4 U41412 VE++\\s0.699 US2097152", "BINF FLO7 IDCL4VPGSZ6OBIO35GFIDCWJGL2ZBM76V6UKKDKXA NITroubadix HN60 HO0 HR0 SF11634 SS167565105641 SL2 SUADC0 VE++\\s0.704 US2097152", "BINF "+SidPassiveUser+" IDMYAWASFR4R7CZVTRMJCBL643M3J5ZGI4CZE42KQ NIMiraculix DEsdc++ HN51 HO0 HR0 SF4465 SS11308078932 SL6 SUADC0 VE++\\s0.704 US1048576"}; userINF.addAll(Arrays.asList(users)); String[] files = new String[] { "Lain Iwakura.avi", "Alice Mizuki.mp3", "Yasuo Iwakura.png" }; sharedFiles.addAll(Arrays.asList(files)); sharedFilesizes = new long[files.length]; sharedFilesizes[0] = 0; sharedFilesizes[1] = 1025; sharedFilesizes[2] = 1024 * 1024 * 4; } public static class TestUDPHandler extends UDPhandler { public final Semaphore searchRSSent = new Semaphore(0); public TestUDPHandler(DCClient dcc) { super(dcc); } @Override public void sendPacket(ByteBuffer packet, InetSocketAddress target) { searchRSSent.release(); } @Override public void receivedPacket(InetSocketAddress from, ByteBuffer packet, boolean possiblyEncrypted) { super.receivedPacket(from, packet, possiblyEncrypted); } } @BeforeClass public static void setUpBeforeClass() throws Exception { DCClient.setInitializer(new Initializer() { @Override protected IUDPHandler createUDPHandler(DCClient dcc) { return tudpH = new TestUDPHandler(dcc); } }); dcc = new DCClient(); dcc.start(new NullProgressMonitor()); Hub.setConnectionInjector(new ConnectionInjector() { @Override public IConnection getConnection(ICryptoManager manager,String addy, ConnectionProtocol connectionProt,boolean encryption,HashValue fingerPrint) { return new TestHubConnection(addy, connectionProt, encryption,fingerPrint); } }); } @AfterClass public static void tearDownAfterClass() throws Exception { dcc.stop(false); } @Before public void setUp() throws Exception { FavHub fh = new FavHub("adcs://127.0.0.1:456"); hub = (Hub) fh.connect(dcc); thc = (TestHubConnection)hub.getConnection(); Thread.sleep(200); } @After public void tearDown() throws Exception { tudpH.searchRSSent.drainPermits(); hub.close(); hub = null; thc = null; } @Test public void testLoginADC() throws Exception { try { //on connect we Send supports String hsup = thc.pollNextMessage(false); assertTrue(hsup.startsWith("HSUP ")); /* Example messages from start... ISUP ADBASE ADTIGR ADUCM0 ADPING ISID 7KJB IINF CT32 VEDSHub\sTheta NIFuture][Imperial-Networks DEWelcome\sto\sImperial\sNetworks ISTA 000 Running\sTheta\sVersion\sof\sDSHub. ISTA 000 Hub\sis\sup\ssince\sThu\sMar\s20\s19:36:54\sCET\s2008 */ //as an answer the hub sends: his supports, inf and gives us a SID hub.receivedCommand("ISUP ADBASE ADTIGR ADUCM0 ADPING"); hub.receivedCommand("ISID "+SidOwn); String hubnick= "HubNick"; String hubDe = "HubDescription"; hub.receivedCommand("IINF NI"+hubnick+" DE"+hubDe+" VEStatusMessage"); assertEquals(hubnick, hub.getHubname()); assertEquals(hubDe, hub.getTopic()); String clientInf = thc.pollNextMessage(true); IUser u = hub.getSelf(); //BINF 7KJB IDPUK62XDM5GLHOJ4NZ6KCFUGPEW5B3FKC4HMKRSA PDEZROSKSJ54SNWZFEIECDFAH5IGJRYMM5SZ2RWHQ NIQuicksilver SL4 SS695217057756 SF9712 HN1 HR0 HO0 VE++\s0.704 US5242 SUADC0; assertTrue(clientInf.startsWith("BINF "+SidOwn+" ")); //add some users.. for (String s: userINF) { hub.receivedCommand(s); } assertEquals(ConnectionState.CONNECTED, hub.getState()); //now receive own inf String receivedINF = clientInf.replace(" PD"+u.getPD(), ""); //just delete PID from own INF String ip = "127.0.0.1"; //TODO check that IP and CT are accepted receivedINF = receivedINF.replace("I40.0.0.0", "I4"+ip); //and replace IP.. hub.receivedCommand(receivedINF); //receive own INF assertEquals("Received client inf: "+receivedINF,ConnectionState.LOGGEDIN, hub.getState()); assertEquals("found: "+(userINF.size()+1)+" "+hub.getUsers().size(),userINF.size()+1 , hub.getUsers().size() ); //check all users noted down.. assertTrue(!hub.getUserByNick("Miraculix").isTCPActive()); // check modechar correctly found assertTrue(hub.getUserByNick("Asterix").isTCPActive()); // same } catch (Exception e) { fail(e.toString()); } checkErrorLogsEmpty(); } @Test public void testSearch() throws Exception { goToLoggedInState(); //for now on ignore further INF messages.. thc.addIgnore(ignoreINFprefix); //first check search with empty share //FSCH XCIY +TCP4 ANbug ANmafia ANsi ANluchian-la ANfurat TOmanual String searchPassive = "BSCH "+SidPassiveUser+" ANIwakura TOauto"; //TRXKVJNWYEQYJQGUQ4CR2RI3YMTUHP2FKGVRKNMMQ String searchTTHmiss = "BSCH "+SidPassiveUser+" TRN7RLQKGSVAQOUWZKWYTCKGADQLSTP6K2CSOBBJI TOauto"; hub.receivedCommand(searchPassive); Thread.sleep(500); // allow for time until search is executed.. assertTrue(thc.isMessageSentEmpty()); hub.receivedCommand(searchTTHmiss); Thread.sleep(500); // allow for time until search is executed.. assertTrue(thc.isMessageSentEmpty()); prepareSharedFiles(); //check passive search that hits.. hub.receivedCommand(searchPassive); //should produce 2 hits now Thread.sleep(500); // allow for time until search is executed.. String one = thc.pollNextMessage(false); String two = thc.pollNextMessage(false); assertTrue((one.contains("Lain") && two.contains("Yasuo"))^ (one.contains("Yasuo") && two.contains("Lain"))); assertTrue(thc.isMessageSentEmpty()); // check passive search that fails.. hub.receivedCommand(searchPassive+" ANSerial\nExperiments"); Thread.sleep(500); assertTrue(thc.isMessageSentEmpty()); //either no message or just an INF // check active search that hits.. hub.receivedCommand(searchPassive.replace(SidPassiveUser, SidActiveUser)); assertTrue(tudpH.searchRSSent.tryAcquire(2, 500, TimeUnit.MILLISECONDS)); FileListFile file = null; for (FileListFile f : dcc.getOwnFileList().getRoot()) { file = f; break; } assertNotNull(file); FileSearch fs = new FileSearch(file.getTTHRoot()); dcc.register(fs); dcc.search(fs); String searchMessageSent = thc.pollNextMessage(true); assertTrue("Not a search",searchMessageSent.contains("SCH ")); assertTrue("search not contained correct hash", searchMessageSent.contains(file.getTTHRoot().toString())); String packet = "URES "+CIDAsterix+" TO"+fs.getToken()+" TR"+file.getTTHRoot() +" SI"+file.getSize()+" FN"+AbstractADCCommand.doReplaces(file.getPath().replace(File.separatorChar , '/')) +"\n"; tudpH.receivedPacket(new InetSocketAddress("127.0.0.1", 5000), ByteBuffer.wrap(packet.getBytes(DCProtocol.ADC_CHARENCODING)), true); assertEquals("expected exactly one File: ",1, fs.getNrOfFiles()); String packet2 = packet.replace(CIDAsterix, CIDObelix); //same result from different user.. byte[] packet2bytes = packet2.getBytes(DCProtocol.ADC_CHARENCODING); byte[] packet2Ency = UDPEncryption.encryptMessage(packet2bytes, fs.getEncryptionKey()); tudpH.receivedPacket(new InetSocketAddress("127.0.0.1", 5001), ByteBuffer.wrap(packet2Ency), true); assertEquals("encrypted File not received: "+fs.getNrOfResults(),2, fs.getNrOfResults()); removeSharedFiles(); thc.removeIgnore(ignoreINFprefix); //clean up ignore.. checkErrorLogsEmpty(); } @Test public void testChat() throws Exception { goToLoggedInState(); final String[] m = new String[5]; final IUser[] u = new IUser[5]; hub.registerHubListener(new HubListenerAdapter() { public void mcReceived(IUser sender, String message,boolean me) { m[0] = message; u[0] = sender; } public void mcReceived(String message) { m[1] = message; } public void statusMessage(String message, int severity) { m[2] = message; } public void pmReceived(PrivateMessage pm) { m[3] = pm.getMessage(); u[3] = pm.getFrom(); u[4] = pm.getSender(); } public void feedReceived(FeedType ft, String message) { m[4] = message; } }); //EMSG EBHH WUNF hello\sBot PMEBHH //BMSG EBHH Hello String message = "Hello"; hub.receivedCommand("BMSG "+SidAsterix+" "+message); hub.receivedCommand("EMSG "+SidAsterix+" "+SidOwn+" "+message+" PM"+SidAsterix); IUser asterix = hub.getUserByNick("Asterix"); assertEquals(message,m[0]); assertEquals(asterix,u[0]); assertNull(m[1]); //TODO implement test.. assertNull(m[2]); assertEquals(message,m[3]); assertEquals(asterix,u[3]); assertEquals(asterix,u[4]); assertNull(m[4]); //TODO implement test.. checkErrorLogsEmpty(); //TODO test message sending } @Test public void testBloomFeature() throws Exception { goToLoggedInState(); prepareSharedFiles(); thc.addIgnore(ignoreINFprefix); thc.clear(); int m = 1024,h = 24,k = 8; hub.receivedCommand("IGET blom / 0 "+(m/8)+" BH"+h+" BK"+k); //test some Blomfilter ByteBuffer bb = thc.pollNextByteBuffer(1000); assertNotNull(bb); String command = ""; while(bb.remaining() > m/8) { command += (char)bb.get(); } assertTrue(command.startsWith("HSND blom / 0 "+(m/8))); byte[] bloomFilter = new byte[m/8]; bb.get(bloomFilter); BloomFilter bloomreceived = new BloomFilter(bloomFilter,h,k); BloomFilter selfBuild = BloomFilter.create(dcc.getOwnFileList(), m, h, k); assertEquals(selfBuild, bloomreceived); for (FileListFile f:dcc.getOwnFileList().getRoot()) { assertTrue("File not Present: "+f.getName(), bloomreceived.possiblyContains(f.getTTHRoot())); } removeSharedFiles(); thc.removeIgnore(ignoreINFprefix); //clean up ignore.. checkErrorLogsEmpty(); } @Test public void testCMDFeature() throws Exception { goToLoggedInState(); assertEquals(0, hub.getUserCommands().size()); int context = 1; String tt = "BINF %[mySID] NI%[line:Nick]\n"; String path = "Change nick..."; hub.receivedCommand("ICMD "+AbstractADCCommand.doReplaces(path) +" CT"+context+" TT"+AbstractADCCommand.doReplaces(tt)); assertEquals(1, hub.getUserCommands().size()); Command com = hub.getUserCommands().get(0); assertEquals(context,com.getWhere()); assertEquals(tt, com.getCommand()); assertEquals(path, com.getPath()); assertEquals(path, com.getName()); //as it has no folder structure.. checkErrorLogsEmpty(); } public static void checkErrorLogsEmpty() { assertEquals("error.log not empty",0,LoggerFactory.getErrorLog().length()); assertEquals("Platform .log not empty",0, new File(new File(PI.getStoragePath(),".metadata"),".log").length()); } private void goToLoggedInState() throws Exception { testLoginADC(); } private static void createFiles(File parent) throws IOException { if (!parent.isDirectory()) { parent.mkdirs(); } for (int i = 0 ; i < sharedFiles.size(); i++ ) { File target = new File(parent,sharedFiles.get(i)); FileOutputStream fos = new FileOutputStream(target); for (long w = 0; w < sharedFilesizes[i];w++) { fos.write(i); } fos.flush(); fos.close(); } } public static void prepareSharedFiles() throws Exception { createFiles(sharedStuff); SharedDir sd = new SharedDir("shared",sharedStuff); //final Semaphore waitHashingFinished = new Semaphore(0); dcc.getHashEngine().registerHashedListener(new IHashedListener() { int count = sharedFiles.size(); public void hashed(File f, long duration, long remainingSize) { if (--count == 0) { dcc.getFilelist().refresh(true); } } }); dcc.getFavFolders().storeSharedDirs(Collections.singletonList(sd)); //now wait some seconds for the hashing while( dcc.getFilelist().getNumberOfFiles() < sharedFiles.size() ) { GH.sleep(500); dcc.getFilelist().refresh(true); } assertEquals(sharedFiles.size(), dcc.getOwnFileList().getNumberOfFiles()); } public static void removeSharedFiles() throws Exception { for (File f:sharedStuff.listFiles()) { assertTrue(f.delete()); } dcc.getFavFolders().storeSharedDirs(Collections.<SharedDir>emptyList()); dcc.getFilelist().refresh(true); assertEquals(0, dcc.getOwnFileList().getNumberOfFiles()); assertTrue(sharedStuff.delete()); } }