source: trunk/src/org/expeditee/agents/mail/MailSession.java@ 424

Last change on this file since 424 was 424, checked in by ra33, 16 years ago
File size: 19.6 KB
Line 
1package org.expeditee.agents.mail;
2
3import java.awt.Color;
4import java.awt.Point;
5import java.io.BufferedReader;
6import java.io.InputStream;
7import java.io.InputStreamReader;
8import java.util.Collection;
9import java.util.Date;
10import java.util.LinkedList;
11import java.util.Properties;
12
13import javax.mail.Address;
14import javax.mail.Authenticator;
15import javax.mail.Folder;
16import javax.mail.Message;
17import javax.mail.MessageRemovedException;
18import javax.mail.MessagingException;
19import javax.mail.Multipart;
20import javax.mail.Part;
21import javax.mail.PasswordAuthentication;
22import javax.mail.Session;
23import javax.mail.Store;
24import javax.mail.Transport;
25import javax.mail.Flags.Flag;
26import javax.mail.Message.RecipientType;
27import javax.mail.event.MessageCountAdapter;
28import javax.mail.event.MessageCountEvent;
29import javax.mail.internet.InternetAddress;
30import javax.mail.internet.MimeMessage;
31
32import org.expeditee.gui.AttributeValuePair;
33import org.expeditee.gui.Frame;
34import org.expeditee.gui.FrameCreator;
35import org.expeditee.gui.FrameGraphics;
36import org.expeditee.gui.MessageBay;
37import org.expeditee.importer.FrameDNDTransferHandler;
38import org.expeditee.items.Text;
39
40public class MailSession {
41 public static final String UNREAD_MESSAGE = " unread message";
42
43 public static boolean _autoConnect = false;
44
45 private static MailSession _theMailSession = null;
46
47 private Session _session;
48
49 private Transport _transport;
50
51 private Store _store;
52
53 private Folder _folder;
54
55 private String _address;
56
57 private String _username;
58
59 private String _password;
60
61 private String _mailServer;
62
63 private String _serverType;
64
65 private Boolean _bConnecting;
66
67 private MailSession(Frame settingsFrame) {
68 _bConnecting = false;
69
70 Properties props = System.getProperties();
71
72 _username = null;
73 _password = "";
74 _mailServer = null;
75 _serverType = null;
76
77 // Set the settings
78 for (Text item : settingsFrame.getBodyTextItems(false)) {
79 if (item.getText().toLowerCase().trim().equals("autoconnect")) {
80 _autoConnect = true;
81 continue;
82 }
83 AttributeValuePair avp = new AttributeValuePair(item.getText());
84 if (!avp.hasPair())
85 continue;
86 String attributeFullCase = avp.getAttribute();
87 String attribute = attributeFullCase.toLowerCase();
88
89 if (attribute.equals("user")) {
90 _username = avp.getValue();
91 props.setProperty("mail.user", _username);
92 } else if (attribute.equals("password")) {
93 _password = avp.getValue();
94 props.setProperty("mail.password", _password);
95 } else if (attribute.equals("address")) {
96 _address = avp.getValue();
97 } else if (attribute.equals("smtpserver")) {
98 props.setProperty("mail.transport.protocol", "smtp");
99 props.setProperty("mail.host", avp.getValue());
100 props.setProperty("mail.smtp.starttls.enable", "true");
101 props.setProperty("mail.smtp.host", avp.getValue());
102 props.setProperty("mail.smtp.auth", "true");
103 } else if (attribute.equals("popserver")) {
104 _mailServer = avp.getValue();
105 _serverType = "pop3s";
106 props.setProperty("mail.pop3.host", _mailServer);
107 } else if (attribute.equals("imapserver")) {
108 _mailServer = avp.getValue();
109 _serverType = "imaps";
110 props.setProperty("mail.imap.host", _mailServer);
111 }
112 }
113
114 // Create the authenticator
115 Authenticator auth = null;
116 if (_username != null) {
117 auth = new SMTPAuthenticator(_username, _password);
118 }
119 java.security.Security
120 .addProvider(new com.sun.net.ssl.internal.ssl.Provider());
121
122 // -- Attaching to default Session, or we could start a new one --
123 _session = Session.getDefaultInstance(props, auth);
124 try {
125 // Set up the mail receiver
126 _store = _session.getStore(_serverType);
127
128 // Connect the mail sender
129 _transport = _session.getTransport();
130 if (_autoConnect) {
131 connectThreaded();
132 }
133 } catch (Exception e) {
134 MessageBay.errorMessage("Error in ExpMail setup");
135 }
136 }
137
138 /**
139 * Attempts to connect the mail transporter to the mail server.
140 *
141 */
142 public static void connect() {
143 if (_theMailSession._bConnecting) {
144 MessageBay.errorMessage("Already connecting to mail server");
145 return;
146 } else if (_theMailSession != null) {
147 _theMailSession.connectThreaded();
148 }
149 }
150
151 private void connectThreaded() {
152 Thread t = new ConnectThread(this);
153 t.start();
154 }
155
156 public synchronized void connectServers() {
157 try {
158 if (!_transport.isConnected()) {
159 // MessageBay.displayMessage("Connecting to SMTP server...");
160 _bConnecting = true;
161 _transport.connect();
162 // MessageBay.displayMessage("SMTP server connected",
163 // Color.green);
164 } else {
165 MessageBay.warningMessage("SMTP server already connected");
166 }
167 } catch (MessagingException e) {
168 MessageBay.errorMessage("Error connecting to SMTP server");
169 }
170
171 if (_mailServer != null && !_store.isConnected()) {
172 try {
173 // Text message = MessageBay.displayMessage("Connecting to "
174 // + _mailServer + "...");
175 _store.connect(_mailServer, _username, _password);
176
177 // -- Try to get hold of the default folder --
178 _folder = _store.getDefaultFolder();
179 if (_folder == null)
180 throw new Exception("No default folder");
181 // -- ...and its INBOX --
182 _folder = _folder.getFolder("INBOX");
183 if (_folder == null)
184 throw new Exception("No INBOX");
185 // -- Open the folder for read only --
186 _folder.open(Folder.READ_WRITE);
187 _folder.addMessageCountListener(new MessageCountAdapter() {
188 @Override
189 public void messagesAdded(MessageCountEvent e) {
190 try {
191 MessageBay.displayMessage("New mail message!",
192 null, Color.green, true, "getMailByID "
193 + _folder.getMessageCount());
194 /*
195 * TODO use messageID incase mail gets deleted
196 * externally
197 */
198 } catch (MessagingException e1) {
199 e1.printStackTrace();
200 }
201 displayUnreadMailCount();
202 }
203 });
204
205 new Thread() {
206 public void run() {
207 for (;;) {
208 try {
209 Thread.sleep(5000);
210 /*
211 * sleep for freq milliseconds. This is to force
212 * the IMAP server to send us EXISTS
213 * notifications
214 */
215 // TODO: Is synchronisation needed?
216 _folder.getMessageCount();
217 _folder.exists();
218 // _folder.getUnreadMessageCount();
219 } catch (Exception e) {
220 e.printStackTrace();
221 MessageBay
222 .errorMessage("Mail connection unavailable");
223 finalise();
224 break;
225 }
226 }
227 }
228 }.start();
229
230 MessageBay.displayMessage("Mail connection complete",
231 Color.GREEN);
232
233 displayUnreadMailCount();
234
235 } catch (Exception e) {
236 // e.printStackTrace();
237 MessageBay.errorMessage("Error connecting to " + _mailServer);
238 }
239 }
240 _bConnecting = false;
241 }
242
243 public void displayUnreadMailCount() {
244 int unreadCount = getUnreadCount();
245 Text text = new Text(-1, unreadCount + UNREAD_MESSAGE
246 + (unreadCount == 1 ? "" : "s"), Color.BLUE, null);
247 if (unreadCount > 0)
248 text.addAction("getUnreadMail " + unreadCount);
249 MessageBay.displayMessage(text);
250 }
251
252 public static boolean sendTextMessage(String to, String cc, String bcc,
253 String subject, String body, Object attachments) {
254
255 if (_theMailSession == null) {
256 MessageBay.errorMessage("Add mail settings to profile frame");
257 return false;
258 }
259
260 if (_theMailSession._bConnecting) {
261 MessageBay.errorMessage("Busy connecting to mail server...");
262 return false;
263 }
264
265 return _theMailSession
266 .sendText(to, cc, bcc, subject, body, attachments);
267 }
268
269 private synchronized boolean sendText(String to, String cc, String bcc,
270 String subject, String body, Object attachments) {
271 if (!_transport.isConnected()) {
272 MessageBay
273 .warningMessage("Not connected to server, attempting to reconnect...");
274 try {
275 _bConnecting = true;
276 _transport.connect();
277 _bConnecting = false;
278 } catch (Exception e) {
279 MessageBay.errorMessage("Could not connect to mail server");
280 _bConnecting = false;
281 return false;
282 }
283 }
284
285 if (to == null) {
286 MessageBay.errorMessage("Add tag @to:<sendToEmailAddress>");
287 return false;
288 }
289
290 try {
291 // -- Create a new message --
292 Message msg = new MimeMessage(_session);
293
294 // -- Set the FROM and TO fields --
295 msg.setFrom(new InternetAddress(_address));
296 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(
297 to, false));
298
299 // -- We could include CC recipients too --
300 if (cc != null) {
301 msg.setRecipients(Message.RecipientType.CC, InternetAddress
302 .parse(cc, false));
303 }
304
305 if (bcc != null) {
306 msg.setRecipients(Message.RecipientType.BCC, InternetAddress
307 .parse(bcc, false));
308 }
309
310 // -- Set the subject and body text --
311 msg.setSubject(subject);
312 msg.setContent(body.toString(), "text/plain");
313
314 // -- Set some other header information --
315 msg.setHeader("ExpMail", "Expeditee");
316 msg.setSentDate(new Date());
317
318 Transport.send(msg, msg.getRecipients(Message.RecipientType.TO));
319 } catch (Exception e) {
320 MessageBay.errorMessage("Error sending mail: " + e.getMessage());
321 return false;
322 }
323 MessageBay.displayMessage("Message sent OK.");
324 return true;
325 }
326
327 public static void init(Frame settingsFrame) {
328
329 if (settingsFrame == null)
330 return;
331
332 if (_theMailSession == null)
333 _theMailSession = new MailSession(settingsFrame);
334 }
335
336 private class SMTPAuthenticator extends javax.mail.Authenticator {
337 private String _username;
338
339 private String _password;
340
341 public SMTPAuthenticator(String username, String password) {
342 _username = username;
343 _password = password;
344 }
345
346 @Override
347 public PasswordAuthentication getPasswordAuthentication() {
348 return new PasswordAuthentication(_username, _password);
349 }
350 }
351
352 public static MailSession getInstance() {
353 return _theMailSession;
354 }
355
356 /**
357 * Closes the mail folders.
358 *
359 * @return true if the folders needed to be closed.
360 */
361 public synchronized boolean finalise() {
362 boolean result = false;
363 try {
364 if (_transport != null && _transport.isConnected()) {
365 _transport.close();
366 result = true;
367 }
368
369 if (_folder != null && _folder.isOpen()) {
370 _folder.close(false);
371 result = true;
372 }
373
374 if (_store != null && _store.isConnected()) {
375 _store.close();
376 result = true;
377 }
378 } catch (Exception e) {
379
380 }
381 return result;
382 }
383
384 public int getUnreadCount() {
385 try {
386 return _folder.getUnreadMessageCount();
387 } catch (MessagingException e) {
388 // TODO Auto-generated catch block
389 e.printStackTrace();
390 }
391 return 0;
392 }
393
394 /**
395 * Gets mail and puts a list of mail items with links to the messages
396 * content. TODO: Put reply and forward button on the frame...
397 *
398 * @param flag
399 * @param isPresent
400 * @param frame
401 * @param point
402 * @return
403 */
404 public String getMailString(Flag flag, Boolean isPresent) {
405 StringBuffer sb = new StringBuffer();
406 // -- Get the message wrappers and process them --
407 Message[] msgs;
408 try {
409 msgs = _folder.getMessages();
410 for (int msgNum = 0; msgNum < msgs.length; msgNum++) {
411
412 if (flag == null
413 || msgs[msgNum].getFlags().contains(flag) == isPresent) {
414 if (sb.length() > 0) {
415 sb.append('\n').append('\n').append(
416 "-----------------------------").append('\n')
417 .append('\n');
418 }
419 // Only get messages that have not been read
420 sb.append(getTextMessage(msgs[msgNum]));
421 }
422 }
423 } catch (MessagingException e) {
424 e.printStackTrace();
425 }
426 return sb.toString();
427 }
428
429 /**
430 * Gets mail and puts a list of mail items with links to the messages
431 * content. TODO: Put reply and forward button on the frame...
432 *
433 * @param flag
434 * @param isPresent
435 * @param frame
436 * @param point
437 * @return
438 */
439 public Collection<Text> getMail(Flag flag, Boolean isPresent, Frame frame,
440 Point point, int numberOfMessages) {
441 if (_folder == null)
442 return null;
443
444 Collection<Text> mailItems = new LinkedList<Text>();
445 // -- Get the message wrappers and process them --
446 Message[] msgs;
447 try {
448 msgs = _folder.getMessages();
449
450 // msgs[0].get
451
452 int messagesRead = 0;
453
454 for (int msgNum = msgs.length - 1; messagesRead < numberOfMessages
455 && msgNum >= 0; msgNum--) {
456 if (flag == null
457 || msgs[msgNum].getFlags().contains(flag) == isPresent) {
458 Text newItem = readMessage(msgs[msgNum], msgNum + 1, frame,
459 point);
460 // TODO: May want to reverse the order of mail messages
461 if (newItem != null) {
462 mailItems.add(newItem);
463 point.y += newItem.getBoundsHeight();
464 messagesRead++;
465 } else {
466 newItem = null;
467 }
468 }
469
470 }
471 } catch (MessagingException e) {
472 e.printStackTrace();
473 }
474
475 return mailItems;
476 }
477
478 public Text getMail(Frame frame, Point point, int msgNum) {
479 if (_folder == null)
480 return null;
481
482 // -- Get the message wrappers and process them --
483 try {
484 Message[] msgs = _folder.getMessages();
485 return readMessage(msgs[msgNum], msgNum + 1, frame, point);
486 } catch (ArrayIndexOutOfBoundsException ae) {
487 /*
488 * Just return null... error message will be displayed in the
489 * calling method
490 */
491 } catch (Exception e) {
492 e.printStackTrace();
493 }
494
495 return null;
496 }
497
498 private Text readMessage(final Message message, final int messageNo,
499 final Frame frame, final Point point) {
500
501 final Text source = FrameDNDTransferHandler.importString(
502 "Reading message " + messageNo + "...", point);
503
504 new Thread() {
505 public void run() {
506 try {
507 String subject = message.getSubject();
508 source.setText("[" + messageNo + "] " + subject);
509 // Create a frameCreator
510 final FrameCreator frames = new FrameCreator(frame
511 .getFramesetName(), frame.getPath(), subject,
512 false, false);
513
514 frames.addText("@date: " + message.getSentDate(), null,
515 null, null, false);
516
517 // Get the header information
518 String from = ((InternetAddress) message.getFrom()[0])
519 .toString();
520 Text fromAddressItem = frames.addText("@from: " + from,
521 null, null, null, false);
522
523 addRecipients(message, frames, _address, RecipientType.TO,
524 "@to: ");
525 addRecipients(message, frames, null, RecipientType.CC,
526 "@cc: ");
527
528 // Read reply to addresses
529 Text reply = addAddresses(message, frames, from, message
530 .getReplyTo(), "@replyTo: ");
531 /*
532 * If the only person to reply to is the person who sent the
533 * mail add a tag that just says reply
534 */
535 if (reply == null) {
536 reply = frames.addText("@reply", null, null, null,
537 false);
538 reply.setPosition(10 + fromAddressItem.getX()
539 + fromAddressItem.getBoundsWidth(),
540 fromAddressItem.getY());
541 }
542 reply.addAction("reply");
543 // frames.addSpace(15);
544
545 // -- Get the message part (i.e. the message itself) --
546 Part messagePart = message;
547 Object content = messagePart.getContent();
548 // -- or its first body part if it is a multipart
549 // message --
550 if (content instanceof Multipart) {
551 messagePart = ((Multipart) content).getBodyPart(0);
552 // System.out.println("[ Multipart Message ]");
553 }
554 // -- Get the content type --
555 String contentType = messagePart.getContentType()
556 .toLowerCase();
557 // -- If the content is plain text, we can print it --
558 // System.out.println("CONTENT:" + contentType);
559 if (contentType.startsWith("text/plain")
560 || contentType.startsWith("text/html")) {
561 InputStream is = messagePart.getInputStream();
562 BufferedReader reader = new BufferedReader(
563 new InputStreamReader(is));
564 String thisLine = reader.readLine();
565 StringBuffer nextText = new StringBuffer();
566 while (thisLine != null) {
567 // A blank line is a signal to start a new text item
568 if (thisLine.trim().equals("")) {
569 addTextItem(frames, nextText.toString());
570 nextText = new StringBuffer();
571 } else {
572 nextText.append(thisLine).append('\n');
573 }
574 thisLine = reader.readLine();
575 }
576 addTextItem(frames, nextText.toString());
577 }
578 message.setFlag(Flag.SEEN, true);
579
580 frames.save();
581 source.setLink(frames.getName());
582 FrameGraphics.requestRefresh(true);
583 } catch (MessageRemovedException mre) {
584 source.setText("Message removed from inbox");
585 } catch (MessagingException e) {
586 String message = e.getMessage();
587 if (message == null) {
588 e.printStackTrace();
589 MessageBay.errorMessage("GetMail error!");
590 } else {
591 MessageBay.errorMessage("GetMail error: " + message);
592 }
593 } catch (Exception e) {
594 MessageBay.errorMessage("Error reading mail: "
595 + e.getMessage());
596 e.printStackTrace();
597 }
598 }
599
600 /**
601 * @param frames
602 * @param nextText
603 */
604 private void addTextItem(final FrameCreator frames, String nextText) {
605 nextText = nextText.trim();
606 if (nextText.length() == 0)
607 return;
608 // Remove the last char if its a newline
609 if (nextText.charAt(nextText.length() - 1) == '\n')
610 nextText = nextText.substring(0, nextText.length() - 1);
611 // TODO: Make the space a setting in frame creator
612 frames.addSpace(10);
613 frames.addText(nextText, null, null, null, false);
614 }
615 }.start();
616 return source;
617 }
618
619 /**
620 * "getTextMessage()" method to print a message.
621 */
622 public String getTextMessage(Message message) {
623 StringBuffer sb = new StringBuffer();
624
625 try {
626 // Get the header information
627 String from = ((InternetAddress) message.getFrom()[0])
628 .getPersonal();
629 if (from == null)
630 from = ((InternetAddress) message.getFrom()[0]).getAddress();
631 sb.append("FROM: " + from).append('\n');
632 String subject = message.getSubject();
633 sb.append("SUBJECT: " + subject).append('\n').append('\n');
634 // -- Get the message part (i.e. the message itself) --
635 Part messagePart = message;
636 Object content = messagePart.getContent();
637 // -- or its first body part if it is a multipart message --
638 if (content instanceof Multipart) {
639 messagePart = ((Multipart) content).getBodyPart(0);
640 System.out.println("[ Multipart Message ]");
641 }
642 // -- Get the content type --
643 String contentType = messagePart.getContentType();
644 // -- If the content is plain text, we can print it --
645 // System.out.println("CONTENT:" + contentType);
646 if (contentType.startsWith("text/plain")
647 || contentType.startsWith("text/html")) {
648 InputStream is = messagePart.getInputStream();
649 BufferedReader reader = new BufferedReader(
650 new InputStreamReader(is));
651 String thisLine = reader.readLine();
652 while (thisLine != null) {
653 sb.append(thisLine).append('\n');
654 thisLine = reader.readLine();
655 }
656 }
657 message.setFlag(Flag.SEEN, true);
658 } catch (Exception ex) {
659 ex.printStackTrace();
660 }
661 sb.deleteCharAt(sb.length() - 1);
662 return sb.toString();
663 }
664
665 public Folder getFolder() {
666 return _folder;
667 }
668
669 /**
670 * @param message
671 * @param frames
672 * @param type
673 * @throws MessagingException
674 */
675 private Text addAddresses(final Message message, final FrameCreator frames,
676 final String excludeAddress, final Address[] addresses,
677 String typeTag) throws MessagingException {
678 if (addresses == null)
679 return null;
680
681 StringBuffer sb = new StringBuffer();
682 boolean foundOtherRecipients = false;
683 for (Address addy : addresses) {
684 // Only show the to flag if this message was sent to
685 // other people
686 if (excludeAddress == null
687 || !addy.toString().toLowerCase().contains(
688 excludeAddress.toLowerCase())) {
689 foundOtherRecipients = true;
690 }
691 if (sb.length() > 0) {
692 sb.append(", ");
693 }
694 sb.append(addy.toString());
695 }
696 Text reply = null;
697 if (foundOtherRecipients) {
698 reply = frames.addText(typeTag + sb.toString(), null, null, null,
699 false);
700 }
701 return reply;
702 }
703
704 private Text addRecipients(final Message message,
705 final FrameCreator frames, String excludeAddress,
706 RecipientType type, String typeTag) throws MessagingException {
707 // Read and display all the recipients of the message
708 Address[] toRecipients = message.getRecipients(type);
709 return addAddresses(message, frames, excludeAddress, toRecipients,
710 typeTag);
711 }
712}
Note: See TracBrowser for help on using the repository browser.