001/*
002 * acme4j - Java ACME client
003 *
004 * Copyright (C) 2022 Richard "Shred" Körber
005 *   http://acme4j.shredzone.org
006 *
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
013 */
014package org.shredzone.acme4j.smime.exception;
015
016import static java.util.Collections.unmodifiableList;
017
018import java.util.ArrayList;
019import java.util.Collections;
020import java.util.List;
021import java.util.Optional;
022
023import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
024import org.bouncycastle.i18n.ErrorBundle;
025import org.bouncycastle.i18n.LocalizedException;
026import org.shredzone.acme4j.exception.AcmeException;
027
028/**
029 * This exception is thrown when the challenge email message is invalid.
030 * <p>
031 * If this exception is thrown, the challenge message does not match the actual challenge
032 * or has other issues. It <em>must</em> be rejected.
033 * <p>
034 * Reasons may be (for example):
035 * <ul>
036 *     <li>Unexpected sender address</li>
037 *     <li>Bad S/MIME signature</li>
038 * </ul>
039 *
040 * @since 2.15
041 */
042public class AcmeInvalidMessageException extends AcmeException {
043    private static final long serialVersionUID = 5607857024718309330L;
044
045    private final List<ErrorBundle> errors;
046
047    /**
048     * Creates a new {@link AcmeInvalidMessageException}.
049     *
050     * @param msg
051     *         Reason of the exception
052     */
053    public AcmeInvalidMessageException(String msg) {
054        super(msg);
055        this.errors = Collections.emptyList();
056    }
057
058    /**
059     * Creates a new {@link AcmeInvalidMessageException}.
060     *
061     * @param msg
062     *         Reason of the exception
063     * @param errors
064     *         List of {@link ErrorBundle} with further details
065     * @since 2.16
066     */
067    public AcmeInvalidMessageException(String msg, List<ErrorBundle> errors) {
068        super(msg);
069        this.errors = unmodifiableList(errors);
070    }
071
072    /**
073     * Creates a new {@link AcmeInvalidMessageException}.
074     *
075     * @param msg
076     *         Reason of the exception
077     * @param cause
078     *         Cause
079     */
080    public AcmeInvalidMessageException(String msg, Throwable cause) {
081        super(msg, cause);
082        var errors = new ArrayList<ErrorBundle>(1);
083        Optional.ofNullable(cause)
084                .filter(LocalizedException.class::isInstance)
085                .map(LocalizedException.class::cast)
086                .map(LocalizedException::getErrorMessage)
087                .ifPresent(errors::add);
088        this.errors = unmodifiableList(errors);
089    }
090
091    /**
092     * Returns a list with further error details, if available. The list may be empty, but
093     * is never {@code null}.
094     *
095     * @since 2.16
096     */
097    @SuppressFBWarnings("EI_EXPOSE_REP")   // errors is always an unmodifiable list
098    public List<ErrorBundle> getErrors() {
099        return errors;
100    }
101
102}