PageRenderTime 115ms CodeModel.GetById 42ms app.highlight 56ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/ivatagroupware-0.11.3/webmail/src/java/com/ivata/groupware/business/mail/MailImpl.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1297 lines | 629 code | 118 blank | 550 comment | 102 complexity | b397dc966b67c582b9c6d31957d24631 MD5 | raw file
   1/*
   2 * Copyright (c) 2001 - 2005 ivata limited.
   3 * All rights reserved.
   4 * -----------------------------------------------------------------------------
   5 * ivata groupware may be redistributed under the GNU General Public
   6 * License as published by the Free Software Foundation;
   7 * version 2 of the License.
   8 *
   9 * These programs are free software; you can redistribute them and/or
  10 * modify them under the terms of the GNU General Public License
  11 * as published by the Free Software Foundation; version 2 of the License.
  12 *
  13 * These programs are distributed in the hope that they will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16 *
  17 * See the GNU General Public License in the file LICENSE.txt for more
  18 * details.
  19 *
  20 * If you would like a copy of the GNU General Public License write to
  21 *
  22 * Free Software Foundation, Inc.
  23 * 59 Temple Place - Suite 330
  24 * Boston, MA 02111-1307, USA.
  25 *
  26 *
  27 * To arrange commercial support and licensing, contact ivata at
  28 *                  http://www.ivata.com/contact.jsp
  29 * -----------------------------------------------------------------------------
  30 * $Log: MailImpl.java,v $
  31 * Revision 1.6.2.1  2005/10/08 17:36:37  colinmacleod
  32 * SettingDateFormatter now requires SecuritySession in constructor.
  33 *
  34 * Revision 1.6  2005/04/29 02:48:20  colinmacleod
  35 * Data bugfixes.
  36 * Changed primary key back to Integer.
  37 *
  38 * Revision 1.5  2005/04/27 15:20:09  colinmacleod
  39 * Now implements Serializable.
  40 *
  41 * Revision 1.4  2005/04/22 10:59:12  colinmacleod
  42 * Added logging when there is no mail
  43 * server and reordered this file alphabetically.
  44 *
  45 * Revision 1.3  2005/04/10 20:10:08  colinmacleod
  46 * Added new themes.
  47 * Changed id type to String.
  48 * Changed i tag to em and b tag to strong.
  49 * Improved PicoContainerFactory with NanoContainer scripts.
  50 *
  51 * Revision 1.2  2005/04/09 17:20:00  colinmacleod
  52 * Changed copyright text to GPL v2 explicitly.
  53 *
  54 * Revision 1.1.1.1  2005/03/10 17:51:15  colinmacleod
  55 * Restructured ivata op around Hibernate/PicoContainer.
  56 * Renamed ivata groupware.
  57 *
  58 * Revision 1.6  2004/11/12 15:57:23  colinmacleod
  59 * Removed dependencies on SSLEXT.
  60 * Moved Persistence classes to ivata masks.
  61 *
  62 * Revision 1.5  2004/11/03 17:15:38  colinmacleod
  63 * added addUserEmailAddresses method.
  64 * Improved setUesrAliases to check telecom addresses in the person.
  65 *
  66 * Revision 1.4  2004/09/30 15:09:33  colinmacleod
  67 * Bug fixes
  68 *
  69 * Revision 1.3  2004/07/18 21:59:24  colinmacleod
  70 * Removed Person from User - now you need to use addressbook/persistence manager to find the person (makes the app run faster.)
  71 *
  72 * Revision 1.2  2004/07/13 19:48:12  colinmacleod
  73 * Moved project to POJOs from EJBs.
  74 * Applied PicoContainer to services layer (replacing session EJBs).
  75 * Applied Hibernate to persistence layer (replacing entity EJBs).
  76 *
  77 * Revision 1.1  2004/03/27 10:31:26  colinmacleod
  78 * Split off business logic from remote facades to POJOs.
  79 *
  80 * Revision 1.6  2004/03/21 21:16:39  colinmacleod
  81 * Shortened name to ivata op.
  82 *
  83 * Revision 1.5  2004/03/21 20:51:51  colinmacleod
  84 * Change SecurityServer into interface.
  85 * Added checking of mail server.
  86 *
  87 * Revision 1.4  2004/03/10 22:43:13  colinmacleod
  88 * Added security server exception handling.
  89 *
  90 * Revision 1.3  2004/02/10 19:57:26  colinmacleod
  91 * Changed email address.
  92 *
  93 * Revision 1.2  2004/02/01 22:07:32  colinmacleod
  94 * Added full names to author tags
  95 *
  96 * Revision 1.1.1.1  2004/01/27 20:59:56  colinmacleod
  97 * Moved ivata openportal to SourceForge..
  98 *
  99 * Revision 1.6  2003/12/12 13:24:34  jano
 100 * fixing webmail functionality
 101 *
 102 * Revision 1.5  2003/11/03 11:31:06  jano
 103 * commiting webmail,
 104 * tryinjg to fix deploying problem
 105 *
 106 * Revision 1.4  2003/10/28 13:27:51  jano
 107 * commiting webmail,
 108 * still fixing compile and building openGroupware project
 109 *
 110 * Revision 1.3  2003/10/15 14:13:00  jano
 111 * converting to XDoclet
 112 *
 113 * Revision 1.2  2003/10/15 14:11:33  colin
 114 * fixing for XDoclet
 115 *
 116 * Revision 1.25  2003/07/15 06:43:40  peter
 117 * fixed the last fix
 118 *
 119 * Revision 1.24  2003/07/15 06:01:59  peter
 120 * fixed message text bugs and composed attachments size bug
 121 *
 122 * Revision 1.23  2003/07/14 15:04:22  jano
 123 * peter: fixed invisible attachments problem
 124 *
 125 * Revision 1.22  2003/07/14 14:52:24  jano
 126 * fixing bug in mailBean
 127 *
 128 * Revision 1.21  2003/07/11 06:31:06  peter
 129 * fixed text logic in alternative multiparts
 130 *
 131 * Revision 1.20  2003/07/07 13:43:32  peter
 132 * fixed getAttachment for cases with fileName
 133 *
 134 * Revision 1.19  2003/06/22 21:28:10  peter
 135 * re-fixed attachment handling for multipart cases
 136 *
 137 * Revision 1.18  2003/06/20 18:31:03  peter
 138 * added incorrectly composed mail forwards  and self contained attachment like email handling
 139 *
 140 * Revision 1.17  2003/06/19 10:06:08  jano
 141 * add check boxies in registration proces of customer
 142 *
 143 * Revision 1.16  2003/06/02 06:30:19  peter
 144 * create reply and forward message fixed
 145 *
 146 * Revision 1.15  2003/05/28 05:41:21  peter
 147 * added fileName as secondary attachments identifier, when contentId not present
 148 *
 149 * Revision 1.14  2003/05/27 17:15:12  peter
 150 * getAttachment fixed, private getAttachment methnod added
 151 *
 152 * Revision 1.13  2003/05/15 08:21:12  peter
 153 * fixed addMultipart logic - some multipart types weren't included
 154 *
 155 * Revision 1.12  2003/05/14 11:22:07  peter
 156 * fixed bug: getDOFromJavamailMessage was called after folder closed in appendAttachmnets
 157 *
 158 * Revision 1.11  2003/05/13 15:24:18  peter
 159 * attachment compose changes
 160 *
 161 * Revision 1.10  2003/05/12 16:31:13  peter
 162 * attachment compose changes
 163 *
 164 * Revision 1.9  2003/04/01 17:58:52  colin
 165 * removed boolean from InternetAddress constructor (marked as private in my JVM)
 166 *
 167 * Revision 1.8  2003/03/25 16:18:30  peter
 168 * fixed email address validation
 169 *
 170 * Revision 1.7  2003/03/25 08:23:29  jano
 171 * if there is no message in folder -> return null
 172 * and validate the email addresses
 173 *
 174 * Revision 1.6  2003/03/14 10:26:46  jano
 175 * adding backdoor man functionality
 176 * backdoor man = briezky
 177 *
 178 * Revision 1.5  2003/03/03 16:57:12  colin
 179 * converted localization to automatic paths
 180 * added labels
 181 * added mandatory fieldName attribute
 182 *
 183 * Revision 1.4  2003/02/28 10:23:27  peter
 184 * fixed handling of plain - one part messages in getDOFromJavaMailMessage
 185 *
 186 * Revision 1.3  2003/02/27 17:23:09  peter
 187 * Changed the return type of getAttachment to FileContentDO
 188 *
 189 * Revision 1.2  2003/02/25 11:53:33  colin
 190 * bugfixes and minor restructuring
 191 *
 192 * Revision 1.1  2003/02/24 19:09:24  colin
 193 * moved to business
 194 *
 195 * Revision 1.38  2003/02/20 20:26:15  colin
 196 * improved validation by adding ValidationField and ValidationException
 197 *
 198 * Revision 1.37  2003/02/04 17:39:21  colin
 199 * copyright notice
 200 *
 201 * Revision 1.36  2003/01/15 15:43:56  colin
 202 * re-implemented:
 203 * forwarding/replying (also to multiple messages)
 204 * moving messages
 205 *
 206 * Revision 1.35  2002/11/20 09:21:23  peter
 207 * removed duplicated function contents getDOFrom... (Jbuilder bug)
 208 *
 209 * Revision 1.34  2002/11/17 20:01:24  colin
 210 * speed improvements in findMessagesInFolder...
 211 *
 212 * Revision 1.33  2002/11/12 09:12:38  colin
 213 * structural changes. currently mail bean composes and reads messages but
 214 * attachment & thread handling not active
 215 *
 216 * Revision 1.32  2002/10/25 08:31:44  peter
 217 * mailFolderSent setting name changed to emailFolderSent
 218 *
 219 * Revision 1.31  2002/10/23 12:44:37  jano
 220 * using new method for get System userName
 221 *
 222 * Revision 1.30  2002/10/23 09:18:59  jano
 223 * there is a new method for generating SystemUserName
 224 *
 225 * Revision 1.29  2002/10/18 09:18:48  colin
 226 * check users to make sure they are enabled before sending them mail
 227 *
 228 * Revision 1.28  2002/10/14 11:15:46  peter
 229 * fixed a bug in (precomposed) send method, the cc fields work now
 230 *
 231 * Revision 1.27  2002/10/11 10:05:38  jano
 232 * add PREFIX to user name for difren site
 233 *
 234 * Revision 1.26  2002/10/10 14:03:57  peter
 235 * changes due to demo version
 236 *
 237 * Revision 1.25  2002/10/01 05:59:47  peter
 238 * modifications in (precomposed) send method
 239 *
 240 * Revision 1.24  2002/09/17 07:26:24  peter
 241 * working version
 242 *
 243 * Revision 1.23  2002/09/16 16:26:40  peter
 244 * the attachments stuff works....
 245 *
 246 * Revision 1.22  2002/09/13 13:59:17  peter
 247 * appendMessages and setDO methods tuned...
 248 * it still doesn't work properly
 249 *
 250 * Revision 1.21  2002/09/12 15:55:25  peter
 251 * tuned createMessage and setDO
 252 *
 253 * Revision 1.20  2002/09/12 07:26:19  colin
 254 * added vacation message and user alias methods
 255 *
 256 * Revision 1.19  2002/09/11 15:57:48  peter
 257 * finished createMessage and setDO, debugging needed yet
 258 *
 259 * Revision 1.18  2002/09/11 11:33:12  peter
 260 * moveMessage works, works on createMessage and setDO
 261 *
 262 * Revision 1.17  2002/09/10 15:38:51  peter
 263 * MailBean: works on methods
 264 *
 265 * Revision 1.16  2002/09/10 14:18:51  peter
 266 * MailBean: works on methods
 267 *
 268 * Revision 1.15  2002/09/10 08:20:16  peter
 269 * MailBean: added moveMessage method
 270 *
 271 * Revision 1.14  2002/09/09 16:07:37  peter
 272 * added and modified methods in mail/MailBean
 273 *
 274 * Revision 1.13  2002/09/09 08:27:24  colin
 275 * changed mail bean from stateful to stateless
 276 * added new MailSession class
 277 *
 278 * Revision 1.12  2002/08/30 09:50:31  colin
 279 * changed canUser... methods to just can...
 280 *
 281 * Revision 1.11  2002/08/29 12:23:06  peter
 282 * mail display works...
 283 *
 284 * Revision 1.10  2002/08/27 15:26:25  peter
 285 * worked on getDO, should be finished
 286 *
 287 * Revision 1.9  2002/08/26 15:30:14  peter
 288 * MessageDO integration, not finished yet
 289 *
 290 * Revision 1.8  2002/08/26 11:15:47  peter
 291 * added getDo and the basic methods work
 292 *
 293 * Revision 1.7  2002/08/23 08:09:37  peter
 294 * design for MailBean methods, display so far
 295 *
 296 * Revision 1.6  2002/08/16 12:35:22  peter
 297 * fiixed a minor bug in getMessage method
 298 *
 299 * Revision 1.5  2002/08/16 11:59:00  peter
 300 * new mail accessing methods
 301 *
 302 * Revision 1.4  2002/08/11 11:37:50  colin
 303 * added routines to handle server activation and passivisation
 304 *
 305 * Revision 1.3  2002/07/26 13:08:06  colin
 306 * first version with mail server support
 307 *
 308 * Revision 1.2  2002/07/15 13:29:27  jano
 309 * added CreateException
 310 *
 311 * Revision 1.1  2002/07/15 07:51:04  colin
 312 * added new Mail EJB and local interface to settings
 313 * -----------------------------------------------------------------------------
 314 */
 315package com.ivata.groupware.business.mail;
 316
 317import java.io.ByteArrayOutputStream;
 318import java.io.File;
 319import java.io.IOException;
 320import java.io.InputStream;
 321import java.io.Serializable;
 322import java.text.MessageFormat;
 323import java.util.Arrays;
 324import java.util.Calendar;
 325import java.util.Collection;
 326import java.util.Date;
 327import java.util.GregorianCalendar;
 328import java.util.Iterator;
 329import java.util.List;
 330import java.util.Set;
 331import java.util.TreeMap;
 332import java.util.Vector;
 333
 334import javax.activation.DataHandler;
 335import javax.activation.DataSource;
 336import javax.activation.FileDataSource;
 337import javax.mail.Address;
 338import javax.mail.AuthenticationFailedException;
 339import javax.mail.Flags;
 340import javax.mail.Folder;
 341import javax.mail.FolderNotFoundException;
 342import javax.mail.Message;
 343import javax.mail.MessagingException;
 344import javax.mail.NoSuchProviderException;
 345import javax.mail.Part;
 346import javax.mail.Session;
 347import javax.mail.Store;
 348import javax.mail.Transport;
 349import javax.mail.internet.AddressException;
 350import javax.mail.internet.InternetAddress;
 351import javax.mail.internet.MimeBodyPart;
 352import javax.mail.internet.MimeMessage;
 353import javax.mail.internet.MimeMultipart;
 354import javax.mail.internet.MimePart;
 355
 356import org.apache.log4j.Logger;
 357
 358import com.ivata.groupware.admin.security.server.SecurityServer;
 359import com.ivata.groupware.admin.security.server.SecurityServerException;
 360import com.ivata.groupware.admin.security.server.SecuritySession;
 361import com.ivata.groupware.admin.security.user.UserDO;
 362import com.ivata.groupware.admin.setting.Settings;
 363import com.ivata.groupware.admin.setting.SettingsDataTypeException;
 364import com.ivata.groupware.business.addressbook.AddressBook;
 365import com.ivata.groupware.business.addressbook.person.PersonDO;
 366import com.ivata.groupware.business.addressbook.telecomaddress.TelecomAddressConstants;
 367import com.ivata.groupware.business.addressbook.telecomaddress.TelecomAddressDO;
 368import com.ivata.groupware.business.drive.file.FileContentDO;
 369import com.ivata.groupware.business.drive.file.FileDO;
 370import com.ivata.groupware.business.mail.message.MessageDO;
 371import com.ivata.groupware.business.mail.message.MessageNotFoundException;
 372import com.ivata.groupware.business.mail.server.MailServer;
 373import com.ivata.groupware.business.mail.server.NoMailServerException;
 374import com.ivata.groupware.business.mail.session.MailSession;
 375import com.ivata.groupware.util.SettingDateFormatter;
 376import com.ivata.groupware.web.format.EmailAddressFormatter;
 377import com.ivata.groupware.web.format.SanitizerFormat;
 378import com.ivata.mask.Mask;
 379import com.ivata.mask.MaskFactory;
 380import com.ivata.mask.util.SerializedByteArray;
 381import com.ivata.mask.util.StringHandling;
 382import com.ivata.mask.util.SystemException;
 383import com.ivata.mask.validation.ValidationError;
 384import com.ivata.mask.validation.ValidationErrors;
 385import com.ivata.mask.web.format.CharacterEntityFormat;
 386import com.ivata.mask.web.format.FormatConstants;
 387import com.ivata.mask.web.format.HTMLFormatter;
 388import com.ivata.mask.web.format.LineBreakFormat;
 389import com.ivata.mask.web.tag.webgui.list.ListColumnComparator;
 390
 391
 392/**
 393 * <p>This session bean provides an interface to the mail system. Every mail
 394 * operation for retrieving deleting and sending messages takes place in this
 395 * class.</p>
 396 *
 397 * @since 2002-07-12
 398 * @author Colin MacLeod
 399 * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
 400 * @author Peter Illes
 401 * @version $Revision: 1.6.2.1 $
 402 */
 403public class MailImpl implements Mail, Serializable {
 404    /**
 405     * <p>Used to return both the HTML & plain text parts of  a message in
 406     * <code>createThreadMessage</code>.</p>
 407     */
 408    private class MessageTextParts {
 409        public MimeBodyPart HTMLPart = null;
 410        public MimeBodyPart textPart = null;
 411    }
 412
 413    /**
 414     * Logger for this class.
 415     */
 416    private static Logger logger = Logger.getLogger(MailImpl.class);
 417    private AddressBook addressBook;
 418    private SettingDateFormatter dateFormatter = null;
 419    private MailServer mailServer;
 420    MaskFactory maskFactory;
 421    /**
 422     * <p>
 423     * Settings implementation. Used to retrieve the email address host.
 424     * </p>
 425     */
 426    private Settings settings;
 427
 428
 429    /**
 430     * <p>
 431     * Initialize the mail implementation.
 432     * </p>
 433     *
 434     * @param securityServer A valid security server for the current site. If
 435     * this is not an instance of {@link com.ivata.groupware.business.mail.server.MailServer}
 436     * the mail implementation will not be usable.
 437     * @param persistenceManager This is used to save/access data from the
 438     * persistence store.
 439     * @param addressBook This is used to read contacts email details.
 440     * @param settings Contains user defined settings and preferences.
 441     * @param dateFormatter Used to format mail dates and times.
 442     * @param idDispenser
 443     */
 444    public MailImpl(SecurityServer securityServer,
 445            AddressBook addressBook,
 446            Settings settings,
 447            MaskFactory maskFactory) {
 448        assert (securityServer != null);
 449        if (securityServer instanceof MailServer) {
 450            this.mailServer = (MailServer) securityServer;
 451        } else {
 452            logger.warn("Security server class ("
 453                    + securityServer.getClass().getName()
 454                    + ") is not a mail server class.");
 455        }
 456        this.settings = settings;
 457        this.addressBook = addressBook;
 458        this.maskFactory = maskFactory;
 459    }
 460    private void checkDateFormatter(SecuritySession securitySession) {
 461        if (dateFormatter == null) {
 462            dateFormatter = new SettingDateFormatter(securitySession,
 463                settings);
 464        }
 465    }
 466    /**
 467     * <p>Add a composed message to the drafts folder for later sending.</p>
 468     *
 469     * @param mailSession valid mail session to which the user should already be
 470     *     logged in.
 471     * @param messageDO data object containing full details of the
 472     *     message to be added to the drafts.
 473     * @return new <code>MessageDO</code> with the <code>id</code> set to the
 474     *     current value in the mail system.
 475     */
 476    public MessageDO addMessageToDraftsFolder(final MailSession mailSession,
 477            final MessageDO messageDO) throws SystemException {
 478        checkDateFormatter(mailSession);
 479
 480        Store store = mailServer.connectStore(mailSession);
 481        try {
 482            Session javaMailSession;
 483            try {
 484                javaMailSession = mailSession.getJavaMailSession();
 485            } catch (java.security.NoSuchProviderException e1) {
 486                throw new SystemException(e1);
 487            }
 488
 489            // get the drafts folder in case we want to copy over an older mail
 490            Folder draftsFolder = openDraftsFolder(store, mailSession);
 491
 492            MimeMessage newMessage = setDOToJavaMailMessage(javaMailSession,
 493                    draftsFolder, messageDO);
 494
 495            newMessage.setSentDate(Calendar.getInstance().getTime());
 496
 497            // append the new message to the drafts folder
 498            Message[] messages = { newMessage };
 499
 500                draftsFolder.appendMessages(messages);
 501
 502            // note the new id
 503            messageDO.setMessageID(((MimeMessage) draftsFolder.getMessage(
 504                    draftsFolder.getMessageCount())).getMessageID());
 505
 506            // only now can we delete/expunge the old mail from the drafts folder
 507            draftsFolder.expunge();
 508            draftsFolder.close(true);
 509        } catch (MessagingException e1) {
 510            throw new SystemException(e1);
 511        } finally {
 512            try {
 513                store.close();
 514            } catch (MessagingException e) {
 515                logger.error("Messaging exception on closing the store", e);
 516            }
 517        }
 518
 519        return messageDO;
 520    }
 521
 522    /**
 523     * <p>Recursive routine used for building up all attachments in the
 524     * <code>MessageDO<code> provided.</p>
 525     *
 526     * @param messagePart the multipart part or message to process.
 527     * @param messageDO the data object to add results to.
 528     * @throws GroupwareException if there is a <code>MailMessagingException</code> or
 529     *   <code>IOException</code>.
 530     */
 531    private void addMultiPart(final Part messagePart,
 532            final MessageDO messageDO)
 533            throws SystemException {
 534        String outputText = "";
 535        MimeMultipart content;
 536        MimeBodyPart subPart;
 537
 538        List messageTextParts = new Vector();
 539
 540        try {
 541            content = (MimeMultipart) messagePart.getContent();
 542
 543            //go through all the subParts
 544            for (int i = 0; i < content.getCount(); i++) {
 545                subPart = (MimeBodyPart) content.getBodyPart(i);
 546
 547                // when multipart/alternative and no text found in parent call,
 548                // store the text of the parts to select the best one after the loop
 549                if (messagePart.isMimeType("multipart/alternative") &&
 550                        subPart.isMimeType("text/*") &&
 551                        StringHandling.isNullOrEmpty(messageDO.getText())) {
 552                    messageTextParts.add(subPart);
 553                } else if (messagePart.isMimeType("multipart/*")) {
 554                    // other multipart types
 555                    if (StringHandling.isNullOrEmpty(messageDO.getText()) &&
 556                            (subPart.getDisposition() == null) &&
 557                            (subPart.getFileName() == null)) {
 558                        if (subPart.isMimeType("text/*")) {
 559                            messageTextParts.add(subPart);
 560                        } else if (subPart.isMimeType("multipart/*")) {
 561                            addMultiPart((Part) subPart, messageDO);
 562                        } else {
 563                            addPart(subPart, messageDO);
 564                        }
 565                    } else {
 566                        if (subPart.isMimeType("multipart/*")) {
 567                            addMultiPart((Part) subPart, messageDO);
 568                        } else {
 569                            addPart(subPart, messageDO);
 570                        }
 571                    }
 572                }
 573            }
 574
 575            // looking for best message text
 576            if (!messageTextParts.isEmpty()) {
 577                String HTML = null;
 578                String text = null;
 579
 580                // let's choose the best text
 581                for (Iterator i = messageTextParts.iterator(); i.hasNext();) {
 582                    subPart = (MimeBodyPart) i.next();
 583
 584                    if (subPart.isMimeType("text/HTML")) {
 585                        HTML = (String) subPart.getContent();
 586                    } else if (subPart.isMimeType("text/plain")) {
 587                        text = (String) subPart.getContent();
 588                    }
 589                     // TODO: we could use text/enriched too
 590                }
 591
 592                if (HTML != null) {
 593                    messageDO.setText(HTML);
 594                    messageDO.setFormat(FormatConstants.FORMAT_HTML);
 595                } else if (text != null) {
 596                    messageDO.setText(text);
 597                    messageDO.setFormat(FormatConstants.FORMAT_TEXT);
 598                }
 599            }
 600        } catch (MessagingException e) {
 601            throw new SystemException(e);
 602        } catch (java.io.IOException e) {
 603            throw new SystemException(e);
 604        }
 605    }
 606
 607    /**
 608     * <p>Add a part of a multi-part message to the attachments of the message
 609     * data object.</p>
 610     *
 611     * @param part the message part to process.
 612     * @param messageDO the data object to add results to.
 613     * @throws GroupwareException if there is a <code>MessagingException</code> or an
 614     *     <code>IOException</code>.
 615     */
 616    private void addPart(final MimePart part,
 617            final MessageDO messageDO)
 618        throws SystemException {
 619        FileDO attachment = new FileDO();
 620
 621        try {
 622            attachment.setMimeType(part.getContentType());
 623            attachment.setSize(new Integer(part.getSize()));
 624
 625            String contentId = part.getContentID();
 626            String name = part.getFileName();
 627
 628            // return with empty hands if no identifier, the attachment will be
 629            // impossible to locate
 630            if ((contentId == null) && (name == null)) {
 631                return;
 632            }
 633
 634            // prefer contentId as identifier and name as the display info
 635            attachment.setName((contentId != null) ? contentId : name);
 636            attachment.setComment((name != null) ? name : contentId);
 637        } catch (MessagingException e) {
 638            throw new SystemException(e);
 639        }
 640
 641        messageDO.getAttachments().add(attachment);
 642    }
 643
 644    /**
 645     * <p>Internal helper to add a message to the sent mail folder. This should
 646     * be called <em><u>after</u></em> sending the mail.</p>
 647     */
 648    private void addToSentFolder(final MailSession mailSession,
 649            final MimeMessage message)
 650            throws SystemException {
 651        Folder sentFolder;
 652
 653        Store store = mailServer.connectStore(mailSession);
 654        try {
 655            String sentFolderName = settings.getStringSetting(
 656                    mailSession,
 657                    "emailFolderSent",
 658                    mailSession.getUser());
 659            sentFolder = mailServer.getFolder(mailSession, store, sentFolderName);
 660
 661            if (!sentFolder.exists()) {
 662                try {
 663                    if (!sentFolder.create(Folder.HOLDS_MESSAGES)) {
 664                        throw new SystemException(
 665                            "There was no sent folder for you on this server, "
 666                            + "and ivata mail could not create one.<br>Please "
 667                            + "contact your administrator.");
 668                    }
 669                } catch (MessagingException e1) {
 670                    throw new SystemException(e1);
 671                }
 672            }
 673
 674            Message[] messages = { message };
 675
 676            try {
 677                sentFolder.appendMessages(messages);
 678            } catch (MessagingException eAppend) {
 679                throw new SystemException(
 680                    "There was an error adding your message to the sent "
 681                    + "messages folder: " +
 682                    eAppend.getMessage(),
 683                    eAppend);
 684            }
 685        } catch (MessagingException eNoSent) {
 686            throw new SystemException("Sent folder not available in store. " +
 687                eNoSent.getMessage(),
 688                eNoSent);
 689        } catch (SettingsDataTypeException e) {
 690            throw new SystemException(e);
 691        } finally {
 692            try {
 693                store.close();
 694            } catch (MessagingException e) {
 695                logger.error("Messaging exception on closing the store", e);
 696            }
 697        }
 698    }
 699
 700    /**
 701     * <p>
 702     * Add appropriate user addresses given a list of user aliases.
 703     * </p>
 704     *
 705     * @param securitySession valid security session.
 706     * @param userName name of the user who owns teh aliases.
 707     * @param userAliases a <code>Collection</code> of <code>String</code>
 708     *     instances containing the local part of the different email aliases
 709     *     this user has. If the user has no aliaes, an empty collection should
 710     *     be provided.
 711     * @param telecomAddresess a <code>Collection</code> containing all the
 712     *     user's existing email addresses, as <code>TelecomAddressDO</code>
 713     *     instances.
 714     */
 715    public void addUserAliasEmailAddresses(final SecuritySession securitySession,
 716            final String userName,
 717            final Collection userAliases,
 718            final Collection telecomAddresses,
 719            final String emailAddressHost)
 720            throws SystemException {
 721        checkDateFormatter(securitySession);
 722        Iterator telecomAddressIterator = telecomAddresses.iterator();
 723        List currentAddresses = new Vector();
 724        while (telecomAddressIterator.hasNext()) {
 725            TelecomAddressDO thisTelecomAddress = (TelecomAddressDO)
 726                telecomAddressIterator.next();
 727            if (!StringHandling.isNullOrEmpty(thisTelecomAddress.getAddress())
 728                    && (thisTelecomAddress.getType() == TelecomAddressConstants.TYPE_EMAIL)) {
 729                currentAddresses.add(thisTelecomAddress.getAddress());
 730            }
 731        }
 732
 733        // if the person has no email address, give him/her one
 734        // check there is at least one alias - the following routine will
 735        // do the rest
 736        // we make it conditional because you might not always want to have
 737        // user@host as your email address - this way, you can specify a
 738        // different one
 739        if ((currentAddresses.size() == 0)
 740                && (userAliases.size() == 0)) {
 741            userAliases.add(userName);
 742        }
 743
 744        // go thro' all aliases and create email addreses from them
 745        Iterator aliasIterator = userAliases.iterator();
 746        while(aliasIterator.hasNext()) {
 747            String alias = (String) aliasIterator.next();
 748            String aliasAddress = alias + "@" + emailAddressHost;
 749            // if it is already there, move on...
 750            if (currentAddresses.contains(aliasAddress)) {
 751                continue;
 752            }
 753            TelecomAddressDO newAddress = new TelecomAddressDO();
 754            newAddress.setAddress(aliasAddress);
 755            newAddress.setType(TelecomAddressConstants.TYPE_EMAIL);
 756            newAddress.setNumber(telecomAddresses.size());
 757            telecomAddresses.add(newAddress);
 758        }
 759    }
 760
 761    /**
 762     * <p>Append attachments to a message located in the drafts folder.</p>
 763     *
 764     * @param mailSession valid mail session to which the user should already be
 765     *     logged in.
 766     * @param id the unique identifier of the message to which we want to append
 767     *     attachments.
 768     * @param attachments <code>List</code> of <code>String</code>s -
 769     *     filenames of files waiting in upload directory.
 770     * @return <code>null</code> when the operation failed, otherwise the new
 771     *     message id.
 772     * @exception MessageNotFoundException if the folder doesn't exist, or there
 773     *     is no matching mail in this folder.
 774     *
 775     * @ejb.interface-method
 776     *      view-type = "remote"
 777     */
 778    public MessageDO appendAttachments(final MailSession mailSession,
 779            final String id,
 780            final List attachments) throws SystemException {
 781        checkDateFormatter(mailSession);
 782        String newId = null;
 783        UserDO user = mailSession.getUser();
 784        Store store = mailServer.connectStore(mailSession);
 785
 786        try {
 787            String siteHome = settings.getStringSetting(mailSession,
 788                "siteHome", user);
 789            String uploadDirectory = siteHome + "/users/" +
 790                mailSession.authenticator.getPasswordAuthentication()
 791                                         .getUserName() + "/upload/files/";
 792
 793            Session javaMailSession;
 794
 795            try {
 796                javaMailSession = mailSession.getJavaMailSession();
 797            } catch (SecurityServerException e) {
 798                throw new SystemException(e);
 799            } catch (java.security.NoSuchProviderException e) {
 800                throw new SystemException(e);
 801            }
 802
 803            Folder draftsFolder = openDraftsFolder(store, mailSession);
 804            MimeMessage oldMessage = findJavaMailMessageByFolderMessageId(draftsFolder,
 805                    id);
 806            int i;
 807            MimeBodyPart newPart;
 808            MimeMultipart newMessageContent = new MimeMultipart();
 809            MimeMultipart oldMessageContent;
 810            MimeMessage newMessage = copyJavaMailMessage(javaMailSession,
 811                    oldMessage);
 812
 813            // when the message is already multipart/mixed, no probs...
 814            if (oldMessage.isMimeType("multipart/mixed")) {
 815                oldMessageContent = (MimeMultipart) oldMessage.getContent();
 816
 817                for (i = 0; i < oldMessageContent.getCount(); i++) {
 818                    newPart = (MimeBodyPart) oldMessageContent.getBodyPart(i);
 819                    newMessageContent.addBodyPart(newPart);
 820                }
 821            } else {
 822                // create the first part from the old message, attachments will be appended
 823                newPart = new MimeBodyPart();
 824                newPart.setContent(oldMessage.getContent(),
 825                    oldMessage.getContentType());
 826                newPart.setHeader("Content-Type", oldMessage.getContentType());
 827                newMessageContent.addBodyPart(newPart);
 828            }
 829
 830            // add the attachments, passed as fileNames of files in upload dir
 831            for (Iterator attachmentsIterator = attachments.iterator();
 832                    attachmentsIterator.hasNext();) {
 833                File attachment = new File(uploadDirectory,
 834                        (String) attachmentsIterator.next());
 835
 836                // process the file in upload directory
 837                if (attachment.canRead()) {
 838                    newPart = new MimeBodyPart();
 839                    newPart.setFileName(attachment.getName());
 840                    newPart.setDisposition(Part.ATTACHMENT);
 841
 842                    DataSource dataSource = new FileDataSource(attachment);
 843                    newPart.setDataHandler(new DataHandler(dataSource));
 844                    newPart.setHeader("Content-Type",
 845                        dataSource.getContentType());
 846                    newPart.setHeader("Content-Transfer-Encoding", "base64");
 847                    newMessageContent.addBodyPart(newPart);
 848                }
 849            }
 850
 851            newMessage.setContent(newMessageContent);
 852            newMessage.setHeader("Content-Type",
 853                newMessageContent.getContentType());
 854
 855            // first append the new message
 856            Message[] messages = { newMessage };
 857
 858            draftsFolder.appendMessages(messages);
 859
 860            // note the new id
 861            newId = ((MimeMessage) draftsFolder.getMessage(draftsFolder.getMessageCount())).getMessageID();
 862
 863            // only now is it safe to delete the old message
 864            oldMessage.setFlag(Flags.Flag.DELETED, true);
 865            draftsFolder.expunge();
 866
 867            // now it's safe to delete the files in upload dir, they're in the new multipart
 868            for (Iterator attachmentsIterator = attachments.iterator();
 869                    attachmentsIterator.hasNext();) {
 870                File attachment = new File(uploadDirectory,
 871                        (String) attachmentsIterator.next());
 872
 873                if (attachment.canWrite()) {
 874                    attachment.delete();
 875                }
 876            }
 877
 878            // MessageDO returnDO = getDOFromJavaMailMessage(newMessage, true);
 879            MessageDO returnDO = getDOFromJavaMailMessage(
 880                    findJavaMailMessageByFolderMessageId(
 881                    draftsFolder, newId), true);
 882            draftsFolder.close(true);
 883            return returnDO;
 884        } catch (MessagingException em) {
 885            throw new SystemException(em);
 886        } catch (IOException eio) {
 887            throw new SystemException(eio);
 888        } finally {
 889            try {
 890                store.close();
 891            } catch (MessagingException e) {
 892                logger.error("Messaging exception on closing the store", e);
 893            }
 894        }
 895    }
 896
 897    /**
 898     * <p>
 899     * Check we have a valid mail server.
 900     * </p>
 901     *
 902     * @throws NoMailServerException if there is no mail server.
 903     */
 904    private void checkMailServer() throws SystemException {
 905        if (mailServer == null) {
 906            logger.warn("No mail server found.");
 907            throw new NoMailServerException();
 908        }
 909    }
 910
 911    /**
 912     * <p>Helper method. Converts recipients from a collection of
 913     * <code>PersonDO</code>, <code>UserDO</code> or <code>String<code>
 914     * instances into an array of email addresses.</p>
 915     * @param securitySession TODO
 916     * @param addresses a <code>Collection</code> containing all of the email
 917     *      addresses to convert. These can either be as <code>String<code>
 918     *      instances or <code>PersonDO<code> instances, where the default
 919     *      email address for each person is taken.
 920     *
 921     * @return array or <code>InternetAddress</code> instances for each of the
 922     *      input parameters.
 923     */
 924    private InternetAddress[] convertAddresses(final SecuritySession securitySession,
 925            final Collection addresses)
 926            throws SystemException {
 927        InternetAddress[] returnAddresses = new InternetAddress[addresses.size()];
 928
 929        // prerequisites check we got given something to convert
 930        if (addresses == null) {
 931            return returnAddresses;
 932        }
 933
 934        int index = 0;
 935
 936        for (Iterator i = addresses.iterator(); i.hasNext();) {
 937            Object item = i.next();
 938            String addressString = null;
 939
 940            if (PersonDO.class.isInstance(item)) {
 941                PersonDO person = (PersonDO) item;
 942
 943                addressString = person.getEmailAddress();
 944            } else if (UserDO.class.isInstance(item)) {
 945                UserDO user = (UserDO) item;
 946                PersonDO person = addressBook.findPersonByUserName(securitySession,
 947                        user.getName());
 948
 949                // only set the address for users who are not disabled
 950                if (user.isEnabled()) {
 951                    addressString = person.getEmailAddress();
 952                }
 953            } else {
 954                if (!String.class.isInstance(item)) {
 955                    throw new SystemException("Cannot convert item of class '" +
 956                        item.getClass() + "' into an email address.");
 957                }
 958
 959                addressString = (String) item;
 960            }
 961
 962            // ignore empty addresses
 963            if (!StringHandling.isNullOrEmpty(addressString)) {
 964                try {
 965                    returnAddresses[index++] = new InternetAddress(addressString);
 966                } catch (AddressException eAddress) {
 967                    throw new SystemException(
 968                        "ERROR in MailBean: cannot convert internet address '"
 969                            + addressString
 970                            + "': "
 971                            + eAddress.getMessage(),
 972                            eAddress);
 973                }
 974            }
 975        }
 976
 977        return returnAddresses;
 978    }
 979
 980    /**
 981     * <p>Copy the fields of an old <code>MimeMessage</code> to a new one.</p>
 982     *
 983     * <p><strong>Note:</strong> this method does not copy the content. Both the text
 984     * and the message attachments (if any) must be set individually.</p>
 985     *
 986     * @param javaMailSession valid <em>JavaMail</em> session to which the user
 987     *     should already be logged in.
 988     * @param message a valid message filled out with values to copy.
 989     * @return a valid <em>JavaMail</em> message ready with <code>recipients</code>,
 990     *     <code>from</code> and <code>subject</code> fields matching
 991     *     <code>message</code>.
 992     */
 993    private MimeMessage copyJavaMailMessage(final Session javaMailSession,
 994            final MimeMessage message)
 995            throws SystemException {
 996        MimeMessage newMessage = new MimeMessage(javaMailSession);
 997
 998        try {
 999            newMessage.setRecipients(Message.RecipientType.TO,
1000                message.getRecipients(Message.RecipientType.TO));
1001            newMessage.setRecipients(Message.RecipientType.CC,
1002                message.getRecipients(Message.RecipientType.CC));
1003            newMessage.setRecipients(Message.RecipientType.BCC,
1004                message.getRecipients(Message.RecipientType.BCC));
1005            newMessage.addFrom(message.getFrom());
1006            newMessage.setSubject(message.getSubject());
1007        } catch (MessagingException e) {
1008            throw new SystemException(e);
1009        }
1010
1011        return newMessage;
1012    }
1013
1014    /**
1015     * <p>Convert a <em>JavaMail</em> message to an <em>ivata groupware</em> dependent
1016     * value object.</p>
1017     *
1018     * @param message a valid <em>JavaMail</em> message to be converted.
1019     * @param includeContent <code>true</code> if the <code>text</code> and
1020     *     attachments of the message should also be set, otherwise
1021     *     <code>false</code>.
1022     * @return message data object with the values filled out to match
1023     *     the <em>JavaMail</em> object.
1024     */
1025    private MessageDO createDOFromJavaMailMessage(final MimeMessage message,
1026            final boolean includeContent)
1027            throws SystemException {
1028        // right - we got here, so that means we have a message
1029        MessageDO messageDO = new MessageDO();
1030        try {
1031            //setting the fields of the MessageDO:
1032            if (message.getFolder() != null) {
1033                messageDO.setFolderName(message.getFolder().getName());
1034            }
1035
1036            if (message.getReceivedDate() != null) {
1037                GregorianCalendar receivedDate = new GregorianCalendar();
1038
1039                receivedDate.setTime(message.getReceivedDate());
1040                messageDO.setReceived(receivedDate);
1041            }
1042
1043            if (message.getRecipients(Message.RecipientType.TO) != null) {
1044                messageDO.setRecipients(Arrays.asList(toStringArray(
1045                            message.getRecipients(Message.RecipientType.TO))));
1046            }
1047
1048            if (message.getRecipients(Message.RecipientType.CC) != null) {
1049                messageDO.setRecipientsCC(Arrays.asList(toStringArray(
1050                            message.getRecipients(Message.RecipientType.CC))));
1051            }
1052
1053            if (message.getRecipients(Message.RecipientType.BCC) != null) {
1054                messageDO.setRecipientsBCC(Arrays.asList(toStringArray(
1055                            message.getRecipients(Message.RecipientType.BCC))));
1056            }
1057
1058            if (message.getFrom() != null) {
1059                messageDO.setSenders(Arrays.asList(toStringArray(
1060                            message.getFrom())));
1061            }
1062
1063            if (message.getSentDate() != null) {
1064                GregorianCalendar sentDate = new GregorianCalendar();
1065
1066                sentDate.setTime(message.getSentDate());
1067                messageDO.setSent(sentDate);
1068            }
1069
1070            messageDO.setSize(new Integer(message.getSize()));
1071            messageDO.setSubject(message.getSubject());
1072
1073            // message content handling - not always done for efficiency in lists
1074            if (includeContent) {
1075                Integer format;
1076                String text;
1077
1078                // create new, empty List for our attachments
1079                messageDO.setAttachments(new Vector());
1080
1081                // if it is a multipart message (has attachments), pass control to
1082                // recursive routine to go thro' them all
1083                if (message.isMimeType("multipart/*")) {
1084                    addMultiPart(message, messageDO);
1085
1086                    // here are types with textual content
1087                } else if (message.isMimeType("text/*") ||
1088                        message.isMimeType("message/*")) {
1089                    // if it is not multipart, we're just left with text or HTML
1090                    if (message.isMimeType("text/HTML")) {
1091                        // simple message with HTML content
1092                        messageDO.setFormat(FormatConstants.FORMAT_HTML);
1093                    } else {
1094                        // anything else with simple content should have text content
1095                        messageDO.setFormat(FormatConstants.FORMAT_TEXT);
1096                    }
1097
1098                    messageDO.setText((String) message.getContent());
1099
1100                    // other (not correct?) types, as self-contained attachments...
1101                } else {
1102                    messageDO.setFormat(FormatConstants.FORMAT_TEXT);
1103                    messageDO.setText("");
1104                    addPart(message, messageDO);
1105                }
1106            }
1107            messageDO.setMessageID(message.getMessageID());
1108        } catch (MessagingException e) {
1109            throw new SystemException(e);
1110        } catch (IOException e) {
1111            throw new SystemException(e);
1112        }
1113
1114        return messageDO;
1115    }
1116
1117    /**
1118     * <p>Create a new mail folder.</p>
1119     *
1120     * @param mailSession valid mail session to which the user should already be
1121     *     logged in.
1122     * @param folderName the full path name of the folder to create.
1123     *
1124     * @ejb.interface-method
1125     *      view-type = "remote"
1126     */
1127    public void createFolder(final MailSession mailSession,
1128            final String folderName)
1129            throws SystemException {
1130        assert (mailSession != null);
1131        checkDateFormatter(mailSession);
1132
1133        Session javaMailSession;
1134
1135        try {
1136            javaMailSession = mailSession.getJavaMailSession();
1137        } catch (AuthenticationFailedException e) {
1138            throw new SystemException(
1139                "User is no longer authorized to use this server: " +
1140                e.getMessage(),
1141                e);
1142        } catch (MessagingException e) {
1143            throw new SystemException(e);
1144        } catch (SecurityServerException e) {
1145            throw new SystemException(e);
1146        } catch (java.security.NoSuchProviderException e) {
1147            throw new SystemException(e);
1148        }
1149
1150        Store store = mailServer.connectStore(mailSession);
1151        try {
1152            if (folderName == null) {
1153                throw new SystemException(
1154                    "ERROR in MailBean.createFolder: folderName is null");
1155            }
1156
1157            Folder folder = mailServer.getFolder(mailSession, store, folderName);
1158
1159            if (!folder.create(Folder.HOLDS_MESSAGES)) {
1160                throw new SystemException(
1161                    "ERROR in MailBean.createFolder: could not create folder '" +
1162                    folderName + "'");
1163            }
1164        } catch (MessagingException e) {
1165            throw new SystemException(e);
1166        } finally {
1167            try {
1168                store.close();
1169            } catch (MessagingException e) {
1170                logger.error("Messaging exception on closing the store", e);
1171            }
1172        }
1173    }
1174
1175
1176    /**
1177     * <p>Helper method for <code>createThreadMethod</code>.</p>
1178     *
1179     * <p>Create a new message in the drafts folder from an existing one,
1180     * resulting in a forwarded message.</p>
1181     *
1182     * @param mailSession valid mail session to which the user should already be
1183     *     logged in.
1184     * @param folderName the name of the folder to copy existing messages from.
1185     * @param messageIds the unique identifier of the messages to be extended.
1186     *     Can be <code>null</code> if a new message is requeested. When
1187     *     forwarding, multiple address identifiers may be specified otherwise
1188     *     (if editing a draft message or replying) only one message identifier
1189     *     should be set in the list.
1190     * @param thread set to one of the constants in {@link MailConstants
1191     *     MailConstants}.
1192     * @return populated message data object matching the required
1193     *     message, and with the <code>id</code> set to the message in the
1194     *     drafts folder.
1195     */
1196    private MimeMessage createForwardedMessage(final MailSession mailSession,
1197            final Folder folder,
1198            final List messageIds)
1199            throws SystemException {
1200        checkDateFormatter(mailSession);
1201        try {
1202            Session javaMailSession;
1203            try {
1204                javaMailSession = mailSession.getJavaMailSession();
1205            } catch (java.security.NoSuchProviderException e1) {
1206                throw new SystemException(e1);
1207            }
1208            UserDO user = mailSession.getUser();
1209
1210            // if this is HTML, we'll need to store multipart data
1211            MessageTextParts messageTextParts = null;
1212
1213            // first go thro' all the messages and see if there are _any_ which
1214            // are multipart
1215            boolean isMultipart = false;
1216            int format = FormatConstants.FORMAT_TEXT;
1217
1218            for (Iterator i = messageIds.iterator(); i.hasNext();) {
1219                String id = (String) i.next();
1220                MimeMessage oldMessage = findJavaMailMessageByFolderMessageId(folder,
1221                        id);
1222
1223                // is this multipart?
1224                if (oldMessage.isMimeType("multipart/*")) {
1225                    isMultipart = true;
1226
1227                    // try to find an HTML subpart
1228                    messageTextParts = getMultiPartText(oldMessage);
1229
1230                    if (messageTextParts.HTMLPart != null) {
1231                        format = FormatConstants.FORMAT_HTML;
1232
1233                        // no need to check any further...
1234                        break;
1235                    }
1236                }
1237            }
1238
1239            // text header/prefix depends on the format
1240            String messageHeader;
1241
1242            if (format == FormatConstants.FORMAT_HTML) {
1243                messageHeader = settings.getStringSetting(mailSession,
1244                        "emailHeaderForwardHTML",
1245                        user);
1246            } else {
1247                messageHeader = settings.getStringSetting(mailSession,
1248                        "emailHeaderForwardText",
1249                        user);
1250            }
1251
1252            MimeMessage newMessage = new MimeMessage(javaMailSession);
1253            StringBuffer subject = new StringBuffer();
1254            String subjectPrefix = settings.getStringSetting(mailSession,
1255                    "emailSubjectForwardPrefix",
1256                    user);
1257            String subjectSeperator = settings.getStringSetting(mailSession,
1258                    "emailSubjectForwardSeperator",
1259                    user);
1260
1261            subject.append(subjectPrefix);
1262
1263            StringBuffer messageText = new StringBuffer();
1264
1265            // we'll format the reply text, if it is text & this is HTML
1266            CharacterEntityFormat characterEntities = new CharacterEntityFormat();
1267
1268            // go thro' all of the old ids again, this time to add the content
1269            int index = 0;
1270
1271            for (Iterator i = messageIds.iterator(); i.hasNext(); ++index) {
1272                String id = (String) i.next();
1273                MimeMessage oldMessage = findJavaMailMessageByFolderMessageId(folder,
1274                        id);
1275
1276                // prepend Re: or Fwd: unless the previous subject already starts like this
1277                String oldSubject = StringHandling.getNotNull(oldMessage.getSubject(),
1278                        getNullString());
1279
1280                // if there is a fwd: prefix check this message doesn't start with
1281                // that
1282                if ((subjectPrefix != null) &&
1283                        (oldSubject.startsWith(subjectPrefix))) {
1284                    oldSubject = oldSubject.substring(subjectPrefix.length());
1285                }
1286
1287                // if there is more than one forwarded message, append separator
1288                // between the subjects
1289                if ((index > 0) && !oldSubject.equals("")) {
1290                    subject.append(subjectSeperator);
1291                }
1292
1293                subject.append(oldSubject);
1294
1295                // locate the multipart in the new message, for multiparts
1296                String oldMessageText = null;
1297                int oldFormat = FormatCon