All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hcl.domino.jna.mime.JNAMimeWriter Maven / Gradle / Ivy

There is a newer version: 1.44.0
Show newest version
/*
 * ==========================================================================
 * Copyright (C) 2019-2022 HCL America, Inc. ( http://www.hcl.com/ )
 *                            All rights reserved.
 * ==========================================================================
 * 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 .
 *
 * 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.hcl.domino.jna.mime;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Set;
import java.util.function.Consumer;

import com.hcl.domino.commons.gc.APIObjectAllocations;
import com.hcl.domino.commons.gc.IAPIObject;
import com.hcl.domino.commons.util.NotesErrorUtils;
import com.hcl.domino.commons.util.StringUtil;
import com.hcl.domino.data.Document;
import com.hcl.domino.exception.IncompatibleImplementationException;
import com.hcl.domino.exception.ObjectDisposedException;
import com.hcl.domino.jna.data.JNADocument;
import com.hcl.domino.jna.internal.DisposableMemory;
import com.hcl.domino.jna.internal.NotesStringUtils;
import com.hcl.domino.jna.internal.capi.NotesCAPI;
import com.hcl.domino.jna.internal.gc.allocations.JNADocumentAllocations;
import com.hcl.domino.jna.internal.gc.allocations.JNARichtextMimeConversionSettingsAllocations;
import com.hcl.domino.jna.internal.gc.handles.LockUtil;
import com.hcl.domino.mime.MimeWriter;
import com.hcl.domino.mime.RichTextMimeConversionSettings;
import com.hcl.domino.misc.NotesConstants;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;

import jakarta.mail.Message;
import jakarta.mail.MessagingException;

public class JNAMimeWriter extends JNAMimeBase implements MimeWriter {
	
	public JNAMimeWriter(IAPIObject parent) {
		super(parent);
	}
	
	/**
	 * Writes the data of a {@link Message} to the MIMEStream in small chunks.
	 * 
	 * @param mimeStreamPtr pointer to MIMEStream
	 * @param mimeMessage message to write
	 * @throws IOException in case of I/O errors
	 * @throws MessagingException in case of errors accessing the {@link Message}
	 */
	private void writeMimeToStream(Pointer mimeStreamPtr, Message mimeMessage) throws IOException, MessagingException {
		//size of in-memory buffer to transfer MIME data from Message object to Domino MIME stream
		final int BUFFERSIZE = 16384;
		
		final DisposableMemory buf = new DisposableMemory(BUFFERSIZE);
		
		OutputStream os = new OutputStream() {
			int bytesInBuffer = 0;

			@Override
			public void write(int b) throws IOException {
				buf.setByte(bytesInBuffer, (byte) (b & 0xff));
				bytesInBuffer++;
				if (bytesInBuffer == buf.size()) {
					flushBuffer();
				}
			}

			@Override
			public void close() throws IOException {
				flushBuffer();
			}

			private void flushBuffer() throws IOException {
				if (bytesInBuffer > 0) {
					int resultAsInt = NotesCAPI.get().MIMEStreamWrite(buf, bytesInBuffer, mimeStreamPtr);

					if (resultAsInt == NotesConstants.MIME_STREAM_IO) {
						throw new IOException("I/O error received during MIME stream operation");
					}

					bytesInBuffer = 0;
				}
			}
		};
		try {
			mimeMessage.writeTo(os);
		} finally {
			os.close();
		}
	}

	/**
	 * Generic method to write data to a MIMEStream.
	 * 
	 * @param doc target document
	 * @param itemName item name for MIME content
	 * @param streamConsumer consumer that writes data into the MIMEStream
	 * @param dataType itemize flags (write header, body or both)
	 */
	private void writeMime(Document doc, String itemName, Consumer streamConsumer, Set dataType) {
		if (!(doc instanceof JNADocument)) {
			throw new IncompatibleImplementationException(doc, JNADocument.class);
		}
		JNADocument jnaDoc = (JNADocument) doc;

		if (jnaDoc.isDisposed()) {
			throw new ObjectDisposedException(jnaDoc);
		}

		if ("$file".equalsIgnoreCase(itemName)) { //$NON-NLS-1$
			throw new IllegalArgumentException(MessageFormat.format("Invalid item name: {0}", itemName));
		}

		if (dataType.isEmpty()) {
			throw new IllegalArgumentException("Either header or body should be exported");
		}

		Memory itemNameMem = NotesStringUtils.toLMBCS(itemName, false);
		
		int dwOpenFlags = NotesConstants.MIME_STREAM_OPEN_WRITE;
		
		if (dataType.contains(WriteMimeDataType.BODY) &&
				!dataType.contains(WriteMimeDataType.HEADERS)) {

			//write just the body
			JNADocument tmpDoc = (JNADocument) jnaDoc.getParentDatabase().createDocument();
			try {
				Pointer mimeStreamPtr = createMimeStream(tmpDoc, itemNameMem, dwOpenFlags);
				try {
					streamConsumer.accept(mimeStreamPtr);

					int dwItemizeFlags = NotesConstants.MIME_STREAM_ITEMIZE_FULL;
					
					JNADocumentAllocations tmpDocAllocations = (JNADocumentAllocations) tmpDoc.getAdapter(APIObjectAllocations.class);
					
					short wItemNameLen = itemNameMem == null ? 0 : (short)(itemNameMem.size() & 0xffff);
					LockUtil.lockHandle(tmpDocAllocations.getNoteHandle(), (docHdlByVal) -> {
						NotesCAPI.get().MIMEStreamItemize(docHdlByVal, itemNameMem,
								wItemNameLen, dwItemizeFlags, mimeStreamPtr);
						
						return null;
						
					});
				}
				finally {
					disposeMimeStream(mimeStreamPtr);
				}
				
				//remove old items from target note
				String iname = StringUtil.isEmpty(itemName) ? "Body" : itemName; //$NON-NLS-1$
				while (doc.hasItem(iname)) {
					doc.removeItem(iname);
				}
				
				//copy created MIME items from temp note to target note
				tmpDoc.forEachItem(iname, (item, loop) -> {
					item.copyToDocument(doc, false);
				});
				
				//copy part data that exceeded 64k
				tmpDoc.forEachItem("$file", (item, loop) -> { //$NON-NLS-1$
					item.copyToDocument(doc, false);
				});
			}
			finally {
				tmpDoc.dispose();
			}
		}
		else {
			Pointer mimeStreamPtr = createMimeStream(jnaDoc, itemNameMem, dwOpenFlags);
			try {
				streamConsumer.accept(mimeStreamPtr);
				
				JNADocumentAllocations jnaDocAllocations = (JNADocumentAllocations) jnaDoc.getAdapter(APIObjectAllocations.class);
				
				LockUtil.lockHandle(jnaDocAllocations.getNoteHandle(), (docHdlByVal) -> {
					int dwItemizeFlags = 0;
					if (dataType.contains(WriteMimeDataType.HEADERS)) {
						dwItemizeFlags |= NotesConstants.MIME_STREAM_ITEMIZE_HEADERS;
					}
					if (dataType.contains(WriteMimeDataType.BODY)) {
						dwItemizeFlags |= NotesConstants.MIME_STREAM_ITEMIZE_BODY;
					}
					if (dataType.contains(WriteMimeDataType.NO_DELETE_ATTACHMENTS)) {
						dwItemizeFlags |= NotesConstants.MIME_STREAM_NO_DELETE_ATTACHMENTS;
					}
					
					short wItemNameLen = itemNameMem == null ? 0 : (short)(itemNameMem.size() & 0xffff);
					NotesCAPI.get().MIMEStreamItemize(docHdlByVal, itemNameMem,
							wItemNameLen, dwItemizeFlags, mimeStreamPtr);
					
					return null;
				});
			}
			finally {
				disposeMimeStream(mimeStreamPtr);
			}
		}
	}

	@Override
	public void writeMime(Document doc, String itemName, Message mimeMessage, Set dataType) throws IOException, MessagingException {
		IOException[] ioEx = new IOException[1];
		MessagingException[] msgEx = new MessagingException[1];
		
		writeMime(doc, itemName, (mimeStreamPtr) -> {
			try {
				writeMimeToStream(mimeStreamPtr, mimeMessage);
			} catch (IOException e) {
				ioEx[0] = e;
				return;
			} catch (MessagingException e) {
				msgEx[0] = e;
			}
		}, dataType);
		
		if (ioEx[0] != null) {
			throw ioEx[0];
		}
		if (msgEx[0] != null) {
			throw msgEx[0];
		}
	}

	@Override
	public void writeMime(Document doc, String itemName, InputStream in, Set dataType) throws IOException {
		IOException[] ioEx = new IOException[1];
		
		writeMime(doc, itemName, (mimeStreamPtr) -> {

			byte[] buffer = new byte[60000];
			int len;

			try(DisposableMemory bufferMem = new DisposableMemory(buffer.length)) {
				while ((len=in.read(buffer))>0) {
					bufferMem.write(0, buffer,0, len);

					int resultAsInt = NotesCAPI.get().MIMEStreamWrite(bufferMem, len, mimeStreamPtr);

					if (resultAsInt == NotesConstants.MIME_STREAM_IO) {
						throw new IOException("I/O error received during MIME stream operation");
					}
				}
			}
			catch (IOException e) {
				ioEx[0] = e;
				return;
			}
		}, dataType);

		if (ioEx[0] != null) {
			throw ioEx[0];
		}
	}

	@Override
	public RichTextMimeConversionSettings createRichTextMimeConversionSettings() {
		return new JNARichtextMimeConversionSettings(getParentDominoClient());
	}
	
	@Override
	public void convertToMime(Document doc, RichTextMimeConversionSettings convertSettings) {
		if (!(doc instanceof JNADocument)) {
			throw new IllegalArgumentException("The document object is not of type JNADocument");
		}
		JNADocument jnaDoc = (JNADocument) doc;
		if (jnaDoc.isDisposed()) {
			throw new ObjectDisposedException(jnaDoc);
		}

		JNADocumentAllocations allocations = (JNADocumentAllocations) jnaDoc.getAdapter(APIObjectAllocations.class);

		LockUtil.lockHandle(allocations.getNoteHandle(), (noteHandleByVal) -> {
			short noteFlags;
			
			try(DisposableMemory retFlags = new DisposableMemory(2)) {
				retFlags.clear();

				NotesCAPI.get().NSFNoteGetInfo(noteHandleByVal, NotesConstants._NOTE_FLAGS, retFlags);
				noteFlags = retFlags.getShort(0);
			}
			
			boolean isCanonical = (noteFlags & NotesConstants.NOTE_FLAG_CANONICAL) == NotesConstants.NOTE_FLAG_CANONICAL;
			boolean isMime = doc.hasMIMEPart();

			if (convertSettings!=null) {
				if (!(convertSettings instanceof JNARichtextMimeConversionSettings)) {
					throw new IllegalArgumentException("The settings object is not of type JNARichtextMimeConversionSettings");
				}
				
				JNARichtextMimeConversionSettings jnaConvertSettings = (JNARichtextMimeConversionSettings) convertSettings;
				JNARichtextMimeConversionSettingsAllocations jnaConvertSettingsAllocations = (JNARichtextMimeConversionSettingsAllocations) jnaConvertSettings.getAdapter(APIObjectAllocations.class);
				
				jnaConvertSettingsAllocations.lockAndGetSettingsPointer((settingsPtr) -> {
					short result = NotesCAPI.get().MIMEConvertCDParts(noteHandleByVal, isCanonical, isMime, settingsPtr);
					NotesErrorUtils.checkResult(result);
					return null;
				});
			}
			else {
				short result = NotesCAPI.get().MIMEConvertCDParts(noteHandleByVal, isCanonical, isMime, null);
				NotesErrorUtils.checkResult(result);
			}
			
			return null;
		});
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy