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

org.shredzone.acme4j.smime.email.ResponseGenerator Maven / Gradle / Ivy

The newest version!
/*
 * acme4j - Java ACME client
 *
 * Copyright (C) 2021 Richard "Shred" Körber
 *   http://acme4j.shredzone.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
package org.shredzone.acme4j.smime.email;

import static java.util.Objects.requireNonNull;
import static jakarta.mail.Message.RecipientType.TO;
import static org.shredzone.acme4j.smime.email.ResponseBodyGenerator.RESPONSE_BODY_TYPE;

import java.util.Properties;

import edu.umd.cs.findbugs.annotations.Nullable;
import jakarta.mail.Address;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.Session;
import jakarta.mail.internet.MimeMessage;

/**
 * A helper for creating an email response to the "challenge" email.
 * 

* According to RFC-8823, the response email must be DKIM signed. This is * not done by the response generator, but must be done by the outbound MTA. * * @see RFC 8823 * @since 2.12 */ public class ResponseGenerator { private static final int LINE_LENGTH = 72; private static final String CRLF = "\r\n"; private final EmailProcessor processor; private ResponseBodyGenerator generator = this::defaultBodyGenerator; private @Nullable String header; private @Nullable String footer; /** * Creates a new {@link ResponseGenerator}. * * @param processor * {@link EmailProcessor} of the challenge email. */ public ResponseGenerator(EmailProcessor processor) { this.processor = requireNonNull(processor, "processor"); } /** * Adds a custom header to the response mail body. *

* There is no need to set a header, since the response email is usually not read by * humans. If a header is set, it must contain ASCII encoded plain text. * * @param header * Header text to be used, or {@code null} if no header is to be used. * @return itself */ public ResponseGenerator withHeader(@Nullable String header) { if (header != null && !header.endsWith(CRLF)) { this.header = header.concat(CRLF); } else { this.header = header; } return this; } /** * Adds a custom footer to the response mail body. *

* There is no need to set a footer, since the response email is usually not read by * humans. If a footer is set, it must contain ASCII encoded plain text. * * @param footer * Footer text to be used, or {@code null} if no footer is to be used. * @return itself */ public ResponseGenerator withFooter(@Nullable String footer) { this.footer = footer; return this; } /** * Sets a {@link ResponseBodyGenerator} that is used for generating a response body. *

* Use this generator to individually style the email body, for example to use a * multipart body. However, be aware that the response mail is evaluated by a machine, * and usually not read by humans, so the body should be designed as simple as * possible. *

* The default body generator will just concatenate the header, the armored key * authorization body, and the footer. * * @param generator * {@link ResponseBodyGenerator} to be used, or {@code null} to use the * default one. * @return itself */ public ResponseGenerator withGenerator(@Nullable ResponseBodyGenerator generator) { this.generator = generator != null ? generator : this::defaultBodyGenerator; return this; } /** * Generates the response email. *

* A simple default mail session is used for generation. * * @return Generated {@link Message}. * @since 2.16 */ public Message generateResponse() throws MessagingException { return generateResponse(Session.getDefaultInstance(new Properties())); } /** * Generates the response email. *

* Note that according to RFC-8823, this message must have a valid DKIM or S/MIME * signature. This is not done here, but usually performed by the outbound * MTA. * * @param session * {@code javax.mail} {@link Session} to be used for this mail. * @return Generated {@link Message}. */ public Message generateResponse(Session session) throws MessagingException { var response = new MimeMessage(requireNonNull(session, "session")); response.setSubject("Re: ACME: " + processor.getToken1()); response.setFrom(processor.getRecipient()); if (!processor.getReplyTo().isEmpty()) { for (var rto : processor.getReplyTo()) { response.addRecipient(TO, rto); } } else { response.addRecipients(TO, new Address[] {processor.getSender()}); } if (processor.getMessageId().isPresent()) { response.setHeader("In-Reply-To", processor.getMessageId().get()); } var wrappedAuth = processor.getAuthorization() .replaceAll("(.{" + LINE_LENGTH + "})", "$1" + CRLF); var responseBody = new StringBuilder(); responseBody.append("-----BEGIN ACME RESPONSE-----").append(CRLF); responseBody.append(wrappedAuth); if (!wrappedAuth.endsWith(CRLF)) { responseBody.append(CRLF); } responseBody.append("-----END ACME RESPONSE-----").append(CRLF); generator.setContent(response, responseBody.toString()); return response; } /** * The default body generator. It just sets the response body, optionally framed by * the given header and footer. * * @param response * response {@link Message} to fill. * @param responseBody * Response body that must be added to the message. */ private void defaultBodyGenerator(Message response, String responseBody) throws MessagingException { var body = new StringBuilder(); if (header != null) { body.append(header); } body.append(responseBody); if (footer != null) { body.append(footer); } response.setContent(body.toString(), RESPONSE_BODY_TYPE); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy