0001: /*
0002: * Copyright (c) 2001 - 2005 ivata limited.
0003: * All rights reserved.
0004: * -----------------------------------------------------------------------------
0005: * ivata groupware may be redistributed under the GNU General Public
0006: * License as published by the Free Software Foundation;
0007: * version 2 of the License.
0008: *
0009: * These programs are free software; you can redistribute them and/or
0010: * modify them under the terms of the GNU General Public License
0011: * as published by the Free Software Foundation; version 2 of the License.
0012: *
0013: * These programs are distributed in the hope that they will be useful,
0014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0016: *
0017: * See the GNU General Public License in the file LICENSE.txt for more
0018: * details.
0019: *
0020: * If you would like a copy of the GNU General Public License write to
0021: *
0022: * Free Software Foundation, Inc.
0023: * 59 Temple Place - Suite 330
0024: * Boston, MA 02111-1307, USA.
0025: *
0026: *
0027: * To arrange commercial support and licensing, contact ivata at
0028: * http://www.ivata.com/contact.jsp
0029: * -----------------------------------------------------------------------------
0030: * $Log: MailImpl.java,v $
0031: * Revision 1.12 2005/10/11 18:42:35 colinmacleod
0032: * Fixed checkstyle issues.
0033: *
0034: * Revision 1.11 2005/10/11 18:25:13 colinmacleod
0035: * Removed bcc and cc from simple send method.
0036: *
0037: * Revision 1.10 2005/10/10 16:06:39 colinmacleod
0038: * Merged in changes from releases 0.11.2 and 0.11.3.
0039: *
0040: * Revision 1.9 2005/10/09 09:39:27 colinmacleod
0041: * Merged changes from ivata groupware v0.11.2 back into main trunk.
0042: *
0043: * Revision 1.8 2005/10/02 14:08:59 colinmacleod
0044: * Added/improved log4j logging.
0045: *
0046: * Revision 1.7 2005/09/14 16:16:52 colinmacleod
0047: * Removed unused local and class variables.
0048: * Added serialVersionUID.
0049: *
0050: * Revision 1.6.2.1 2005/10/08 17:36:37 colinmacleod
0051: * SettingDateFormatter now requires SecuritySession in constructor.
0052: *
0053: * Revision 1.6 2005/04/29 02:48:20 colinmacleod
0054: * Data bugfixes.
0055: * Changed primary key back to Integer.
0056: *
0057: * Revision 1.5 2005/04/27 15:20:09 colinmacleod
0058: * Now implements Serializable.
0059: *
0060: * Revision 1.4 2005/04/22 10:59:12 colinmacleod
0061: * Added logging when there is no mail
0062: * server and reordered this file alphabetically.
0063: *
0064: * Revision 1.3 2005/04/10 20:10:08 colinmacleod
0065: * Added new themes.
0066: * Changed id type to String.
0067: * Changed i tag to em and b tag to strong.
0068: * Improved PicoContainerFactory with NanoContainer scripts.
0069: *
0070: * Revision 1.2 2005/04/09 17:20:00 colinmacleod
0071: * Changed copyright text to GPL v2 explicitly.
0072: *
0073: * Revision 1.1.1.1 2005/03/10 17:51:15 colinmacleod
0074: * Restructured ivata op around Hibernate/PicoContainer.
0075: * Renamed ivata groupware.
0076: *
0077: * Revision 1.6 2004/11/12 15:57:23 colinmacleod
0078: * Removed dependencies on SSLEXT.
0079: * Moved Persistence classes to ivata masks.
0080: *
0081: * Revision 1.5 2004/11/03 17:15:38 colinmacleod
0082: * added addUserEmailAddresses method.
0083: * Improved setUesrAliases to check telecom addresses in the person.
0084: *
0085: * Revision 1.4 2004/09/30 15:09:33 colinmacleod
0086: * Bug fixes
0087: *
0088: * Revision 1.3 2004/07/18 21:59:24 colinmacleod
0089: * Removed Person from User - now you need to use addressbook/persistence
0090: * manager to find the person (makes the app run faster.)
0091: *
0092: * Revision 1.2 2004/07/13 19:48:12 colinmacleod
0093: * Moved project to POJOs from EJBs.
0094: * Applied PicoContainer to services layer (replacing session EJBs).
0095: * Applied Hibernate to persistence layer (replacing entity EJBs).
0096: *
0097: * Revision 1.1 2004/03/27 10:31:26 colinmacleod
0098: * Split off business logic from remote facades to POJOs.
0099: *
0100: * Revision 1.6 2004/03/21 21:16:39 colinmacleod
0101: * Shortened name to ivata op.
0102: *
0103: * Revision 1.5 2004/03/21 20:51:51 colinmacleod
0104: * Change SecurityServer into interface.
0105: * Added checking of mail server.
0106: *
0107: * Revision 1.4 2004/03/10 22:43:13 colinmacleod
0108: * Added security server exception handling.
0109: *
0110: * Revision 1.3 2004/02/10 19:57:26 colinmacleod
0111: * Changed email address.
0112: *
0113: * Revision 1.2 2004/02/01 22:07:32 colinmacleod
0114: * Added full names to author tags
0115: *
0116: * Revision 1.1.1.1 2004/01/27 20:59:56 colinmacleod
0117: * Moved ivata openportal to SourceForge..
0118: *
0119: * Revision 1.6 2003/12/12 13:24:34 jano
0120: * fixing webmail functionality
0121: *
0122: * Revision 1.5 2003/11/03 11:31:06 jano
0123: * commiting webmail,
0124: * tryinjg to fix deploying problem
0125: *
0126: * Revision 1.4 2003/10/28 13:27:51 jano
0127: * commiting webmail,
0128: * still fixing compile and building openGroupware project
0129: *
0130: * Revision 1.3 2003/10/15 14:13:00 jano
0131: * converting to XDoclet
0132: *
0133: * Revision 1.2 2003/10/15 14:11:33 colin
0134: * fixing for XDoclet
0135: *
0136: * Revision 1.25 2003/07/15 06:43:40 peter
0137: * fixed the last fix
0138: *
0139: * Revision 1.24 2003/07/15 06:01:59 peter
0140: * fixed message text bugs and composed attachments size bug
0141: *
0142: * Revision 1.23 2003/07/14 15:04:22 jano
0143: * peter: fixed invisible attachments problem
0144: *
0145: * Revision 1.22 2003/07/14 14:52:24 jano
0146: * fixing bug in mailBean
0147: *
0148: * Revision 1.21 2003/07/11 06:31:06 peter
0149: * fixed text logic in alternative multiparts
0150: *
0151: * Revision 1.20 2003/07/07 13:43:32 peter
0152: * fixed getAttachment for cases with fileName
0153: *
0154: * Revision 1.19 2003/06/22 21:28:10 peter
0155: * re-fixed attachment handling for multipart cases
0156: *
0157: * Revision 1.18 2003/06/20 18:31:03 peter
0158: * added incorrectly composed mail forwards and self contained attachment like
0159: * email handling
0160: *
0161: * Revision 1.17 2003/06/19 10:06:08 jano
0162: * add check boxies in registration proces of customer
0163: *
0164: * Revision 1.16 2003/06/02 06:30:19 peter
0165: * create reply and forward message fixed
0166: *
0167: * Revision 1.15 2003/05/28 05:41:21 peter
0168: * added fileName as secondary attachments identifier, when contentId not
0169: * present
0170: *
0171: * Revision 1.14 2003/05/27 17:15:12 peter
0172: * getAttachment fixed, private getAttachment methnod added
0173: *
0174: * Revision 1.13 2003/05/15 08:21:12 peter
0175: * fixed addMultipart logic - some multipart types weren't included
0176: *
0177: * Revision 1.12 2003/05/14 11:22:07 peter
0178: * fixed bug: getDOFromJavamailMessage was called after folder closed in
0179: * appendAttachmnets
0180: *
0181: * Revision 1.11 2003/05/13 15:24:18 peter
0182: * attachment compose changes
0183: *
0184: * Revision 1.10 2003/05/12 16:31:13 peter
0185: * attachment compose changes
0186: *
0187: * Revision 1.9 2003/04/01 17:58:52 colin
0188: * removed boolean from InternetAddress constructor (marked as private in my JVM)
0189: *
0190: * Revision 1.8 2003/03/25 16:18:30 peter
0191: * fixed email address validation
0192: *
0193: * Revision 1.7 2003/03/25 08:23:29 jano
0194: * if there is no message in folder -> return null
0195: * and validate the email addresses
0196: *
0197: * Revision 1.6 2003/03/14 10:26:46 jano
0198: * adding backdoor man functionality
0199: * backdoor man = briezky
0200: *
0201: * Revision 1.5 2003/03/03 16:57:12 colin
0202: * converted localization to automatic paths
0203: * added labels
0204: * added mandatory fieldName attribute
0205: *
0206: * Revision 1.4 2003/02/28 10:23:27 peter
0207: * fixed handling of plain - one part messages in getDOFromJavaMailMessage
0208: *
0209: * Revision 1.3 2003/02/27 17:23:09 peter
0210: * Changed the return type of getAttachment to FileContentDO
0211: *
0212: * Revision 1.2 2003/02/25 11:53:33 colin
0213: * bugfixes and minor restructuring
0214: *
0215: * Revision 1.1 2003/02/24 19:09:24 colin
0216: * moved to business
0217: *
0218: * Revision 1.38 2003/02/20 20:26:15 colin
0219: * improved validation by adding ValidationField and ValidationException
0220: *
0221: * Revision 1.37 2003/02/04 17:39:21 colin
0222: * copyright notice
0223: *
0224: * Revision 1.36 2003/01/15 15:43:56 colin
0225: * re-implemented:
0226: * forwarding/replying (also to multiple messages)
0227: * moving messages
0228: *
0229: * Revision 1.35 2002/11/20 09:21:23 peter
0230: * removed duplicated function contents getDOFrom... (Jbuilder bug)
0231: *
0232: * Revision 1.34 2002/11/17 20:01:24 colin
0233: * speed improvements in findMessagesInFolder...
0234: *
0235: * Revision 1.33 2002/11/12 09:12:38 colin
0236: * structural changes. currently mail bean composes and reads messages but
0237: * attachment & thread handling not active
0238: *
0239: * Revision 1.32 2002/10/25 08:31:44 peter
0240: * mailFolderSent setting name changed to emailFolderSent
0241: *
0242: * Revision 1.31 2002/10/23 12:44:37 jano
0243: * using new method for get System userName
0244: *
0245: * Revision 1.30 2002/10/23 09:18:59 jano
0246: * there is a new method for generating SystemUserName
0247: *
0248: * Revision 1.29 2002/10/18 09:18:48 colin
0249: * check users to make sure they are enabled before sending them mail
0250: *
0251: * Revision 1.28 2002/10/14 11:15:46 peter
0252: * fixed a bug in (precomposed) send method, the cc fields work now
0253: *
0254: * Revision 1.27 2002/10/11 10:05:38 jano
0255: * add PREFIX to user name for difren site
0256: *
0257: * Revision 1.26 2002/10/10 14:03:57 peter
0258: * changes due to demo version
0259: *
0260: * Revision 1.25 2002/10/01 05:59:47 peter
0261: * modifications in (precomposed) send method
0262: *
0263: * Revision 1.24 2002/09/17 07:26:24 peter
0264: * working version
0265: *
0266: * Revision 1.23 2002/09/16 16:26:40 peter
0267: * the attachments stuff works....
0268: *
0269: * Revision 1.22 2002/09/13 13:59:17 peter
0270: * appendMessages and setDO methods tuned...
0271: * it still doesn't work properly
0272: *
0273: * Revision 1.21 2002/09/12 15:55:25 peter
0274: * tuned createMessage and setDO
0275: *
0276: * Revision 1.20 2002/09/12 07:26:19 colin
0277: * added vacation message and user alias methods
0278: *
0279: * Revision 1.19 2002/09/11 15:57:48 peter
0280: * finished createMessage and setDO, debugging needed yet
0281: *
0282: * Revision 1.18 2002/09/11 11:33:12 peter
0283: * moveMessage works, works on createMessage and setDO
0284: *
0285: * Revision 1.17 2002/09/10 15:38:51 peter
0286: * MailBean: works on methods
0287: *
0288: * Revision 1.16 2002/09/10 14:18:51 peter
0289: * MailBean: works on methods
0290: *
0291: * Revision 1.15 2002/09/10 08:20:16 peter
0292: * MailBean: added moveMessage method
0293: *
0294: * Revision 1.14 2002/09/09 16:07:37 peter
0295: * added and modified methods in mail/MailBean
0296: *
0297: * Revision 1.13 2002/09/09 08:27:24 colin
0298: * changed mail bean from stateful to stateless
0299: * added new MailSession class
0300: *
0301: * Revision 1.12 2002/08/30 09:50:31 colin
0302: * changed canUser... methods to just can...
0303: *
0304: * Revision 1.11 2002/08/29 12:23:06 peter
0305: * mail display works...
0306: *
0307: * Revision 1.10 2002/08/27 15:26:25 peter
0308: * worked on getDO, should be finished
0309: *
0310: * Revision 1.9 2002/08/26 15:30:14 peter
0311: * MessageDO integration, not finished yet
0312: *
0313: * Revision 1.8 2002/08/26 11:15:47 peter
0314: * added getDo and the basic methods work
0315: *
0316: * Revision 1.7 2002/08/23 08:09:37 peter
0317: * design for MailBean methods, display so far
0318: *
0319: * Revision 1.6 2002/08/16 12:35:22 peter
0320: * fiixed a minor bug in getMessage method
0321: *
0322: * Revision 1.5 2002/08/16 11:59:00 peter
0323: * new mail accessing methods
0324: *
0325: * Revision 1.4 2002/08/11 11:37:50 colin
0326: * added routines to handle server activation and passivisation
0327: *
0328: * Revision 1.3 2002/07/26 13:08:06 colin
0329: * first version with mail server support
0330: *
0331: * Revision 1.2 2002/07/15 13:29:27 jano
0332: * added CreateException
0333: *
0334: * Revision 1.1 2002/07/15 07:51:04 colin
0335: * added new Mail EJB and local interface to settings
0336: * -----------------------------------------------------------------------------
0337: */
0338: package com.ivata.groupware.business.mail;
0339:
0340: import com.ivata.groupware.admin.security.server.SecurityServer;
0341: import com.ivata.groupware.admin.security.server.SecurityServerException;
0342: import com.ivata.groupware.admin.security.server.SecuritySession;
0343: import com.ivata.groupware.admin.security.user.UserDO;
0344: import com.ivata.groupware.admin.setting.Settings;
0345: import com.ivata.groupware.admin.setting.SettingsDataTypeException;
0346: import com.ivata.groupware.business.addressbook.AddressBook;
0347: import com.ivata.groupware.business.addressbook.person.PersonDO;
0348: import com.ivata.groupware.business.addressbook.telecomaddress.TelecomAddressConstants;
0349: import com.ivata.groupware.business.addressbook.telecomaddress.TelecomAddressDO;
0350: import com.ivata.groupware.business.drive.file.FileContentDO;
0351: import com.ivata.groupware.business.drive.file.FileDO;
0352: import com.ivata.groupware.business.mail.message.MessageDO;
0353: import com.ivata.groupware.business.mail.message.MessageNotFoundException;
0354: import com.ivata.groupware.business.mail.server.MailServer;
0355: import com.ivata.groupware.business.mail.server.NoMailServerException;
0356: import com.ivata.groupware.business.mail.session.MailSession;
0357: import com.ivata.groupware.util.SettingDateFormatter;
0358: import com.ivata.groupware.web.format.EmailAddressFormatter;
0359: import com.ivata.groupware.web.format.SanitizerFormat;
0360:
0361: import com.ivata.mask.Mask;
0362: import com.ivata.mask.MaskFactory;
0363: import com.ivata.mask.util.SerializedByteArray;
0364: import com.ivata.mask.util.StringHandling;
0365: import com.ivata.mask.util.SystemException;
0366: import com.ivata.mask.validation.ValidationError;
0367: import com.ivata.mask.validation.ValidationErrors;
0368: import com.ivata.mask.web.format.CharacterEntityFormat;
0369: import com.ivata.mask.web.format.FormatConstants;
0370: import com.ivata.mask.web.format.HTMLFormatter;
0371: import com.ivata.mask.web.format.LineBreakFormat;
0372: import com.ivata.mask.web.tag.webgui.list.ListColumnComparator;
0373:
0374: import org.apache.log4j.Logger;
0375:
0376: import java.io.ByteArrayOutputStream;
0377: import java.io.File;
0378: import java.io.IOException;
0379: import java.io.InputStream;
0380: import java.io.Serializable;
0381:
0382: import java.text.MessageFormat;
0383:
0384: import java.util.Arrays;
0385: import java.util.Calendar;
0386: import java.util.Collection;
0387: import java.util.Date;
0388: import java.util.GregorianCalendar;
0389: import java.util.Iterator;
0390: import java.util.List;
0391: import java.util.Set;
0392: import java.util.TreeMap;
0393: import java.util.Vector;
0394:
0395: import javax.activation.DataHandler;
0396: import javax.activation.DataSource;
0397: import javax.activation.FileDataSource;
0398:
0399: import javax.mail.Address;
0400: import javax.mail.AuthenticationFailedException;
0401: import javax.mail.Flags;
0402: import javax.mail.Folder;
0403: import javax.mail.FolderNotFoundException;
0404: import javax.mail.Message;
0405: import javax.mail.MessagingException;
0406: import javax.mail.NoSuchProviderException;
0407: import javax.mail.Part;
0408: import javax.mail.Session;
0409: import javax.mail.Store;
0410: import javax.mail.Transport;
0411: import javax.mail.internet.AddressException;
0412: import javax.mail.internet.InternetAddress;
0413: import javax.mail.internet.MimeBodyPart;
0414: import javax.mail.internet.MimeMessage;
0415: import javax.mail.internet.MimeMultipart;
0416: import javax.mail.internet.MimePart;
0417:
0418: /**
0419: * <p>This session bean provides an interface to the mail system. Every mail
0420: * operation for retrieving deleting and sending messages takes place in this
0421: * class.</p>
0422: *
0423: * @since 2002-07-12
0424: * @author Colin MacLeod
0425: * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
0426: * @author Peter Illes
0427: * @version $Revision: 1.12 $
0428: */
0429: public class MailImpl implements Mail, Serializable {
0430: /**
0431: * Serialization version (for <code>Serializable</code> interface).
0432: */
0433: private static final long serialVersionUID = 1L;
0434:
0435: /**
0436: * <p>Logger for this class.</p>
0437: */
0438: private static final Logger logger = Logger
0439: .getLogger(MailImpl.class);
0440: private AddressBook addressBook;
0441: private SettingDateFormatter dateFormatter = null;
0442: private MailServer mailServer;
0443: MaskFactory maskFactory;
0444:
0445: /**
0446: * <p>
0447: * Settings implementation. Used to retrieve the email address host.
0448: * </p>
0449: */
0450: private Settings settings;
0451:
0452: /**
0453: * <p>
0454: * Initialize the mail implementation.
0455: * </p>
0456: *
0457: * @param securityServer A valid security server for the current site. If
0458: * this is not an instance of {@link com.ivata.groupware.business.mail.server.MailServer}
0459: * the mail implementation will not be usable.
0460: * @param persistenceManager This is used to save/access data from the
0461: * persistence store.
0462: * @param addressBook This is used to read contacts email details.
0463: * @param settings Contains user defined settings and preferences.
0464: * @param dateFormatter Used to format mail dates and times.
0465: * @param idDispenser
0466: */
0467: public MailImpl(SecurityServer securityServer,
0468: AddressBook addressBook, Settings settings,
0469: MaskFactory maskFactory) {
0470: assert (securityServer != null);
0471:
0472: if (securityServer instanceof MailServer) {
0473: this .mailServer = (MailServer) securityServer;
0474: } else {
0475: logger.warn("Security server class ("
0476: + securityServer.getClass().getName()
0477: + ") is not a mail server class.");
0478: }
0479:
0480: this .settings = settings;
0481: this .addressBook = addressBook;
0482: this .maskFactory = maskFactory;
0483: }
0484:
0485: private void checkDateFormatter(SecuritySession securitySession) {
0486: if (dateFormatter == null) {
0487: dateFormatter = new SettingDateFormatter(securitySession,
0488: settings);
0489: }
0490: }
0491:
0492: /**
0493: * <p>Add a composed message to the drafts folder for later sending.</p>
0494: *
0495: * @param mailSession valid mail session to which the user should already be
0496: * logged in.
0497: * @param messageDO data object containing full details of the
0498: * message to be added to the drafts.
0499: * @return new <code>MessageDO</code> with the <code>id</code> set to the
0500: * current value in the mail system.
0501: */
0502: public MessageDO addMessageToDraftsFolder(
0503: final MailSession mailSession, final MessageDO messageDO)
0504: throws SystemException {
0505: if (logger.isDebugEnabled()) {
0506: logger
0507: .debug("addMessageToDraftsFolder(MailSession mailSession = "
0508: + mailSession
0509: + ", MessageDO messageDO = "
0510: + messageDO + ") - start");
0511: }
0512:
0513: checkDateFormatter(mailSession);
0514:
0515: Store store = mailServer.connectStore(mailSession);
0516:
0517: try {
0518: Session javaMailSession;
0519:
0520: try {
0521: javaMailSession = mailSession.getJavaMailSession();
0522: } catch (java.security.NoSuchProviderException e1) {
0523: logger
0524: .error(
0525: "addMessageToDraftsFolder(MailSession, MessageDO)",
0526: e1);
0527:
0528: throw new SystemException(e1);
0529: }
0530:
0531: // get the drafts folder in case we want to copy over an older mail
0532: Folder draftsFolder = openDraftsFolder(store, mailSession);
0533:
0534: MimeMessage newMessage = setDOToJavaMailMessage(
0535: javaMailSession, draftsFolder, messageDO);
0536:
0537: newMessage.setSentDate(Calendar.getInstance().getTime());
0538:
0539: // append the new message to the drafts folder
0540: Message[] messages = { newMessage };
0541:
0542: draftsFolder.appendMessages(messages);
0543:
0544: // note the new id
0545: messageDO.setMessageID(((MimeMessage) draftsFolder
0546: .getMessage(draftsFolder.getMessageCount()))
0547: .getMessageID());
0548:
0549: // only now can we delete/expunge the old mail from the drafts
0550: // folder
0551: draftsFolder.expunge();
0552: draftsFolder.close(true);
0553: } catch (MessagingException e1) {
0554: logger.error(
0555: "addMessageToDraftsFolder(MailSession, MessageDO)",
0556: e1);
0557:
0558: throw new SystemException(e1);
0559: } finally {
0560: try {
0561: store.close();
0562: } catch (MessagingException e) {
0563: logger.error(
0564: "Messaging exception on closing the store", e);
0565: }
0566: }
0567:
0568: if (logger.isDebugEnabled()) {
0569: logger
0570: .debug("addMessageToDraftsFolder(MailSession, MessageDO) - end - return value = "
0571: + messageDO);
0572: }
0573:
0574: return messageDO;
0575: }
0576:
0577: /**
0578: * <p>Recursive routine used for building up all attachments in the
0579: * <code>MessageDO<code> provided.</p>
0580: *
0581: * @param messagePart the multipart part or message to process.
0582: * @param messageDO the data object to add results to.
0583: * @throws GroupwareException if there is a <code>MailMessagingException</code> or
0584: * <code>IOException</code>.
0585: */
0586: private void addMultiPart(final Part messagePart,
0587: final MessageDO messageDO) throws SystemException {
0588: if (logger.isDebugEnabled()) {
0589: logger.debug("addMultiPart(Part messagePart = "
0590: + messagePart + ", MessageDO messageDO = "
0591: + messageDO + ") - start");
0592: }
0593:
0594: MimeMultipart content;
0595: MimeBodyPart subPart;
0596:
0597: List messageTextParts = new Vector();
0598:
0599: try {
0600: content = (MimeMultipart) messagePart.getContent();
0601:
0602: //go through all the subParts
0603: for (int i = 0; i < content.getCount(); i++) {
0604: subPart = (MimeBodyPart) content.getBodyPart(i);
0605:
0606: // when multipart/alternative and no text found in parent call,
0607: // store the text of the parts to select the best one after the loop
0608: if (messagePart.isMimeType("multipart/alternative")
0609: && subPart.isMimeType("text/*")
0610: && StringHandling.isNullOrEmpty(messageDO
0611: .getText())) {
0612: messageTextParts.add(subPart);
0613: } else if (messagePart.isMimeType("multipart/*")) {
0614: // other multipart types
0615: if (StringHandling.isNullOrEmpty(messageDO
0616: .getText())
0617: && (subPart.getDisposition() == null)
0618: && (subPart.getFileName() == null)) {
0619: if (subPart.isMimeType("text/*")) {
0620: messageTextParts.add(subPart);
0621: } else if (subPart.isMimeType("multipart/*")) {
0622: addMultiPart((Part) subPart, messageDO);
0623: } else {
0624: addPart(subPart, messageDO);
0625: }
0626: } else {
0627: if (subPart.isMimeType("multipart/*")) {
0628: addMultiPart((Part) subPart, messageDO);
0629: } else {
0630: addPart(subPart, messageDO);
0631: }
0632: }
0633: }
0634: }
0635:
0636: // looking for best message text
0637: if (!messageTextParts.isEmpty()) {
0638: String HTML = null;
0639: String text = null;
0640:
0641: // let's choose the best text
0642: for (Iterator i = messageTextParts.iterator(); i
0643: .hasNext();) {
0644: subPart = (MimeBodyPart) i.next();
0645:
0646: if (subPart.isMimeType("text/HTML")) {
0647: HTML = (String) subPart.getContent();
0648: } else if (subPart.isMimeType("text/plain")) {
0649: text = (String) subPart.getContent();
0650: }
0651:
0652: // TODO: we could use text/enriched too
0653: }
0654:
0655: if (HTML != null) {
0656: messageDO.setText(HTML);
0657: messageDO.setFormat(FormatConstants.FORMAT_HTML);
0658: } else if (text != null) {
0659: messageDO.setText(text);
0660: messageDO.setFormat(FormatConstants.FORMAT_TEXT);
0661: }
0662: }
0663: } catch (MessagingException e) {
0664: logger.error("addMultiPart(Part, MessageDO)", e);
0665:
0666: throw new SystemException(e);
0667: } catch (java.io.IOException e) {
0668: logger.error("addMultiPart(Part, MessageDO)", e);
0669:
0670: throw new SystemException(e);
0671: }
0672:
0673: if (logger.isDebugEnabled()) {
0674: logger.debug("addMultiPart(Part, MessageDO) - end");
0675: }
0676: }
0677:
0678: /**
0679: * <p>Add a part of a multi-part message to the attachments of the message
0680: * data object.</p>
0681: *
0682: * @param part the message part to process.
0683: * @param messageDO the data object to add results to.
0684: * @throws GroupwareException if there is a <code>MessagingException</code> or an
0685: * <code>IOException</code>.
0686: */
0687: private void addPart(final MimePart part, final MessageDO messageDO)
0688: throws SystemException {
0689: if (logger.isDebugEnabled()) {
0690: logger.debug("addPart(MimePart part = " + part
0691: + ", MessageDO messageDO = " + messageDO
0692: + ") - start");
0693: }
0694:
0695: FileDO attachment = new FileDO();
0696:
0697: try {
0698: attachment.setMimeType(part.getContentType());
0699: attachment.setSize(new Integer(part.getSize()));
0700:
0701: String contentId = part.getContentID();
0702: String name = part.getFileName();
0703:
0704: // return with empty hands if no identifier, the attachment will be
0705: // impossible to locate
0706: if ((contentId == null) && (name == null)) {
0707: if (logger.isDebugEnabled()) {
0708: logger.debug("addPart(MimePart, MessageDO) - end");
0709: }
0710:
0711: if (logger.isDebugEnabled()) {
0712: logger.debug("addPart(MimePart, MessageDO) - end");
0713: }
0714:
0715: return;
0716: }
0717:
0718: // prefer contentId as identifier and name as the display info
0719: attachment.setName((contentId != null) ? contentId : name);
0720: attachment.setComment((name != null) ? name : contentId);
0721: } catch (MessagingException e) {
0722: logger.error("addPart(MimePart, MessageDO)", e);
0723:
0724: throw new SystemException(e);
0725: }
0726:
0727: messageDO.getAttachments().add(attachment);
0728:
0729: if (logger.isDebugEnabled()) {
0730: logger.debug("addPart(MimePart, MessageDO) - end");
0731: }
0732: }
0733:
0734: /**
0735: * <p>Internal helper to add a message to the sent mail folder. This should
0736: * be called <em><u>after</u></em> sending the mail.</p>
0737: */
0738: private void addToSentFolder(final MailSession mailSession,
0739: final MimeMessage message) throws SystemException {
0740: if (logger.isDebugEnabled()) {
0741: logger.debug("addToSentFolder(MailSession mailSession = "
0742: + mailSession + ", MimeMessage message = "
0743: + message + ") - start");
0744: }
0745:
0746: Folder sentFolder;
0747:
0748: Store store = mailServer.connectStore(mailSession);
0749:
0750: try {
0751: String sentFolderName = settings.getStringSetting(
0752: mailSession, "emailFolderSent", mailSession
0753: .getUser());
0754: sentFolder = mailServer.getFolder(mailSession, store,
0755: sentFolderName);
0756:
0757: if (!sentFolder.exists()) {
0758: try {
0759: if (!sentFolder.create(Folder.HOLDS_MESSAGES)) {
0760: throw new SystemException(
0761: "There was no sent folder for you on this server, "
0762: + "and ivata mail could not create one.<br>Please "
0763: + "contact your administrator.");
0764: }
0765: } catch (MessagingException e1) {
0766: logger
0767: .error(
0768: "addToSentFolder(MailSession, MimeMessage)",
0769: e1);
0770:
0771: throw new SystemException(e1);
0772: }
0773: }
0774:
0775: Message[] messages = { message };
0776:
0777: try {
0778: sentFolder.appendMessages(messages);
0779: } catch (MessagingException eAppend) {
0780: logger.error(
0781: "addToSentFolder(MailSession, MimeMessage)",
0782: eAppend);
0783:
0784: throw new SystemException(
0785: "There was an error adding your message to the sent "
0786: + "messages folder: "
0787: + eAppend.getMessage(), eAppend);
0788: }
0789: } catch (MessagingException eNoSent) {
0790: logger.error("addToSentFolder(MailSession, MimeMessage)",
0791: eNoSent);
0792:
0793: throw new SystemException(
0794: "Sent folder not available in store. "
0795: + eNoSent.getMessage(), eNoSent);
0796: } catch (SettingsDataTypeException e) {
0797: logger
0798: .error("addToSentFolder(MailSession, MimeMessage)",
0799: e);
0800:
0801: throw new SystemException(e);
0802: } finally {
0803: try {
0804: store.close();
0805: } catch (MessagingException e) {
0806: logger.error(
0807: "Messaging exception on closing the store", e);
0808: }
0809: }
0810:
0811: if (logger.isDebugEnabled()) {
0812: logger
0813: .debug("addToSentFolder(MailSession, MimeMessage) - end");
0814: }
0815: }
0816:
0817: /**
0818: * <p>
0819: * Add appropriate user addresses given a list of user aliases.
0820: * </p>
0821: *
0822: * @param securitySession valid security session.
0823: * @param userName name of the user who owns teh aliases.
0824: * @param userAliases a <code>Collection</code> of <code>String</code>
0825: * instances containing the local part of the different email aliases
0826: * this user has. If the user has no aliaes, an empty collection should
0827: * be provided.
0828: * @param telecomAddresess a <code>Collection</code> containing all the
0829: * user's existing email addresses, as <code>TelecomAddressDO</code>
0830: * instances.
0831: */
0832: public void addUserAliasEmailAddresses(
0833: final SecuritySession securitySession,
0834: final String userName, final Collection userAliases,
0835: final Collection telecomAddresses,
0836: final String emailAddressHost) throws SystemException {
0837: if (logger.isDebugEnabled()) {
0838: logger
0839: .debug("addUserAliasEmailAddresses(SecuritySession securitySession = "
0840: + securitySession
0841: + ", String userName = "
0842: + userName
0843: + ", Collection userAliases = "
0844: + userAliases
0845: + ", Collection telecomAddresses = "
0846: + telecomAddresses
0847: + ", String emailAddressHost = "
0848: + emailAddressHost + ") - start");
0849: }
0850:
0851: checkDateFormatter(securitySession);
0852:
0853: Iterator telecomAddressIterator = telecomAddresses.iterator();
0854: List currentAddresses = new Vector();
0855:
0856: while (telecomAddressIterator.hasNext()) {
0857: TelecomAddressDO this TelecomAddress = (TelecomAddressDO) telecomAddressIterator
0858: .next();
0859:
0860: if (!StringHandling.isNullOrEmpty(this TelecomAddress
0861: .getAddress())
0862: && (this TelecomAddress.getType() == TelecomAddressConstants.TYPE_EMAIL)) {
0863: currentAddresses.add(this TelecomAddress.getAddress());
0864: }
0865: }
0866:
0867: // if the person has no email address, give him/her one
0868: // check there is at least one alias - the following routine will
0869: // do the rest
0870: // we make it conditional because you might not always want to have
0871: // user@host as your email address - this way, you can specify a
0872: // different one
0873: if ((currentAddresses.size() == 0) && (userAliases.size() == 0)) {
0874: userAliases.add(userName);
0875: }
0876:
0877: // go thro' all aliases and create email addreses from them
0878: Iterator aliasIterator = userAliases.iterator();
0879:
0880: while (aliasIterator.hasNext()) {
0881: String alias = (String) aliasIterator.next();
0882: String aliasAddress = alias + "@" + emailAddressHost;
0883:
0884: // if it is already there, move on...
0885: if (currentAddresses.contains(aliasAddress)) {
0886: continue;
0887: }
0888:
0889: TelecomAddressDO newAddress = new TelecomAddressDO();
0890: newAddress.setAddress(aliasAddress);
0891: newAddress.setType(TelecomAddressConstants.TYPE_EMAIL);
0892: newAddress.setNumber(telecomAddresses.size());
0893: telecomAddresses.add(newAddress);
0894: }
0895:
0896: if (logger.isDebugEnabled()) {
0897: logger
0898: .debug("addUserAliasEmailAddresses(SecuritySession, String, Collection, Collection, String) - end");
0899: }
0900: }
0901:
0902: /**
0903: * <p>Append attachments to a message located in the drafts folder.</p>
0904: *
0905: * @param mailSession valid mail session to which the user should already be
0906: * logged in.
0907: * @param id the unique identifier of the message to which we want to append
0908: * attachments.
0909: * @param attachments <code>List</code> of <code>String</code>s -
0910: * filenames of files waiting in upload directory.
0911: * @return <code>null</code> when the operation failed, otherwise the new
0912: * message id.
0913: * @exception MessageNotFoundException if the folder doesn't exist, or there
0914: * is no matching mail in this folder.
0915: *
0916: * @ejb.interface-method
0917: * view-type = "remote"
0918: */
0919: public MessageDO appendAttachments(final MailSession mailSession,
0920: final String id, final List attachments)
0921: throws SystemException {
0922: if (logger.isDebugEnabled()) {
0923: logger.debug("appendAttachments(MailSession mailSession = "
0924: + mailSession + ", String id = " + id
0925: + ", List attachments = " + attachments
0926: + ") - start");
0927: }
0928:
0929: checkDateFormatter(mailSession);
0930:
0931: String newId = null;
0932: UserDO user = mailSession.getUser();
0933: Store store = mailServer.connectStore(mailSession);
0934:
0935: try {
0936: String siteHome = settings.getStringSetting(mailSession,
0937: "siteHome", user);
0938: String uploadDirectory = siteHome
0939: + "/users/"
0940: + mailSession.authenticator
0941: .getPasswordAuthentication().getUserName()
0942: + "/upload/files/";
0943:
0944: Session javaMailSession;
0945:
0946: try {
0947: javaMailSession = mailSession.getJavaMailSession();
0948: } catch (SecurityServerException e) {
0949: logger.error(
0950: "appendAttachments(MailSession, String, List)",
0951: e);
0952:
0953: throw new SystemException(e);
0954: } catch (java.security.NoSuchProviderException e) {
0955: logger.error(
0956: "appendAttachments(MailSession, String, List)",
0957: e);
0958:
0959: throw new SystemException(e);
0960: }
0961:
0962: Folder draftsFolder = openDraftsFolder(store, mailSession);
0963: MimeMessage oldMessage = findJavaMailMessageByFolderMessageId(
0964: draftsFolder, id);
0965: int i;
0966: MimeBodyPart newPart;
0967: MimeMultipart newMessageContent = new MimeMultipart();
0968: MimeMultipart oldMessageContent;
0969: MimeMessage newMessage = copyJavaMailMessage(
0970: javaMailSession, oldMessage);
0971:
0972: // when the message is already multipart/mixed, no probs...
0973: if (oldMessage.isMimeType("multipart/mixed")) {
0974: oldMessageContent = (MimeMultipart) oldMessage
0975: .getContent();
0976:
0977: for (i = 0; i < oldMessageContent.getCount(); i++) {
0978: newPart = (MimeBodyPart) oldMessageContent
0979: .getBodyPart(i);
0980: newMessageContent.addBodyPart(newPart);
0981: }
0982: } else {
0983: // create the first part from the old message, attachments will be appended
0984: newPart = new MimeBodyPart();
0985: newPart.setContent(oldMessage.getContent(), oldMessage
0986: .getContentType());
0987: newPart.setHeader("Content-Type", oldMessage
0988: .getContentType());
0989: newMessageContent.addBodyPart(newPart);
0990: }
0991:
0992: // add the attachments, passed as fileNames of files in upload dir
0993: for (Iterator attachmentsIterator = attachments.iterator(); attachmentsIterator
0994: .hasNext();) {
0995: File attachment = new File(uploadDirectory,
0996: (String) attachmentsIterator.next());
0997:
0998: // process the file in upload directory
0999: if (attachment.canRead()) {
1000: newPart = new MimeBodyPart();
1001: newPart.setFileName(attachment.getName());
1002: newPart.setDisposition(Part.ATTACHMENT);
1003:
1004: DataSource dataSource = new FileDataSource(
1005: attachment);
1006: newPart.setDataHandler(new DataHandler(dataSource));
1007: newPart.setHeader("Content-Type", dataSource
1008: .getContentType());
1009: newPart.setHeader("Content-Transfer-Encoding",
1010: "base64");
1011: newMessageContent.addBodyPart(newPart);
1012: }
1013: }
1014:
1015: newMessage.setContent(newMessageContent);
1016: newMessage.setHeader("Content-Type", newMessageContent
1017: .getContentType());
1018:
1019: // first append the new message
1020: Message[] messages = { newMessage };
1021:
1022: draftsFolder.appendMessages(messages);
1023:
1024: // note the new id
1025: newId = ((MimeMessage) draftsFolder.getMessage(draftsFolder
1026: .getMessageCount())).getMessageID();
1027:
1028: // only now is it safe to delete the old message
1029: oldMessage.setFlag(Flags.Flag.DELETED, true);
1030: draftsFolder.expunge();
1031:
1032: // now it's safe to delete the files in upload dir, they're in the new multipart
1033: for (Iterator attachmentsIterator = attachments.iterator(); attachmentsIterator
1034: .hasNext();) {
1035: File attachment = new File(uploadDirectory,
1036: (String) attachmentsIterator.next());
1037:
1038: if (attachment.canWrite()) {
1039: attachment.delete();
1040: }
1041: }
1042:
1043: // MessageDO returnDO = getDOFromJavaMailMessage(newMessage, true);
1044: MessageDO returnDO = getDOFromJavaMailMessage(
1045: findJavaMailMessageByFolderMessageId(draftsFolder,
1046: newId), true);
1047: draftsFolder.close(true);
1048:
1049: if (logger.isDebugEnabled()) {
1050: logger
1051: .debug("appendAttachments(MailSession, String, List) - end - return value = "
1052: + returnDO);
1053: }
1054:
1055: return returnDO;
1056: } catch (MessagingException em) {
1057: logger.error(
1058: "appendAttachments(MailSession, String, List)", em);
1059:
1060: throw new SystemException(em);
1061: } catch (IOException eio) {
1062: logger
1063: .error(
1064: "appendAttachments(MailSession, String, List)",
1065: eio);
1066:
1067: throw new SystemException(eio);
1068: } finally {
1069: try {
1070: store.close();
1071: } catch (MessagingException e) {
1072: logger.error(
1073: "Messaging exception on closing the store", e);
1074: }
1075: }
1076: }
1077:
1078: /**
1079: * <p>
1080: * Check we have a valid mail server.
1081: * </p>
1082: *
1083: * @throws NoMailServerException if there is no mail server.
1084: */
1085: private void checkMailServer() throws SystemException {
1086: if (logger.isDebugEnabled()) {
1087: logger.debug("checkMailServer() - start");
1088: }
1089:
1090: if (mailServer == null) {
1091: logger.warn("No mail server found.");
1092: throw new NoMailServerException();
1093: }
1094:
1095: if (logger.isDebugEnabled()) {
1096: logger.debug("checkMailServer() - end");
1097: }
1098: }
1099:
1100: /**
1101: * <p>Helper method. Converts recipients from a collection of
1102: * <code>PersonDO</code>, <code>UserDO</code> or <code>String<code>
1103: * instances into an array of email addresses.</p>
1104: * @param securitySession TODO
1105: * @param addresses a <code>Collection</code> containing all of the email
1106: * addresses to convert. These can either be as <code>String<code>
1107: * instances or <code>PersonDO<code> instances, where the default
1108: * email address for each person is taken.
1109: *
1110: * @return array or <code>InternetAddress</code> instances for each of the
1111: * input parameters.
1112: */
1113: private InternetAddress[] convertAddresses(
1114: final SecuritySession securitySession,
1115: final Collection addresses) throws SystemException {
1116: if (logger.isDebugEnabled()) {
1117: logger
1118: .debug("convertAddresses(SecuritySession securitySession = "
1119: + securitySession
1120: + ", Collection addresses = "
1121: + addresses
1122: + ") - start");
1123: }
1124:
1125: InternetAddress[] returnAddresses = new InternetAddress[addresses
1126: .size()];
1127:
1128: // prerequisites check we got given something to convert
1129: if (addresses == null) {
1130: if (logger.isDebugEnabled()) {
1131: logger
1132: .debug("convertAddresses(SecuritySession, Collection) - end - return value = "
1133: + returnAddresses);
1134: }
1135:
1136: return returnAddresses;
1137: }
1138:
1139: int index = 0;
1140:
1141: for (Iterator i = addresses.iterator(); i.hasNext();) {
1142: Object item = i.next();
1143: String addressString = null;
1144:
1145: if (PersonDO.class.isInstance(item)) {
1146: PersonDO person = (PersonDO) item;
1147:
1148: addressString = person.getEmailAddress();
1149: } else if (UserDO.class.isInstance(item)) {
1150: UserDO user = (UserDO) item;
1151: PersonDO person = addressBook.findPersonByUserName(
1152: securitySession, user.getName());
1153:
1154: // only set the address for users who are not disabled
1155: if (user.isEnabled()) {
1156: addressString = person.getEmailAddress();
1157: }
1158: } else {
1159: if (!String.class.isInstance(item)) {
1160: throw new SystemException(
1161: "Cannot convert item of class '"
1162: + item.getClass()
1163: + "' into an email address.");
1164: }
1165:
1166: addressString = (String) item;
1167: }
1168:
1169: // ignore empty addresses
1170: if (!StringHandling.isNullOrEmpty(addressString)) {
1171: try {
1172: returnAddresses[index++] = new InternetAddress(
1173: addressString);
1174: } catch (AddressException eAddress) {
1175: logger
1176: .error(
1177: "convertAddresses(SecuritySession, Collection)",
1178: eAddress);
1179:
1180: throw new SystemException(
1181: "ERROR in MailBean: cannot convert internet address '"
1182: + addressString + "': "
1183: + eAddress.getMessage(), eAddress);
1184: }
1185: }
1186: }
1187:
1188: if (logger.isDebugEnabled()) {
1189: logger
1190: .debug("convertAddresses(SecuritySession, Collection) - end - return value = "
1191: + returnAddresses);
1192: }
1193:
1194: return returnAddresses;
1195: }
1196:
1197: /**
1198: * <p>Copy the fields of an old <code>MimeMessage</code> to a new one.</p>
1199: *
1200: * <p><strong>Note:</strong> this method does not copy the content. Both the text
1201: * and the message attachments (if any) must be set individually.</p>
1202: *
1203: * @param javaMailSession valid <em>JavaMail</em> session to which the user
1204: * should already be logged in.
1205: * @param message a valid message filled out with values to copy.
1206: * @return a valid <em>JavaMail</em> message ready with <code>recipients</code>,
1207: * <code>from</code> and <code>subject</code> fields matching
1208: * <code>message</code>.
1209: */
1210: private MimeMessage copyJavaMailMessage(
1211: final Session javaMailSession, final MimeMessage message)
1212: throws SystemException {
1213: if (logger.isDebugEnabled()) {
1214: logger
1215: .debug("copyJavaMailMessage(Session javaMailSession = "
1216: + javaMailSession
1217: + ", MimeMessage message = "
1218: + message
1219: + ") - start");
1220: }
1221:
1222: MimeMessage newMessage = new MimeMessage(javaMailSession);
1223:
1224: try {
1225: newMessage.setRecipients(Message.RecipientType.TO, message
1226: .getRecipients(Message.RecipientType.TO));
1227: newMessage.setRecipients(Message.RecipientType.CC, message
1228: .getRecipients(Message.RecipientType.CC));
1229: newMessage.setRecipients(Message.RecipientType.BCC, message
1230: .getRecipients(Message.RecipientType.BCC));
1231: newMessage.addFrom(message.getFrom());
1232: newMessage.setSubject(message.getSubject());
1233: } catch (MessagingException e) {
1234: logger
1235: .error("copyJavaMailMessage(Session, MimeMessage)",
1236: e);
1237:
1238: throw new SystemException(e);
1239: }
1240:
1241: if (logger.isDebugEnabled()) {
1242: logger
1243: .debug("copyJavaMailMessage(Session, MimeMessage) - end - return value = "
1244: + newMessage);
1245: }
1246:
1247: return newMessage;
1248: }
1249:
1250: /**
1251: * <p>Convert a <em>JavaMail</em> message to an <em>ivata groupware</em> dependent
1252: * value object.</p>
1253: *
1254: * @param message a valid <em>JavaMail</em> message to be converted.
1255: * @param includeContent <code>true</code> if the <code>text</code> and
1256: * attachments of the message should also be set, otherwise
1257: * <code>false</code>.
1258: * @return message data object with the values filled out to match
1259: * the <em>JavaMail</em> object.
1260: */
1261: private MessageDO createDOFromJavaMailMessage(
1262: final MimeMessage message, final boolean includeContent)
1263: throws SystemException {
1264: if (logger.isDebugEnabled()) {
1265: logger
1266: .debug("createDOFromJavaMailMessage(MimeMessage message = "
1267: + message
1268: + ", boolean includeContent = "
1269: + includeContent + ") - start");
1270: }
1271:
1272: // right - we got here, so that means we have a message
1273: MessageDO messageDO = new MessageDO();
1274:
1275: try {
1276: //setting the fields of the MessageDO:
1277: if (message.getFolder() != null) {
1278: messageDO.setFolderName(message.getFolder().getName());
1279: }
1280:
1281: if (message.getReceivedDate() != null) {
1282: GregorianCalendar receivedDate = new GregorianCalendar();
1283:
1284: receivedDate.setTime(message.getReceivedDate());
1285: messageDO.setReceived(receivedDate);
1286: }
1287:
1288: if (message.getRecipients(Message.RecipientType.TO) != null) {
1289: messageDO
1290: .setRecipients(Arrays
1291: .asList(toStringArray(message
1292: .getRecipients(Message.RecipientType.TO))));
1293: }
1294:
1295: if (message.getRecipients(Message.RecipientType.CC) != null) {
1296: messageDO
1297: .setRecipientsCC(Arrays
1298: .asList(toStringArray(message
1299: .getRecipients(Message.RecipientType.CC))));
1300: }
1301:
1302: if (message.getRecipients(Message.RecipientType.BCC) != null) {
1303: messageDO
1304: .setRecipientsBCC(Arrays
1305: .asList(toStringArray(message
1306: .getRecipients(Message.RecipientType.BCC))));
1307: }
1308:
1309: if (message.getFrom() != null) {
1310: messageDO.setSenders(Arrays
1311: .asList(toStringArray(message.getFrom())));
1312: }
1313:
1314: if (message.getSentDate() != null) {
1315: GregorianCalendar sentDate = new GregorianCalendar();
1316:
1317: sentDate.setTime(message.getSentDate());
1318: messageDO.setSent(sentDate);
1319: }
1320:
1321: messageDO.setSize(new Integer(message.getSize()));
1322: messageDO.setSubject(message.getSubject());
1323:
1324: // message content handling - not always done for efficiency in lists
1325: if (includeContent) {
1326: // create new, empty List for our attachments
1327: messageDO.setAttachments(new Vector());
1328:
1329: // if it is a multipart message (has attachments), pass control to
1330: // recursive routine to go thro' them all
1331: if (message.isMimeType("multipart/*")) {
1332: addMultiPart(message, messageDO);
1333:
1334: // here are types with textual content
1335: } else if (message.isMimeType("text/*")
1336: || message.isMimeType("message/*")) {
1337: // if it is not multipart, we're just left with text or HTML
1338: if (message.isMimeType("text/HTML")) {
1339: // simple message with HTML content
1340: messageDO
1341: .setFormat(FormatConstants.FORMAT_HTML);
1342: } else {
1343: // anything else with simple content should have text content
1344: messageDO
1345: .setFormat(FormatConstants.FORMAT_TEXT);
1346: }
1347:
1348: messageDO.setText((String) message.getContent());
1349:
1350: // other (not correct?) types, as self-contained attachments...
1351: } else {
1352: messageDO.setFormat(FormatConstants.FORMAT_TEXT);
1353: messageDO.setText("");
1354: addPart(message, messageDO);
1355: }
1356: }
1357:
1358: messageDO.setMessageID(message.getMessageID());
1359: } catch (MessagingException e) {
1360: logger
1361: .error(
1362: "createDOFromJavaMailMessage(MimeMessage, boolean)",
1363: e);
1364:
1365: throw new SystemException(e);
1366: } catch (IOException e) {
1367: logger
1368: .error(
1369: "createDOFromJavaMailMessage(MimeMessage, boolean)",
1370: e);
1371:
1372: throw new SystemException(e);
1373: }
1374:
1375: if (logger.isDebugEnabled()) {
1376: logger
1377: .debug("createDOFromJavaMailMessage(MimeMessage, boolean) - end - return value = "
1378: + messageDO);
1379: }
1380:
1381: return messageDO;
1382: }
1383:
1384: /**
1385: * <p>Create a new mail folder.</p>
1386: *
1387: * @param mailSession valid mail session to which the user should already be
1388: * logged in.
1389: * @param folderName the full path name of the folder to create.
1390: *
1391: * @ejb.interface-method
1392: * view-type = "remote"
1393: */
1394: public void createFolder(final MailSession mailSession,
1395: final String folderName) throws SystemException {
1396: if (logger.isDebugEnabled()) {
1397: logger.debug("createFolder(MailSession mailSession = "
1398: + mailSession + ", String folderName = "
1399: + folderName + ") - start");
1400: }
1401:
1402: assert (mailSession != null);
1403: checkDateFormatter(mailSession);
1404:
1405: Store store = mailServer.connectStore(mailSession);
1406:
1407: try {
1408: if (folderName == null) {
1409: throw new SystemException(
1410: "ERROR in MailBean.createFolder: folderName is null");
1411: }
1412:
1413: Folder folder = mailServer.getFolder(mailSession, store,
1414: folderName);
1415:
1416: if (!folder.create(Folder.HOLDS_MESSAGES)) {
1417: throw new SystemException(
1418: "ERROR in MailBean.createFolder: could not create folder '"
1419: + folderName + "'");
1420: }
1421: } catch (MessagingException e) {
1422: logger.error("createFolder(MailSession, String)", e);
1423:
1424: throw new SystemException(e);
1425: } finally {
1426: try {
1427: store.close();
1428: } catch (MessagingException e) {
1429: logger.error(
1430: "Messaging exception on closing the store", e);
1431: }
1432: }
1433:
1434: if (logger.isDebugEnabled()) {
1435: logger.debug("createFolder(MailSession, String) - end");
1436: }
1437: }
1438:
1439: /**
1440: * <p>Helper method for <code>createThreadMethod</code>.</p>
1441: *
1442: * <p>Create a new message in the drafts folder from an existing one,
1443: * resulting in a forwarded message.</p>
1444: *
1445: * @param mailSession valid mail session to which the user should already be
1446: * logged in.
1447: * @param folderName the name of the folder to copy existing messages from.
1448: * @param messageIds the unique identifier of the messages to be extended.
1449: * Can be <code>null</code> if a new message is requeested. When
1450: * forwarding, multiple address identifiers may be specified otherwise
1451: * (if editing a draft message or replying) only one message identifier
1452: * should be set in the list.
1453: * @param thread set to one of the constants in {@link MailConstants
1454: * MailConstants}.
1455: * @return populated message data object matching the required
1456: * message, and with the <code>id</code> set to the message in the
1457: * drafts folder.
1458: */
1459: private MimeMessage createForwardedMessage(
1460: final MailSession mailSession, final Folder folder,
1461: final List messageIds) throws SystemException {
1462: if (logger.isDebugEnabled()) {
1463: logger
1464: .debug("createForwardedMessage(MailSession mailSession = "
1465: + mailSession
1466: + ", Folder folder = "
1467: + folder
1468: + ", List messageIds = "
1469: + messageIds + ") - start");
1470: }
1471:
1472: checkDateFormatter(mailSession);
1473:
1474: try {
1475: Session javaMailSession;
1476:
1477: try {
1478: javaMailSession = mailSession.getJavaMailSession();
1479: } catch (java.security.NoSuchProviderException e1) {
1480: logger
1481: .error(
1482: "createForwardedMessage(MailSession, Folder, List)",
1483: e1);
1484:
1485: throw new SystemException(e1);
1486: }
1487:
1488: UserDO user = mailSession.getUser();
1489:
1490: // if this is HTML, we'll need to store multipart data
1491: MessageTextParts messageTextParts = null;
1492:
1493: int format = FormatConstants.FORMAT_TEXT;
1494:
1495: for (Iterator i = messageIds.iterator(); i.hasNext();) {
1496: String id = (String) i.next();
1497: MimeMessage oldMessage = findJavaMailMessageByFolderMessageId(
1498: folder, id);
1499:
1500: // is this multipart?
1501: if (oldMessage.isMimeType("multipart/*")) {
1502: // try to find an HTML subpart
1503: messageTextParts = getMultiPartText(oldMessage);
1504:
1505: if (messageTextParts.HTMLPart != null) {
1506: format = FormatConstants.FORMAT_HTML;
1507:
1508: // no need to check any further...
1509: break;
1510: }
1511: }
1512: }
1513:
1514: // text header/prefix depends on the format
1515: String messageHeader;
1516:
1517: if (format == FormatConstants.FORMAT_HTML) {
1518: messageHeader = settings.getStringSetting(mailSession,
1519: "emailHeaderForwardHTML", user);
1520: } else {
1521: messageHeader = settings.getStringSetting(mailSession,
1522: "emailHeaderForwardText", user);
1523: }
1524:
1525: MimeMessage newMessage = new MimeMessage(javaMailSession);
1526: StringBuffer subject = new StringBuffer();
1527: String subjectPrefix = settings.getStringSetting(
1528: mailSession, "emailSubjectForwardPrefix", user);
1529: String subjectSeperator = settings.getStringSetting(
1530: mailSession, "emailSubjectForwardSeperator", user);
1531:
1532: subject.append(subjectPrefix);
1533:
1534: StringBuffer messageText = new StringBuffer();
1535:
1536: // we'll format the reply text, if it is text & this is HTML
1537: CharacterEntityFormat characterEntities = new CharacterEntityFormat();
1538:
1539: // go thro' all of the old ids again, this time to add the content
1540: int index = 0;
1541:
1542: for (Iterator i = messageIds.iterator(); i.hasNext(); ++index) {
1543: String id = (String) i.next();
1544: MimeMessage oldMessage = findJavaMailMessageByFolderMessageId(
1545: folder, id);
1546:
1547: // prepend Re: or Fwd: unless the previous subject already starts like this
1548: String oldSubject = StringHandling.getNotNull(
1549: oldMessage.getSubject(), getNullString());
1550:
1551: // if there is a fwd: prefix check this message doesn't start with
1552: // that
1553: if ((subjectPrefix != null)
1554: && (oldSubject.startsWith(subjectPrefix))) {
1555: oldSubject = oldSubject.substring(subjectPrefix
1556: .length());
1557: }
1558:
1559: // if there is more than one forwarded message, append separator
1560: // between the subjects
1561: if ((index > 0) && !oldSubject.equals("")) {
1562: subject.append(subjectSeperator);
1563: }
1564:
1565: subject.append(oldSubject);
1566:
1567: // locate the multipart in the new message, for multiparts
1568: String oldMessageText = null;
1569: int oldFormat = FormatConstants.FORMAT_TEXT;
1570:
1571: if (oldMessage.isMimeType("multipart/*")) {
1572: // if there is an HTML text, use that
1573: if (messageTextParts.HTMLPart != null) {
1574: oldMessageText = (String) messageTextParts.HTMLPart
1575: .getContent();
1576: oldFormat = FormatConstants.FORMAT_HTML;
1577: } else if (messageTextParts.textPart != null) {
1578: oldMessageText = (String) messageTextParts.textPart
1579: .getContent();
1580: }
1581: } else {
1582: oldMessageText = (String) oldMessage.getContent();
1583: }
1584:
1585: // if we have to build the text with a prefix/header, do that
1586: if ((oldMessageText != null) && (messageHeader != null)) {
1587: messageText.append(formatMessageHeader(
1588: messageHeader, oldMessage));
1589: }
1590:
1591: if (oldMessageText != null) {
1592: // we have to watch it if the old format wasn't HTML before
1593: if ((format == FormatConstants.FORMAT_HTML)
1594: && !(oldFormat == FormatConstants.FORMAT_HTML)) {
1595: messageText.append("<pre>\n");
1596: oldMessageText = characterEntities
1597: .format(oldMessageText);
1598: }
1599:
1600: messageText.append(oldMessageText);
1601:
1602: if ((format == FormatConstants.FORMAT_HTML)
1603: && !(oldFormat == FormatConstants.FORMAT_HTML)) {
1604: messageText.append("\n</pre>\n");
1605: }
1606: }
1607: }
1608:
1609: if (format == FormatConstants.FORMAT_HTML) {
1610: // we want to work with copies of the original texts,
1611: // create a new HTML content multipart
1612: MimeMultipart newTextContent = createHTMLContent(messageText
1613: .toString());
1614:
1615: // set the multipart/alternative as the content of the base part
1616: newMessage.setContent(newTextContent);
1617: newMessage.setHeader("Content-Type", newTextContent
1618: .getContentType());
1619:
1620: // plain text replies and forwards are easier
1621: } else {
1622: newMessage.setContent(messageText.toString(),
1623: "text/plain");
1624: }
1625:
1626: if (logger.isDebugEnabled()) {
1627: logger
1628: .debug("createForwardedMessage(MailSession, Folder, List) - end - return value = "
1629: + newMessage);
1630: }
1631:
1632: return newMessage;
1633: } catch (MessagingException e) {
1634: logger
1635: .error(
1636: "createForwardedMessage(MailSession, Folder, List)",
1637: e);
1638:
1639: throw new SystemException(e);
1640: } catch (IOException e) {
1641: logger
1642: .error(
1643: "createForwardedMessage(MailSession, Folder, List)",
1644: e);
1645:
1646: throw new SystemException(e);
1647: }
1648: }
1649:
1650: /**
1651: * <p>Create text content of a message, if the text is <code>HTML</code>
1652: * format. This actually creates an alternative multipart type, and loads
1653: * both the <code>HTML</code> content and the equivalent plain text.</p>
1654: *
1655: * @param hTMLText the new HTML text of the body content
1656: * @exception MessagingException thrown by <code>MimeBodyPart.setContent</code>
1657: * @return the newx content of the alternative multipart message content.
1658: */
1659: private MimeMultipart createHTMLContent(final String hTMLText)
1660: throws MessagingException {
1661: if (logger.isDebugEnabled()) {
1662: logger.debug("createHTMLContent(String hTMLText = "
1663: + hTMLText + ") - start");
1664: }
1665:
1666: // HTML desired, the base part will be multipart/alternative
1667: MimeMultipart newTextContent = new MimeMultipart("alternative");
1668:
1669: // the richer format the last, so plain text first
1670: MimeBodyPart mimePart = new MimeBodyPart();
1671: SanitizerFormat sanitizer = new SanitizerFormat();
1672:
1673: sanitizer.setSourceName("user input");
1674: sanitizer.setTextOnly(true);
1675: mimePart.setContent(sanitizer.format(hTMLText), "text/plain");
1676: mimePart.setHeader("Content-Type", "text/plain");
1677: newTextContent.addBodyPart(mimePart);
1678:
1679: // TODO: this is not efficient - it parses the HTML tree twice :-(
1680: // replace with HTMLParser calls directly
1681: // the HTML part now
1682: mimePart = new MimeBodyPart();
1683: sanitizer.setTextOnly(false);
1684: mimePart.setContent(sanitizer.format(hTMLText), "text/HTML");
1685: mimePart.setHeader("Content-Type", "text/HTML");
1686: newTextContent.addBodyPart(mimePart);
1687:
1688: if (logger.isDebugEnabled()) {
1689: logger
1690: .debug("createHTMLContent(String) - end - return value = "
1691: + newTextContent);
1692: }
1693:
1694: return newTextContent;
1695: }
1696:
1697: /**
1698: * <p>Helper method for <code>createThreadMethod</code>.</p>
1699: *
1700: * <p>Create a new message in the drafts folder from an existing one,
1701: * resulting in a message which replies to the original one.</p>
1702: *
1703: * @param mailSession valid mail session to which the user should already be
1704: * logged in.
1705: * @param folderName the name of the folder to copy existing messages from.
1706: * @param messageIds the unique identifier of the messages to be extended.
1707: * Can be <code>null</code> if a new message is requeested. When
1708: * forwarding, multiple address identifiers may be specified otherwise
1709: * (if editing a draft message or replying) only one message identifier
1710: * should be set in the list.
1711: * @param thread set to one of the congstants in {@link MailConstants
1712: * MailConstants}.
1713: * @return populated message data object matching the required
1714: * message, and with the <code>id</code> set to the message in the
1715: * drafts folder.
1716: */
1717: private MimeMessage createReplyMessage(
1718: final MailSession mailSession, final Folder folder,
1719: final String id, final Integer thread)
1720: throws SystemException {
1721: if (logger.isDebugEnabled()) {
1722: logger
1723: .debug("createReplyMessage(MailSession mailSession = "
1724: + mailSession
1725: + ", Folder folder = "
1726: + folder
1727: + ", String id = "
1728: + id
1729: + ", Integer thread = "
1730: + thread
1731: + ") - start");
1732: }
1733:
1734: try {
1735: UserDO user = mailSession.getUser();
1736: MimeMessage oldMessage = this
1737: .findJavaMailMessageByFolderMessageId(folder, id);
1738:
1739: // if this is HTML, we'll need to store multipart data
1740: MessageTextParts messageTextParts = null;
1741:
1742: // we will build the subject up using a prefix (possibly)
1743: StringBuffer subject = new StringBuffer();
1744: String subjectPrefix = null;
1745:
1746: // there may also be a text prefix (= header)
1747: StringBuffer messageText = new StringBuffer();
1748: String oldMessageText = null;
1749: String messageHeader = null;
1750:
1751: // we'll format the reply text
1752: HTMLFormatter formatter = new HTMLFormatter();
1753:
1754: // mail API methods for reply, replyall
1755: MimeMessage newMessage = (MimeMessage) oldMessage
1756: .reply(MailConstants.THREAD_REPLY_ALL
1757: .equals(thread));
1758:
1759: // emailReplyIndent means '> ' (without the quotes)
1760: // note: HTML formatting might remove this below!!
1761: LineBreakFormat lineBreaks = new LineBreakFormat();
1762:
1763: lineBreaks.setPrepend(settings.getStringSetting(
1764: mailSession, "emailReplyIndent", user));
1765: formatter.add(lineBreaks);
1766:
1767: // first the subject prefix
1768: subjectPrefix = settings.getStringSetting(mailSession,
1769: "emailSubjectReplyPrefix", user);
1770:
1771: // for the reply we want to make a new multipart, if
1772: // this is an HTML mail
1773: if (oldMessage.isMimeType("multipart/*")) {
1774: messageHeader = settings.getStringSetting(mailSession,
1775: "emailHeaderReplyHTML", user);
1776: messageTextParts = getMultiPartText(oldMessage);
1777: } else {
1778: messageHeader = settings.getStringSetting(mailSession,
1779: "emailHeaderReplyText", user);
1780: }
1781:
1782: // prepend Re: unless the previous subject already starts like this
1783: String oldSubject = StringHandling.getNotNull(oldMessage
1784: .getSubject(), getNullString());
1785:
1786: if ((subjectPrefix != null)
1787: && (!oldSubject.startsWith(subjectPrefix))) {
1788: subject.append(subjectPrefix);
1789: }
1790:
1791: subject.append(oldSubject);
1792: newMessage.setSubject(subject.toString());
1793:
1794: // locate the multipart in the new message, for multiparts
1795: int format = FormatConstants.FORMAT_TEXT;
1796:
1797: if (oldMessage.isMimeType("multipart/*")) {
1798: // if there is an HTML text, use that
1799: if (messageTextParts.HTMLPart != null) {
1800: oldMessageText = (String) messageTextParts.HTMLPart
1801: .getContent();
1802: format = FormatConstants.FORMAT_HTML;
1803: } else if (messageTextParts.textPart != null) {
1804: oldMessageText = (String) messageTextParts.textPart
1805: .getContent();
1806: }
1807: } else {
1808: oldMessageText = (String) oldMessage.getContent();
1809: }
1810:
1811: // if we have to build the text with a prefix/header, do that
1812: if ((oldMessageText != null) && (messageHeader != null)) {
1813: messageText.append(formatMessageHeader(messageHeader,
1814: oldMessage));
1815: }
1816:
1817: if (oldMessageText != null) {
1818: messageText.append(oldMessageText);
1819: }
1820:
1821: if (format == FormatConstants.FORMAT_HTML) {
1822: // we want to work with copies of the original texts,
1823: // create a new HTML content multipart
1824: MimeMultipart newTextContent = createHTMLContent(messageText
1825: .toString());
1826:
1827: // set the multipart/alternative as the content of the base part
1828: newMessage.setContent(newTextContent);
1829: newMessage.setHeader("Content-Type", newTextContent
1830: .getContentType());
1831:
1832: // plain text replies and forwards are easier
1833: } else {
1834: newMessage.setContent(messageText.toString(),
1835: "text/plain");
1836: }
1837:
1838: if (logger.isDebugEnabled()) {
1839: logger
1840: .debug("createReplyMessage(MailSession, Folder, String, Integer) - end - return value = "
1841: + newMessage);
1842: }
1843:
1844: return newMessage;
1845: } catch (MessagingException e) {
1846: logger
1847: .error(
1848: "createReplyMessage(MailSession, Folder, String, Integer)",
1849: e);
1850:
1851: throw new SystemException(e);
1852: } catch (IOException e) {
1853: logger
1854: .error(
1855: "createReplyMessage(MailSession, Folder, String, Integer)",
1856: e);
1857:
1858: throw new SystemException(e);
1859: }
1860: }
1861:
1862: /**
1863: * <p>Create a new message in the drafts folder from an existing one,
1864: * based on a 'thread'. The thread indicates that the message is a:<br/>
1865: * <ul>
1866: * <li>reply to all recipients of a previous message</li>
1867: * <li>reply to one recipient of a previous message</li>
1868: * <li>previous message(s) forwarded to new recipients</li>
1869: * <li>an existing (draft) message being altered for resending</li>
1870: * </ul></p>
1871: *
1872: * <p>This new message in the drafts folder can then be used to store
1873: * attachments or for reviewing before sending.</p>
1874: *
1875: * @param mailSession valid mail session to which the user should already be
1876: * logged in.
1877: * @param folderName the name of the folder to copy existing messages from.
1878: * @param messageIds the unique identifier of the messages to be extended.
1879: * Can be <code>null</code> if a new message is requeested. When
1880: * forwarding, multiple address identifiers may be specified otherwise
1881: * (if editing a draft message or replying) only one message identifier
1882: * should be set in the list.
1883: * @param thread set to one of the constants in {@link MailConstants
1884: * MailConstants}.
1885: * @return populated message data object matching the required
1886: * message, and with the <code>id</code> set to the message in the
1887: * drafts folder.
1888: *
1889: * @ejb.interface-method
1890: * view-type = "remote"
1891: */
1892: public MessageDO createThreadMessage(final MailSession mailSession,
1893: final String folderName, final List messageIds,
1894: final Integer thread) throws SystemException {
1895: if (logger.isDebugEnabled()) {
1896: logger
1897: .debug("createThreadMessage(MailSession mailSession = "
1898: + mailSession
1899: + ", String folderName = "
1900: + folderName
1901: + ", List messageIds = "
1902: + messageIds
1903: + ", Integer thread = "
1904: + thread + ") - start");
1905: }
1906:
1907: checkDateFormatter(mailSession);
1908:
1909: MimeMessage newMessage = null;
1910: MessageDO messageDO = null;
1911: Store store = mailServer.connectStore(mailSession);
1912:
1913: try {
1914: // check if everything's set to create based on thread
1915: if ((folderName == null) || (messageIds == null)
1916: || (messageIds.size() == 0) || (thread == null)) {
1917: throw new SystemException(
1918: "ERROR in MailBean.createThreadMessage: these parameters cannot be empty:"
1919: + " folderName (specified: '"
1920: + folderName
1921: + "') messageIds (specified: '"
1922: + messageIds
1923: + "') thread (specified: '" + thread
1924: + "').");
1925: }
1926:
1927: // prerequisite - check this is a forward thread if there are > 1 messages
1928: if ((messageIds.size() > 1)
1929: && !MailConstants.THREAD_FORWARD.equals(thread)) {
1930: throw new SystemException(
1931: "ERROR in MailBean: too many messages ("
1932: + messageIds.size()
1933: + ") in thread. Only 'forward' can have multiple messages.");
1934: }
1935:
1936: // create a new message appropriate to the thread type requested
1937: Folder folder = mailServer.getFolder(mailSession, store,
1938: folderName);
1939:
1940: folder.open(Folder.READ_ONLY);
1941:
1942: if (MailConstants.THREAD_FORWARD.equals(thread)) {
1943: newMessage = createForwardedMessage(mailSession,
1944: folder, messageIds);
1945: } else {
1946: String messageId = (String) messageIds.get(0);
1947:
1948: if (MailConstants.THREAD_DRAFT.equals(thread)) {
1949: MimeMessage oldMessage = this
1950: .findJavaMailMessageByFolderMessageId(
1951: folder, messageId);
1952: newMessage = new MimeMessage(oldMessage);
1953: } else {
1954: newMessage = createReplyMessage(mailSession,
1955: folder, messageId, thread);
1956: }
1957: }
1958:
1959: // now we should have the new message, let's put it to the drafts folder
1960: Folder draftsFolder = openDraftsFolder(store, mailSession);
1961:
1962: messageDO = getDOFromJavaMailMessage(newMessage, true);
1963:
1964: Message[] messages = { newMessage };
1965:
1966: draftsFolder.appendMessages(messages);
1967: messageDO.setFolderName(draftsFolder.getName());
1968: draftsFolder.close(true);
1969: folder.close(false);
1970: } catch (MessagingException e) {
1971: logger
1972: .error(
1973: "createThreadMessage(MailSession, String, List, Integer)",
1974: e);
1975:
1976: throw new SystemException(e);
1977: } finally {
1978: try {
1979: store.close();
1980: } catch (MessagingException e) {
1981: logger.error(
1982: "Messaging exception on closing the store", e);
1983: }
1984: }
1985:
1986: if (logger.isDebugEnabled()) {
1987: logger
1988: .debug("createThreadMessage(MailSession, String, List, Integer) - end - return value = "
1989: + messageDO);
1990: }
1991:
1992: return messageDO;
1993: }
1994:
1995: /**
1996: * <p>Delete a list of messages from the trash folder.</p>
1997: *
1998: * @param mailSession valid mail session to which the user should already be
1999: * logged in.
2000: * @param ids the unique identifiers (<code>String</code> instances) of the
2001: * messages to be removed.
2002: * @exception MessageNotFoundException if the folder doesn't exist, or there
2003: * is no matching mail in this folder.
2004: *
2005: * @ejb.interface-method
2006: * view-type = "remote"
2007: */
2008: public void deleteMessagesFromTrash(final MailSession mailSession,
2009: final List ids) throws SystemException {
2010: if (logger.isDebugEnabled()) {
2011: logger
2012: .debug("deleteMessagesFromTrash(MailSession mailSession = "
2013: + mailSession
2014: + ", List ids = "
2015: + ids
2016: + ") - start");
2017: }
2018:
2019: checkDateFormatter(mailSession);
2020:
2021: Store store = mailServer.connectStore(mailSession);
2022:
2023: try {
2024: UserDO user = mailSession.getUser();
2025: Folder trashFolder = mailServer.getFolder(mailSession,
2026: store, settings.getStringSetting(mailSession,
2027: "emailFolderTrash", user));
2028:
2029: trashFolder.open(Folder.READ_WRITE);
2030:
2031: // now mark all the original messages as deleted
2032: for (Iterator i = ids.iterator(); i.hasNext();) {
2033: String id = (String) i.next();
2034: MimeMessage message = findJavaMailMessageByFolderMessageId(
2035: trashFolder, id);
2036:
2037: message.setFlag(Flags.Flag.DELETED, true);
2038: }
2039:
2040: trashFolder.expunge();
2041: trashFolder.close(true);
2042: } catch (MessagingException e) {
2043: logger.error("deleteMessagesFromTrash(MailSession, List)",
2044: e);
2045:
2046: throw new SystemException(e);
2047: } finally {
2048: try {
2049: store.close();
2050: } catch (MessagingException e) {
2051: logger.error(
2052: "Messaging exception on closing the store", e);
2053: }
2054: }
2055:
2056: if (logger.isDebugEnabled()) {
2057: logger
2058: .debug("deleteMessagesFromTrash(MailSession, List) - end");
2059: }
2060: }
2061:
2062: /**
2063: * <p>Check whether or not a given folder pathname exists.</p>
2064: *
2065: * @param mailSession valid mail session to which the user should already be
2066: * logged in.
2067: * @param folderName the full path name of the folder to check.
2068: * @return <code>true</code> if the folder exists, otherwise <code>false</code>.
2069: *
2070: * @ejb.interface-method
2071: * view-type = "remote"
2072: */
2073: public boolean doesFolderExist(final MailSession mailSession,
2074: final String folderName) throws SystemException {
2075: if (logger.isDebugEnabled()) {
2076: logger.debug("doesFolderExist(MailSession mailSession = "
2077: + mailSession + ", String folderName = "
2078: + folderName + ") - start");
2079: }
2080:
2081: assert (mailSession != null);
2082: checkDateFormatter(mailSession);
2083:
2084: boolean folderExists = false;
2085:
2086: Store store = mailServer.connectStore(mailSession);
2087:
2088: try {
2089: if (folderName == null) {
2090: throw new SystemException(
2091: "ERROR in MailBean.doesFolderExist: folderName is null");
2092: }
2093:
2094: Folder folder = mailServer.getFolder(mailSession, store,
2095: folderName);
2096:
2097: folderExists = folder.exists();
2098: } catch (MessagingException e) {
2099: logger.error("doesFolderExist(MailSession, String)", e);
2100:
2101: throw new SystemException(e);
2102: } finally {
2103: try {
2104: store.close();
2105: } catch (MessagingException e) {
2106: logger.error(
2107: "Messaging exception on closing the store", e);
2108: }
2109: }
2110:
2111: if (logger.isDebugEnabled()) {
2112: logger
2113: .debug("doesFolderExist(MailSession, String) - end - return value = "
2114: + folderExists);
2115: }
2116:
2117: return folderExists;
2118: }
2119:
2120: /**
2121: * <p>Locate the message in the folder given with the specified unique
2122: * identifier.</p>
2123: *
2124: * @param folder folder to search for the message in.
2125: * @param id the unique identifier of the message.
2126: * @return the message in this folder with the id required, or
2127: * <code>null</code> if no such message exists.
2128: * @exception MessageNotFoundException if the folder doesn't exist, or there
2129: * is no matching mail in this folder.
2130: */
2131: private MimeMessage findJavaMailMessageByFolderMessageId(
2132: final Folder folder, final String id)
2133: throws SystemException {
2134: if (logger.isDebugEnabled()) {
2135: logger
2136: .debug("findJavaMailMessageByFolderMessageId(Folder folder = "
2137: + folder
2138: + ", String id = "
2139: + id
2140: + ") - start");
2141: }
2142:
2143: MimeMessage message = null;
2144:
2145: // folder name used for errors only
2146: String folderName = (folder == null) ? "[none]" : folder
2147: .getName();
2148:
2149: try {
2150: // prerequisites, the id cannot be null, and folder must exist and be open
2151: if (StringHandling.isNullOrEmpty(id)) {
2152: throw new MessageNotFoundException(folderName, id,
2153: "you must specify a valid message id");
2154: } else if (folder == null) {
2155: throw new MessageNotFoundException(folderName, id,
2156: "you must specify a valid folder");
2157: } else if (!folder.exists()) {
2158: throw new MessageNotFoundException(folderName, id,
2159: folderName + " does not exist");
2160: } else if (!folder.isOpen()) {
2161: throw new MessageNotFoundException(folderName, id,
2162: folderName + " is not open");
2163: }
2164:
2165: // go thro' each one and compare ids - it's slow but it works
2166: // TODO: - examine ways to cache the mail folders
2167: for (int messageNumber = 1; messageNumber <= folder
2168: .getMessageCount(); ++messageNumber) {
2169: MimeMessage messageTest = (MimeMessage) folder
2170: .getMessage(messageNumber);
2171:
2172: if (id.equals(messageTest.getMessageID())) {
2173: message = messageTest;
2174:
2175: break;
2176: }
2177: }
2178: } catch (FolderNotFoundException e) {
2179: logger
2180: .error(
2181: "findJavaMailMessageByFolderMessageId(Folder, String)",
2182: e);
2183:
2184: throw new MessageNotFoundException(folderName, id,
2185: "FolderNotFoundException looking for " + folderName
2186: + ": " + e.getMessage(), e);
2187: } catch (MessagingException e) {
2188: logger
2189: .error(
2190: "findJavaMailMessageByFolderMessageId(Folder, String)",
2191: e);
2192:
2193: throw new SystemException(e);
2194: }
2195:
2196: if (logger.isDebugEnabled()) {
2197: logger
2198: .debug("findJavaMailMessageByFolderMessageId(Folder, String) - end - return value = "
2199: + message);
2200: }
2201:
2202: return message;
2203: }
2204:
2205: /**
2206: * <p>This method retrieves the requested message and sets all the
2207: * attributes of a MessageDO object for use on client side.</p>
2208: *
2209: * @param mailSession valid mail session to which the user should already be
2210: * logged in.
2211: * @param folderName the name of the folder the message is located in.
2212: * @param id the unique identifier of the message.
2213: * @return a MessageDO instance filled up with the messages attributes,
2214: * except the contents of the attachments.
2215: * @exception MessageNotFoundException if the folder doesn't exist, or there
2216: * is no matching mail in this folder.
2217: *
2218: * @ejb.interface-method
2219: * view-type = "remote"
2220: */
2221: public MessageDO findMessageByFolderMessageId(
2222: final MailSession mailSession, final String folderName,
2223: final String id) throws SystemException {
2224: if (logger.isDebugEnabled()) {
2225: logger
2226: .debug("findMessageByFolderMessageId(MailSession mailSession = "
2227: + mailSession
2228: + ", String folderName = "
2229: + folderName
2230: + ", String id = "
2231: + id
2232: + ") - start");
2233: }
2234:
2235: checkDateFormatter(mailSession);
2236:
2237: MessageDO messageDO = null;
2238:
2239: Store store = mailServer.connectStore(mailSession);
2240:
2241: try {
2242: if (folderName == null) {
2243: logger
2244: .error("MailBean.findMessageByFolderMessageId(): "
2245: + "folderName is null.");
2246:
2247: return null;
2248: }
2249:
2250: // right, we now have to find the message using the folder name and the message number
2251: Folder folder = mailServer.getFolder(mailSession, store,
2252: folderName);
2253:
2254: // now open the folder
2255: folder.open(Folder.READ_ONLY);
2256:
2257: MimeMessage message = findJavaMailMessageByFolderMessageId(
2258: folder, id);
2259:
2260: // if some other program download the message -> we didn't find it so return null
2261: if (message != null) {
2262: messageDO = getDOFromJavaMailMessage(message, true);
2263: }
2264:
2265: folder.close(false);
2266: } catch (MessagingException e) {
2267: logger
2268: .error(
2269: "findMessageByFolderMessageId(MailSession, String, String)",
2270: e);
2271:
2272: throw new SystemException(e);
2273: } finally {
2274: try {
2275: store.close();
2276: } catch (MessagingException e) {
2277: logger.error(
2278: "Messaging exception on closing the store", e);
2279: }
2280: }
2281:
2282: if (logger.isDebugEnabled()) {
2283: logger
2284: .debug("findMessageByFolderMessageId(MailSession, String, String) - end - return value = "
2285: + messageDO);
2286: }
2287:
2288: return messageDO;
2289: }
2290:
2291: /**
2292: * <p>Used in the main folder index page, this method returns the contents
2293: * of a folder as a <code>List</code> of
2294: * <code>MessageDO</code> instances.</p>
2295: *
2296: * <p><strong>Note:</strong> for efficiency reasons, this method does not fill the
2297: * text, format or attachment values of the returned <code>MessageDO</code>
2298: * instance. For that, you must call
2299: * {@link #findMessageByFolderMessageId findMessageByFolderMessageId}.</p>
2300: *
2301: * @param mailSession valid mail session to which the user should already be
2302: * logged in.
2303: * @param folderName the name of the folder to list.
2304: * @param sortBy the field to sort the returned list by. Set to one of the
2305: * <code>SORT_...</code> constants in {@link MailConstants MailConstants}.
2306: * @param sortAscending if <code>true</code> then the messages are sorted in
2307: * ascending order, otherwise (<code>false</code>) they are descending.
2308: * @return <code>List</code> of
2309: * <code>MessageDO</code> instances.
2310: *
2311: * @ejb.interface-method
2312: * view-type = "remote"
2313: */
2314: public List findMessagesInFolder(final MailSession mailSession,
2315: final String folderName, final Integer sortBy,
2316: final boolean sortAscending) throws SystemException {
2317: if (logger.isDebugEnabled()) {
2318: logger
2319: .debug("findMessagesInFolder(MailSession mailSession = "
2320: + mailSession
2321: + ", String folderName = "
2322: + folderName
2323: + ", Integer sortBy = "
2324: + sortBy
2325: + ", boolean sortAscending = "
2326: + sortAscending + ") - start");
2327: }
2328:
2329: checkDateFormatter(mailSession);
2330:
2331: UserDO user = mailSession.getUser();
2332: MessageDO messageDO;
2333: Message[] messages;
2334: int i;
2335:
2336: // make a tree map to sort the items
2337: ListColumnComparator comparator = new ListColumnComparator(
2338: sortAscending);
2339: TreeMap map = new TreeMap(comparator);
2340:
2341: // some types must be sorted numerically, rather than alphanumerically
2342: comparator.setSortNumerically(MailConstants.SORT_RECEIVED
2343: .equals(sortBy)
2344: || MailConstants.SORT_SENT.equals(sortBy)
2345: || MailConstants.SORT_SIZE.equals(sortBy));
2346:
2347: Store store = mailServer.connectStore(mailSession);
2348:
2349: try {
2350: Folder folderProcessed = mailServer.getFolder(mailSession,
2351: store, folderName);
2352:
2353: // go thro and sort the contents
2354: String sentKey;
2355: StringBuffer key = new StringBuffer(32);
2356: GregorianCalendar received;
2357: GregorianCalendar sent;
2358:
2359: // first go thro' and make sure there is an ivata message id for each
2360: // message - only necessary for incoming mails where message id
2361: // might not be unique
2362: String inboxFolderName = settings.getStringSetting(
2363: mailSession, "emailFolderInbox", user);
2364:
2365: if (folderName.equalsIgnoreCase(inboxFolderName)) {
2366: folderProcessed.open(Folder.READ_WRITE);
2367: messages = folderProcessed.getMessages();
2368:
2369: int length = messages.length;
2370:
2371: for (i = 0; i < length; i++) {
2372: MimeMessage message = (MimeMessage) messages[i];
2373:
2374: // check there is an ivata message id in the message id string
2375: String messageId = message.getMessageID();
2376:
2377: if ((messageId == null)
2378: || (messageId.indexOf("ivata-") == -1)) {
2379: // create a new message dependent value object
2380: MessageDO newMessage = createDOFromJavaMailMessage(
2381: message, true);
2382: Integer newId = newMessage.getId();
2383:
2384: if (messageId == null) {
2385: messageId = "";
2386: }
2387:
2388: messageId += ("ivata-" + newId);
2389:
2390: MimeMessage ivataMessage = new MimeMessage(
2391: message);
2392:
2393: ivataMessage.setHeader("Message-ID", messageId);
2394: message.setFlag(Flags.Flag.DELETED, true);
2395:
2396: Message[] ivataMessages = { ivataMessage };
2397:
2398: folderProcessed.appendMessages(ivataMessages);
2399: }
2400: }
2401:
2402: // close the folder to save changes and re-open read only
2403: folderProcessed.close(true);
2404: }
2405:
2406: // now we can be sure-ish that message ids are unique -
2407: // the only way they can't be unique is if this is not the inbox
2408: // and JavaMail doesn't create a unique id when we make a new mail
2409: folderProcessed.open(Folder.READ_ONLY);
2410: messages = folderProcessed.getMessages();
2411:
2412: for (i = 0; i < messages.length; i++) {
2413: MimeMessage message = (MimeMessage) messages[i];
2414:
2415: messageDO = getDOFromJavaMailMessage(message, false);
2416: sent = messageDO.getSent();
2417: sentKey = ((sent == null) ? "" : new Long(sent
2418: .getTime().getTime()).toString());
2419: key.delete(0, key.capacity());
2420:
2421: // which field sorts is determined by the value of sortBy...
2422: if (MailConstants.SORT_FOLDER.equals(sortBy)) {
2423: key.append(StringHandling.getNotNull(messageDO
2424: .getFolderName()));
2425: } else if (MailConstants.SORT_ID.equals(sortBy)) {
2426: key.append(StringHandling.getNotNull(messageDO
2427: .getMessageID()));
2428: } else if (MailConstants.SORT_RECEIVED.equals(sortBy)) {
2429: received = messageDO.getReceived();
2430: key.append(((received == null) ? "" : new Long(
2431: received.getTime().getTime()).toString()));
2432: } else if (MailConstants.SORT_RECIPIENTS.equals(sortBy)) {
2433: key.append(StringHandling.getNotNull(messageDO
2434: .getRecipients()));
2435: } else if (MailConstants.SORT_RECIPIENTS_BCC
2436: .equals(sortBy)) {
2437: key.append(StringHandling.getNotNull(messageDO
2438: .getRecipientsBCC()));
2439: } else if (MailConstants.SORT_RECIPIENTS_CC
2440: .equals(sortBy)) {
2441: key.append(StringHandling.getNotNull(messageDO
2442: .getRecipientsCC()));
2443: } else if (MailConstants.SORT_SENDERS.equals(sortBy)) {
2444: key.append(StringHandling.getNotNull(messageDO
2445: .getSenders()));
2446: } else if (MailConstants.SORT_SIZE.equals(sortBy)) {
2447: key.append(StringHandling.getNotNull(messageDO
2448: .getSize()));
2449: } else if (!MailConstants.SORT_SENT.equals(sortBy)) {
2450: // default is subject - note sent date is appended below
2451: key.append(StringHandling.getNotNull(messageDO
2452: .getSubject()));
2453: }
2454:
2455: // append the sent date to the key for secondary sorting
2456: key.append(sentKey);
2457: map.put(key.toString(), messageDO);
2458: }
2459: } catch (NoSuchProviderException e) {
2460: logger
2461: .error(
2462: "findMessagesInFolder(MailSession, String, Integer, boolean)",
2463: e);
2464:
2465: throw new SystemException(e);
2466: } catch (MessagingException e) {
2467: logger
2468: .error(
2469: "findMessagesInFolder(MailSession, String, Integer, boolean)",
2470: e);
2471:
2472: throw new SystemException(e);
2473: } catch (SettingsDataTypeException e) {
2474: logger
2475: .error(
2476: "findMessagesInFolder(MailSession, String, Integer, boolean)",
2477: e);
2478:
2479: throw new SystemException(e);
2480: } finally {
2481: try {
2482: store.close();
2483: } catch (MessagingException e) {
2484: logger.error(
2485: "Messaging exception on closing the store", e);
2486: }
2487: }
2488:
2489: List returnList = new Vector(map.values());
2490:
2491: if (logger.isDebugEnabled()) {
2492: logger
2493: .debug("findMessagesInFolder(MailSession, String, Integer, boolean) - end - return value = "
2494: + returnList);
2495: }
2496:
2497: return returnList;
2498: }
2499:
2500: /**
2501: * <p>Format a header for a reply, or a forwarded message.</p>
2502: *
2503: * @param messageHeader the header to format.
2504: * @param oldMessage the old message to read data from
2505: * @return formatted message header.
2506: */
2507: private String formatMessageHeader(final String messageHeader,
2508: final MimeMessage oldMessage) throws SystemException {
2509: if (logger.isDebugEnabled()) {
2510: logger.debug("formatMessageHeader(String messageHeader = "
2511: + messageHeader + ", MimeMessage oldMessage = "
2512: + oldMessage + ") - start");
2513: }
2514:
2515: Date date;
2516: String oldSentDate;
2517: String oldSubject;
2518:
2519: try {
2520: date = oldMessage.getSentDate();
2521: oldSubject = StringHandling.getNotNull(oldMessage
2522: .getSubject(), getNullString());
2523: } catch (MessagingException e) {
2524: logger.error("formatMessageHeader(String, MimeMessage)", e);
2525:
2526: throw new SystemException(e);
2527: }
2528:
2529: if (date == null) {
2530: oldSentDate = "";
2531: } else {
2532: oldSentDate = dateFormatter.format(date);
2533: }
2534:
2535: EmailAddressFormatter emailFormatter = new EmailAddressFormatter();
2536:
2537: try {
2538: // the same four arguments are used in the same order
2539: // for both reply & forward headers in resource files
2540: String oldSenders = StringHandling.getNotNull(
2541: emailFormatter.format(oldMessage.getFrom()),
2542: getNullString());
2543: String oldRecipients = StringHandling
2544: .getNotNull(
2545: emailFormatter
2546: .format(oldMessage
2547: .getRecipients(MimeMessage.RecipientType.TO)),
2548: getNullString());
2549: Object[] arguments = { oldSenders, oldRecipients,
2550: oldSubject, oldSentDate };
2551:
2552: String returnString = MessageFormat.format(messageHeader,
2553: arguments);
2554:
2555: if (logger.isDebugEnabled()) {
2556: logger
2557: .debug("formatMessageHeader(String, MimeMessage) - end - return value = "
2558: + returnString);
2559: }
2560:
2561: return returnString;
2562: } catch (MessagingException e) {
2563: logger.error("formatMessageHeader(String, MimeMessage)", e);
2564:
2565: throw new SystemException(e);
2566: }
2567: }
2568:
2569: /**
2570: * <p>Retrieve an attachment's content and it's MimeType. This method is
2571: * used to by the download servlet.</p>
2572: *
2573: * @param mailSession valid mail session to which the user should already be
2574: * logged in.
2575: * @param folderName the name of the folder the message is located in.
2576: * @param messageId the unique identifier of the message.
2577: * @param attachmentId the unique identifier of the attachment.
2578: * @return attachment data object containing attachment content
2579: * and mime type.
2580: * @exception MessageNotFoundException if the folder doesn't exist, or there
2581: * is no matching mail in this folder.
2582: *
2583: * @ejb.interface-method
2584: * view-type = "remote"
2585: */
2586: public final FileContentDO getAttachment(
2587: final MailSession mailSession, final String folderName,
2588: final String messageId, final String attachmentId)
2589: throws SystemException {
2590: if (logger.isDebugEnabled()) {
2591: logger.debug("getAttachment(MailSession mailSession = "
2592: + mailSession + ", String folderName = "
2593: + folderName + ", String messageId = " + messageId
2594: + ", String attachmentId = " + attachmentId
2595: + ") - start");
2596: }
2597:
2598: checkDateFormatter(mailSession);
2599:
2600: FileContentDO attachmentDO = null;
2601:
2602: Store store = mailServer.connectStore(mailSession);
2603:
2604: try {
2605: // right, we now have to find the message using the folder name and the message number
2606: Folder folder = mailServer.getFolder(mailSession, store,
2607: folderName);
2608:
2609: // now open the folder
2610: folder.open(Folder.READ_ONLY);
2611:
2612: Message message = findJavaMailMessageByFolderMessageId(
2613: folder, messageId);
2614: InputStream inStream = null;
2615: String mimeType = null;
2616:
2617: // prerequisites - the message must be multipart, otherwise it can
2618: // be only a self-contained attachment
2619: if (message.isMimeType("multipart/*")) {
2620: MimeMultipart multiMessage = (MimeMultipart) message
2621: .getContent();
2622: MimeBodyPart subPart = getAttachment(multiMessage,
2623: attachmentId);
2624: inStream = subPart.getInputStream();
2625: mimeType = subPart.getContentType();
2626: } else {
2627: String withOutAngels = attachmentId.substring(1,
2628: attachmentId.length() - 1);
2629:
2630: if (((message.getFileName() != null) && attachmentId
2631: .equals(message.getFileName()))
2632: || ((message.getFileName() != null) && withOutAngels
2633: .equals(message.getFileName()))) {
2634: inStream = message.getInputStream();
2635: mimeType = message.getContentType();
2636: }
2637: }
2638:
2639: if ((inStream != null) && (mimeType != null)) {
2640: int oneByte;
2641:
2642: ByteArrayOutputStream outStream = new ByteArrayOutputStream();
2643:
2644: while ((oneByte = inStream.read()) != -1) {
2645: outStream.write(oneByte);
2646: }
2647:
2648: attachmentDO = new FileContentDO(
2649: new SerializedByteArray(outStream.toByteArray()),
2650: mimeType);
2651: }
2652:
2653: folder.close(false);
2654: } catch (MessagingException e) {
2655: logger
2656: .error(
2657: "getAttachment(MailSession, String, String, String)",
2658: e);
2659:
2660: throw new SystemException(e);
2661: } catch (IOException e) {
2662: logger
2663: .error(
2664: "getAttachment(MailSession, String, String, String)",
2665: e);
2666:
2667: throw new SystemException(e);
2668: } finally {
2669: try {
2670: store.close();
2671: } catch (MessagingException e) {
2672: logger.error(
2673: "Messaging exception on closing the store", e);
2674: }
2675: }
2676:
2677: if (logger.isDebugEnabled()) {
2678: logger
2679: .debug("getAttachment(MailSession, String, String, String) - end - return value = "
2680: + attachmentDO);
2681: }
2682:
2683: return attachmentDO;
2684: }
2685:
2686: /**
2687: * <p>This method goes through multiparts recursively looking for a part
2688: * with the given content id
2689: * @param multiPart the <code>MimeMultipart</code> to search
2690: * @param attachmentId the id of the wanted part
2691: * @return <code>MimeBodyPart</code> with the wanted id or <code>null</code>
2692: * when there wasn't such part in the given multipart
2693: * @throws MessagingException
2694: * @throws IOException
2695: */
2696: private MimeBodyPart getAttachment(final MimeMultipart multiPart,
2697: final String attachmentId) throws MessagingException,
2698: IOException {
2699: if (logger.isDebugEnabled()) {
2700: logger.debug("getAttachment(MimeMultipart multiPart = "
2701: + multiPart + ", String attachmentId = "
2702: + attachmentId + ") - start");
2703: }
2704:
2705: MimeBodyPart attachment = (MimeBodyPart) multiPart
2706: .getBodyPart(attachmentId);
2707:
2708: // if the attachment was not found in this level, it's more embedded...
2709: if (attachment == null) {
2710: MimeBodyPart embeddedPart;
2711:
2712: for (int i = 0; i < multiPart.getCount(); i++) {
2713: embeddedPart = (MimeBodyPart) multiPart.getBodyPart(i);
2714:
2715: // go deeper to multiparts
2716: if (embeddedPart.isMimeType("multipart/*")) {
2717: // done when something returned from recursive call
2718: if ((attachment = getAttachment(
2719: (MimeMultipart) embeddedPart.getContent(),
2720: attachmentId)) != null) {
2721: break;
2722: }
2723: } else if ((embeddedPart.getFileName() != null)
2724: && (attachmentId.indexOf(embeddedPart
2725: .getFileName()) != -1)) {
2726: attachment = embeddedPart;
2727:
2728: break;
2729: }
2730: }
2731: }
2732:
2733: if (logger.isDebugEnabled()) {
2734: logger
2735: .debug("getAttachment(MimeMultipart, String) - end - return value = "
2736: + attachment);
2737: }
2738:
2739: return attachment;
2740: }
2741:
2742: /**
2743: * <p>Convert a <em>JavaMail</em> message to an <em>ivata groupware</em> dependent
2744: * value object.</p>
2745: *
2746: * @param message a valid <em>JavaMail</em> message to be converted.
2747: * @param includeContent <code>true</code> if the <code>text</code> and
2748: * attachments of the message should also be set, otherwise
2749: * <code>false</code>.
2750: * @return message data object with the values filled out to match
2751: * the <em>JavaMail</em> object.
2752: */
2753: private MessageDO getDOFromJavaMailMessage(
2754: final MimeMessage message, final boolean includeContent)
2755: throws SystemException {
2756: if (logger.isDebugEnabled()) {
2757: logger
2758: .debug("getDOFromJavaMailMessage(MimeMessage message = "
2759: + message
2760: + ", boolean includeContent = "
2761: + includeContent + ") - start");
2762: }
2763:
2764: // TODO: this is almost the same as the createDO... method just now
2765: // right - we got here, so that means we have a message
2766: MessageDO messageDO = new MessageDO();
2767:
2768: try {
2769: //setting the fields of the MessageDO:
2770: if (message.getFolder() != null) {
2771: messageDO.setFolderName(message.getFolder().getName());
2772: }
2773:
2774: if (message.getReceivedDate() != null) {
2775: GregorianCalendar receivedDate = new GregorianCalendar();
2776:
2777: receivedDate.setTime(message.getReceivedDate());
2778: messageDO.setReceived(receivedDate);
2779: }
2780:
2781: if (message.getRecipients(Message.RecipientType.TO) != null) {
2782: messageDO
2783: .setRecipients(Arrays
2784: .asList(toStringArray(message
2785: .getRecipients(Message.RecipientType.TO))));
2786: }
2787:
2788: if (message.getRecipients(Message.RecipientType.CC) != null) {
2789: messageDO
2790: .setRecipientsCC(Arrays
2791: .asList(toStringArray(message
2792: .getRecipients(Message.RecipientType.CC))));
2793: }
2794:
2795: if (message.getRecipients(Message.RecipientType.BCC) != null) {
2796: messageDO
2797: .setRecipientsBCC(Arrays
2798: .asList(toStringArray(message
2799: .getRecipients(Message.RecipientType.BCC))));
2800: }
2801:
2802: if (message.getFrom() != null) {
2803: messageDO.setSenders(Arrays
2804: .asList(toStringArray(message.getFrom())));
2805: }
2806:
2807: if (message.getSentDate() != null) {
2808: GregorianCalendar sentDate = new GregorianCalendar();
2809:
2810: sentDate.setTime(message.getSentDate());
2811: messageDO.setSent(sentDate);
2812: }
2813:
2814: messageDO.setSize(new Integer(message.getSize()));
2815: messageDO.setSubject(message.getSubject());
2816:
2817: // message content handling - not always done for efficiency in lists
2818: if (includeContent) {
2819: // create new, empty List for our attachments
2820: messageDO.setAttachments(new Vector());
2821:
2822: // if it is a multipart message (has attachments), pass control to
2823: // recursive routine to go thro' them all
2824: if (message.isMimeType("multipart/*")) {
2825: addMultiPart(message, messageDO);
2826:
2827: // here are types with textual content
2828: } else if (message.isMimeType("text/*")
2829: || message.isMimeType("message/*")) {
2830: // if it is not multipart, we're just left with text or HTML
2831: if (message.isMimeType("text/HTML")) {
2832: // simple message with HTML content
2833: messageDO
2834: .setFormat(FormatConstants.FORMAT_HTML);
2835: } else {
2836: // anything else with simple content should have text content
2837: messageDO
2838: .setFormat(FormatConstants.FORMAT_TEXT);
2839: }
2840:
2841: messageDO.setText((String) message.getContent());
2842:
2843: // other (not correct?) types, as self-contained attachments...
2844: } else {
2845: messageDO.setFormat(FormatConstants.FORMAT_TEXT);
2846: messageDO.setText("");
2847: addPart(message, messageDO);
2848: }
2849: }
2850:
2851: messageDO.setMessageID(message.getMessageID());
2852: } catch (MessagingException e) {
2853: logger
2854: .error(
2855: "getDOFromJavaMailMessage(MimeMessage, boolean)",
2856: e);
2857:
2858: throw new SystemException(e);
2859: } catch (IOException e) {
2860: logger
2861: .error(
2862: "getDOFromJavaMailMessage(MimeMessage, boolean)",
2863: e);
2864:
2865: throw new SystemException(e);
2866: }
2867:
2868: if (logger.isDebugEnabled()) {
2869: logger
2870: .debug("getDOFromJavaMailMessage(MimeMessage, boolean) - end - return value = "
2871: + messageDO);
2872: }
2873:
2874: return messageDO;
2875: }
2876:
2877: /**
2878: * <p>Helper for <code>createThreadMessage</code>. This method processes a
2879: * <em>Multipart</em> message or part and returns just the body parts
2880: * representing text in either plain-text and/or <code>HTML</code> format.</p>
2881: *
2882: * @param message or part to be processed.
2883: * @return the text of the message or part, it's plain text or <code>HTML</code>.
2884: */
2885: private MessageTextParts getMultiPartText(final Part message)
2886: throws SystemException {
2887: if (logger.isDebugEnabled()) {
2888: logger.debug("getMultiPartText(Part message = " + message
2889: + ") - start");
2890: }
2891:
2892: MessageTextParts messageTextParts = new MessageTextParts();
2893: MimeMultipart content;
2894: MimeBodyPart subPart;
2895: int i;
2896:
2897: try {
2898: content = (MimeMultipart) message.getContent();
2899:
2900: //go through all the subParts
2901: for (i = 0; i < content.getCount(); i++) {
2902: subPart = (MimeBodyPart) content.getBodyPart(i);
2903:
2904: //when multipart/alternative, we are close to find what we want...
2905: if (message.isMimeType("multipart/alternative")) {
2906: if (subPart.isMimeType("text/plain")
2907: && (messageTextParts.textPart == null)) {
2908: messageTextParts.textPart = subPart;
2909: } else if (subPart.isMimeType("text/HTML")
2910: && (messageTextParts.HTMLPart == null)) {
2911: messageTextParts.HTMLPart = subPart;
2912:
2913: // usually the HTML part will come after the plain text
2914: // if the plain text is set, we're all finished
2915: if (messageTextParts.textPart != null) {
2916: break;
2917: }
2918: }
2919: } else {
2920: // if this is not alternative multipart, recurse to get the text
2921: if (subPart.isMimeType("multipart/*")) {
2922: messageTextParts = getMultiPartText((Part) subPart);
2923:
2924: // when we are on a text subpart and text not set yet, this
2925: // should be it...
2926: } else if (subPart.isMimeType("text/plain")
2927: && (messageTextParts.textPart == null)) {
2928: messageTextParts.textPart = subPart;
2929: } else if (subPart.isMimeType("text/HTML")
2930: && (messageTextParts.HTMLPart == null)) {
2931: messageTextParts.HTMLPart = subPart;
2932:
2933: break;
2934: }
2935: }
2936: }
2937: } catch (MessagingException e) {
2938: logger.error("getMultiPartText(Part)", e);
2939:
2940: throw new SystemException(e);
2941: } catch (java.io.IOException e) {
2942: logger.error("getMultiPartText(Part)", e);
2943:
2944: throw new SystemException(e);
2945: }
2946:
2947: if (logger.isDebugEnabled()) {
2948: logger
2949: .debug("getMultiPartText(Part) - end - return value = "
2950: + messageTextParts);
2951: }
2952:
2953: return messageTextParts;
2954: }
2955:
2956: /**
2957: * <p>Generic string to use when some message text/subject is null.</p>
2958: *
2959: * @return replacement text to use when a message element is null.
2960: */
2961: private String getNullString() {
2962: if (logger.isDebugEnabled()) {
2963: logger.debug("getNullString() - start");
2964: }
2965:
2966: // TODO: i18n
2967: if (logger.isDebugEnabled()) {
2968: logger
2969: .debug("getNullString() - end - return value = [none]");
2970: }
2971:
2972: return "[none]";
2973: }
2974:
2975: /**
2976: * <p>Retrieve all of the email aliases for the user provided, on the curent
2977: * email server. The aliases returned each one containing just the 'user' (or
2978: * 'local') part of the email address, before the '@' sign.</p>
2979: *
2980: * @param userName the name of the user for whom to retrieve the email aliases.
2981: * @return a <code>Collection</code> of <code>String</code> instances containing
2982: * the local part of the different email aliases this user has. If the user
2983: * has no aliaes, an empty collection is returned.
2984: */
2985: public List getUserAliases(final SecuritySession securitySession,
2986: final String userName) throws SystemException {
2987: if (logger.isDebugEnabled()) {
2988: logger
2989: .debug("getUserAliases(SecuritySession securitySession = "
2990: + securitySession
2991: + ", String userName = "
2992: + userName + ") - start");
2993: }
2994:
2995: checkMailServer();
2996: checkDateFormatter(securitySession);
2997:
2998: List aliases = null;
2999: aliases = mailServer.getUserAliases(securitySession, userName);
3000:
3001: if (logger.isDebugEnabled()) {
3002: logger
3003: .debug("getUserAliases(SecuritySession, String) - end - return value = "
3004: + aliases);
3005: }
3006:
3007: return aliases;
3008: }
3009:
3010: /**
3011: * <p>Get the vacation message for the user provided.</p>
3012: *
3013: * <p>The vacation message is a text the user can set, which will be sent
3014: * to all emails received at this address while the user is not present.</p>
3015: *
3016: * @param userName the name of the user for whom to get the vacation message.
3017: * @return the vacation message text for this user. If the user has no
3018: * vacation message set, <code>null</code> is returned.
3019: */
3020: public final String getVacationMessage(
3021: final SecuritySession securitySession, final String userName)
3022: throws SystemException {
3023: if (logger.isDebugEnabled()) {
3024: logger
3025: .debug("getVacationMessage(SecuritySession securitySession = "
3026: + securitySession
3027: + ", String userName = "
3028: + userName + ") - start");
3029: }
3030:
3031: checkMailServer();
3032: checkDateFormatter(securitySession);
3033:
3034: String vacationMessage = null;
3035: vacationMessage = mailServer.getVacationMessage(
3036: securitySession, userName);
3037:
3038: if (logger.isDebugEnabled()) {
3039: logger
3040: .debug("getVacationMessage(SecuritySession, String) - end - return value = "
3041: + vacationMessage);
3042: }
3043:
3044: return vacationMessage;
3045: }
3046:
3047: /**
3048: * <p>Get the time the specified mail folder was last modified as a
3049: * <code>long</code>. This can then be saved and compared to subsequent
3050: * calls of this method to see if the folder has changed.</p>
3051: *
3052: * @param userName the name of the user for whom to locate the folder.
3053: * @param folderName the name of the folder to locate.
3054: * @return operating system specific timestamp indicating when the
3055: * folder was last changed.
3056: *
3057: * @ejb.interface-method
3058: * view-type = "remote"
3059: */
3060: public final boolean hasNewMessages(
3061: final SecuritySession securitySession,
3062: final String userName, final String folderName)
3063: throws SystemException {
3064: if (logger.isDebugEnabled()) {
3065: logger
3066: .debug("hasNewMessages(SecuritySession securitySession = "
3067: + securitySession
3068: + ", String userName = "
3069: + userName
3070: + ", String folderName = "
3071: + folderName + ") - start");
3072: }
3073:
3074: checkMailServer();
3075:
3076: if (userName == null) {
3077: throw new SystemException(
3078: "ERROR in MailBean.getFolderModified: userName is null.");
3079: }
3080:
3081: if (folderName == null) {
3082: throw new SystemException(
3083: "ERROR in MailBean.getFolderModified: folderName is null.");
3084: }
3085:
3086: boolean returnboolean = mailServer.hasNewMessages(
3087: securitySession, userName, folderName);
3088:
3089: if (logger.isDebugEnabled()) {
3090: logger
3091: .debug("hasNewMessages(SecuritySession, String, String) - end - return value = "
3092: + returnboolean);
3093: }
3094:
3095: return returnboolean;
3096: }
3097:
3098: /**
3099: * <p>Login to the mail system. This method should be called before any other,
3100: * to establish the mail session and store.</p>
3101: *
3102: * @param userName this user name is used to log into the remote system.
3103: * @param password the clear-text password to log into the remote system.
3104: * @throws GroupwareException if the person cannot log in.
3105: * @return the mail session (class <code>Session</code>) in a
3106: * <code>SessionSerializer</code>.
3107: *
3108: * @ejb.interface-method
3109: * view-type = "local"
3110: */
3111: public MailSession login(final UserDO user, final String password)
3112: throws SystemException {
3113: if (logger.isDebugEnabled()) {
3114: logger.debug("login(UserDO user = " + user
3115: + ", String password = " + password + ") - start");
3116: }
3117:
3118: checkMailServer();
3119:
3120: MailSession returnMailSession = (MailSession) mailServer.login(
3121: user, password);
3122:
3123: if (logger.isDebugEnabled()) {
3124: logger
3125: .debug("login(UserDO, String) - end - return value = "
3126: + returnMailSession);
3127: }
3128:
3129: return returnMailSession;
3130: }
3131:
3132: /**
3133: * <p>Move a list of messages from one folder to another.</p>
3134: *
3135: * @param mailSession valid mail session to which the user should already be
3136: * logged in.
3137: * @param folderName the name of the folder the messages are currently located in.
3138: * @param ids the unique identifiers (<code>String</code> instances) of the
3139: * messages to be moved.
3140: * @param targetFolderName the name of the the folder the message will be moved
3141: * to.
3142: * @exception MessageNotFoundException if the folder doesn't exist, or there
3143: * is no matching mail in this folder.
3144: *
3145: * @ejb.interface-method
3146: * view-type = "remote"
3147: */
3148: public void moveMessages(final MailSession mailSession,
3149: final String folderName, final List ids,
3150: final String targetFolderName) throws SystemException {
3151: if (logger.isDebugEnabled()) {
3152: logger.debug("moveMessages(MailSession mailSession = "
3153: + mailSession + ", String folderName = "
3154: + folderName + ", List ids = " + ids
3155: + ", String targetFolderName = " + targetFolderName
3156: + ") - start");
3157: }
3158:
3159: Store store = mailServer.connectStore(mailSession);
3160: checkDateFormatter(mailSession);
3161:
3162: try {
3163: Folder folder = mailServer.getFolder(mailSession, store,
3164: folderName);
3165:
3166: folder.open(Folder.READ_WRITE);
3167:
3168: Folder targetFolder = mailServer.getFolder(mailSession,
3169: store, targetFolderName);
3170:
3171: if (!targetFolder.exists()) {
3172: targetFolder.create(Folder.HOLDS_MESSAGES);
3173: }
3174:
3175: targetFolder.open(Folder.READ_WRITE);
3176:
3177: List messages = new Vector();
3178:
3179: for (Iterator i = ids.iterator(); i.hasNext();) {
3180: String id = (String) i.next();
3181: MimeMessage message = findJavaMailMessageByFolderMessageId(
3182: folder, id);
3183:
3184: if (message == null) {
3185: logger
3186: .error("MailBean.moveMessage:I couldn't find a message with id"
3187: + id
3188: + " in folder '"
3189: + folder.getName() + "'");
3190: } else {
3191: messages.add(message);
3192: }
3193: }
3194:
3195: if (!messages.isEmpty()) {
3196: Message[] messageArray = {};
3197:
3198: messageArray = (Message[]) messages
3199: .toArray(messageArray);
3200: targetFolder.appendMessages(messageArray);
3201: targetFolder.close(false);
3202:
3203: // now mark all the original messages as deleted
3204: for (Iterator i = messages.iterator(); i.hasNext();) {
3205: Message message = (Message) i.next();
3206:
3207: message.setFlag(Flags.Flag.DELETED, true);
3208: }
3209: }
3210:
3211: folder.expunge();
3212: folder.close(true);
3213: } catch (NoSuchProviderException e) {
3214: logger.error(
3215: "moveMessages(MailSession, String, List, String)",
3216: e);
3217:
3218: throw new SystemException(e);
3219: } catch (MessagingException e) {
3220: logger.error(
3221: "moveMessages(MailSession, String, List, String)",
3222: e);
3223:
3224: throw new SystemException(e);
3225: } finally {
3226: try {
3227: store.close();
3228: } catch (MessagingException e) {
3229: logger.error(
3230: "Messaging exception on closing the store", e);
3231: }
3232: }
3233:
3234: if (logger.isDebugEnabled()) {
3235: logger
3236: .debug("moveMessages(MailSession, String, List, String) - end");
3237: }
3238: }
3239:
3240: /**
3241: * <p>Helper method to get the name of the drafts folder from the settings,
3242: * and then open the folder in read-write mode.</p>
3243: *
3244: * @param store connected store.
3245: * @param javaMailSession valid <em>JavaMail</em> session to which the user
3246: * should already be logged in.
3247: * @return opened drafts folder.
3248: * @exception NoSuchProviderException thrown by
3249: * <code>Session.getStore</code> if there is no <em>IMAP</em> store
3250: * available.
3251: * @exception MessagingException throw by <code>JavaMail</code> if there is
3252: * problem locating or opening the drafts folder.
3253: * @exception SettingsDataTypeException if there is a problem accessing the
3254: * setting "emailFolderDrafts".
3255: */
3256: private Folder openDraftsFolder(final Store store,
3257: final MailSession mailSession) throws SystemException {
3258: if (logger.isDebugEnabled()) {
3259: logger.debug("openDraftsFolder(Store store = " + store
3260: + ", MailSession mailSession = " + mailSession
3261: + ") - start");
3262: }
3263:
3264: assert (store != null);
3265: assert (store.isConnected());
3266: assert (mailSession != null);
3267: checkDateFormatter(mailSession);
3268:
3269: UserDO user = mailSession.getUser();
3270:
3271: try {
3272: // first get the name of the drafts folder from the settings
3273: String draftsFolderName = settings.getStringSetting(
3274: mailSession, "emailFolderDrafts", user);
3275: Folder draftsFolder = mailServer.getFolder(mailSession,
3276: store, draftsFolderName);
3277:
3278: if (!(draftsFolder.exists() || draftsFolder
3279: .create(Folder.HOLDS_MESSAGES))) {
3280: throw new SystemException(
3281: "ERROR in MailBean.openDraftsFolder: Cannot create drafts folder.");
3282: }
3283:
3284: draftsFolder.open(Folder.READ_WRITE);
3285:
3286: if (logger.isDebugEnabled()) {
3287: logger
3288: .debug("openDraftsFolder(Store, MailSession) - end - return value = "
3289: + draftsFolder);
3290: }
3291:
3292: return draftsFolder;
3293: } catch (MessagingException e) {
3294: logger.error("openDraftsFolder(Store, MailSession)", e);
3295:
3296: throw new SystemException(e);
3297: }
3298: }
3299:
3300: /**
3301: * <p>Helper. This method calculates which of an old message's attachments
3302: * are still required and sets the content of the new message on this
3303: * basis.</p>
3304: *
3305: * <p>This method contains functionality from <code>setDO...</code> which
3306: * has been split off to avoid a long <code>if...else</code>.</p>
3307: *
3308: * @param messageDO data object containing the latest changes from
3309: * client-side.
3310: * @param newBasePart the new base part of the message with content
3311: * pre-prepared.
3312: * @param oldMessage previous message retained from drafts folder.
3313: * @param newMessage new message being constructed from the draft message and
3314: * messageDO changes.
3315: */
3316: private void retainAttachments(final MessageDO messageDO,
3317: final MimeBodyPart newBasePart,
3318: final MimeMessage oldMessage, final MimeMessage newMessage)
3319: throws SystemException {
3320: if (logger.isDebugEnabled()) {
3321: logger.debug("retainAttachments(MessageDO messageDO = "
3322: + messageDO + ", MimeBodyPart newBasePart = "
3323: + newBasePart + ", MimeMessage oldMessage = "
3324: + oldMessage + ", MimeMessage newMessage = "
3325: + newMessage + ") - start");
3326: }
3327:
3328: try {
3329: MimeMultipart newMasterContent = new MimeMultipart();
3330: MimeMultipart oldContent = (MimeMultipart) oldMessage
3331: .getContent();
3332:
3333: // iterate through all the BodyParts (from 1 as it should be the base part)
3334: for (int j = 1; j < oldContent.getCount(); j++) {
3335: MimeBodyPart mimePart = new MimeBodyPart();
3336:
3337: mimePart = (MimeBodyPart) oldContent.getBodyPart(j);
3338:
3339: // if there are no attachments to remove, no need to find out if this one has to die
3340: if (messageDO.getAttachments() != null) {
3341: // look if this BodyPart is in the list of attachments to delete
3342: for (Iterator i = messageDO.getAttachments()
3343: .iterator(); i.hasNext();) {
3344: String id = ((FileDO) i.next()).getName();
3345:
3346: // if it's an embedded message, try with messageId
3347: if ((mimePart.isMimeType("message/*") && (((MimeMessage) mimePart
3348: .getContent()).getMessageID()
3349: .equals(id)))
3350: || ((mimePart.getFileName() != null) && mimePart
3351: .getFileName().equals(id))
3352: || ((mimePart.getContentID() != null) && mimePart
3353: .getContentID().equals(id))) {
3354: newMasterContent.addBodyPart(mimePart);
3355: }
3356: }
3357: }
3358: }
3359:
3360: // if there's at least one attachment left, it's worth to let it be multipart/mixed
3361: // and put the new base as the first part in this multipart
3362: if (newMasterContent.getCount() > 0) {
3363: newMasterContent.addBodyPart(newBasePart, 0);
3364: newMessage.setContent(newMasterContent);
3365:
3366: //if there are no attachments, only the base part will be there
3367: } else {
3368: if (messageDO.getFormat() == FormatConstants.FORMAT_HTML) {
3369: newMessage.setContent((MimeMultipart) newBasePart
3370: .getContent());
3371: newMessage.setHeader("Content-Type", newBasePart
3372: .getContentType());
3373: } else {
3374: newMessage.setText(messageDO.getText());
3375: }
3376: }
3377: } catch (MessagingException e) {
3378: logger
3379: .error(
3380: "retainAttachments(MessageDO, MimeBodyPart, MimeMessage, MimeMessage)",
3381: e);
3382:
3383: throw new SystemException(e);
3384: } catch (IOException e) {
3385: logger
3386: .error(
3387: "retainAttachments(MessageDO, MimeBodyPart, MimeMessage, MimeMessage)",
3388: e);
3389:
3390: throw new SystemException(e);
3391: }
3392:
3393: if (logger.isDebugEnabled()) {
3394: logger
3395: .debug("retainAttachments(MessageDO, MimeBodyPart, MimeMessage, MimeMessage) - end");
3396: }
3397: }
3398:
3399: /**
3400: * <p>Send a mime email message that is already composed. If <code>id</code>
3401: * has been set in <code>messageDO</code> it is assumed to point to a
3402: * message in the drafts folder. Attachments are copied from the message
3403: * who match the contents of <code>getAttachmentIds</code>. (All other
3404: * attachments are discarded.)</p>
3405: *
3406: * @param mailSession valid mail session to which the user should already be
3407: * logged in.
3408: * @param messageDO data object containing full details of the
3409: * message to be sent.
3410: *
3411: * @ejb.interface-method
3412: * view-type = "remote"
3413: */
3414: public void send(final MailSession mailSession,
3415: final MessageDO messageDO) throws SystemException {
3416: if (logger.isDebugEnabled()) {
3417: logger.debug("send(MailSession mailSession = "
3418: + mailSession + ", MessageDO messageDO = "
3419: + messageDO + ") - start");
3420: }
3421:
3422: checkDateFormatter(mailSession);
3423:
3424: Store store = mailServer.connectStore(mailSession);
3425:
3426: try {
3427: // if mail is disabled, just get out
3428: if (!settings.getBooleanSetting(mailSession, "emailEnable",
3429: null).booleanValue()) {
3430: if (logger.isDebugEnabled()) {
3431: logger.debug("send(MailSession, MessageDO) - end");
3432: }
3433:
3434: if (logger.isDebugEnabled()) {
3435: logger.debug("send(MailSession, MessageDO) - end");
3436: }
3437:
3438: return;
3439: }
3440:
3441: Session javaMailSession;
3442:
3443: try {
3444: javaMailSession = mailSession.getJavaMailSession();
3445: } catch (SecurityServerException e1) {
3446: logger.error("send(MailSession, MessageDO)", e1);
3447:
3448: throw new SystemException(e1);
3449: } catch (java.security.NoSuchProviderException e1) {
3450: logger.error("send(MailSession, MessageDO)", e1);
3451:
3452: throw new SystemException(e1);
3453: }
3454:
3455: // get the drafts folder in case we want to copy over an older mail
3456: Folder draftsFolder = openDraftsFolder(store, mailSession);
3457: MimeMessage newMessage = setDOToJavaMailMessage(
3458: javaMailSession, draftsFolder, messageDO);
3459:
3460: newMessage.setSentDate(Calendar.getInstance().getTime());
3461:
3462: // this bit actually sends the message
3463: Transport.send(newMessage);
3464:
3465: // add the message to the sent folder
3466: addToSentFolder(mailSession, newMessage);
3467:
3468: // only now can we delete/expunge the old mail from the drafts folder
3469: draftsFolder.expunge();
3470: draftsFolder.close(true);
3471: } catch (MessagingException e) {
3472: logger.error("send(MailSession, MessageDO)", e);
3473:
3474: throw new SystemException(e);
3475: } catch (SettingsDataTypeException e) {
3476: logger.error("send(MailSession, MessageDO)", e);
3477:
3478: throw new SystemException(e);
3479: } finally {
3480: try {
3481: store.close();
3482: } catch (MessagingException e) {
3483: logger.error(
3484: "Messaging exception on closing the store", e);
3485: }
3486: }
3487:
3488: if (logger.isDebugEnabled()) {
3489: logger.debug("send(MailSession, MessageDO) - end");
3490: }
3491: }
3492:
3493: /**
3494: * <p>Send an mime email message without using a data object.</p>
3495: *
3496: * @param mailSession valid mail session to which the user should already be
3497: * logged in.
3498: * @param fromAddress the address of the person sending the mail. This must
3499: * be formatted according to <a
3500: * href='http://www.faqs.org/rfcs/rfc822.HTML'>RFC822</a>.
3501: * @param to recipients, a <code>Collection</code> containing instances of
3502: * <code>String</code> or <code>UserDO</code> or
3503: * <code>PersonDO</code>. A mixture of these types is allowed. If the
3504: * type of an instance is <code>String</code>, then it must be formatted
3505: * according to <a href='http://www.faqs.org/rfcs/rfc822.HTML'>RFC822</a>.
3506: * Otherwise, if the type is <code>PersonDO</code>, the method
3507: * <code>getEmailAddress</code> must return a valid address string for
3508: * this person.
3509: * @param subject clear-text email subject field.
3510: * @param content any valid email content type, as defined by
3511: * <code>MimeMessage</code>.
3512: * @param contentType mime type for the <code>content</code> field, as
3513: * defined by <code>MimeMessage</code>.
3514: * @param addToSentFolder if set to <code>true</code> then the mail is added
3515: * to the sent folder of the current email session.
3516: * @ejb.interface-method
3517: * view-type = "both"
3518: */
3519: public void send(final MailSession mailSession,
3520: final String fromAddress, final Collection to,
3521: final String subject, Object content, String contentType,
3522: boolean addToSentFolder) throws SystemException {
3523: if (logger.isDebugEnabled()) {
3524: logger.debug("send(MailSession mailSession = "
3525: + mailSession + ", String fromAddress = "
3526: + fromAddress + ", Collection to = " + to
3527: + ", String subject = " + subject
3528: + ", Object content = " + content
3529: + ", String contentType = " + contentType
3530: + ", boolean addToSentFolder = " + addToSentFolder
3531: + ") - start");
3532: }
3533:
3534: checkDateFormatter(mailSession);
3535:
3536: try {
3537: try {
3538: // if mail is disabled, just get out
3539: if (!settings.getBooleanSetting(mailSession,
3540: "emailEnable", null).booleanValue()) {
3541: if (logger.isDebugEnabled()) {
3542: logger
3543: .debug("send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean) - end");
3544: }
3545:
3546: if (logger.isDebugEnabled()) {
3547: logger
3548: .debug("send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean) - end");
3549: }
3550:
3551: return;
3552: }
3553: } catch (SettingsDataTypeException e) {
3554: logger
3555: .error(
3556: "send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean)",
3557: e);
3558:
3559: throw new SystemException(e);
3560: }
3561:
3562: // see if we have a mail session
3563: Session javaMailSession;
3564:
3565: try {
3566: javaMailSession = mailSession.getJavaMailSession();
3567: } catch (AuthenticationFailedException e) {
3568: logger
3569: .error(
3570: "send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean)",
3571: e);
3572:
3573: throw new SystemException(
3574: "User is no longer authorized to use this server: "
3575: + e.getMessage(), e);
3576: } catch (MessagingException e) {
3577: logger
3578: .error(
3579: "send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean)",
3580: e);
3581:
3582: throw new SystemException(e);
3583: } catch (SecurityServerException e) {
3584: logger
3585: .error(
3586: "send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean)",
3587: e);
3588:
3589: throw new SystemException(e);
3590: } catch (java.security.NoSuchProviderException e) {
3591: logger
3592: .error(
3593: "send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean)",
3594: e);
3595:
3596: throw new SystemException(e);
3597: }
3598:
3599: Store store = javaMailSession.getStore("imap");
3600:
3601: if (store == null) {
3602: throw new SystemException(
3603: "ERROR in MailBean: could not access the mail store");
3604: }
3605:
3606: MimeMessage message = new MimeMessage(javaMailSession);
3607:
3608: try {
3609: message.setFrom(new InternetAddress(fromAddress));
3610: message.setSubject(subject);
3611: message.setRecipients(MimeMessage.RecipientType.TO,
3612: convertAddresses(null, to));
3613: message.setContent(content, contentType);
3614: message.setSentDate(Calendar.getInstance().getTime());
3615: } catch (MessagingException e) {
3616: logger
3617: .error(
3618: "send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean)",
3619: e);
3620:
3621: throw new SystemException("Please check your input: "
3622: + e.getMessage(), e);
3623: }
3624:
3625: // send it
3626: try {
3627: Transport.send(message);
3628: } catch (MessagingException e) {
3629: logger
3630: .error(
3631: "send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean)",
3632: e);
3633:
3634: throw new SystemException(
3635: "The message could not be sent: "
3636: + e.getMessage(), e);
3637: }
3638:
3639: // add the message to the sent items folder
3640: if (addToSentFolder) {
3641: addToSentFolder(mailSession, message);
3642: }
3643: } catch (NoSuchProviderException e) {
3644: logger
3645: .error(
3646: "send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean)",
3647: e);
3648:
3649: throw new SystemException(e);
3650: }
3651:
3652: if (logger.isDebugEnabled()) {
3653: logger
3654: .debug("send(MailSession, String, Collection, Collection, Collection, String, Object, String, boolean) - end");
3655: }
3656: }
3657:
3658: /**
3659: * <p>Convert an <em>ivata groupware</em> message data object into a
3660: * new <em>JavaMail</em> message that's ready to be sent.</p>
3661: *
3662: * <p><strong>Note:</strong> this method will look for an existing message (in the
3663: * drafts folder) to use for the attachments. This is only done if the
3664: * <code>MessageDO</code> has a valid <code>id</code>, and the drafts folder
3665: * provided is not <code>null</code>. The previous mail is marked for
3666: * deletion in the drafts folder, and this can be confirmed by expunging that
3667: * folder and calling <code>close</code> with <code>true</code>. This is
3668: * not done internally as it may depend on further actions (such as adding
3669: * another mail successfully to the folder).</p>
3670: *
3671: * @param javaMailSession valid <em>JavaMail</em> session to which the user
3672: * should already be logged in.
3673: * @param draftsFolder the opened drafts to retrieve a previous message from.
3674: * The previous message is identified by the <code>id</code> attribute
3675: * of the <code>messageDO</code>. If this parameter is <code>null</code>
3676: * then no previous mail is considered.
3677: * @param messageDO a valid message data object with the values
3678: * filled out to match the <em>JavaMail</em> object which will be sent.
3679: * @return a valid <em>JavaMail</em> message ready to be sent.
3680: * @exception MessageNotFoundException if the folder doesn't exist, or there
3681: * is no matching mail in this folder.
3682: */
3683: private MimeMessage setDOToJavaMailMessage(
3684: final Session javaMailSession, final Folder draftsFolder,
3685: final MessageDO messageDO) throws SystemException {
3686: if (logger.isDebugEnabled()) {
3687: logger
3688: .debug("setDOToJavaMailMessage(Session javaMailSession = "
3689: + javaMailSession
3690: + ", Folder draftsFolder = "
3691: + draftsFolder
3692: + ", MessageDO messageDO = "
3693: + messageDO
3694: + ") - start");
3695: }
3696:
3697: MimeMessage newMessage = new MimeMessage(javaMailSession);
3698:
3699: try {
3700: newMessage.addFrom(convertAddresses(null, messageDO
3701: .getSenders()));
3702: newMessage.setSubject(messageDO.getSubject());
3703: newMessage.setRecipients(MimeMessage.RecipientType.TO,
3704: convertAddresses(null, messageDO.getRecipients()));
3705:
3706: if (messageDO.getRecipientsCC() != null) {
3707: newMessage.setRecipients(MimeMessage.RecipientType.CC,
3708: convertAddresses(null, messageDO
3709: .getRecipientsCC()));
3710: }
3711:
3712: if (messageDO.getRecipientsBCC() != null) {
3713: newMessage.setRecipients(MimeMessage.RecipientType.BCC,
3714: convertAddresses(null, messageDO
3715: .getRecipientsBCC()));
3716: }
3717:
3718: newMessage.setSentDate(Calendar.getInstance().getTime());
3719:
3720: // first try to identify any existing message we might be copying
3721: MimeMessage oldMessage = null;
3722:
3723: if ((messageDO.getMessageID() != null)
3724: && (draftsFolder != null)) {
3725: oldMessage = findJavaMailMessageByFolderMessageId(
3726: draftsFolder, messageDO.getMessageID());
3727: }
3728:
3729: MimeBodyPart newBasePart = new MimeBodyPart();
3730:
3731: // new base part is needed if the format is HTML or if there were
3732: // previous attachments we want to retain
3733: if (messageDO.getFormat() == FormatConstants.FORMAT_TEXT) {
3734: newBasePart.setText(messageDO.getText());
3735: } else {
3736: // HTML desired, the base part will be multipart/alternative
3737: MimeMultipart newTextContent = createHTMLContent(messageDO
3738: .getText());
3739:
3740: // set the multipart/alternative as the content of the base part
3741: newBasePart.setContent(newTextContent);
3742: newBasePart.setHeader("Content-Type", newTextContent
3743: .getContentType());
3744: }
3745:
3746: // if the message has no id, or if it had no attachments, then this
3747: // is a fresh new message -
3748: // no need to copy the contents of another message from the
3749: // drafts folder
3750: if ((oldMessage == null)
3751: || !oldMessage.isMimeType("multipart/mixed")
3752: || (messageDO.getAttachments() == null)) {
3753: // no attachments - simply decide whether we have HTML or just text
3754: if (messageDO.getFormat() == FormatConstants.FORMAT_HTML) {
3755: newMessage.setContent((MimeMultipart) newBasePart
3756: .getContent());
3757: newMessage.setHeader("Content-Type", newBasePart
3758: .getContentType());
3759: } else {
3760: newMessage.setText(messageDO.getText());
3761: }
3762: } else {
3763: // the old message had attachments - we need to examine which
3764: // of them are still required
3765: retainAttachments(messageDO, newBasePart, oldMessage,
3766: newMessage);
3767:
3768: // mark the old message for deletion
3769: oldMessage.setFlag(Flags.Flag.DELETED, true);
3770: }
3771: } catch (StackOverflowError e) {
3772: logger
3773: .error(
3774: "setDOToJavaMailMessage(Session, Folder, MessageDO)",
3775: e);
3776:
3777: e.printStackTrace();
3778: } catch (MessagingException e) {
3779: logger
3780: .error(
3781: "setDOToJavaMailMessage(Session, Folder, MessageDO)",
3782: e);
3783:
3784: throw new SystemException(e);
3785: } catch (IOException e) {
3786: logger
3787: .error(
3788: "setDOToJavaMailMessage(Session, Folder, MessageDO)",
3789: e);
3790:
3791: throw new SystemException(e);
3792: }
3793:
3794: if (logger.isDebugEnabled()) {
3795: logger
3796: .debug("setDOToJavaMailMessage(Session, Folder, MessageDO) - end - return value = "
3797: + newMessage);
3798: }
3799:
3800: return newMessage;
3801: }
3802:
3803: /**
3804: * <p>Set all of the email aliases for the user provided, on the curent
3805: * email server. Each alias in the collection should contain just the 'user'
3806: * (or 'local') part of the email address, before the '@' sign.</p>
3807: *
3808: * @param userName the name of the user for whom to retrieve the email aliases.
3809: * @param userAliases a <code>Collection</code> of <code>String</code>
3810: * instances containing the local part of the different email aliases
3811: * this user has. If the user has no aliaes, an empty collection should
3812: * be provided.
3813: */
3814: public final void setUserAliases(
3815: final SecuritySession securitySession,
3816: final String userName, final Collection userAliases)
3817: throws SystemException {
3818: if (logger.isDebugEnabled()) {
3819: logger
3820: .debug("setUserAliases(SecuritySession securitySession = "
3821: + securitySession
3822: + ", String userName = "
3823: + userName
3824: + ", Collection userAliases = "
3825: + userAliases + ") - start");
3826: }
3827:
3828: checkMailServer();
3829: checkDateFormatter(securitySession);
3830:
3831: // for the aliases which have been removed, remove the email addresses
3832: List currentUserAliases = mailServer.getUserAliases(
3833: securitySession, userName);
3834: PersonDO person = addressBook.findPersonByUserName(
3835: securitySession, userName);
3836: UserDO user = person.getUser();
3837: Set telecomAddresses = person.getTelecomAddresses();
3838:
3839: String emailAddressHost = settings.getStringSetting(
3840: securitySession, "emailAddressHost", user);
3841:
3842: // now go thro' the current aliases and see which ones have been removed
3843: // to remove the associated email addresses, if any
3844: Iterator currentUserAliasesIterator = currentUserAliases
3845: .iterator();
3846: List removedEmailAddresses = new Vector();
3847: List removedEmailTelecomAddresses = new Vector();
3848: List currentEmailAddresses = new Vector();
3849:
3850: while (currentUserAliasesIterator.hasNext()) {
3851: String alias = (String) currentUserAliasesIterator.next();
3852:
3853: if (!userAliases.contains(alias)) {
3854: removedEmailAddresses.add(alias + "@"
3855: + emailAddressHost);
3856: }
3857: }
3858:
3859: Iterator telecomAddressIterator = telecomAddresses.iterator();
3860:
3861: while (telecomAddressIterator.hasNext()) {
3862: TelecomAddressDO this TelecomAddress = (TelecomAddressDO) telecomAddressIterator
3863: .next();
3864:
3865: if (this TelecomAddress.getType() == TelecomAddressConstants.TYPE_EMAIL) {
3866: if (removedEmailAddresses.contains(this TelecomAddress
3867: .getAddress())) {
3868: removedEmailTelecomAddresses
3869: .add(this TelecomAddress);
3870: } else {
3871: currentEmailAddresses.add(this TelecomAddress
3872: .getAddress());
3873: }
3874: }
3875: }
3876:
3877: int sizeBefore = telecomAddresses.size();
3878:
3879: if (user.getId() != null) {
3880: addUserAliasEmailAddresses(securitySession, userName,
3881: userAliases, telecomAddresses, emailAddressHost);
3882: }
3883:
3884: int sizeAfter = telecomAddresses.size();
3885:
3886: // if some email telecom addresses were removed or added, update the
3887: // person
3888: if ((removedEmailTelecomAddresses.size() > 0)
3889: || (sizeAfter != sizeBefore)) {
3890: Iterator removedEmailTelecomAddressesIterator = removedEmailTelecomAddresses
3891: .iterator();
3892:
3893: while (removedEmailTelecomAddressesIterator.hasNext()) {
3894: telecomAddresses
3895: .remove(removedEmailTelecomAddressesIterator
3896: .next());
3897: }
3898:
3899: addressBook.amendPerson(securitySession, person);
3900: }
3901:
3902: // remove the user name itself from the aliases - if it is included
3903: userAliases.remove(userName);
3904:
3905: mailServer.setUserAliases(securitySession, userName,
3906: userAliases);
3907:
3908: if (logger.isDebugEnabled()) {
3909: logger
3910: .debug("setUserAliases(SecuritySession, String, Collection) - end");
3911: }
3912: }
3913:
3914: /**
3915: * <p>Set the vacation message for the user provided.</p>
3916: *
3917: * <p>The vacation message is a text the user can set, which will be sent
3918: * to all emails received at this address while the user is not present.</p>
3919: *
3920: * @param userName the name of the user for whom to get the vacation message.
3921: * @param vacationMessage vacation message text for this user. If the user
3922: * has no vacation message set, set to <code>null</code>.
3923: */
3924: public final void setVacationMessage(
3925: final SecuritySession securitySession,
3926: final String userName, final String vacationMessage)
3927: throws SystemException {
3928: if (logger.isDebugEnabled()) {
3929: logger
3930: .debug("setVacationMessage(SecuritySession securitySession = "
3931: + securitySession
3932: + ", String userName = "
3933: + userName
3934: + ", String vacationMessage = "
3935: + vacationMessage + ") - start");
3936: }
3937:
3938: checkMailServer();
3939: checkDateFormatter(securitySession);
3940: mailServer.setVacationMessage(securitySession, userName, null);
3941:
3942: if (logger.isDebugEnabled()) {
3943: logger
3944: .debug("setVacationMessage(SecuritySession, String, String) - end");
3945: }
3946: }
3947:
3948: /**
3949: * <p>Helper method to convert an array of <code>Address</code> instances to
3950: * strings.</p>
3951: *
3952: * @param addresses non-<code>null</code> array of <code>Address</code>
3953: * instances to be converted to strings.
3954: * @return array of strings representing the <code>Address.toString</code>
3955: * values.
3956: */
3957: private String[] toStringArray(final Address[] addresses) {
3958: if (logger.isDebugEnabled()) {
3959: logger.debug("toStringArray(Address[] addresses = "
3960: + addresses + ") - start");
3961: }
3962:
3963: int i;
3964: String[] retArray = new String[addresses.length];
3965:
3966: for (i = 0; i < addresses.length; i++) {
3967: retArray[i] = addresses[i].toString();
3968: }
3969:
3970: if (logger.isDebugEnabled()) {
3971: logger
3972: .debug("toStringArray(Address[]) - end - return value = "
3973: + retArray);
3974: }
3975:
3976: return retArray;
3977: }
3978:
3979: /**
3980: * <p>Confirm all of the elements of the message are present and valid,
3981: * before the message is sent.</p>
3982: *
3983: * @param messageDO data object to check for consistency and
3984: * completeness.
3985: * @return a collection of validation errors if any of the
3986: * mandatory fields are missing, or if fields contain invalid values.
3987: *
3988: * @ejb.interface-method
3989: * view-type = "remote"
3990: */
3991: public ValidationErrors validate(
3992: final SecuritySession securitySession,
3993: final MessageDO messageDO) {
3994: if (logger.isDebugEnabled()) {
3995: logger.debug("validate(SecuritySession securitySession = "
3996: + securitySession + ", MessageDO messageDO = "
3997: + messageDO + ") - start");
3998: }
3999:
4000: checkDateFormatter(securitySession);
4001:
4002: ValidationErrors errors = new ValidationErrors();
4003: Mask mask = maskFactory.getMask(MessageDO.class);
4004:
4005: // recipients is mandatory
4006: if ((messageDO.getRecipients() == null)
4007: || (messageDO.getRecipients().size() == 0)) {
4008: errors.add(new ValidationError("compose", Mail.BUNDLE_PATH,
4009: mask.getField("recipients"), "errors.required"));
4010: }
4011:
4012: // senders is mandatory
4013: if ((messageDO.getSenders() == null)
4014: || (messageDO.getSenders().size() == 0)) {
4015: errors.add(new ValidationError("compose", Mail.BUNDLE_PATH,
4016: mask.getField("senders"), "errors.required"));
4017: }
4018:
4019: // subject is mandatory for now - see todo above
4020: if ((messageDO == null)
4021: || StringHandling.isNullOrEmpty(messageDO.getSubject())) {
4022: errors.add(new ValidationError("compose", Mail.BUNDLE_PATH,
4023: mask.getField("subject"), "errors.required"));
4024: }
4025:
4026: // text is mandatory for now - see todo above
4027: if ((messageDO == null)
4028: || StringHandling.isNullOrEmpty(messageDO.getText())) {
4029: errors.add(new ValidationError("compose", Mail.BUNDLE_PATH,
4030: mask.getField("text"), "errors.required"));
4031: }
4032:
4033: // validate email adresses
4034: if (messageDO.getRecipients() != null) {
4035: for (Iterator i = messageDO.getRecipients().iterator(); i
4036: .hasNext();) {
4037: String currentRecipient = "";
4038: currentRecipient = (String) i.next();
4039:
4040: try {
4041: InternetAddress.parse(currentRecipient, true);
4042: } catch (AddressException e) {
4043: logger.error(
4044: "validate(SecuritySession, MessageDO)", e);
4045:
4046: errors
4047: .add(new ValidationError(
4048: "",
4049: Mail.BUNDLE_PATH,
4050: mask.getField("to"),
4051: "errors.mail.wrongEmailAddress",
4052: Arrays
4053: .asList(new String[] { currentRecipient })));
4054: } catch (Exception e) {
4055: logger.error("Exception (" + e.getClass().getName()
4056: + ") trying to parse internet address '"
4057: + currentRecipient + "'", e);
4058: errors
4059: .add(new ValidationError(
4060: "",
4061: Mail.BUNDLE_PATH,
4062: mask.getField("bcc"),
4063: "errors.mail.wrongEmailAddress",
4064: Arrays
4065: .asList(new String[] { currentRecipient })));
4066: }
4067: }
4068: }
4069:
4070: if (messageDO.getRecipientsCC() != null) {
4071: for (Iterator i = messageDO.getRecipientsCC().iterator(); i
4072: .hasNext();) {
4073: String currentRecipientCC = "";
4074: currentRecipientCC = (String) i.next();
4075:
4076: try {
4077: InternetAddress.parse(currentRecipientCC, true);
4078: } catch (AddressException e) {
4079: logger.error(
4080: "validate(SecuritySession, MessageDO)", e);
4081:
4082: errors
4083: .add(new ValidationError(
4084: "",
4085: Mail.BUNDLE_PATH,
4086: mask.getField("cc"),
4087: "errors.mail.wrongEmailAddress",
4088: Arrays
4089: .asList(new String[] { currentRecipientCC })));
4090: } catch (Exception e) {
4091: logger.error("Exception (" + e.getClass().getName()
4092: + ") trying to parse internet address '"
4093: + currentRecipientCC + "'", e);
4094: errors
4095: .add(new ValidationError(
4096: "",
4097: Mail.BUNDLE_PATH,
4098: mask.getField("bcc"),
4099: "errors.mail.wrongEmailAddress",
4100: Arrays
4101: .asList(new String[] { currentRecipientCC })));
4102: }
4103: }
4104: }
4105:
4106: if (messageDO.getRecipientsBCC() != null) {
4107: for (Iterator i = messageDO.getRecipientsBCC().iterator(); i
4108: .hasNext();) {
4109: String currentRecipientBCC = "";
4110: currentRecipientBCC = (String) i.next();
4111:
4112: try {
4113: InternetAddress.parse(currentRecipientBCC, true);
4114: } catch (AddressException e) {
4115: logger.error(
4116: "validate(SecuritySession, MessageDO)", e);
4117:
4118: errors
4119: .add(new ValidationError(
4120: "",
4121: Mail.BUNDLE_PATH,
4122: mask.getField("bcc"),
4123: "errors.mail.wrongEmailAddress",
4124: Arrays
4125: .asList(new String[] { currentRecipientBCC })));
4126: } catch (Exception e) {
4127: logger.error("Exception (" + e.getClass().getName()
4128: + ") trying to parse internet address '"
4129: + currentRecipientBCC + "'", e);
4130: errors
4131: .add(new ValidationError(
4132: "",
4133: Mail.BUNDLE_PATH,
4134: mask.getField("bcc"),
4135: "errors.mail.wrongEmailAddress",
4136: Arrays
4137: .asList(new String[] { currentRecipientBCC })));
4138: }
4139: }
4140: }
4141:
4142: if (logger.isDebugEnabled()) {
4143: logger
4144: .debug("validate(SecuritySession, MessageDO) - end - return value = "
4145: + errors);
4146: }
4147:
4148: return errors;
4149: }
4150:
4151: /**
4152: * <p>Used to return both the HTML & plain text parts of a message in
4153: * <code>createThreadMessage</code>.</p>
4154: */
4155: private class MessageTextParts {
4156: public MimeBodyPart HTMLPart = null;
4157: public MimeBodyPart textPart = null;
4158: }
4159: }
|