com.hcl.domino.jna.mime.JNAMimeReader Maven / Gradle / Ivy
/*
* ==========================================================================
* 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.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.util.Properties;
import java.util.Set;
import com.hcl.domino.DominoException;
import com.hcl.domino.commons.gc.IAPIObject;
import com.hcl.domino.data.Database;
import com.hcl.domino.data.Document;
import com.hcl.domino.exception.IncompatibleImplementationException;
import com.hcl.domino.exception.MimePartNotFoundException;
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.mime.MimeReader;
import com.hcl.domino.misc.NotesConstants;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import jakarta.mail.internet.MimeMessage;
public class JNAMimeReader extends JNAMimeBase implements MimeReader {
public JNAMimeReader(IAPIObject> parent) {
super(parent);
}
private int toOpenFlagsAsInt(Set dataType) {
int dwOpenFlags = NotesConstants.MIME_STREAM_OPEN_READ;
if (dataType.contains(ReadMimeDataType.MIMEHEADERS)) {
dwOpenFlags |= NotesConstants.MIME_STREAM_MIME_INCLUDE_HEADERS;
}
if (dataType.contains(ReadMimeDataType.RFC822HEADERS)) {
dwOpenFlags |= NotesConstants.MIME_STREAM_RFC2822_INCLUDE_HEADERS;
}
return dwOpenFlags;
}
@Override
public void readMIME(Document doc, String itemName, Set dataType, OutputStream targetOut) throws IOException {
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));
}
Memory itemNameMem = NotesStringUtils.toLMBCS(itemName, false);
int dwOpenFlags = toOpenFlagsAsInt(dataType);
Pointer mimeStreamPtr = createMimeStream(jnaDoc, itemNameMem, dwOpenFlags);
try {
int MAX_BUFFER_SIZE = 60000;
try(DisposableMemory pchData = new DisposableMemory(MAX_BUFFER_SIZE)) {
IntByReference puiDataLen = new IntByReference();
while (true) {
int resultAsInt = NotesCAPI.get().MIMEStreamRead(pchData,
puiDataLen, MAX_BUFFER_SIZE, mimeStreamPtr);
if (resultAsInt == NotesConstants.MIME_STREAM_IO) {
throw new DominoException("I/O error reading MIMEStream");
}
int len = puiDataLen.getValue();
if (len > 0) {
byte[] data = pchData.getByteArray(0, len);
targetOut.write(data);
}
else {
break;
}
if (resultAsInt == NotesConstants.MIME_STREAM_EOS) {
break;
}
}
}
}
finally {
disposeMimeStream(mimeStreamPtr);
}
}
@Override
public MimeMessage readMIME(Document doc, String itemName, 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));
}
final Exception[] ex = new Exception[1];
MimeMessage msg = AccessController.doPrivileged((PrivilegedAction) () -> {
Path tmpFile = null;
try {
//use a temp file to not store the MIME content twice in memory (raw + parsed)
tmpFile = Files.createTempFile("dominojna_mime_", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tmpFile.toFile()))) {
readMIME(jnaDoc, itemName, dataType, out);
}
try (InputStream fIn = Files.newInputStream(tmpFile);
BufferedInputStream bufIn = new BufferedInputStream(fIn)) {
Properties props = System.getProperties();
jakarta.mail.Session mailSession = jakarta.mail.Session.getInstance(props, null);
MimeMessage message = new MimeMessage(mailSession, bufIn);
return message;
}
}
catch (Exception e1) {
ex[0] = e1;
return null;
}
finally {
if(tmpFile != null) {
try {
Files.deleteIfExists(tmpFile);
} catch (IOException e2) {
ex[0] = e2;
}
}
}
});
if (ex[0] != null) {
int errId = 0;
if(ex[0] instanceof MimePartNotFoundException) {
throw (MimePartNotFoundException)ex[0];
} else if (ex[0] instanceof DominoException) {
errId = ((DominoException)ex[0]).getId();
}
Database parentDb = doc.getParentDatabase();
throw new DominoException(errId, MessageFormat.format(
"Error parsing the MIME content of document with UNID {0} and item name {1} in database {2}!!{3}",
doc.getUNID(), itemName, parentDb.getServer(), parentDb.getRelativeFilePath()), ex[0]);
}
return msg;
}
@Override
public InputStream readMIMEAsStream(Document doc, String itemName, Set dataType) {
MIMEInputStream in = new MIMEInputStream(doc, itemName, dataType, 2000);
getAllocations().registerStream(in);
return in;
}
/**
* {@link Reader} adapter for the MIME stream
*
* @author Karsten Lehmann
*/
private class MIMEInputStream extends InputStream {
private Document m_doc;
private String m_itemName;
private Set m_dataType;
private Pointer m_mimeStreamPtr;
private boolean m_closed;
private byte[] m_buffer;
private int m_bufferPos;
private int m_leftInBuffer;
public MIMEInputStream(Document doc, String itemName, Set dataType,
int bufSize) {
m_doc = doc;
m_itemName = itemName;
m_dataType = dataType;
if (bufSize<=0) {
throw new IllegalArgumentException(MessageFormat.format("Buffer size must be greater than 0: {0}", bufSize));
}
m_buffer = new byte[bufSize];
}
private void init() {
if (m_mimeStreamPtr!=null) {
return;
}
if (!(m_doc instanceof JNADocument)) {
throw new IncompatibleImplementationException(m_doc, JNADocument.class);
}
JNADocument jnaDoc = (JNADocument) m_doc;
if (jnaDoc.isDisposed()) {
throw new ObjectDisposedException(jnaDoc);
}
if ("$file".equalsIgnoreCase(m_itemName)) { //$NON-NLS-1$
throw new IllegalArgumentException(MessageFormat.format("Invalid item name: {0}", m_itemName));
}
Memory itemNameMem = NotesStringUtils.toLMBCS(m_itemName, false);
int dwOpenFlags = toOpenFlagsAsInt(m_dataType);
m_mimeStreamPtr = createMimeStream(jnaDoc, itemNameMem, dwOpenFlags);
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
@Override
public int read() throws IOException {
if (m_leftInBuffer == 0) {
//end reached, read more data
int read = readInto(m_buffer);
if (read==-1 || read==0) {
return -1;
}
m_leftInBuffer = read;
m_bufferPos = 0;
}
byte b = m_buffer[m_bufferPos++];
m_leftInBuffer--;
return (int) (b & 0xff);
}
/**
* This function copies the MIME stream content into a {@link Writer}.
*
* @param buffer buffer to receive the MIME stream data
* @return number of bytes read or -1 for EOF
* @throws IOException in case of MIME stream I/O errors
*/
public int readInto(byte[] buffer) throws IOException {
checkClosed();
init();
int maxBufferSize = buffer.length;
try(DisposableMemory pchData = new DisposableMemory(maxBufferSize)) {
IntByReference puiDataLen = new IntByReference();
puiDataLen.setValue(0);
int resultAsInt = NotesCAPI.get().MIMEStreamRead(pchData,
puiDataLen, m_buffer.length, m_mimeStreamPtr);
if (resultAsInt == NotesConstants.MIME_STREAM_IO) {
throw new DominoException("I/O error reading MIMEStream");
}
int len = puiDataLen.getValue();
if (len > 0) {
pchData.read(0, buffer, 0, len);
return len;
}
else {
return -1;
}
}
}
private void checkClosed() {
if (m_closed) {
throw new IllegalStateException("Reader is already closed");
}
}
@Override
public synchronized void close() throws IOException {
//reader is automatically closed when the DominoClient gets closed
if (m_closed || m_mimeStreamPtr==null) {
return;
}
disposeMimeStream(m_mimeStreamPtr);
m_mimeStreamPtr = null;
getAllocations().unregisterStream(this);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy