/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.net.mail; import totalcross.io.*; import totalcross.sys.*; import totalcross.util.*; /** * This class represents a MIME body part, which are contained by Multipart objects. * * @since TotalCross 1.13 */ public class Part { public static final String INLINE = "inline"; public static final String ATTACHMENT = "attachment"; static final String MULTIPART = "multipart/"; public static final String PLAIN = "text/plain"; public static final String HTML = "text/html"; public static final String BINARY = "application/octet-stream"; /** * Defines if this part's content should be inlined in the body of the message, or attached as a file.<br> * Its value MUST be set to either INLINE (default value) or ATTACHMENT, using the constants defined by the class * Part. */ public String disposition = INLINE; /** * File name of the attachment represented by this part (if any).<br> * If assigned with a non-null value, the field disposition value is ignored and the part treated as ATTACHMENT. */ public String fileName; protected Object content; protected String mimeType; protected DataContentHandler contentHandler; protected PartHeaders headers = new PartHeaders(); /** * Add this value to the existing values for this header name. * * @param name * the name of this header * @param value * the value for this header * @since TotalCross 1.22 */ public void addHeader(String name, String value) { headers.addHeader(name, value); } /** * Returns the Content-Type of the content of this part. Returns null if the Content-Type could not be determined.<br> * * The MIME typing system is used to name Content-types. * * @return The ContentType of this part, or null if it could not be determined * * @since TotalCross 1.22 */ public String getContentType() { return mimeType; } /** * A convenience method for setting this part's content.<br> * * Note that a DataContentHandler class for the specified type should be available, otherwise the content will be * handled by the BinaryContentHandler, which writes the content as a base 64 encoded String.<br> * * @see totalcross.net.mail.BinaryContentHandler * @param content * this part's content * @param mimeType * content's MIME type * @throws IllegalArgumentException * if the given content is a Part object * @since TotalCross 1.13 */ public void setContent(Object content, String mimeType) { if (content instanceof Part) throw new IllegalArgumentException("A body part cannot be set as content of another body part"); this.content = content; this.mimeType = mimeType; contentHandler = DataHandler.getDataContentHandler(mimeType); } /** * This method sets the given Multipart object as this part's content. * * @param multipart * the multipart object that is the part's content * @since TotalCross 1.13 */ public void setContent(Multipart multipart) { this.content = multipart; contentHandler = null; this.mimeType = MULTIPART + multipart.subType + "; boundary=\"" + new String(multipart.boundary) + "\""; } /** * A convenience method for setting this part's content based on the type of the passed object.<br> * If the given object type is Multipart, it's MIME type is set to "multipart/mixed".<br> * If the given object type is String, it's MIME type is set to "text/plain".<br> * Otherwise, the given object will be handled as "application/octet-stream" by the BinaryContentHandler. * * @param content * this part's content * @throws IllegalArgumentException * if the given content is a Part object * @since TotalCross 1.13 */ public void setContent(Object content) { if (content instanceof Part) throw new IllegalArgumentException("A body part cannot be set as content of another body part"); if (content instanceof Multipart) setContent((Multipart) content); else { if (content instanceof String) this.mimeType = PLAIN; else this.mimeType = BINARY; this.content = content; contentHandler = DataHandler.getDataContentHandler(mimeType); } } /** * A convenience method that sets the given String as this part's content with a MIME type of "text/plain". * * @param text * the text that is this part's content. * @since TotalCross 1.13 */ public void setText(String text) { this.content = text; this.mimeType = PLAIN; contentHandler = DataHandler.getDataContentHandler(mimeType); } /** * Writes this part to the given Stream. The output is typically an aggregation of the Part attributes and an * appropriately encoded byte stream from its 'content'. * * Classes that extends the Part class decide on the appropriate encoding algorithm to be used. * * The received Stream is typically used for sending. * * @param stream * the output stream that will receive this part's encoded representation * @throws IOException * if an error occurs writing to the stream * @throws MessagingException * if an error occurs fetching the data to be written * @since TotalCross 1.13 */ public void writeTo(Stream stream) throws IOException, MessagingException { addHeader("Content-Type", mimeType); //flsobral@tc125: It is not an error to use "type", but better safe than sorry. String headerLine; if (headers != null && (headerLine = headers.getHeaderString()) != null) stream.writeBytes(headerLine); if (content instanceof Multipart) ((Multipart) content).writeTo(stream); else contentHandler.writeTo(this, mimeType, stream); } /** * Returns the content of this part. * * @return the content of this part. * @since TotalCross 1.13 */ public Object getContent() { return content; //flsobral@tc124_26: now we return the content, regardless of its type. } } /** * Collection of headers from a Part. * * @since TotalCross 1.22 */ class PartHeaders { private Vector headers; PartHeaders() { headers = new Vector(); } void addHeader(String name, String value) { Header newHeader = new Header(name, value); int insertIndex = -1; for (int i = headers.size() - 1; i >= 0 && insertIndex == -1; i--) if (((Header) headers.items[i]).hash == newHeader.hash) insertIndex = i; if (insertIndex == -1) headers.addElement(newHeader); else headers.insertElementAt(newHeader, insertIndex); } String getHeaderString() { int count = headers.size(); if (count <= 0) return null; StringBuffer headerBuf = new StringBuffer(256); for (int i = 0; i < count; i++) { Header header = (Header) headers.items[i]; headerBuf.append(header.name).append(": ").append(header.value).append(Convert.CRLF); } return headerBuf.toString(); } } /** * A Part header. * * @since TotalCross 1.22 */ class Header { String name; String value; int hash; Header(String name, String value) { this.name = name; this.value = value; this.hash = name.toLowerCase().hashCode(); } }