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

Last change on this file since 1280 was 1280, checked in by bln4, 5 years ago

Renamed MailMode action to ToggleBay
Renamed FrameCreator enums to more desirable names (David request)
Created test for altered functionality of FrameCreator as documented below.

FrameCreator now more cleanly obeys the specification of the enum parameter to its constructor. For example, override existing frameset ensures that the old frameset has been deleted (moved to trash).

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