/* * � Copyright IBM Corp. 2012 * * Licensed 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 com.ibm.domino.commons.mime; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.List; import java.util.Vector; import lotus.domino.Document; import lotus.domino.MIMEEntity; import lotus.domino.MIMEHeader; import lotus.domino.NotesException; import lotus.domino.Session; import lotus.domino.Stream; import org.apache.james.mime4j.MimeIOException; import org.apache.james.mime4j.field.address.Address; import org.apache.james.mime4j.field.address.AddressList; import org.apache.james.mime4j.message.BinaryBody; import org.apache.james.mime4j.message.Body; import org.apache.james.mime4j.message.BodyPart; import org.apache.james.mime4j.message.Entity; import org.apache.james.mime4j.message.Header; import org.apache.james.mime4j.message.Message; import org.apache.james.mime4j.message.Multipart; import org.apache.james.mime4j.message.TextBody; import org.apache.james.mime4j.parser.Field; import com.ibm.commons.util.StringUtil; import com.ibm.domino.commons.model.ModelException; public class MimeMessageParser { private static final String ITEM_BODY = "Body"; //$NON-NLS-1$ private static final String ITEM_SUBJECT = "Subject"; //$NON-NLS-1$ private static final String ITEM_FORM = "Form"; //$NON-NLS-1$ private static final String ITEM_SEND_TO = "SendTo"; //$NON-NLS-1$ private static final String ITEM_COPY_TO = "CopyTo"; //$NON-NLS-1$ private static final String ITEM_BLINDCOPY_TO = "BlindCopyTo"; //$NON-NLS-1$ private static final String ITEM_IN_REPLY_TO = "In_Reply_To"; //$NON-NLS-1$ private static final String ITEM_RETURN_RECEIPT = "ReturnReceipt"; //$NON-NLS-1$ private static final String ITEM_DISPOSITION_NOTIFICATION_TO = "Disposition_Notification_To"; //$NON-NLS-1$ private static final String HEADER_CONTENT_TYPE = "Content-Type"; //$NON-NLS-1$ private static final String HEADER_IN_REPLY_TO = "In-Reply-To"; //$NON-NLS-1$ private static final String HEADER_DISPOSITION_NOTIFICATION_TO = "Disposition-Notification-To"; //$NON-NLS-1$ private InputStream _is; public MimeMessageParser(InputStream is) { _is = is; } public void fromMime(Document document) throws MimeIOException, IOException, ModelException { try { Message mimeMsg = new Message(_is); List<Field> fields = mimeMsg.getHeader().getFields(); if ( fields == null || fields.size() == 0 ) { throw new ModelException("Message is empty", ModelException.ERR_INVALID_INPUT); // $NLX-MimeMessageParser.Messageisempty-1$ } document.replaceItemValue(ITEM_FORM, "Memo"); //$NON-NLS-1$ // PLEASE READ if you are adding code that writes items to the note. // This code can execute either when the document is new or when an existing // document is being updated. In the latter case, it's important to remove // items that are not represented in the MIME input. document.removeItem(ITEM_SUBJECT); String subject = mimeMsg.getSubject(); if ( StringUtil.isNotEmpty(subject) ) { document.replaceItemValue(ITEM_SUBJECT, subject); } AddressList to = mimeMsg.getTo(); writeAddresses(document, ITEM_SEND_TO, to); AddressList cc = mimeMsg.getCc(); writeAddresses(document, ITEM_COPY_TO, cc); AddressList bcc = mimeMsg.getBcc(); writeAddresses(document, ITEM_BLINDCOPY_TO, bcc); document.removeItem(ITEM_IN_REPLY_TO); Header header = mimeMsg.getHeader(); Field inReplyTo = header.getField(HEADER_IN_REPLY_TO); if ( inReplyTo != null ) { String value = inReplyTo.getBody(); document.replaceItemValue(ITEM_IN_REPLY_TO, value); } document.removeItem(ITEM_DISPOSITION_NOTIFICATION_TO); document.removeItem(ITEM_RETURN_RECEIPT); Field receiptTo = header.getField(HEADER_DISPOSITION_NOTIFICATION_TO); if ( receiptTo != null ) { String value = receiptTo.getBody(); document.replaceItemValue(ITEM_DISPOSITION_NOTIFICATION_TO, value); document.replaceItemValue(ITEM_RETURN_RECEIPT, "1"); } writeEntity(document, null, mimeMsg); } catch (NotesException e) { throw new ModelException("Notes document error.", e); // $NLX-MimeMessageParser.Notesdocumenterror-1$ } } private static void writeEntity(Document document, MIMEEntity parent, Entity entity) throws IOException, NotesException { MIMEEntity notesEntity = null; if ( parent == null ) { if (document.hasItem(ITEM_BODY)) { // SPR #BBRL9RECLY: Remove existing body or createMIMEEntity // will fail. document.removeItem(ITEM_BODY); } notesEntity = document.createMIMEEntity(); } else { notesEntity = parent.createChildEntity(); } String mediaType = entity.getMimeType(); Body body = entity.getBody(); if ( body instanceof Multipart ) { // Set the content type MIMEHeader notesHeader = notesEntity.createHeader(HEADER_CONTENT_TYPE); //$NON-NLS-1$ notesHeader.setHeaderVal(mediaType); Multipart multipart = (Multipart)body; for (BodyPart part : multipart.getBodyParts()) { writeEntity(document, notesEntity, part); } } else { Header header = entity.getHeader(); // Handle content disposition String filename = entity.getFilename(); String disposition = entity.getDispositionType(); if ( disposition != null ) { MIMEHeader notesHeader = notesEntity.createHeader("Content-Disposition"); //$NON-NLS-1$ String value = disposition; if ( filename != null ) { value += "; filename=\"" + filename + "\""; //$NON-NLS-1$ //$NON-NLS-2$ } notesHeader.setHeaderVal(value); } // Handle content ID Field id = header.getField("Content-ID"); //$NON-NLS-1$ if ( id != null ) { MIMEHeader notesHeader = notesEntity.createHeader("Content-ID"); //$NON-NLS-1$ notesHeader.setHeaderVal(id.getBody()); } // Write a simple part's content Session session = document.getParentDatabase().getParent(); if ( body instanceof TextBody) { Reader reader = ((TextBody)body).getReader(); Stream stream = session.createStream(); readerToNotesStream(stream, reader); // Write the part's content notesEntity.setContentFromText(stream, mediaType, MIMEEntity.ENC_NONE); // SPR# WJBJ9SP4TD: Preserve the char set parameter on the Content-Type header String ctValue = null; Field ct = header.getField(HEADER_CONTENT_TYPE); if ( ct != null ) { ctValue = ct.getBody(); } if ( ctValue != null && !ctValue.equalsIgnoreCase(mediaType) ) { MIMEHeader notesHeader = getNotesHeader(notesEntity, HEADER_CONTENT_TYPE); if ( notesHeader != null ) { notesHeader.setHeaderValAndParams(ctValue); } } } else if ( body instanceof BinaryBody ) { InputStream is = ((BinaryBody)body).getInputStream(); Stream stream = session.createStream(); inputStreamToNotesStream(stream, is); notesEntity.setContentFromBytes(stream, mediaType, MIMEEntity.ENC_NONE); } else { // TODO: Is this an error condition? } } // If this is a top level entity, flush the MIME contents to the // in-memory note. if ( parent == null ) { try { document.closeMIMEEntities(true, ITEM_BODY); } catch (NotesException e) { // Special case when the MIME input has no content: closeMIMEEntities throws // an exception with an error code of 546. It's safe to ignore that, but // rethrow all other exceptions. if ( e.id != 546 ) { throw e; } } } } private static MIMEHeader getNotesHeader(MIMEEntity entity, String headerName) throws NotesException { MIMEHeader header = null; Vector<MIMEHeader> headers = entity.getHeaderObjects(); for ( MIMEHeader thisHeader : headers ) { if ( thisHeader.getHeaderName().equalsIgnoreCase(headerName) ) { header = thisHeader; break; } } return header; } private static void writeAddresses(Document document, String itemName, AddressList addresses) throws NotesException { document.removeItem(itemName); if ( addresses != null && addresses.size() > 0 ) { Vector<String> values = new Vector<String>(); for ( int i = 0; i < addresses.size(); i++ ) { Address address = addresses.get(i); values.add(address.toString()); } document.replaceItemValue(itemName, values); } } private static void readerToNotesStream(Stream stream, Reader reader) throws IOException, NotesException { BufferedReader br = new BufferedReader(reader); String line = br.readLine(); while ( line != null ) { stream.writeText(line); stream.writeText("\r\n"); //$NON-NLS-1$ line = br.readLine(); } } private static void inputStreamToNotesStream(Stream stream, InputStream is) throws IOException, NotesException { int ch = is.read(); while ( ch != -1 ) { byte b[] = new byte[1]; b[0] = (byte)ch; stream.write(b); ch = is.read(); } } }