/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. */ package com.liferay.mail.reader.mailbox; import com.liferay.mail.reader.attachment.AttachmentHandler; import com.liferay.mail.reader.attachment.DefaultAttachmentHandler; import com.liferay.mail.reader.constants.MailConstants; import com.liferay.mail.reader.exception.MailException; import com.liferay.mail.reader.exception.NoSuchFolderException; import com.liferay.mail.reader.exception.NoSuchMessageException; import com.liferay.mail.reader.internal.imap.IMAPAccessor; import com.liferay.mail.reader.internal.imap.IMAPConnection; import com.liferay.mail.reader.internal.util.AccountLock; import com.liferay.mail.reader.model.Account; import com.liferay.mail.reader.model.Attachment; import com.liferay.mail.reader.model.Folder; import com.liferay.mail.reader.model.MailFile; import com.liferay.mail.reader.model.Message; import com.liferay.mail.reader.model.MessagesDisplay; import com.liferay.mail.reader.service.AccountLocalServiceUtil; import com.liferay.mail.reader.service.AttachmentLocalServiceUtil; import com.liferay.mail.reader.service.FolderLocalServiceUtil; import com.liferay.mail.reader.service.MessageLocalServiceUtil; import com.liferay.petra.mail.InternetAddressUtil; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.language.LanguageUtil; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.ListUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import javax.mail.Address; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; /** * @author Scott Lee */ public class IMAPMailbox extends BaseMailbox { public IMAPMailbox(User user, Account account, String password) { setUser(user); setAccount(account); if (account != null) { _imapAccessor = new IMAPAccessor(user, account, password); } else { _imapAccessor = null; } } @Override public Folder addFolder(String displayName) throws PortalException { String[] names = _imapAccessor.addFolder(displayName); return FolderLocalServiceUtil.addFolder( user.getUserId(), account.getAccountId(), names[0], names[1], 0); } @Override public void deleteAttachment(long attachmentId) throws PortalException { AttachmentLocalServiceUtil.deleteAttachment(attachmentId); } @Override public void deleteFolder(long folderId) throws PortalException { if ((account.getDraftFolderId() == folderId) || (account.getInboxFolderId() == folderId) || (account.getSentFolderId() == folderId) || (account.getTrashFolderId() == folderId)) { throw new MailException(MailException.FOLDER_REQUIRED); } _imapAccessor.deleteFolder(folderId); FolderLocalServiceUtil.deleteFolder(folderId); } @Override public void deleteMessages(long folderId, long[] messageIds) throws PortalException { if ((account.getDraftFolderId() != folderId) && (account.getTrashFolderId() != folderId)) { Folder trashFolder = FolderLocalServiceUtil.getFolder( account.getTrashFolderId()); _imapAccessor.moveMessages( folderId, trashFolder.getFolderId(), messageIds, true); } else { _imapAccessor.deleteMessages(folderId, messageIds); } } @Override public AttachmentHandler getAttachment(long attachmentId) throws IOException, PortalException { Attachment attachment = AttachmentLocalServiceUtil.getAttachment( attachmentId); Message message = MessageLocalServiceUtil.getMessage( attachment.getMessageId()); if (account.getDraftFolderId() == attachment.getFolderId()) { return new DefaultAttachmentHandler( AttachmentLocalServiceUtil.getInputStream(attachmentId), null); } else { return _imapAccessor.getAttachment( attachment.getFolderId(), message.getRemoteMessageId(), attachment.getContentPath()); } } @Override public Message getMessage( long folderId, String keywords, int messageNumber, String orderByField, String orderByType) throws PortalException { MessagesDisplay messagesDisplay = getMessagesDisplay( folderId, keywords, messageNumber, 1, orderByField, orderByType); List<Message> messages = messagesDisplay.getMessages(); return messages.get(0); } @Override public MessagesDisplay getMessagesDisplay( long folderId, String keywords, int pageNumber, int messagesPerPage, String orderByField, String orderByType) throws PortalException { if (orderByField.equals(MailConstants.ORDER_BY_ADDRESS)) { orderByField = "sender"; if (account.getSentFolderId() == folderId) { orderByField = "to"; } } else if (!orderByField.equals(MailConstants.ORDER_BY_SENT_DATE) && !orderByField.equals(MailConstants.ORDER_BY_SIZE) && !orderByField.equals(MailConstants.ORDER_BY_SUBJECT)) { orderByField = MailConstants.ORDER_BY_SENT_DATE; } List<Message> messages = new ArrayList<>(messagesPerPage); int messageCount = MessageLocalServiceUtil.populateMessages( messages, folderId, keywords, pageNumber, messagesPerPage, orderByField, orderByType); if (Validator.isNull(keywords) && (account.getDraftFolderId() != folderId)) { Folder folder = FolderLocalServiceUtil.getFolder(folderId); messageCount = folder.getRemoteMessageCount(); } return new MessagesDisplay( messages, pageNumber, messagesPerPage, messageCount); } @Override public boolean hasNewMessages(long folderId) throws PortalException { return _imapAccessor.hasNewMessages(folderId); } @Override public void moveMessages(long folderId, long[] messageIds) throws PortalException { for (long messageId : messageIds) { Message message = MessageLocalServiceUtil.getMessage(messageId); Account account = AccountLocalServiceUtil.getAccount( message.getAccountId()); long sourceFolderId = message.getFolderId(); if ((account.getDraftFolderId() == sourceFolderId) || (account.getSentFolderId() == sourceFolderId)) { throw new MailException( MailException.FOLDER_INVALID_DESTINATION); } _imapAccessor.moveMessages( sourceFolderId, folderId, new long[] {messageId}, true); } } @Override public InternetAddress[] parseAddresses(String addresses) throws PortalException { InternetAddress[] internetAddresses = new InternetAddress[0]; try { internetAddresses = InternetAddress.parse(addresses, true); for (int i = 0; i < internetAddresses.length; i++) { InternetAddress internetAddress = internetAddresses[i]; if (!Validator.isEmailAddress(internetAddress.getAddress())) { StringBundler sb = new StringBundler(4); sb.append(internetAddress.getPersonal()); sb.append(StringPool.LESS_THAN); sb.append(internetAddress.getAddress()); sb.append(StringPool.GREATER_THAN); throw new MailException( MailException.MESSAGE_INVALID_ADDRESS, sb.toString()); } } } catch (AddressException ae) { throw new MailException( MailException.MESSAGE_INVALID_ADDRESS, ae, addresses); } return internetAddresses; } @Override public void renameFolder(long folderId, String displayName) throws PortalException { Folder folder = FolderLocalServiceUtil.getFolder(folderId); String[] names = _imapAccessor.renameFolder(folderId, displayName); FolderLocalServiceUtil.updateFolder( folderId, names[0], names[1], folder.getRemoteMessageCount()); } @Override public Message saveDraft( long accountId, long messageId, String to, String cc, String bcc, String subject, String body, List<MailFile> mailFiles) throws PortalException { Account account = AccountLocalServiceUtil.getAccount(accountId); StringBundler sb = new StringBundler(); sb.append(user.getFullName()); sb.append(" <"); sb.append(account.getAddress()); sb.append(StringPool.GREATER_THAN); String sender = sb.toString(); Address[] toAddresses = parseAddresses(to); Address[] ccAddresses = parseAddresses(cc); Address[] bccAddresses = parseAddresses(bcc); if ((toAddresses.length == 0) && (ccAddresses.length == 0) && (bccAddresses.length == 0)) { throw new MailException(MailException.MESSAGE_HAS_NO_RECIPIENTS); } Message message = null; if (messageId != 0) { message = MessageLocalServiceUtil.updateMessage( messageId, account.getDraftFolderId(), sender, InternetAddressUtil.toString(toAddresses), InternetAddressUtil.toString(ccAddresses), InternetAddressUtil.toString(bccAddresses), null, subject, body, String.valueOf(MailConstants.FLAG_DRAFT), 0); } else { message = MessageLocalServiceUtil.addMessage( user.getUserId(), account.getDraftFolderId(), sender, to, cc, bcc, null, subject, body, String.valueOf(MailConstants.FLAG_DRAFT), 0, null); } if (mailFiles == null) { return message; } for (MailFile mailFile : mailFiles) { AttachmentLocalServiceUtil.addAttachment( user.getUserId(), message.getMessageId(), null, mailFile.getFileName(), mailFile.getSize(), mailFile.getFile()); } return message; } @Override public void sendMessage(long accountId, long messageId) throws PortalException { Account account = AccountLocalServiceUtil.getAccount(accountId); Message message = MessageLocalServiceUtil.getMessage(messageId); Address[] toAddresses = parseAddresses(message.getTo()); Address[] ccAddresses = parseAddresses(message.getCc()); Address[] bccAddresses = parseAddresses(message.getBcc()); if ((toAddresses.length == 0) && (ccAddresses.length == 0) && (bccAddresses.length == 0)) { throw new MailException(MailException.MESSAGE_HAS_NO_RECIPIENTS); } List<Attachment> attachments = AttachmentLocalServiceUtil.getAttachments(messageId); List<MailFile> mailFiles = new ArrayList<>(); for (Attachment attachment : attachments) { File file = AttachmentLocalServiceUtil.getFile( attachment.getAttachmentId()); MailFile mailFile = new MailFile( file, attachment.getFileName(), attachment.getSize()); mailFiles.add(mailFile); } _imapAccessor.sendMessage( account.getPersonalName(), account.getAddress(), toAddresses, ccAddresses, bccAddresses, message.getSubject(), message.getBody(), mailFiles); MessageLocalServiceUtil.deleteMessage(messageId); } @Override public void synchronize() throws PortalException { if (_log.isDebugEnabled()) { _log.debug( "Synchronizing all folders for accountId " + account.getAccountId()); } updateFolders(); List<Folder> folders = FolderLocalServiceUtil.getFolders( account.getAccountId()); String key = AccountLock.getKey( user.getUserId(), account.getAccountId()); if (AccountLock.acquireLock(key)) { try { for (Folder folder : folders) { _imapAccessor.storeEnvelopes(folder.getFolderId(), true); } } finally { AccountLock.releaseLock(key); } } } @Override public void synchronizeFolder(long folderId) throws PortalException { if (_log.isDebugEnabled()) { _log.debug("Synchronizing folder " + folderId); } String key = AccountLock.getKey( user.getUserId(), account.getAccountId()); if (AccountLock.acquireLock(key)) { try { _imapAccessor.storeEnvelopes(folderId, false); } finally { AccountLock.releaseLock(key); } } } @Override public void synchronizeMessage(long messageId) throws PortalException { Message message = MessageLocalServiceUtil.getMessage(messageId); long remoteMessageId = message.getRemoteMessageId(); if (remoteMessageId == 0) { return; } try { _imapAccessor.storeContents( message.getFolderId(), new long[] {message.getRemoteMessageId()}); } catch (IOException ioe) { throw new MailException(ioe); } } @Override public void synchronizePage( long folderId, int pageNumber, int messagesPerPage) throws PortalException { long[] remoteMessageIds = _imapAccessor.getMessageUIDs( folderId, pageNumber, messagesPerPage); List<Long> missingRemoteMessageIdsList = new ArrayList<>(); for (int i = 0; i < remoteMessageIds.length; i++) { long remoteMessageId = remoteMessageIds[i]; try { MessageLocalServiceUtil.getMessage(folderId, remoteMessageId); } catch (NoSuchMessageException nsme) { missingRemoteMessageIdsList.add(remoteMessageId); } } if (!missingRemoteMessageIdsList.isEmpty()) { long[] missingRemoteMessageIds = ArrayUtil.toArray( missingRemoteMessageIdsList.toArray( new Long[missingRemoteMessageIdsList.size()])); _imapAccessor.storeEnvelopes(folderId, missingRemoteMessageIds); } } @Override public void updateFlags( long folderId, long[] messageIds, int flag, boolean value) throws PortalException { Folder folder = FolderLocalServiceUtil.getFolder(folderId); Account account = AccountLocalServiceUtil.getAccount( folder.getAccountId()); for (long messageId : messageIds) { MessageLocalServiceUtil.updateFlag(messageId, flag, value); } if (account.getDraftFolderId() == folderId) { _imapAccessor.updateFlags(folderId, messageIds, flag, value, false); } else { _imapAccessor.updateFlags(folderId, messageIds, flag, value, true); } } @Override public void updateFolders() throws PortalException { if (_log.isDebugEnabled()) { _log.debug("Updating folders"); } List<javax.mail.Folder> jxFolders = _imapAccessor.getFolders(); for (javax.mail.Folder jxFolder : jxFolders) { try { FolderLocalServiceUtil.getFolder( account.getAccountId(), jxFolder.getFullName()); } catch (NoSuchFolderException nsfe) { FolderLocalServiceUtil.addFolder( user.getUserId(), account.getAccountId(), jxFolder.getFullName(), jxFolder.getName(), 0); } } long draftFolderId = account.getDraftFolderId(); long inboxFolderId = account.getInboxFolderId(); long sentFolderId = account.getSentFolderId(); long trashFolderId = account.getTrashFolderId(); if (draftFolderId <= 0) { draftFolderId = getFolderId("draft"); } if (inboxFolderId <= 0) { inboxFolderId = getFolderId("inbox"); } if (sentFolderId <= 0) { sentFolderId = getFolderId("sent"); } if (sentFolderId <= 0) { sentFolderId = getFolderId("sent-mail"); } if (trashFolderId <= 0) { trashFolderId = getFolderId("trash"); } updateFolders( inboxFolderId, draftFolderId, sentFolderId, trashFolderId); } @Override public void validateAccount( String incomingHostName, int incomingPort, boolean incomingSecure, String outgoingHostName, int outgoingPort, boolean outgoingSecure, String login, String password) throws PortalException { IMAPConnection imapConnection = new IMAPConnection( incomingHostName, incomingPort, incomingSecure, outgoingHostName, outgoingPort, outgoingSecure, login, password); imapConnection.testConnection(); } protected long getFolderId(String type) { List<String> words = new ArrayList<>(); for (Locale locale : LanguageUtil.getAvailableLocales()) { String translation = StringUtil.toLowerCase( LanguageUtil.get(locale, type)); words.addAll(ListUtil.toList(translation.split(StringPool.SPACE))); } List<Folder> folders = FolderLocalServiceUtil.getFolders( account.getAccountId()); for (String word : words) { for (Folder folder : folders) { String folderName = StringUtil.toLowerCase( folder.getDisplayName()); if (folderName.contains(word)) { return folder.getFolderId(); } } } return 0; } private static final Log _log = LogFactoryUtil.getLog(IMAPMailbox.class); private final IMAPAccessor _imapAccessor; }