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    };