package com.fsck.k9.mailstore;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Stack;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Multipart;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MimeBodyPart;
import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.internet.MimeMultipart;
import com.fsck.k9.mailstore.util.FileFactory;
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.io.EOLConvertingInputStream;
import org.apache.james.mime4j.parser.ContentHandler;
import org.apache.james.mime4j.parser.MimeStreamParser;
import org.apache.james.mime4j.stream.BodyDescriptor;
import org.apache.james.mime4j.stream.Field;
import org.apache.james.mime4j.stream.MimeConfig;
public class MimePartStreamParser {
public static MimeBodyPart parse(FileFactory fileFactory, InputStream inputStream)
throws MessagingException, IOException {
MimeBodyPart parsedRootPart = new MimeBodyPart();
MimeConfig parserConfig = new MimeConfig();
parserConfig.setMaxHeaderLen(-1);
parserConfig.setMaxLineLen(-1);
parserConfig.setMaxHeaderCount(-1);
MimeStreamParser parser = new MimeStreamParser(parserConfig);
parser.setContentHandler(new PartBuilder(fileFactory, parsedRootPart));
parser.setRecurse();
try {
parser.parse(new EOLConvertingInputStream(inputStream));
} catch (MimeException e) {
throw new MessagingException("Failed to parse decrypted content", e);
}
return parsedRootPart;
}
private static Body createBody(InputStream inputStream, String transferEncoding,
FileFactory fileFactory) throws IOException {
DeferredFileBody body = new DeferredFileBody(fileFactory, transferEncoding);
OutputStream outputStream = body.getOutputStream();
try {
IOUtils.copy(inputStream, outputStream);
} finally {
outputStream.close();
}
return body;
}
private static class PartBuilder implements ContentHandler {
private final FileFactory fileFactory;
private final MimeBodyPart decryptedRootPart;
private final Stack<Object> stack = new Stack<>();
public PartBuilder(FileFactory fileFactory, MimeBodyPart decryptedRootPart)
throws MessagingException {
this.fileFactory = fileFactory;
this.decryptedRootPart = decryptedRootPart;
}
@Override
public void startMessage() throws MimeException {
if (stack.isEmpty()) {
stack.push(decryptedRootPart);
} else {
Part part = (Part) stack.peek();
Message innerMessage = new MimeMessage();
part.setBody(innerMessage);
stack.push(innerMessage);
}
}
@Override
public void endMessage() throws MimeException {
stack.pop();
}
@Override
public void startBodyPart() throws MimeException {
try {
Multipart multipart = (Multipart) stack.peek();
BodyPart bodyPart = new MimeBodyPart();
multipart.addBodyPart(bodyPart);
stack.push(bodyPart);
} catch (MessagingException e) {
throw new MimeException(e);
}
}
@Override
public void endBodyPart() throws MimeException {
stack.pop();
}
@Override
public void startHeader() throws MimeException {
// Do nothing
}
@Override
public void field(Field parsedField) throws MimeException {
String name = parsedField.getName();
String raw = parsedField.getRaw().toString();
Part part = (Part) stack.peek();
part.addRawHeader(name, raw);
}
@Override
public void endHeader() throws MimeException {
// Do nothing
}
@Override
public void preamble(InputStream is) throws MimeException, IOException {
// Do nothing
}
@Override
public void epilogue(InputStream is) throws MimeException, IOException {
// Do nothing
}
@Override
public void startMultipart(BodyDescriptor bd) throws MimeException {
Part part = (Part) stack.peek();
String mimeType = bd.getMimeType();
String boundary = bd.getBoundary();
MimeMultipart multipart = new MimeMultipart(mimeType, boundary);
part.setBody(multipart);
stack.push(multipart);
}
@Override
public void endMultipart() throws MimeException {
stack.pop();
}
@Override
public void body(BodyDescriptor bd, InputStream inputStream) throws MimeException, IOException {
Part part = (Part) stack.peek();
String transferEncoding = bd.getTransferEncoding();
Body body = createBody(inputStream, transferEncoding, fileFactory);
part.setBody(body);
}
@Override
public void raw(InputStream is) throws MimeException, IOException {
throw new IllegalStateException("Not implemented");
}
}
}