/*********************************************************************************
* 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.net.Socket;
import totalcross.sys.*;
import totalcross.util.*;
/**
* Multipart is a container that holds multiple body parts.
*
* @since TotalCross 1.13
*/
public class Multipart
{
private static final byte[] slash2x = {'-','-'};
/** List of parts contained in this container */
protected Vector parts = new Vector();
protected String subType;
public static final String MIXED = "mixed";
public static final String FORM_DATA = "form-data";
final byte[] boundary = ("===" + new Time().getTimeLong()).getBytes();
public Multipart()
{
subType = MIXED;
}
public Multipart(String subType)
{
this.subType = subType;
}
/**
* Adds a MIME body part to this part container.
*
* @param part
* part to be added to this part container.
* @since TotalCross 1.13
*/
public void addPart(Part part)
{
parts.addElement(part);
}
/**
* Output an appropriately encoded bytestream to the given stream, which is typically used for sending.
*
* @param stream
* the output stream that will receive the encoded bytestream
* @throws IOException
* if an IO related exception occurs
* @throws MessagingException
* if an error occurs fetching the data to be written
* @since TotalCross 1.13
*/
public void writeTo(Stream stream) throws IOException, MessagingException
{
stream.writeBytes(Convert.CRLF_BYTES);
if (subType.equals(FORM_DATA)) //flsobral@tc125_36: added support for the content type "form-data" with chunked transfer encoding when used by HttpStream.
{
stream = new ChunkedStream(stream);
((ChunkedStream) stream).start();
}
stream.writeBytes("MIME Multipart Media Encapsulation, Type: multipart/" + subType + ", Boundary: \"" + new String(boundary) + "\"");
int len = parts.size();
for (int i = 0; i < len; i++)
{
stream.writeBytes(Convert.CRLF_BYTES);
stream.writeBytes(slash2x);
stream.writeBytes(boundary);
stream.writeBytes(Convert.CRLF_BYTES);
((Part) parts.items[i]).writeTo(stream);
}
stream.writeBytes(Convert.CRLF_BYTES);
stream.writeBytes(slash2x);
stream.writeBytes(boundary);
stream.writeBytes(slash2x);
stream.writeBytes(Convert.CRLF_BYTES);
if (stream instanceof ChunkedStream)
{
((ChunkedStream) stream).flush();
stream = ((ChunkedStream) stream).s;
}
}
/**
* Used by multipart with Content-Type "form-data" to support Transfer-Encoding "chunked"
*
* @since TotalCross 1.25
*/
private class ChunkedStream extends Socket
{
private final int SIZE = 1024;
ByteArrayStream bas = new ByteArrayStream(SIZE);
public Stream s;
boolean start = false;
public ChunkedStream(Stream s)
{
this.s = s;
}
public int writeBytes(byte[] buf, int start, int count) throws IOException
{
bas.writeBytes(buf, start, count);
return count;
}
public void start() throws IOException
{
flush();
start = true;
}
public void flush() throws IOException
{
int chunkSize = bas.getPos();
if (chunkSize > 0)
{
if (start)
s.writeBytes(Convert.toString(chunkSize, 16) + Convert.CRLF);
int written = 0;
byte[] basbuf = bas.getBuffer();
do
{
written += s.writeBytes(basbuf, written, chunkSize - written);
} while (written < chunkSize);
bas.reset();
}
}
public void close() throws IOException
{
if (s != null)
{
flush();
s = null;
}
}
protected void finalize()
{
}
}
}