/* * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape Messaging Access SDK Version 3.5 code, * released on or about June 15, 1998. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): ______________________________________. */ /* * Copyright (c) 1997 and 1998 Netscape Communications Corporation * (http://home.netscape.com/misc/trademarks.html) */ package netscape.messaging.mime; import java.io.*; import java.lang.*; import java.util.*; import netscape.messaging.mime.MIMEParser; import netscape.messaging.mime.MIMEDataSink; /** * The MIMEMessage class represents the MIME message. * A MIME Message is made up of a set of headers and message content. * The message content itself comprises one of the MIMEBody types: * MIMEBasicPart, MIMEMultiPart, and MIMEMessagePart. * @author Prasad Yendluri */ public class MIMEMessage implements Cloneable { /** * BodyPart is a MIMEBasicPart object. */ public static final int BASICPART = 0; /** * BodyPart is a MIMEMultiPart object. */ public static final int MULTIPART = 1; /** * BodyPart is a MIMEMessagePart object. */ public static final int MESSAGEPART = 2; //=========================================================================== // // Internal members not visible at the API // //=========================================================================== private static final int UNINITIALIZED = -1; private static final byte[] CRLF = "\r\n".getBytes(); private static final byte[] LF = "\n".getBytes(); private int m_contentTransferEncoding; protected int m_parsedPart; private boolean m_fplaceTwo; // The key to the hashTable below is the name of the header field. // To make sure we handle case differences in the header-name, we // must convert the header name to lower-case always prior to the // hash look-up. The entries added to the hash table are objects of // class Header. private Hashtable m_822HeadersTable; private Vector m_repeatHdrs; // Body private int m_bodyType; // one of BASICPART, MULTIPART, MESSAGEPART private MIMEBodyPart m_theBody; // must be one of BASICPART, MULTIPART, MESSAGEPART // callback support MIMEParser m_parser; MIMEDataSink m_dataSink; private Object m_UserObject; //=========================================================================== // // CONSTRUCTORS // //=========================================================================== /** * Default constructor for the MIMEMessage class. */ public MIMEMessage () { m_822HeadersTable = new Hashtable(); m_parsedPart = 0; m_contentTransferEncoding = UNINITIALIZED; m_bodyType = UNINITIALIZED; m_theBody = null; m_parser = null; m_dataSink = null; } /** * Constructs a MIMEMessage object given a set of RFC822 headers. * @param headers List of rfc822 headers to set in the message. * @exception MIMEException If the headers are malformed. */ public MIMEMessage (Header[] headers) throws MIMEException { m_822HeadersTable = new Hashtable(); m_parsedPart = 0; m_contentTransferEncoding = UNINITIALIZED; m_bodyType = UNINITIALIZED; m_theBody = null; if (headers != null) for (int i = 0, len = headers.length; i < len; i++) { try { setHeader (headers[i].getName(), headers[i].getValue()); } catch (Exception e) { throw new MIMEException (e.getMessage()); } } // for } /** * Constructs a (multi-part) MIMEMessage with given text and file. * If filename parameter is null, builds a text message with textIS. Encoding will be E7BIT. * If textIS parameter is null, builds a message with only the filename file as an attachment. * If both textIS and filename parameters are null, an exception is thrown. * @param textIS InputStream to text that becomes text part of the message. Can be null. * @param filename Full name of file that becomes a part of the message. Can be null. * @param encoding Encoding for the file attachment. To pick default value, pass -1. * @see MIMEBodyPart#BASE64 * @see MIMEBodyPart#QP * @see MIMEBodyPart#BINARY * @see MIMEBodyPart#E7BIT * @see MIMEBodyPart#E8BIT * @exception MIMEException If both textIS and filename are null or encoding is invalid * @exception FileNotFoundException If filename file does not exist * @exception SecurityException If filename file can not be accessed * @exception IOExcepton On IO Errors on filename or textIS. */ public MIMEMessage (InputStream textIS, String filename, int encoding) throws MIMEException, FileNotFoundException, SecurityException, IOException { MIMEBasicPart l_txtPart; MIMEBasicPart l_filePart; FileInputStream l_fis; fileMIMEType l_fmt; // Message initialization m_822HeadersTable = new Hashtable(); m_parsedPart = 0; m_contentTransferEncoding = UNINITIALIZED; m_bodyType = UNINITIALIZED; m_theBody = null; if (textIS == null && filename == null) throw new MIMEException ("Invalid null filename:" + filename); if ((encoding != -1) && (encoding < MIMEBodyPart.BASE64 || encoding > MIMEBodyPart.E8BIT)) throw new MIMEException ("Invalid MIME encoding: " + encoding); if (textIS != null && filename != null) { MIMEMultiPart l_mmp = new MIMEMultiPart (textIS, filename, encoding); this.setBody (l_mmp, false); } else if (textIS != null) // text-part only! { l_txtPart = new MIMEBasicPart (MIMEBasicPart.TEXT); l_txtPart.setContentSubType ("plain"); l_txtPart.setContentTypeParams ("charset=us-ascii"); l_txtPart.setContentEncoding(MIMEBodyPart.E7BIT); //l_txtPart.setContentDisposition(MIMEBodyPart.INLINE); l_txtPart.setBodyData (textIS); this.setBody (l_txtPart, false); } else if (filename != null) // file-part only! { l_fis = new FileInputStream (filename); // The file-part l_fmt = MIMEHelper.getFileMIMEType (filename); if (l_fmt == null) throw new MIMEException ("Can't determine MIME info for file: " + filename); l_filePart = new MIMEBasicPart (l_fmt.content_type); l_filePart.setContentSubType (l_fmt.content_subtype); if (l_fmt.content_params != null) l_filePart.setContentTypeParams (l_fmt.content_params); else l_filePart.setContentTypeParams (new String ("name=" + filename)); if (encoding == -1) l_filePart.setContentEncoding(l_fmt.mime_encoding); else l_filePart.setContentEncoding (encoding); if (l_fmt.content_type == MIMEBasicPart.TEXT); //l_filePart.setContentDisposition (MIMEBodyPart.INLINE); else { //l_filePart.setContentDisposition (MIMEBodyPart.ATTACHMENT); l_filePart.setContentDispParams (new String ("filename=" + l_fmt.file_shortname)); l_filePart.setContentDescription (l_fmt.file_shortname); } // set body-data of this part l_filePart.setBodyData (l_fis); this.setBody (l_filePart, false); } // filePart } // MIMEMessage() //=========================================================================== // // All the Methods Specific to this Class // //=========================================================================== /** * Sets any RFC-822 headers including X-headers. Overwrites the existing * value if header exists already. * @param name Name of the header field. * @param value Value of the header field to be added. * @exception MIMEException If either name or value are NULL * @see MIMEHelper#encodeHeader */ public void setHeader (String name, String value) throws MIMEException { if ((name == null) || (value == null)) throw new MIMEException ("Invalid NULL Header name or value"); String l_hashKey = name.toLowerCase(); // Keys are always lower case Header l_hdr = new Header (name, value); m_822HeadersTable.put (l_hashKey, l_hdr); } /** * Appends the value to an existing RFC822 header. Creates new header if * one does not exist already. * @param name Name of the header field. * @param value Value of the header field to be added. * @exception MIMEException If either name or value are NULL * @see MIMEHelper#encodeHeader */ public void addHeader (String name, String value) throws MIMEException { if ((name == null) || (value == null)) throw new MIMEException ("Invalid NULL Header name or value"); if (m_fplaceTwo == true) { addHeader2 (name, value); return; } String l_hashKey = name.toLowerCase(); // Keys are always lower case Header l_hdr = (Header) m_822HeadersTable.get (l_hashKey); if (l_hdr == null) { // Add a new one. Header l_newhdr = new Header (name, value); m_822HeadersTable.put (l_hashKey, l_newhdr); } else { // Append value to existing one. StringBuffer l_oldvalue = new StringBuffer (l_hdr.getValue()); l_oldvalue.append (value); Header l_newhdr = new Header (name, l_oldvalue.toString()); m_822HeadersTable.put (l_hashKey, l_newhdr); } } private void addHeader2 (String name, String value) throws MIMEException { Header l_oldHdr, l_newhdr; int idx = m_repeatHdrs.size() -1; l_oldHdr = (Header) m_repeatHdrs.elementAt (idx); if (!(l_oldHdr.m_headerName.equalsIgnoreCase (name))) { l_newhdr = new Header (name, value); m_repeatHdrs.addElement (l_newhdr); m_fplaceTwo = true; } else { m_repeatHdrs.removeElementAt (idx); StringBuffer l_oldvalue = new StringBuffer (l_oldHdr.getValue()); l_oldvalue.append (value); l_newhdr = new Header (name, l_oldvalue.toString()); m_repeatHdrs.addElement (l_newhdr); m_fplaceTwo = true; } } // Adds a potential repeat header to the message protected void addRHeader (String name, String value) throws MIMEException { if ((name == null) || (value == null)) throw new MIMEException ("Invalid NULL Header name or value"); String l_hashKey = name.toLowerCase(); // Keys are always lower case Header l_hdr = (Header) m_822HeadersTable.get (l_hashKey); if (l_hdr == null) { // Add a new one. Header l_newhdr = new Header (name, value); m_822HeadersTable.put (l_hashKey, l_newhdr); m_fplaceTwo = false; } else { // Its a repeat header! if (m_repeatHdrs == null) m_repeatHdrs = new Vector(); Header l_newhdr = new Header (name, value); m_repeatHdrs.addElement (l_newhdr); m_fplaceTwo = true; } } /** * Returns the value of the requested header. NULL if the header is not present. * @param name Name of the header field. * @exception MIMEException If name is NULL * @see MIMEHelper#decodeHeader */ public String getHeader (String name) throws MIMEException { if (name == null) throw new MIMEException ("Invalid NULL Header name"); String l_hashKey = name.toLowerCase(); // Keys are always lower case Header l_hdr = (Header) m_822HeadersTable.get (l_hashKey); if (l_hdr != null) return (l_hdr.getValue()); else return (null); } /** * Deletes the requested header. Ignores if the header specified does not exist. * @param name Name of the header field to delete. * @exception MIMEException if name is null */ public void deleteHeader (String name) throws MIMEException { if (name == null) throw new MIMEException ("Invalid NULL Header name"); String l_hashKey = name.toLowerCase(); // Keys are always lower case m_822HeadersTable.remove (l_hashKey); } /** * Returns all the RFC-822 headers in the Message as an array of Header objects. * Content-Type is not returned by this Method, as separate methods to get * Content primary type, sub-type and parameters exist. * @exception MIMEException If no headers exist. * @see MIMEHelper#decodeHeader * @see getContentType * @see getContentSubType * @see getContentTypeParams */ public Header[] getAllHeaders () throws MIMEException { int l_numElements = m_822HeadersTable.size(); int i = 0, l_repeats = 0; Header[] hdrs = null; if (l_numElements <= 0) return (null); if (m_repeatHdrs != null) l_repeats = m_repeatHdrs.size(); hdrs = new Header[l_numElements + l_repeats]; Enumeration he = m_822HeadersTable.elements(); while (he.hasMoreElements()) { hdrs[i++] = (Header) he.nextElement(); } for (int j = 0; j < l_repeats; j++) hdrs[i++] = (Header) m_repeatHdrs.elementAt (j); return (hdrs); } /** * Returns the type of the body of the Message. * @exception MIMEException If no Body exists for the message. * @see MIMEMessage#BASICPART * @see MIMEMessage#MULTIPART * @see MIMEMessage#MESSAGEPART */ public int getBodyType () throws MIMEException { if (m_bodyType != UNINITIALIZED) return (m_bodyType); else throw new MIMEException ("getBodyType(): No body present!"); } /** * Returns the content-type of the Message. * The content-type returned is the MIME content-type. * @exception MIMEException If no Body exists. */ public String getContentType () throws MIMEException { if (m_theBody == null) throw new MIMEException ("getContentType(): No body present!"); return (m_theBody.getContentType()); /* if (m_theBody instanceof MIMEBasicPart) * { * MIMEBasicPart l_body = (MIMEBasicPart) m_theBody; * return (l_body.getContentType()); * } * else if (m_theBody instanceof MIMEMessagePart) * { * MIMEMessagePart l_body = (MIMEMessagePart) m_theBody; * return (l_body.getContentType()); * } * else if (m_theBody instanceof MIMEMultiPart) * { * MIMEMultiPart l_body = (MIMEMultiPart) m_theBody; * return (l_body.getContentType()); * } * else * throw new MIMEException ("getContentType(): Invalid body!"); */ } /** * Returns the content subtype of the Message. NULL if none exists. * @exception MIMEException If no Body exists. */ public String getContentSubType () throws MIMEException { if (m_theBody == null) throw new MIMEException ("getContentSubType(): No body present!"); return (m_theBody.getContentSubType()); /* MIMEBodyPart l_body = (MIMEBodyPart) m_theBody; * return (l_body.getContentSubType()); * * if (m_theBody instanceof MIMEBasicPart) * { * MIMEBasicPart l_body = (MIMEBasicPart) m_theBody; * return (l_body.getContentSubType()); * } * else if (m_theBody instanceof MIMEMessagePart) * { * MIMEMessagePart l_body = (MIMEMessagePart) m_theBody; * return (l_body.getContentSubType()); * } * else if (m_theBody instanceof MIMEMultiPart) * { * MIMEMultiPart l_body = (MIMEMultiPart) m_theBody; * return (l_body.getContentSubType()); * } * else * throw new MIMEException ("getContentSubType(): Invalid body!"); */ } /** * Returns the content-type params of the Message. NULL if none exist. * @exception MIMEException If no Body exists. */ public String getContentTypeParams () throws MIMEException { if (m_theBody == null) throw new MIMEException ("getContentTypeParams(): No body present!"); return (m_theBody.getContentTypeParams()); /* MIMEBodyPart l_body = (MIMEBodyPart) m_theBody; * return (l_body.getContentTypeParams()); * * if (m_theBody instanceof MIMEBasicPart) * { * MIMEBasicPart l_body = (MIMEBasicPart) m_theBody; * return (l_body.getContentTypeParams()); * } * else if (m_theBody instanceof MIMEMessagePart) * { * MIMEMessagePart l_body = (MIMEMessagePart) m_theBody; * return (l_body.getContentTypeParams()); * } * else if (m_theBody instanceof MIMEMultiPart) * { * MIMEMultiPart l_body = (MIMEMultiPart) m_theBody; * return (l_body.getContentTypeParams()); * } * else * throw new MIMEException ("getContentTypeParams(): Invalid body!"); */ } /** * Returns an Object of corresponding MIMEBodyPart type that is * the body of this Message. * @param clone Whether to return a reference to the internal object or a cloned copy; * if true: cloned copy; if false: reference to the object. * @exception MIMEException If no Body exists. * @see MIMEBasicPart * @see MIMEMultiPart * @see MIMEMessagePart */ public Object getBody (boolean clone) throws MIMEException { if (m_theBody == null) throw new MIMEException ("getBody(): No body present!"); if (m_theBody instanceof MIMEBasicPart) { try { MIMEBasicPart l_body = (MIMEBasicPart) m_theBody; return ( clone ? l_body.clone() : l_body ); } catch (CloneNotSupportedException e) { throw new MIMEException (e.getMessage()); } } else if (m_theBody instanceof MIMEMessagePart) { try { MIMEMessagePart l_body = (MIMEMessagePart) m_theBody; return ( clone ? l_body.clone() : l_body ); } catch (CloneNotSupportedException e) { throw new MIMEException (e.getMessage()); } } else if (m_theBody instanceof MIMEMultiPart) { try { MIMEMultiPart l_body = (MIMEMultiPart) m_theBody; return ( clone ? l_body.clone() : l_body ); } catch (CloneNotSupportedException e) { throw new MIMEException (e.getMessage()); } } else throw new MIMEException ("getBody(): Invalid body!"); } /** * Returns an Object of corresponding MIME BodyPart type that is * the body of this Message. * @exception MIMEException If no Body exists. */ protected Object getBody () throws MIMEException { return getBody( true ); } /** * Deletes the body of the Message. Has no effect if no body is present. */ public void deleteBody () { if (m_theBody != null) { m_theBody = null; m_bodyType = UNINITIALIZED; } } /** * Sets the MIMEBasicPart as Body of this Message. This part must already be * constructed and fully set-up. Sets the content-type of the message based on the * type of the part added by this method. * @param part BodyPart to add. * @exception MIMEException If body is already set (or) part is null. * @see MIMEBasicPart */ protected void setBody (MIMEBasicPart part ) throws MIMEException { setBody( part, true ); } /** * Sets the MIMEBodyPart as Body of this Message. This part must already be * constructed and fully set-up. Sets the content-type of the message based on the * type of the part being added. This MIMEBodyPart must be an object of one of the * three concrete classes: MIMEBasicPart, MIMEMultiPart and MIMEMessagePart. * @param part BodyPart to add. * @param clone Whether to store a reference to the object or a cloned copy; * if true: clones a copy; if false: stores a reference to the object. * @exception MIMEException If body is already set, or if part is null, or * if part is not an object of one of the three concrete classes: * MIMEBasicPart, MIMEMultiPart, or MIMEMessagePart. * @see MIMEBasicPart * @see MIMEMultiPart * @see MIMEMessagePart */ public void setBody (MIMEBodyPart part, boolean clone) throws MIMEException { if (part == null) throw new MIMEException ("setBody(): null part passed!"); if (part instanceof MIMEBasicPart) setBody ((MIMEBasicPart) part, clone); else if (part instanceof MIMEMessagePart) setBody ((MIMEMessagePart) part, clone); else if (part instanceof MIMEMultiPart) setBody ((MIMEMultiPart) part, clone); else throw new MIMEException ("setBody(): Invalid part "); } /** * Sets the MIMEBasicPart as Body of this Message. This part must already be * constructed and fully set-up. Sets the content-type of the message based on the * type of the part being added. * @param part BodyPart to add. * @param clone If false stores reference to passed object instead of cloning a copy. * @exception MIMEException If body is already set (or) part is null. * @see MIMEBasicPart */ protected void setBody (MIMEBasicPart part, boolean clone) throws MIMEException { MIMEBasicPart l_part = null; if (m_theBody != null) throw new MIMEException ("setBody(): Body already set!"); if (part == null) throw new MIMEException ("setBody(): null part passed!"); try { l_part = (MIMEBasicPart) ( clone ? part.clone() : part ); } catch (CloneNotSupportedException e) { throw new MIMEException (e.getMessage()); } m_theBody = l_part; m_bodyType = BASICPART; } /** * Sets the MIMEMultiPart as Body of this Message. This part must already be * constructed and fully set-up. Sets the content-type of the message based on the * type of the part being added * @param part BodyPart to add. * @exception MIMEException If body is already set (or) part is null. * @see MIMEBasicPart */ protected void setBody (MIMEMultiPart part) throws MIMEException { setBody( part, true ); } /** * Sets the MIMEMultiPart as Body of this Message. This part should have been * constructed and fully set-up. Sets the content-type of the message accordingly. * @param part BodyPart to add. * @param clone If false stores reference to passed object instead of cloning a copy. * @exception MIMEException If body is already set (or) part is null. * @see MIMEMultiPart */ protected void setBody (MIMEMultiPart part, boolean clone) throws MIMEException { MIMEMultiPart l_part = null; if (m_theBody != null) throw new MIMEException ("setBody(): Body already set!"); if (part == null) throw new MIMEException ("setBody(): null part passed!"); try { l_part = (MIMEMultiPart) ( clone ? part.clone() : part ); } catch (CloneNotSupportedException e) { throw new MIMEException (e.getMessage()); } m_theBody = l_part; m_bodyType = MULTIPART; } /** * Sets the MIMEMessagePart as Body of this Message. This part must already be * constructed and fully set-up. Sets the content-type of the message based on the * type of the part being added. * @param part BodyPart to add. * @exception MIMEException If body is already set (or) part is null. * @see MIMEBasicPart */ protected void setBody (MIMEMessagePart part) throws MIMEException { setBody( part, true ); } /** * Sets the MIMEMessagePart as Body of this Message. This part must already be * constructed and fully set-up. Sets the content-type of the message accordingly. * @param part BodyPart to add. * @param clone If false stores reference to passed object instead of cloning a copy. * @exception MIMEException If body is already set (or) part is null. * @see MIMEMessagePart */ protected void setBody (MIMEMessagePart part, boolean clone) throws MIMEException { MIMEMessagePart l_part = null; if (m_theBody != null) throw new MIMEException ("setBody(): Body already set!"); if (part == null) throw new MIMEException ("setBody(): null part passed!"); try { l_part = (MIMEMessagePart) ( clone ? part.clone() : part ); } catch (CloneNotSupportedException e) { throw new MIMEException (e.getMessage()); } m_theBody = l_part; m_bodyType = MESSAGEPART; } /** * Outputs a byte-stream for this Message in MIME format with transfer encoding * applied to all bodyParts as applicable. * @param fullfilename Filename, including full path to write the byte-stream to. * @exception IOException If an IO error occurs. * @exception MIMEException If any required fields in the bodyPart are not set-up. */ public void putByteStream (String fullfilename) throws IOException, MIMEException { FileOutputStream fos; fos = new FileOutputStream (fullfilename); putByteStream (fos); } /** * Outputs a byte stream for this Message in MIME format with transfer encoding * applied to all bodyParts as applicable. * @param os OutputStream to write to. * @exception IOException If an IO error occurs. * @exception MIMEException If detects an error during encoding. */ public void putByteStream (OutputStream os) throws IOException, MIMEException { // Don't write content-type here. The Body will do that. That is, if it were // a multi-part, it would do that. If it were a basic-part, it would do that etc. if (m_theBody == null) { throw new MIMEException ("MIMEMessage.putByteStream(). No body!"); } // write out all the headers. //StringBuffer l_hdrbuf; boolean l_fMIMEVersionWritten = false; byte[] l_bytebuf; Header[] hdrs = getAllHeaders (); if (hdrs != null) for (int i = 0, len = hdrs.length; i < len; i++) { try { //l_bytebuf = l_hdrbuf.toString().getBytes("iso-8859-1"); if (l_fMIMEVersionWritten == false && (hdrs[i].m_headerName).equalsIgnoreCase("MIME-Version")) l_fMIMEVersionWritten = true; l_bytebuf = MIMEHelper.unicodeToASCII (hdrs[i].getLine()); os.write (l_bytebuf); } catch (Exception e) { throw new MIMEException (hdrs[i].getLine() + "> " + e.getMessage()); } } // for // Now put the MIME Version. if (!l_fMIMEVersionWritten && m_parsedPart==0) { l_bytebuf = MIMEHelper.unicodeToASCII ("MIME-Version: 1.0\r\n"); os.write (l_bytebuf); } // call thebody.putByteStream() if (m_theBody instanceof MIMEBasicPart) { MIMEBasicPart l_body = (MIMEBasicPart) m_theBody; l_body.putByteStream(os); } else if (m_theBody instanceof MIMEMessagePart) { MIMEMessagePart l_body = (MIMEMessagePart) m_theBody; l_body.putByteStream(os); } else if (m_theBody instanceof MIMEMultiPart) { MIMEMultiPart l_body = (MIMEMultiPart) m_theBody; l_body.putByteStream(os, true); // needPreamble! } else throw new MIMEException ("putByteStream(): Invalid body!"); //os.write (CRLF); // terminate the message } // end putByteStream() /** * Clones an instance of this MIMEMessage object. * @exception CloneNotSupportedException If thrown by constituent components. */ public Object clone () throws CloneNotSupportedException { MIMEMessage l_theClone = (MIMEMessage) super.clone(); if (m_822HeadersTable != null) l_theClone.m_822HeadersTable = (Hashtable) m_822HeadersTable.clone(); if (m_repeatHdrs != null) l_theClone.m_repeatHdrs = (Vector) m_repeatHdrs.clone(); if (m_theBody != null) { if (m_theBody instanceof MIMEBasicPart) { MIMEBasicPart l_body = (MIMEBasicPart) m_theBody; l_theClone.m_theBody = (MIMEBasicPart) l_body.clone(); } else if (m_theBody instanceof MIMEMessagePart) { MIMEMessagePart l_body = (MIMEMessagePart) m_theBody; l_theClone.m_theBody = (MIMEMessagePart) l_body.clone(); } else if (m_theBody instanceof MIMEMultiPart) { MIMEMultiPart l_body = (MIMEMultiPart) m_theBody; l_theClone.m_theBody = (MIMEMultiPart) l_body.clone(); } else return (null); l_theClone.m_UserObject = m_UserObject; } // end if return (l_theClone); } protected Object getUserObject() { return m_UserObject; } protected void setUserObject( Object userObject ) { m_UserObject = userObject; } }