///////////////////////////////////////////////////////////////////////////// // // Project ProjectForge Community Edition // www.projectforge.org // // Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de) // // ProjectForge is dual-licensed. // // This community edition is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 3 of the License. // // This community edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General // Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, see http://www.gnu.org/licenses/. // ///////////////////////////////////////////////////////////////////////////// package org.projectforge.mail; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Properties; import javax.mail.Address; import javax.mail.FetchProfile; import javax.mail.Flags; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.Session; import javax.mail.Store; import javax.mail.search.FlagTerm; import org.projectforge.core.ConfigXml; /** * Connects to a mail server and receives mails. * @author Kai Reinhard (k.reinhard@micromata.de) */ public class MailAccount { private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(MailAccount.class); public static final String INBOX = "INBOX"; private Folder folder; private Store store; private MailAccountConfig mailAcccountConfig; public MailAccount(final MailAccountConfig mailAccountConfig) { this.mailAcccountConfig = mailAccountConfig; } /** Gets the stored email of the given user. */ public Mail getMail(final int mailId) { final Mail mail = new Mail(); try { setEnvelope(mail, folder.getMessage(mailId)); mail.setContent(getContent(mail.getMessage())); disconnect(); } catch (IndexOutOfBoundsException ex) { log.warn("Message number out of range: " + mailId); } catch (MessagingException ex) { log.warn("", ex); } catch (IOException ex) { log.warn("", ex); } return mail; } /** * Gets a list of all Emails matching the given filter. * @return ArrayList of all found Email. */ public Mail[] getMails(final MailFilter filter) { if (folder == null || folder.isOpen() == false) { log.error("Folder is not opened, can't get mails: " + this.mailAcccountConfig.getUsername() + "@" + this.mailAcccountConfig.getHostname() + " via " + this.mailAcccountConfig.getProtocol()); return null; } final List<Mail> table = new ArrayList<Mail>(); try { int totalMessages = folder.getMessageCount(); log.debug("New messages: " + folder.getNewMessageCount()); log.debug("Total messages: " + totalMessages); if (totalMessages == 0) { return new Mail[0]; } // Attributes & Flags for all messages .. final Message[] msgs; if (filter.isOnlyRecent() == true) { msgs = folder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)); } else { msgs = folder.getMessages(); } // Use a suitable FetchProfile final FetchProfile fp = new FetchProfile(); fp.add(FetchProfile.Item.ENVELOPE); fp.add(FetchProfile.Item.FLAGS); fp.add("X-Mailer"); folder.fetch(msgs, fp); for (int i = 0; i < msgs.length; i++) { final Mail mail = new Mail(); setEnvelope(mail, msgs[i]); mail.setContent(getContent(mail.getMessage())); // if (filter == null || (mail.isRecent() == true && filter.isRecent() == true) // || (mail.isSeen() == true && filter.isSeen() == true) // || (mail.isDeleted() == true && filter.isDeleted() == true)) { table.add(mail); // } } // No sort the table by date: final Mail[] mailArray = new Mail[table.size()]; table.toArray(mailArray); Arrays.sort(mailArray); return mailArray; } catch (javax.mail.MessagingException ex) { log.info(ex.getMessage(), ex); throw new RuntimeException(ex); } catch (IOException ex) { log.info(ex.getMessage(), ex); throw new RuntimeException(ex); } } /** * Opens the connection to the mailserver. Don't forget to call disconnect if this method returns true! * @param mbox The folder name to open. If null then the default folder will be opened. * @param readwrite If false then the mbox is connected in readonly mode. * @return true on success, otherwise false. */ public boolean connect(final String mbox, final boolean readwrite) { try { // Get a Properties object final Properties props = new Properties(); if (ConfigXml.getInstance().getUsersSSLSocketFactory() != null) { props .put("mail." + mailAcccountConfig.getProtocol() + ".ssl.socketFactory", ConfigXml.getInstance().getUsersSSLSocketFactory()); } final Session session = Session.getDefaultInstance(props, null); // Get a Store object store = null; try { store = session.getStore(mailAcccountConfig.getProtocol()); } catch (javax.mail.NoSuchProviderException ex) { log.error(ex.getMessage(), ex); // serverData.setErrorMessageKey("mail.error.noSuchProviderException"); return false; } if (mailAcccountConfig.getPort() > 0) { store.connect(mailAcccountConfig.getHostname(), mailAcccountConfig.getPort(), mailAcccountConfig.getUsername(), mailAcccountConfig .getPassword()); } else { store.connect(mailAcccountConfig.getHostname(), mailAcccountConfig.getUsername(), mailAcccountConfig.getPassword()); } // Open the Folder folder = store.getDefaultFolder(); if (folder == null) { // serverData.setErrorMessageKey("mail.error.noDefaultFolder"); return false; } if (mbox != null) { folder = folder.getFolder(mbox); if (folder == null) { // serverData.setErrorMessageKey("mail.error.invalidFolder"); return false; } } if (readwrite == true) { // try to open read/write and if that fails try read-only try { folder.open(Folder.READ_WRITE); } catch (MessagingException ex) { log.error("Can't open mbox in read-write mode, try to open folder in read-only mode instead: " + ex.getMessage()); folder.open(Folder.READ_ONLY); } } else { folder.open(Folder.READ_ONLY); } } catch (javax.mail.MessagingException ex) { // serverData.setErrorMessageKey("mail.error.messagingException"); // serverData.setOriginalErrorMessage(ex.getMessage()); log.info(ex.getMessage(), ex); return false; } return true; } /** * Disconnects the folder and store if given and is opened yet. * @return */ public boolean disconnect() { boolean success = true; if (folder != null && folder.isOpen() == true) { try { folder.close(false); } catch (MessagingException ex) { log.error("Exception encountered while trying tho close the folder: " + ex, ex); success = false; } } if (store != null && store.isConnected() == true) { try { store.close(); } catch (MessagingException ex) { log.error("Exception encountered while trying to close the store: " + ex, ex); success = false; } } return success; } protected void setEnvelope(final Mail mail, final Message message) throws javax.mail.MessagingException { mail.setMessage(message); Address[] addr; // ID mail.setMessageNumber(message.getMessageNumber()); // FROM StringBuffer buf = new StringBuffer(); addr = message.getFrom(); if (addr != null) { for (int j = 0; j < addr.length; j++) { if (j > 0) buf.append(","); buf.append(addr[j].toString()); } } mail.setFrom(buf.toString()); // TO addr = message.getRecipients(Message.RecipientType.TO); buf = new StringBuffer(); if (addr != null) { for (int j = 0; j < addr.length; j++) { if (j > 0) buf.append(","); buf.append(addr[j].toString()); } } mail.setTo(buf.toString()); // SUBJECT mail.setSubject(message.getSubject()); // DATE final Date date = message.getSentDate(); if (date != null) { mail.setDate(date); } else { // Needed for compareTo (assume 1.1.1970) mail.setDate(new Date(0)); } // FLAGS final Flags flags = message.getFlags(); final Flags.Flag[] systemFlags = flags.getSystemFlags(); // get the system flags for (int i = 0; i < systemFlags.length; i++) { final Flags.Flag flag = systemFlags[i]; if (flag == Flags.Flag.ANSWERED) { // Ignore this flag } else if (flag == Flags.Flag.DELETED) { mail.setDeleted(true); } else if (flag == Flags.Flag.DRAFT) { // Ignore this flag } else if (flag == Flags.Flag.FLAGGED) { // Ignore this flag } else if (flag == Flags.Flag.RECENT) { mail.setRecent(true); } else if (flag == Flags.Flag.SEEN) { mail.setSeen(true); } else { // skip it } } } private String getContent(final Part msg) throws MessagingException, IOException { final StringBuffer buf = new StringBuffer(); getContent(msg, buf); return buf.toString(); } private void getContent(final Part msg, final StringBuffer buf) throws MessagingException, IOException { if (log.isDebugEnabled() == true) { log.debug("CONTENT-TYPE: " + msg.getContentType()); } String filename = msg.getFileName(); if (filename != null) { log.debug("FILENAME: " + filename); } // Using isMimeType to determine the content type avoids // fetching the actual content data until we need it. if (msg.isMimeType("text/plain")) { log.debug("This is plain text"); try { buf.append(msg.getContent()); } catch (UnsupportedEncodingException ex) { buf.append("Unsupported charset by java mail, sorry: " + "CONTENT-TYPE=[" + msg.getContentType() + "]"); } } else if (msg.isMimeType("text/html")) { log.debug("This is html text"); buf.append(msg.getContent()); } else if (msg.isMimeType("multipart/*")) { log.debug("This is a Multipart"); final Multipart multiPart = (Multipart) msg.getContent(); int count = multiPart.getCount(); for (int i = 0; i < count; i++) { if (i > 0) { buf.append("\n----------\n"); } getContent(multiPart.getBodyPart(i), buf); } } else if (msg.isMimeType("message/rfc822")) { log.debug("This is a Nested Message"); buf.append(msg.getContent()); } else { log.debug("This is an unknown type"); // If we actually want to see the data, and it's not a // MIME type we know, fetch it and check its Java type. final Object obj = msg.getContent(); if (obj instanceof String) { buf.append(obj); } else if (obj instanceof InputStream) { log.debug("Inputstream"); buf.append("Attachement: "); if (filename != null) { buf.append(filename); } else { buf.append("Unsupported format (not a file)."); } } else { log.error("Should not occur"); buf.append("Unsupported type"); } } } }