blob: 454e3f1afff229980c40cdec0a5f59d5d9cef732 [file] [log] [blame]
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.mime4j.message;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.james.mime4j.codec.CodecUtil;
import org.apache.james.mime4j.field.ContentTypeField;
import org.apache.james.mime4j.field.FieldName;
import org.apache.james.mime4j.parser.Field;
import org.apache.james.mime4j.util.ByteArrayBuffer;
import org.apache.james.mime4j.util.ByteSequence;
import org.apache.james.mime4j.util.ContentUtil;
import org.apache.james.mime4j.util.MimeUtil;
/**
* Writes a message (or a part of a message) to an output stream.
* <p>
* This class cannot be instantiated; instead the static instance
* {@link #DEFAULT} implements the default strategy for writing a message.
* <p>
* This class may be subclassed to implement custom strategies for writing
* messages.
*/
public class MessageWriter {
private static final byte[] CRLF = { '\r', '\n' };
private static final byte[] DASHES = { '-', '-' };
/**
* The default message writer.
*/
public static final MessageWriter DEFAULT = new MessageWriter();
/**
* Protected constructor prevents direct instantiation.
*/
protected MessageWriter() {
}
/**
* Write the specified <code>Body</code> to the specified
* <code>OutputStream</code>.
*
* @param body
* the <code>Body</code> to write.
* @param out
* the OutputStream to write to.
* @throws IOException
* if an I/O error occurs.
*/
public void writeBody(Body body, OutputStream out) throws IOException {
if (body instanceof Message) {
writeEntity((Message) body, out);
} else if (body instanceof Multipart) {
writeMultipart((Multipart) body, out);
} else if (body instanceof SingleBody) {
((SingleBody) body).writeTo(out);
} else
throw new IllegalArgumentException("Unsupported body class");
}
/**
* Write the specified <code>Entity</code> to the specified
* <code>OutputStream</code>.
*
* @param entity
* the <code>Entity</code> to write.
* @param out
* the OutputStream to write to.
* @throws IOException
* if an I/O error occurs.
*/
public void writeEntity(Entity entity, OutputStream out) throws IOException {
final Header header = entity.getHeader();
if (header == null)
throw new IllegalArgumentException("Missing header");
writeHeader(header, out);
final Body body = entity.getBody();
if (body == null)
throw new IllegalArgumentException("Missing body");
boolean binaryBody = body instanceof BinaryBody;
OutputStream encOut = encodeStream(out, entity
.getContentTransferEncoding(), binaryBody);
writeBody(body, encOut);
// close if wrapped (base64 or quoted-printable)
if (encOut != out)
encOut.close();
}
/**
* Write the specified <code>Multipart</code> to the specified
* <code>OutputStream</code>.
*
* @param multipart
* the <code>Multipart</code> to write.
* @param out
* the OutputStream to write to.
* @throws IOException
* if an I/O error occurs.
*/
public void writeMultipart(Multipart multipart, OutputStream out)
throws IOException {
ContentTypeField contentType = getContentType(multipart);
ByteSequence boundary = getBoundary(contentType);
writeBytes(multipart.getPreambleRaw(), out);
out.write(CRLF);
for (BodyPart bodyPart : multipart.getBodyParts()) {
out.write(DASHES);
writeBytes(boundary, out);
out.write(CRLF);
writeEntity(bodyPart, out);
out.write(CRLF);
}
out.write(DASHES);
writeBytes(boundary, out);
out.write(DASHES);
out.write(CRLF);
writeBytes(multipart.getEpilogueRaw(), out);
}
/**
* Write the specified <code>Header</code> to the specified
* <code>OutputStream</code>.
*
* @param header
* the <code>Header</code> to write.
* @param out
* the OutputStream to write to.
* @throws IOException
* if an I/O error occurs.
*/
public void writeHeader(Header header, OutputStream out) throws IOException {
for (Field field : header) {
writeBytes(field.getRaw(), out);
out.write(CRLF);
}
out.write(CRLF);
}
protected OutputStream encodeStream(OutputStream out, String encoding,
boolean binaryBody) throws IOException {
if (MimeUtil.isBase64Encoding(encoding)) {
return CodecUtil.wrapBase64(out);
} else if (MimeUtil.isQuotedPrintableEncoded(encoding)) {
return CodecUtil.wrapQuotedPrintable(out, binaryBody);
} else {
return out;
}
}
private ContentTypeField getContentType(Multipart multipart) {
Entity parent = multipart.getParent();
if (parent == null)
throw new IllegalArgumentException(
"Missing parent entity in multipart");
Header header = parent.getHeader();
if (header == null)
throw new IllegalArgumentException(
"Missing header in parent entity");
ContentTypeField contentType = (ContentTypeField) header
.getField(FieldName.CONTENT_TYPE);
if (contentType == null)
throw new IllegalArgumentException(
"Content-Type field not specified");
return contentType;
}
private ByteSequence getBoundary(ContentTypeField contentType) {
String boundary = contentType.getBoundary();
if (boundary == null)
throw new IllegalArgumentException(
"Multipart boundary not specified");
return ContentUtil.encode(boundary);
}
private void writeBytes(ByteSequence byteSequence, OutputStream out)
throws IOException {
if (byteSequence instanceof ByteArrayBuffer) {
ByteArrayBuffer bab = (ByteArrayBuffer) byteSequence;
out.write(bab.buffer(), 0, bab.length());
} else {
out.write(byteSequence.toByteArray());
}
}
}