661 lines
20 KiB
Java
661 lines
20 KiB
Java
/*
|
|
* 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;
|
|
|
|
|
|
|
|
/**
|
|
* The MIMEMessagePart class implements the MIME MessagePart Content Type.
|
|
* @author Prasad Yendluri
|
|
*/
|
|
public class MIMEMessagePart extends MIMEBodyPart implements Cloneable
|
|
{
|
|
|
|
//===========================================================================
|
|
//
|
|
// Internal members not visible at the API
|
|
//
|
|
//===========================================================================
|
|
|
|
// Encodings
|
|
private static final int BINARY = MIMEBodyPart.BINARY;
|
|
private static final int E7BIT = MIMEBodyPart.E7BIT;
|
|
private static final int E8BIT = MIMEBodyPart.E8BIT;
|
|
|
|
|
|
private static final int UNINITIALIZED = -1;
|
|
|
|
private static final String[] m_stringDisposition = { "Attachment", "Inline"};
|
|
private static final String[] m_stringEncoding = { "base64", "quoted-printable",
|
|
"binary", "7bit", "8bit" };
|
|
private static final String MESSAGE = "Message";
|
|
|
|
private int m_contentTransferEncoding;
|
|
private int m_parsedPart;
|
|
|
|
// 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_ExtBodyHeadersTable;
|
|
|
|
private MIMEMessage m_theMessage;
|
|
private InputStream m_partial;
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// CONSTRUCTORS
|
|
//
|
|
//===========================================================================
|
|
|
|
/**
|
|
* Constructs a MIMEMessagePart object.
|
|
* Default constructor for the MIMEMessagePart.
|
|
*/
|
|
public MIMEMessagePart ()
|
|
{
|
|
m_parsedPart = 0;
|
|
m_UserObject = null;
|
|
m_contentDisposition = UNINITIALIZED;
|
|
m_contentTransferEncoding = UNINITIALIZED;
|
|
m_theMessage = null;
|
|
m_partial = null;
|
|
m_ExtBodyHeadersTable = null;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Constructs a MIMEMessagePart object from the given MIMEMessage Object.
|
|
* @param msg The message that should form the body of this part.
|
|
* @exception MIMEException If msg is null.
|
|
*/
|
|
public MIMEMessagePart (MIMEMessage msg) throws MIMEException
|
|
{
|
|
m_parsedPart = 0;
|
|
m_contentDisposition = UNINITIALIZED;
|
|
m_contentTransferEncoding = UNINITIALIZED;
|
|
|
|
if (msg != null)
|
|
{
|
|
try
|
|
{
|
|
m_theMessage = (MIMEMessage) msg.clone();
|
|
}
|
|
catch (CloneNotSupportedException e)
|
|
{
|
|
throw new MIMEException (e.getMessage());
|
|
}
|
|
}
|
|
else
|
|
throw new MIMEException ("MIMEMessagePart(): null message passed." );
|
|
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Methods from Interface MIMEBodyPart
|
|
//
|
|
//===========================================================================
|
|
|
|
/**
|
|
* Returns Content-Type of this MIME Part. Always returns the string "Message".
|
|
*/
|
|
public String getContentType ()
|
|
{
|
|
return (MESSAGE);
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets Content-Transfer-Encoding of this MIME Part.
|
|
* Only BINARY, E7BIT, and E8BIT are applicable to MIMEMessagePart.
|
|
* Additionally, for message/partial and message/external-body, the MIME
|
|
* standard requires E7BIT encoding. It is possible to
|
|
* set encoding prior to setting the contentSubType.
|
|
* If a different value is specified, no error occurs; instead,
|
|
* this method will be overridden by the putByteStream() method.
|
|
* @param encoding Value that represents the encoding.
|
|
* @see MIMEBodyPart#BINARY
|
|
* @see MIMEBodyPart#E7BIT
|
|
* @see MIMEBodyPart#E8BIT
|
|
* @exception MIMEException If the value is not one of BINARY, E7BIT or E8BIT
|
|
*/
|
|
public void setContentEncoding (int encoding) throws MIMEException
|
|
{
|
|
switch (encoding)
|
|
{
|
|
case BINARY:
|
|
case E7BIT:
|
|
case E8BIT:
|
|
m_contentTransferEncoding = encoding;
|
|
break;
|
|
default:
|
|
throw new MIMEException ("Invalid/Inapplicable Content-Transfer-Encoding: " + encoding);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns value of Content-Transfer-Encoding of this MIME Part. -1 if none present.
|
|
* @see MIMEBodyPart#BINARY
|
|
* @see MIMEBodyPart#E7BIT
|
|
* @see MIMEBodyPart#E8BIT
|
|
*/
|
|
public int getContentEncoding ()
|
|
{
|
|
if (m_contentTransferEncoding == UNINITIALIZED)
|
|
return (-1);
|
|
|
|
return (m_contentTransferEncoding);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Methods Specific to this Class alone.
|
|
//
|
|
//===========================================================================
|
|
|
|
|
|
/**
|
|
* Sets the specified message as the body of this part.
|
|
* @param msg Message to be set as body of this part.
|
|
* @exception MIMEException If already set (or) can not be set.
|
|
*/
|
|
protected void setMessage (MIMEMessage msg) throws MIMEException
|
|
{
|
|
setMessage (msg, true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the specified message as the body of this part.
|
|
* @param msg Message to be set as body of this part.
|
|
* @param clone If false stores reference to passed object instead of cloning a copy.
|
|
* @exception MIMEException If already set (or) can not be set.
|
|
*/
|
|
public void setMessage (MIMEMessage msg, boolean clone) throws MIMEException
|
|
{
|
|
if (m_theMessage != null)
|
|
throw new MIMEException ("setMessage(): already set!");
|
|
|
|
if (msg != null)
|
|
{
|
|
try
|
|
{
|
|
m_theMessage = (MIMEMessage) (clone ? msg.clone() : msg);
|
|
}
|
|
catch (CloneNotSupportedException e)
|
|
{
|
|
throw new MIMEException (e.getMessage());
|
|
}
|
|
}
|
|
else
|
|
throw new MIMEException ("setMessage(): null message passed." );
|
|
}
|
|
|
|
/**
|
|
* Returns the MIMEMessage that is the Body of this part.
|
|
* @exception MIMEException If no Message exists in body-data.
|
|
*/
|
|
protected MIMEMessage getMessage () throws MIMEException
|
|
{
|
|
return getMessage (true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the MIMEMessage that is the Body of this part.
|
|
* @param clone If false returns reference to the object instead of a cloned copy.
|
|
* @exception MIMEException If no Message exists in body-data.
|
|
*/
|
|
public MIMEMessage getMessage (boolean clone) throws MIMEException
|
|
{
|
|
if (m_theMessage == null)
|
|
throw new MIMEException ("getMessage(): null Message in body!");
|
|
|
|
try
|
|
{
|
|
MIMEMessage l_msg = (MIMEMessage) (clone ? m_theMessage.clone() : m_theMessage);
|
|
return (l_msg);
|
|
}
|
|
catch (CloneNotSupportedException e)
|
|
{
|
|
throw new MIMEException (e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes the MIMEMessage that is the Body of this part.
|
|
* Noop if no body was ever set.
|
|
*/
|
|
public void deleteMessage ()
|
|
{
|
|
m_theMessage = null;
|
|
}
|
|
|
|
/**
|
|
* Sets the headers associated with External Body, for external-body content-subtypes
|
|
* only. If already set, these headers are
|
|
* ignored for other subtypes (rfc822 and message-partial).
|
|
* @param name Name of the header field. Should not include ':'
|
|
* @param value Value of the header field to be added.
|
|
* @exception MIMEException if either of name or value is null
|
|
*/
|
|
public void setExtBodyHeader (String name, String value) throws MIMEException
|
|
{
|
|
if ((name == null) || (value == null))
|
|
throw new MIMEException ("Invalid NULL Header name or value");
|
|
|
|
if ( name.indexOf (':') > -1)
|
|
throw new MIMEException ("Invalid ':' in Header name");
|
|
|
|
if (m_ExtBodyHeadersTable == null)
|
|
m_ExtBodyHeadersTable = new Hashtable();
|
|
|
|
String l_hashKey = name.toLowerCase(); // Keys are always lower case
|
|
Header l_hdr = new Header (name, value);
|
|
m_ExtBodyHeadersTable.put (l_hashKey, l_hdr);
|
|
}
|
|
|
|
/**
|
|
* Returns the value of the specified header associated with External Body.
|
|
* Applicable to external-body subtypes only.
|
|
* Returns NULL if the header is not present.
|
|
* @param name Name of the header field.
|
|
* @exception MIMEException If name passed is a NULL.
|
|
*/
|
|
public String getExtBodyHeader (String name) throws MIMEException
|
|
{
|
|
if (name == null)
|
|
throw new MIMEException ("Invalid NULL Header name");
|
|
|
|
if (m_ExtBodyHeadersTable == null)
|
|
return (null);
|
|
|
|
String l_hashKey = name.toLowerCase(); // Keys are always lower case
|
|
Header l_hdr = (Header) m_ExtBodyHeadersTable.get (l_hashKey);
|
|
|
|
if (l_hdr != null)
|
|
return (l_hdr.getValue()); // getValue() returns a new string.
|
|
else
|
|
return (null);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns all the headers in this Part as an array of Header objects.
|
|
* @exception MIMEException If no headers exist.
|
|
*/
|
|
public Header[] getAllHeaders () throws MIMEException
|
|
{
|
|
int l_numElements = 0, i = 0;
|
|
|
|
if (m_ExtBodyHeadersTable != null)
|
|
l_numElements = m_ExtBodyHeadersTable.size();
|
|
|
|
if (l_numElements <= 0)
|
|
return (null);
|
|
|
|
Header[] hdrs = new Header [l_numElements];
|
|
Enumeration he = m_ExtBodyHeadersTable.elements();
|
|
|
|
while (he.hasMoreElements())
|
|
{
|
|
hdrs[i++] = (Header) he.nextElement();
|
|
}
|
|
|
|
return (hdrs);
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the message partial. Stores reference to the supplied inputStream.
|
|
* Does not copy data. The data in partial is copied as is by putByteStream().
|
|
* To be used with message/partial subtypes only. It is an error to setPartial()
|
|
* for other subtypes (rfc822 and external-body).
|
|
* @param instream inputStream that supplies the data for this partial.
|
|
* @exception MIMEException if content-subtype is not partial or if already set.
|
|
* @see deletePartial
|
|
*/
|
|
protected void setPartial (InputStream instream) throws MIMEException
|
|
{
|
|
if (m_partial != null)
|
|
throw new MIMEException ("Partial already set.");
|
|
|
|
if ((m_contentSubType != null) &&
|
|
(!m_contentSubType.equalsIgnoreCase("partial")))
|
|
throw new MIMEException ("Content-Subtype partial expected.");
|
|
|
|
m_partial = instream;
|
|
|
|
}
|
|
|
|
/**
|
|
* Deletes the message partial data of this part. Ignores if partial was not set.
|
|
* @see setPartial
|
|
*/
|
|
protected void deletePartial ()
|
|
{
|
|
m_partial = null;
|
|
}
|
|
|
|
/**
|
|
* Outputs a byte-stream for this part with its MIME part headers and encoded
|
|
* body data.
|
|
* @param os OutputStream to write to.
|
|
* @exception IOException If an IO error occurs.
|
|
* @exception MIMEException If encoding error is detected.
|
|
*/
|
|
public void putByteStream (OutputStream os) throws IOException, MIMEException
|
|
{
|
|
boolean l_external_body = false;
|
|
boolean l_message_partial = false;
|
|
String l_contentSubType = null;
|
|
if (m_contentSubType != null)
|
|
l_contentSubType = m_contentSubType.trim();
|
|
|
|
if ((l_contentSubType != null) &&
|
|
(l_contentSubType.equalsIgnoreCase("external-body")))
|
|
{
|
|
l_external_body = true;
|
|
|
|
if (m_ExtBodyHeadersTable == null);
|
|
//throw new MIMEException ("External body header Content-Id required for external-body");
|
|
}
|
|
|
|
if ((l_contentSubType != null) &&
|
|
(l_contentSubType.equalsIgnoreCase("partial")))
|
|
{
|
|
l_message_partial = true;
|
|
|
|
if (m_partial == null)
|
|
throw new MIMEException ("Message partial not set");
|
|
|
|
if (m_contentTransferEncoding != E7BIT && m_contentTransferEncoding != UNINITIALIZED)
|
|
throw new MIMEException ("Bad content-encoding for message/partial");
|
|
|
|
}
|
|
|
|
if ((m_theMessage == null) && (!l_external_body) && (!l_message_partial))
|
|
{
|
|
throw new MIMEException ("putByteStream(): null Message body!");
|
|
}
|
|
|
|
// Write out the headers first
|
|
StringBuffer l_hdrbuf = new StringBuffer (HDRBUFSZ);
|
|
byte[] l_bytebuf;
|
|
|
|
// content-type
|
|
l_hdrbuf.append ("Content-Type: message/");
|
|
|
|
if (m_contentSubType != null)
|
|
l_hdrbuf.append (m_contentSubType);
|
|
else
|
|
{
|
|
if (m_theMessage != null)
|
|
l_hdrbuf.append ("rfc822"); // default
|
|
else
|
|
throw new MIMEException ("putByteStream(): No content-subtype." );
|
|
}
|
|
|
|
if (m_contentTypeParams != null && m_contentTypeParams.length() > 0)
|
|
{
|
|
l_hdrbuf.append ("; ");
|
|
l_hdrbuf.append (m_contentTypeParams);
|
|
}
|
|
|
|
l_hdrbuf.append ("\r\n");
|
|
|
|
// write the header out to os
|
|
try
|
|
{
|
|
//l_bytebuf = l_hdrbuf.toString().getBytes("iso-8859-1");
|
|
l_bytebuf = MIMEHelper.unicodeToASCII (l_hdrbuf.toString());
|
|
os.write (l_bytebuf);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new MIMEException ("Content-Type: " + e.getMessage());
|
|
}
|
|
|
|
// contentID
|
|
if (m_contentID != null)
|
|
{
|
|
l_hdrbuf.setLength (0);
|
|
l_hdrbuf.append ("Content-ID: " + m_contentID);
|
|
l_hdrbuf.append ("\r\n");
|
|
|
|
try
|
|
{
|
|
// l_bytebuf = l_hdrbuf.toString().getBytes("iso-8859-1");
|
|
l_bytebuf = MIMEHelper.unicodeToASCII (l_hdrbuf.toString());
|
|
os.write (l_bytebuf);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new MIMEException ("Content-ID: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
// contentDisposition
|
|
if (m_contentDisposition != UNINITIALIZED)
|
|
{
|
|
l_hdrbuf.setLength (0);
|
|
l_hdrbuf.append ("Content-Disposition: " +
|
|
m_stringDisposition [m_contentDisposition]);
|
|
|
|
if (m_contentDispParams != null)
|
|
{
|
|
l_hdrbuf.append ("; ");
|
|
l_hdrbuf.append (m_contentDispParams);
|
|
}
|
|
|
|
l_hdrbuf.append ("\r\n");
|
|
|
|
try
|
|
{
|
|
//l_bytebuf = l_hdrbuf.toString().getBytes("iso-8859-1");
|
|
l_bytebuf = MIMEHelper.unicodeToASCII (l_hdrbuf.toString());
|
|
os.write (l_bytebuf);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new MIMEException ("Content-Disposition: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
// contentDescription
|
|
if (m_contentDescription != null)
|
|
{
|
|
l_hdrbuf.setLength (0);
|
|
l_hdrbuf.append ("Content-Description: " + m_contentDescription);
|
|
l_hdrbuf.append ("\r\n");
|
|
|
|
try
|
|
{
|
|
//l_bytebuf = l_hdrbuf.toString().getBytes("iso-8859-1");
|
|
l_bytebuf = MIMEHelper.unicodeToASCII (l_hdrbuf.toString());
|
|
os.write (l_bytebuf);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new MIMEException ("Content-Description: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
// Content-transfer-encoding
|
|
//if (m_contentTransferEncoding == UNINITIALIZED)
|
|
//{
|
|
// m_contentTransferEncoding = E7BIT; // default
|
|
//}
|
|
|
|
if (m_contentSubType != null)
|
|
{
|
|
if ((l_message_partial || l_external_body) && m_contentTransferEncoding != UNINITIALIZED)
|
|
m_contentTransferEncoding = E7BIT; // As mandated by MIME standard
|
|
}
|
|
|
|
//if (!l_external_body) // for external-body we write it later
|
|
{
|
|
if (m_contentTransferEncoding != UNINITIALIZED)
|
|
{
|
|
if (l_external_body)
|
|
m_contentTransferEncoding = E7BIT;
|
|
|
|
l_hdrbuf.setLength (0);
|
|
l_hdrbuf.append ("Content-Transfer-Encoding: " +
|
|
m_stringEncoding [m_contentTransferEncoding]);
|
|
l_hdrbuf.append ("\r\n");
|
|
|
|
try
|
|
{
|
|
// l_bytebuf = l_hdrbuf.toString().getBytes("iso-8859-1");
|
|
l_bytebuf = MIMEHelper.unicodeToASCII (l_hdrbuf.toString());
|
|
os.write (l_bytebuf);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new MIMEException ("Content-Transfer-Encoding: " + e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// blank-line after headers
|
|
//l_bytebuf = new String("\r\n").getBytes("iso-8859-1");
|
|
os.write (CRLF);
|
|
|
|
// write out the message part if it is not external-body
|
|
if ((m_theMessage != null) && (!l_external_body) && (!l_message_partial))
|
|
m_theMessage.putByteStream(os);
|
|
else if (l_external_body)
|
|
{
|
|
//os.write (CRLF); // Prior to the headers for the external-body
|
|
|
|
if (m_ExtBodyHeadersTable != null)
|
|
{
|
|
Enumeration he = m_ExtBodyHeadersTable.elements();
|
|
|
|
while (he.hasMoreElements())
|
|
{
|
|
Header l_tmphdr = (Header) he.nextElement();
|
|
|
|
try
|
|
{
|
|
l_bytebuf = MIMEHelper.unicodeToASCII (l_tmphdr.getLine());
|
|
os.write (l_bytebuf);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new MIMEException (l_tmphdr.getLine() + e.getMessage());
|
|
}
|
|
|
|
} // while
|
|
|
|
//if (m_contentTransferEncoding != UNINITIALIZED)
|
|
//{
|
|
// // write out content-encoding
|
|
// l_hdrbuf.setLength (0);
|
|
// l_hdrbuf.append ("Content-Transfer-Encoding: " +
|
|
// m_stringEncoding [m_contentTransferEncoding]);
|
|
// l_hdrbuf.append ("\r\n");
|
|
//
|
|
// try
|
|
// {
|
|
// // l_bytebuf = l_hdrbuf.toString().getBytes("iso-8859-1");
|
|
// l_bytebuf = MIMEHelper.unicodeToASCII (l_hdrbuf.toString());
|
|
// os.write (l_bytebuf);
|
|
// }
|
|
// catch (Exception e)
|
|
// {
|
|
// throw new MIMEException ("Content-Transfer-Encoding: " + e.getMessage());
|
|
// }
|
|
// }
|
|
|
|
os.write (CRLF); // After the headers for the external-body
|
|
|
|
} // if m_ExtBodyHeadersTable
|
|
|
|
} // if l_external_body
|
|
else if (l_message_partial)
|
|
{
|
|
os.write (CRLF);
|
|
byte l_buf [] = new byte [DATABUFSZ];
|
|
int l_len;
|
|
|
|
while ( (l_len = m_partial.read (l_buf, 0, DATABUFSZ)) > 0)
|
|
{
|
|
os.write (l_buf, 0, l_len);
|
|
}
|
|
|
|
} // if l_external_body
|
|
|
|
os.write (CRLF); // terminate the part
|
|
|
|
} // end putByteStream()
|
|
|
|
/**
|
|
* Clones an instance of this MIMEMessagePart object.
|
|
* @exception CloneNotSupportedException If thrown by a constituent components.
|
|
*/
|
|
public Object clone () throws CloneNotSupportedException
|
|
{
|
|
MIMEMessagePart l_theClone = (MIMEMessagePart) super.clone();
|
|
|
|
if (m_theMessage != null)
|
|
{
|
|
l_theClone.m_theMessage = (MIMEMessage) m_theMessage.clone();
|
|
}
|
|
|
|
if (m_contentSubType != null)
|
|
l_theClone.m_contentSubType = m_contentSubType;
|
|
|
|
if (m_contentTypeParams != null)
|
|
l_theClone.m_contentTypeParams = m_contentTypeParams;
|
|
|
|
if (m_contentID != null)
|
|
l_theClone.m_contentID = m_contentID;
|
|
|
|
if (m_contentDispParams != null)
|
|
l_theClone.m_contentDispParams = m_contentDispParams;
|
|
|
|
if (m_contentDescription != null)
|
|
l_theClone.m_contentDescription = m_contentDescription;
|
|
|
|
l_theClone.m_UserObject = m_UserObject;
|
|
|
|
return (l_theClone);
|
|
}
|
|
}
|