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

org.simplejavamail.utils.mail.dkim.DkimMessage Maven / Gradle / Ivy

/*
 * Copyright © 2021 Benny Bottema ([email protected])
 *
 * 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 org.simplejavamail.utils.mail.dkim;

import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeUtility;
import net.markenwerk.utils.data.fetcher.BufferedDataFetcher;
import org.eclipse.angus.mail.smtp.SMTPMessage;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * Extension of {@link SMTPMessage} for the inclusion of a DKIM signature.
 * 
 * @author Torsten Krause (tk at markenwerk dot net)
 * @author Florian Sager
 * @since 1.0.0
 */
public class DkimMessage extends SMTPMessage {

	private static byte[] NL = { (byte) '\r', (byte) '\n' };

	private DkimSigner signer;

	private String encodedBody;

	/**
	 * Created a new {@code DkimMessage} from the given {@link MimeMessage} and
	 * {@link DkimSigner}.
	 * 
	 * @param message
	 *            The {@link MimeMessage} to be signed.
	 * @param signer
	 *            The {@link DkimSigner} to sign the message with.
	 * @throws MessagingException
	 *             If constructing this {@code DkimMessage} failed.
	 */
	public DkimMessage(MimeMessage message, DkimSigner signer) throws MessagingException {
		super(message);
		this.signer = signer;
	}

	/**
	 * Output the message as an RFC 822 format stream, without specified
	 * headers. If the saved flag is not set, the
	 * saveChanges method is called. If the modified
	 * flag is not set and the content array is not null, the
	 * content array is written directly, after writing the
	 * appropriate message headers.
	 *
	 * This method enhances the JavaMail method
	 * {@link MimeMessage#writeTo(OutputStream, String[])} See the according Sun
	 * license, this contribution is CDDL.
	 * 
	 * @exception MessagingException
	 *                If an error occurs while preparing this message for
	 *                writing.
	 * @exception IOException
	 *                If an error occurs while writing to the stream or if an
	 *                error is generated by the javax.activation layer.
	 */
	@Override
	public void writeTo(OutputStream os, String[] ignoreList) throws IOException, MessagingException {

		// inside saveChanges it is assured that content encodings are set in
		// all parts of the body
		if (!saved) {
			saveChanges();
		}

		ByteArrayOutputStream bodyBuffer = new ByteArrayOutputStream();
		if (modified) {
			// write out the body from the dataHandler through the
			// encodingOutputStream into the bodyBuffer
			OutputStream encodingOutputStream = MimeUtility.encode(bodyBuffer, getEncoding());
			getDataHandler().writeTo(encodingOutputStream);
			encodingOutputStream.flush();
			encodingOutputStream.close();
		} else if (null == content) {
			// write the provided contentStream into the bodyBuffer
			new BufferedDataFetcher().copy(getContentStream(), bodyBuffer, true, false);
			bodyBuffer.flush();
			bodyBuffer.close();
		} else {
			// just write the readily available content into the bodyBuffer
			bodyBuffer.write(content);
			bodyBuffer.flush();
			bodyBuffer.close();
		}

		encodedBody = bodyBuffer.toString(UTF_8.name());

		// second, sign the message
		String signatureHeaderLine = signer.sign(this);

		// write the 'DKIM-Signature' header, all other headers and a clear \r\n
		writeln(os, signatureHeaderLine);
		Enumeration headerLines = getNonMatchingHeaderLines(ignoreList);
		while (headerLines.hasMoreElements()) {
			writeln(os, headerLines.nextElement());
		}
		writeln(os);
		os.flush();

		// write the message body
		os.write(bodyBuffer.toByteArray());
		os.flush();
	}

	/**
	 * Returns the encoded body.
	 * 
	 * @return The encoded body.
	 */
	protected String getEncodedBody() {
		return encodedBody;
	}

	@Override
	public void setAllow8bitMIME(boolean allow) {
		// don't allow to switch to 8-bit MIME, instead 7-bit ASCII should be
		// kept because in forwarding scenarios a change to
		// Content-Transfer-Encoding to 7-bit ASCII breaks the DKIM signature
		super.setAllow8bitMIME(false);
	}

	private static void writeln(OutputStream out) throws IOException {
		out.write(NL);
	}

	private static void writeln(OutputStream out, String string) throws IOException {
		byte[] bytes = getBytes(string);
		out.write(bytes);
		out.write(NL);
	}

	private static byte[] getBytes(String string) {
		char[] chars = string.toCharArray();
		byte[] bytes = new byte[chars.length];

		for (int i = 0, n = chars.length; i < n; i++) {
			bytes[i] = (byte) chars[i];
		}

		return bytes;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy