001 package edu.harvard.deas.hyperenc; 002 003 import java.io.BufferedReader; 004 import java.io.File; 005 import java.io.FileInputStream; 006 import java.io.FileNotFoundException; 007 import java.io.FileReader; 008 import java.io.IOException; 009 import java.io.InputStream; 010 import java.lang.reflect.InvocationTargetException; 011 import java.security.MessageDigest; 012 import java.security.NoSuchAlgorithmException; 013 import java.util.HashMap; 014 import java.util.Map; 015 import java.util.Properties; 016 017 import javax.mail.Address; 018 import javax.mail.internet.AddressException; 019 import javax.mail.internet.InternetAddress; 020 import javax.swing.SwingUtilities; 021 022 import org.apache.log4j.Logger; 023 024 import edu.harvard.deas.hyperenc.gui.DBMessageStorage; 025 import edu.harvard.deas.hyperenc.gui.HyperGui; 026 import edu.harvard.deas.hyperenc.gui.MessageStorage; 027 import edu.harvard.deas.hyperenc.util.NNLookup; 028 029 /** 030 * Entry point for the client. Reads config files, constructs storages, 031 * collectors, and communicators, then creates a UI and launches it. 032 */ 033 public class HyperEncryption 034 { 035 private static final Logger logger = Logger.getLogger(HyperEncryption.class); 036 037 /** 038 * Runs the HyperEncryption application. Reads the configuration files and 039 * launches the GUI. 040 * <p> 041 * There are two configuration files that must be present: 042 * <ul> 043 * <li><code>accountsettings.xml</code> - a Java Properties file containing 044 * e-mail account and server information.</li> 045 * <li><code>psn_list.txt</code> - a list of PSNs and associated integer IDs, 046 * one per line</li> 047 * </ul> 048 * At present, the names/locations of the configuration files are not 049 * configurable. This will be changed in a future release. 050 * 051 * @param args 052 * must be empty. No command line arguments are accepted at present. 053 * 054 * @see java.util.Properties 055 */ 056 public static void main(String[] args){ 057 if (args.length != 0) { 058 System.out.println("Usage: HyperEncryption"); 059 System.out.println("Account settings must be stored in accountsettings.xml"); 060 System.exit(1); 061 } 062 063 Properties accountInfo = new Properties(); 064 InputStream instream = null; 065 try { 066 // TODO allow user to specify config file location 067 instream = new FileInputStream(new File("accountsettings.xml")); 068 } catch (FileNotFoundException e) { 069 System.err.println("Required file accountsettings.xml not found"); 070 System.exit(1); 071 } 072 073 try { 074 accountInfo.loadFromXML(instream); 075 } catch (IOException e) { // includes errors caused by invalid format 076 throw new RuntimeException("Could not read accountsettings.xml", e); 077 } 078 079 String myAddress = accountInfo.getProperty("emailAddress"); 080 081 final Address myAddr; 082 try { 083 myAddr = new InternetAddress(myAddress); 084 } catch (AddressException e) { 085 System.err.println("E-mail contact \"" + myAddress + "\" is invalid"); 086 System.exit(1); 087 // stop the compiler whining about myAddr not being initialized: 088 return; 089 } 090 final Contact myContact = new Contact("ME", myAddr, "ME"); 091 092 final ContactList contacts = new DBContactList(myContact); 093 System.out.println("Number of contacts: "+ contacts.size()); 094 095 if(contacts.isEmpty()) { 096 System.out.println("Contact list empty."); 097 } 098 099 // making a master and a slave storage for each contact 100 PageShuffler ps = new BasicPageShuffler(); 101 Map<Contact,HyperStorage> masterstorages = new HashMap<Contact,HyperStorage>(); 102 Map<Contact,HyperStorage> slavestorages = new HashMap<Contact,HyperStorage>(); 103 104 // TODO make filename configurable 105 NNLookup<String> psnList; 106 try { 107 psnList = readPSNList(new File("psn_list.txt")); 108 } catch (FileNotFoundException e) { 109 System.err.println("Could not find PSN list file " + "psn_list.txt"); 110 System.exit(1); 111 return; // so the compiler understands that following code is unreachable 112 } catch (IOException e) { 113 logger.error(e); 114 throw new RuntimeException("Error while reading PSN list file " 115 + "psn_list.txt"); 116 } 117 118 HyperStorageFactory hyperStorageFactory = new DBHyperStorageFactory(psnList); 119 120 for (Contact c : contacts) { 121 System.out.println("Initializing storages for " + c.toString()); 122 masterstorages.put(c, hyperStorageFactory.getHyperStorage(c, Direction.MASTER)); 123 slavestorages.put(c, hyperStorageFactory.getHyperStorage(c, Direction.SLAVE)); 124 } 125 126 // Create an email storage unit, or load the existing one from the database 127 final MessageStorage messageStorage = new DBMessageStorage(myAddr.toString()); 128 129 // Create our HyperCommunicator 130 final HyperCommunicator comm = new EmailHyperCommunicator(accountInfo, contacts); 131 132 // Create HyperCollector object and run 133 final HyperCollector hc = new HyperCollector(masterstorages, slavestorages, 134 myContact, hyperStorageFactory); 135 136 // Finally, launch the GUI in the event dispatch thread 137 // invokeAndWait so that this thread can perform cleanup afterwards (if desired) 138 try { 139 SwingUtilities.invokeAndWait(new Runnable() { 140 public void run() { 141 HyperGui gui = new HyperGui(comm, hc, messageStorage, contacts, myContact); 142 hc.addHListener(gui); 143 gui.setVisible(true); 144 } 145 }); 146 } catch (InvocationTargetException e) { 147 throw new RuntimeException("Uncaught exception thrown from GUI", e); 148 } catch (InterruptedException e) { 149 throw new RuntimeException("Initial thread unexpectedly interrupted!", e); 150 } 151 } 152 153 /** 154 * Reads a file containing a list of PSNs, one per line. The ID comes first, 155 * followed by a comma, followed by a domain name or IP of the PSN. 156 * 157 * @param f the file to read 158 * @throws FileNotFoundException if the file <code>f</code> could not be found 159 * @throws IOException if an IOException occurs while reading the file 160 */ 161 private static NNLookup<String> readPSNList(File f) 162 throws FileNotFoundException, IOException { 163 NNLookup<String> psnList = new NNLookup<String>(); 164 165 BufferedReader in = new BufferedReader(new FileReader(f)); 166 String line = in.readLine(); 167 while (line != null) { // not EOF 168 String[] tokens = line.split(",", 2); 169 int id = Integer.parseInt(tokens[0].trim()); 170 String hostname = tokens[1].trim(); 171 psnList.add(id, hostname); 172 line = in.readLine(); // read next line... 173 } 174 175 return psnList; 176 } 177 178 179 /** Private constructor to enforce non-instantiability. */ 180 private HyperEncryption() {} 181 182 /** 183 * Factory that creates DBHyperStorages in the way that we want. 184 */ 185 private static class DBHyperStorageFactory implements HyperStorageFactory { 186 private NNLookup<String> psnList; 187 188 public DBHyperStorageFactory(NNLookup<String> psnList) { 189 this.psnList = psnList; 190 } 191 192 @Override 193 public HyperStorage getHyperStorage(Contact contact, Direction dir) { 194 try { 195 return new DBHyperStorage( 196 contact, 197 dir, 198 new VSatRandomSource(psnList), 199 MessageDigest.getInstance("md5"), 200 new BasicPageShuffler() 201 ); 202 } catch (NoSuchAlgorithmException e) { 203 throw new RuntimeException( 204 "Unexpected exception when creating MessageDigest instance " 205 + contact.toString(), e); 206 } 207 } 208 } 209 };