001 package edu.harvard.deas.hyperenc.gui;
002
003 import java.awt.Component;
004 import java.awt.Container;
005 import java.awt.Frame;
006 import java.awt.GridBagConstraints;
007 import java.awt.GridBagLayout;
008 import java.awt.event.ActionEvent;
009 import java.awt.event.ActionListener;
010 import java.awt.event.WindowAdapter;
011 import java.awt.event.WindowEvent;
012
013 import javax.mail.Address;
014 import javax.mail.internet.AddressException;
015 import javax.mail.internet.InternetAddress;
016 import javax.swing.BorderFactory;
017 import javax.swing.JButton;
018 import javax.swing.JDialog;
019 import javax.swing.JLabel;
020 import javax.swing.JOptionPane;
021 import javax.swing.JPanel;
022 import javax.swing.JScrollPane;
023 import javax.swing.JTextArea;
024 import javax.swing.JTextField;
025 import javax.swing.border.EtchedBorder;
026
027 import edu.harvard.deas.hyperenc.Contact;
028 import edu.harvard.deas.hyperenc.util.HexCoder;
029
030 /**
031 * A dialog box for adding a new contact.
032 */
033 public class AddContact extends JDialog {
034
035 private static final long serialVersionUID = 1L;
036 private JLabel displayNameLabel, fullNameLabel, emailAddrLabel;
037 private JTextField displayName, fullName, emailAddr;
038 private JTextArea outSecret, inSecret;
039 private JButton add, cancel;
040 private Contact newGuy;
041
042 private byte[] outSecretBytes = null;
043 private byte[] inSecretBytes = null;
044
045 /**
046 * Create a new Add Contact dialog.
047 * @param owner the frame that owns this dialog
048 */
049 public AddContact(Frame owner) {
050 this(owner, "", "", "");
051 }
052
053 /**
054 * Create a new Add Contact dialog with the name and contact fields
055 * initialized according to the given parameters.
056 *
057 * @param owner the frame that owns this dialog
058 * @param initAddr the initial value of the <i>e-mail contact</i> field
059 * @param initFullName the initial value of the <i>full name</i> field
060 * @param initDisplayName the initial value of the <i>display name</i> field
061 */
062 public AddContact(Frame owner, String initAddr, String initFullName,
063 String initDisplayName) {
064 super(owner, "Enter new contact info");
065
066 setModal(true);
067 setSize(400, 400);
068 setLocation(150, 150);
069
070 addWindowListener(new WindowAdapter() {
071 public void windowClosing(WindowEvent e) {
072 setVisible(false); // TODO call cancel instead
073 }
074 });
075
076 // A simple header to let the user know what's going on
077 displayNameLabel = new JLabel("Display Name:");
078 displayNameLabel.setVerticalAlignment(JLabel.CENTER);
079 displayNameLabel.setHorizontalAlignment(JLabel.LEFT);
080
081 displayName = new JTextField(30);
082 displayName.setText(initDisplayName);
083
084 // A simple header to let the user know what's going on
085 fullNameLabel = new JLabel("Full Name:");
086 fullNameLabel.setVerticalAlignment(JLabel.CENTER);
087 fullNameLabel.setHorizontalAlignment(JLabel.LEFT);
088
089 fullName = new JTextField(30);
090 fullName.setText(initFullName);
091
092 // A simple header to let the user know what's going on
093 emailAddrLabel = new JLabel("Email Address:");
094 emailAddrLabel.setVerticalAlignment(JLabel.CENTER);
095 emailAddrLabel.setHorizontalAlignment(JLabel.LEFT);
096
097 emailAddr = new JTextField(30);
098 emailAddr.setText(initAddr);
099
100 // Shared secret entry boxes
101 JLabel outSecretLabel = new JLabel("Outgoing secret:");
102 outSecretLabel.setVerticalAlignment(JLabel.TOP);
103 outSecretLabel.setHorizontalAlignment(JLabel.LEFT);
104 JLabel inSecretLabel = new JLabel("Incoming secret:");
105 inSecretLabel.setVerticalAlignment(JLabel.TOP);
106 inSecretLabel.setHorizontalAlignment(JLabel.LEFT);
107
108 outSecret = new JTextArea(3, 80);
109 inSecret = new JTextArea(3, 80);
110 JScrollPane outSecretPane = new JScrollPane(outSecret);
111 JScrollPane inSecretPane = new JScrollPane(inSecret);
112
113 outSecret.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
114 outSecret.setLineWrap(true);
115
116 inSecret.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
117 inSecret.setLineWrap(true);
118
119 // Create the two main buttons
120 JPanel buttonPanel = new JPanel();
121
122 add = new JButton("Add");
123 cancel = new JButton("Cancel");
124
125 buttonPanel.add(add);
126 buttonPanel.add(cancel);
127
128 // Attempts to make a Contact from the provided information. If successful,
129 // stores the new Contact in newGuy and closes this dialog. If validation of
130 // the entered information fails, displays an appropriate error message.
131 add.addActionListener(new ActionListener() {
132 public void actionPerformed(ActionEvent e) {
133 if (emailAddr.getText().trim().equals("")) {
134 JOptionPane.showMessageDialog(null,
135 "The e-mail contact field may not be left blank",
136 "Error",
137 JOptionPane.ERROR_MESSAGE);
138 return;
139 }
140
141 Address emailAddress;
142 try {
143 emailAddress = makeAddress(emailAddr.getText());
144 } catch (AddressException ex) {
145 JOptionPane.showMessageDialog(null,
146 "The provided e-mail contact is invalid",
147 "Error",
148 JOptionPane.ERROR_MESSAGE);
149 return;
150 }
151
152 String outString = outSecret.getText();
153 String inString = inSecret.getText();
154
155 if (outString.isEmpty() || inString.isEmpty()) {
156 JOptionPane.showMessageDialog(null,
157 "Both outgoing and incoming secret must be specified.",
158 "Error",
159 JOptionPane.ERROR_MESSAGE);
160 return;
161 }
162
163 byte[] os;
164 try {
165 if (outString.length() % 2 == 1)
166 outString += "0";
167 os = HexCoder.decode(outString);
168 } catch (IllegalArgumentException ex) {
169 JOptionPane.showMessageDialog(null,
170 "The outgoing secret may only contain characters 0-9 or a-f.",
171 "Error",
172 JOptionPane.ERROR_MESSAGE);
173 return;
174 }
175
176 byte[] is;
177 try {
178 if (inString.length() % 2 == 1)
179 inString += "0";
180 is = HexCoder.decode(inString);
181 } catch (IllegalArgumentException ex) {
182 JOptionPane.showMessageDialog(null,
183 "The incoming secret may only contain characters 0-9 or a-f.",
184 "Error",
185 JOptionPane.ERROR_MESSAGE);
186 return;
187 }
188
189 outSecretBytes = os;
190 inSecretBytes = is;
191
192 newGuy = makeContact(displayName.getText(), fullName.getText(), emailAddress);
193 setVisible(false);
194 }
195 });
196
197
198 cancel.addActionListener(new ActionListener() {
199 public void actionPerformed(ActionEvent e) {
200 newGuy = null;
201 outSecretBytes = null;
202 inSecretBytes = null;
203 setVisible(false);
204 }
205 });
206
207
208 // Add all components to the frame
209
210 Container pane = this.getContentPane();
211 pane.setLayout(new GridBagLayout());
212 GridBagConstraints gridConstraints = new GridBagConstraints();
213
214 // An empty label to control the size of the grid bag by padding the top
215 JLabel padTop = new JLabel("");
216 addItem(padTop, gridConstraints, 0, 0, 4, 1, GridBagConstraints.HORIZONTAL,
217 GridBagConstraints.CENTER, 0, 0);
218
219 // An empty label to control the size of the grid bag by padding the bottom
220 JLabel padBottom = new JLabel("");
221 addItem(padBottom, gridConstraints, 0, 9, 4, 1, GridBagConstraints.HORIZONTAL,
222 GridBagConstraints.CENTER, 0, 0);
223
224 // An empty label to control the size of the grid bag by padding the left side
225 JLabel padLeft = new JLabel("");
226 addItem(padLeft, gridConstraints, 0, 1, 1, 8, GridBagConstraints.VERTICAL,
227 GridBagConstraints.CENTER, 0, 0);
228
229 // An empty label to control the size of the grid bag by padding the right side
230 JLabel padRight = new JLabel("");
231 addItem(padRight, gridConstraints, 3, 1, 1, 8, GridBagConstraints.VERTICAL,
232 GridBagConstraints.CENTER, 0, 0);
233
234 addItem(displayNameLabel, gridConstraints, 1, 1, 1, 1, GridBagConstraints.NONE,
235 GridBagConstraints.CENTER, 0, 0);
236
237 addItem(displayName, gridConstraints, 2, 1, 1, 1, GridBagConstraints.HORIZONTAL,
238 GridBagConstraints.CENTER, 1.0, 0);
239
240 addItem(fullNameLabel, gridConstraints, 1, 2, 1, 1, GridBagConstraints.NONE,
241 GridBagConstraints.CENTER, 0, 0);
242
243 addItem(fullName, gridConstraints, 2, 2, 1, 1, GridBagConstraints.HORIZONTAL,
244 GridBagConstraints.CENTER, 1.0, 0);
245
246 addItem(emailAddrLabel, gridConstraints, 1, 3, 1, 1, GridBagConstraints.NONE,
247 GridBagConstraints.CENTER, 0, 0);
248
249 addItem(emailAddr, gridConstraints, 2, 3, 1, 1, GridBagConstraints.HORIZONTAL,
250 GridBagConstraints.CENTER, 1.0, 0);
251
252 addItem(outSecretLabel, gridConstraints, 1, 4, 1, 1, GridBagConstraints.NONE,
253 GridBagConstraints.CENTER, 0, 1.0);
254
255 addItem(outSecretPane, gridConstraints, 2, 4, 1, 1, GridBagConstraints.BOTH,
256 GridBagConstraints.CENTER, 1.0, 1.0);
257
258 addItem(inSecretLabel, gridConstraints, 1, 5, 1, 1, GridBagConstraints.NONE,
259 GridBagConstraints.CENTER, 0, 1.0);
260
261 addItem(inSecretPane, gridConstraints, 2, 5, 1, 1, GridBagConstraints.BOTH,
262 GridBagConstraints.CENTER, 1.0, 1.0);
263
264 addItem(buttonPanel, gridConstraints, 1, 7, 2, 1, GridBagConstraints.HORIZONTAL,
265 GridBagConstraints.CENTER, 0, 0);
266
267 this.getRootPane().setDefaultButton(add);
268
269 setVisible(true);
270 }
271
272 /**
273 * Returns the new Contact constructed from the data entered in this form. If
274 * the dialog was canceled by the user, for example via the Cancel button or
275 * by closing the window by other means, then return <code>null</code>.
276 *
277 * @return a new Contact constructed from the data entered in this form, or
278 * <code>null</code> no contact was created
279 */
280 public Contact getNewContact() {
281 return newGuy;
282 }
283
284 /**
285 * Returns the outgoing shared secret entered by the user, as a byte array.
286 * Returns <code>null</code> under the same conditions as
287 * <code>getNewContact</code>.
288 *
289 * @return shared secret for outgoing communication, as a byte array
290 */
291 public byte[] getOutgoingSecret() {
292 return outSecretBytes;
293 }
294
295 /**
296 * Returns the incoming shared secret entered by the user, as a byte array.
297 * Returns <code>null</code> under the same conditions as
298 * <code>getNewContact</code>.
299 *
300 * @return shared secret for incoming communication, as a byte array
301 */
302 public byte[] getIncomingSecret() {
303 return inSecretBytes;
304 }
305
306 /**
307 * Creates and returns a Contact. If <i>display name</i> or <i>full name</i>
308 * contains nothing but whitespace, it is set to a textual representation of
309 * the e-mail contact.
310 *
311 * @param displayName the display name of the new contact
312 * @param fullName the full name of the new contact
313 * @param email the e-mail contact of the new contact
314 * @return the created Contact
315 */
316 private Contact makeContact(String display, String full, Address email) {
317 if (display.trim().equals("")) {
318 display = email.toString();
319 }
320
321 if (full.trim().equals("")) {
322 full = "("+emailAddr.getText()+")";
323 }
324
325 Contact c = new Contact(full, email, display);
326 return c;
327 }
328
329 /**
330 * Tries to create an Address from the given string and return it.
331 *
332 * @param s
333 * String containing an e-mail contact. Leading and trailing whitespace
334 * is trimmed.
335 * @return the created Address
336 * @throws AddressException if the contact creation failed
337 */
338 private Address makeAddress(String s) throws AddressException {
339 InternetAddress a = new InternetAddress(s.trim());
340 a.validate();
341 return a;
342 }
343
344
345 /** Helper function for adding items to the grid bag layout */
346 // XXX order of arguments is impossible to remember, making this method prone
347 // to error; how can we improve it?
348 private void addItem(Component item, GridBagConstraints gc,
349 int xPosition, int yPosition, int width, int height,
350 int fill, int anchor, double wx, double wy) {
351 gc.gridx = xPosition;
352 gc.gridy = yPosition;
353 gc.gridwidth = width;
354 gc.gridheight = height;
355 gc.fill = fill;
356 gc.anchor = anchor;
357 gc.weightx = wx;
358 gc.weighty = wy;
359
360 getContentPane().add(item, gc);
361 }
362
363 /*
364 * Launches an AddContact dialog and prints the results to stdout. Useful for
365 * testing.
366 */
367 /*
368 public static void main(String args[]) {
369 try {
370 UIManager.setLookAndFeel(
371 UIManager.getSystemLookAndFeelClassName());
372 } catch (Exception e) {
373 throw new RuntimeException(e);
374 }
375
376 AddContact ac = new AddContact(null);
377 Contact newContact = ac.getNewContact();
378
379 if (newContact != null) {
380 Address email = newContact.getEmail();
381 String emailStr = "[invalid]";
382 if (email != null)
383 email.toString();
384
385 System.out.println("Display: " + newContact.getDisplayName());
386 System.out.println("Full: " + newContact.getFullName());
387 System.out.println("E-mail: " + email.toString());
388 System.out.println("Out secret: " + HexCoder.encode(ac.getOutgoingSecret()));
389 System.out.println("In secret: " + HexCoder.encode(ac.getIncomingSecret()));
390 }
391
392 System.exit(0);
393 }
394 */
395 }