001/**
002 *
003 * Copyright 2017 Florian Schmaus.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.ox;
018
019import java.io.IOException;
020import java.nio.charset.Charset;
021
022import org.jivesoftware.smack.util.Objects;
023import org.jivesoftware.smack.xml.XmlPullParserException;
024
025import org.jivesoftware.smackx.ox.element.CryptElement;
026import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
027import org.jivesoftware.smackx.ox.element.OpenPgpElement;
028import org.jivesoftware.smackx.ox.element.SignElement;
029import org.jivesoftware.smackx.ox.element.SigncryptElement;
030import org.jivesoftware.smackx.ox.provider.OpenPgpContentElementProvider;
031
032import org.pgpainless.decryption_verification.OpenPgpMetadata;
033
034/**
035 * This class embodies a decrypted and/or verified {@link OpenPgpElement}.
036 * <br>
037 * The content can be one of the following three {@link OpenPgpContentElement}s:
038 * <br><br>
039 * {@link SignElement}: The content is expected to be signed with the senders key, but unencrypted.<br>
040 * {@link CryptElement}: The content is expected to be encrypted, but not signed.<br>
041 * {@link SigncryptElement}: The content is expected to be signed with the senders key and encrypted.<br>
042 * <br>
043 * To determine, of which nature the content of the message is, use {@link #getState()}. You should utilize this
044 * information to cast the return value of {@link #getOpenPgpContentElement()} correctly.
045 * <br>
046 * Use {@link #getMetadata()} in order to get information about the messages encryption status, its signatures etc.
047 */
048public class OpenPgpMessage {
049
050    public enum State {
051        /**
052         * Represents a {@link SigncryptElement}.
053         */
054        signcrypt,
055        /**
056         * Represents a {@link SignElement}.
057         */
058        sign,
059        /**
060         * Represents a {@link CryptElement}.
061         */
062        crypt,
063    }
064
065    private final String element;
066    private final State state;
067    private final OpenPgpMetadata metadata;
068
069    private OpenPgpContentElement openPgpContentElement;
070
071    /**
072     * Constructor.
073     *
074     * @param content XML representation of the decrypted {@link OpenPgpContentElement}.
075     * @param state {@link State} of the {@link OpenPgpContentElement}.
076     * @param metadata Metadata about the encryption.
077     */
078    public OpenPgpMessage(String content, State state, OpenPgpMetadata metadata) {
079        this.metadata = Objects.requireNonNull(metadata);
080        this.state = Objects.requireNonNull(state);
081        this.element = Objects.requireNonNull(content);
082    }
083
084    /**
085     * Constructor.
086     *
087     * @param bytes bytes of the XML representation of the decrypted {@link OpenPgpContentElement}.
088     * @param state {@link State} of the {@link OpenPgpContentElement}.
089     * @param metadata metadata about the encryption.
090     */
091    public OpenPgpMessage(byte[] bytes, State state, OpenPgpMetadata metadata) {
092        this(new String(Objects.requireNonNull(bytes), Charset.forName("UTF-8")), state, metadata);
093    }
094
095    /**
096     * Return the decrypted {@link OpenPgpContentElement} of this message.
097     * To determine, whether the element is a {@link SignElement}, {@link CryptElement} or {@link SigncryptElement},
098     * please consult {@link #getState()}.
099     *
100     * @return {@link OpenPgpContentElement}
101     * @throws XmlPullParserException if the parser encounters an error.
102     * @throws IOException if the parser encounters an error.
103     */
104    public OpenPgpContentElement getOpenPgpContentElement() throws XmlPullParserException, IOException {
105        ensureOpenPgpContentElementSet();
106
107        return openPgpContentElement;
108    }
109
110    private void ensureOpenPgpContentElementSet() throws XmlPullParserException, IOException {
111        if (openPgpContentElement != null)
112            return;
113
114        openPgpContentElement = OpenPgpContentElementProvider.parseOpenPgpContentElement(element);
115        if (openPgpContentElement == null) {
116            return;
117        }
118
119        // Determine the state of the content element.
120        if (openPgpContentElement instanceof SigncryptElement) {
121            if (state != State.signcrypt) {
122                throw new IllegalStateException("OpenPgpContentElement was signed and encrypted, but is not a SigncryptElement.");
123            }
124        } else if (openPgpContentElement instanceof SignElement) {
125            if (state != State.sign) {
126                throw new IllegalStateException("OpenPgpContentElement was signed and unencrypted, but is not a SignElement.");
127            }
128        } else if (openPgpContentElement instanceof CryptElement) {
129            if (state != State.crypt) {
130                throw new IllegalStateException("OpenPgpContentElement was unsigned and encrypted, but is not a CryptElement.");
131            }
132        }
133    }
134
135    /**
136     * Return the state of the message. This value determines, whether the message was a {@link SignElement},
137     * {@link CryptElement} or {@link SigncryptElement}.
138     *
139     * @return state of the content element.
140     * @throws IOException if the parser encounters an error.
141     * @throws XmlPullParserException if the parser encounters and error.
142     */
143    public State getState() throws IOException, XmlPullParserException {
144        ensureOpenPgpContentElementSet();
145        return state;
146    }
147
148    /**
149     * Return metadata about the encrypted message.
150     *
151     * @return metadata TODO javadoc me please
152     */
153    public OpenPgpMetadata getMetadata() {
154        return metadata;
155    }
156}