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.smackx.ox.element.CryptElement;
024import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
025import org.jivesoftware.smackx.ox.element.OpenPgpElement;
026import org.jivesoftware.smackx.ox.element.SignElement;
027import org.jivesoftware.smackx.ox.element.SigncryptElement;
028import org.jivesoftware.smackx.ox.provider.OpenPgpContentElementProvider;
029
030import org.pgpainless.decryption_verification.OpenPgpMetadata;
031import org.xmlpull.v1.XmlPullParserException;
032
033/**
034 * This class embodies a decrypted {@link OpenPgpElement}.
035 */
036public class OpenPgpMessage {
037
038    public enum State {
039        /**
040         * Represents a {@link SigncryptElement}.
041         */
042        signcrypt,
043        /**
044         * Represents a {@link SignElement}.
045         */
046        sign,
047        /**
048         * Represents a {@link CryptElement}.
049         */
050        crypt,
051        ;
052    }
053
054    private final String element;
055    private final State state;
056    private final OpenPgpMetadata metadata;
057
058    private OpenPgpContentElement openPgpContentElement;
059
060    /**
061     * Constructor.
062     *
063     * @param content XML representation of the decrypted {@link OpenPgpContentElement}.
064     * @param state {@link State} of the {@link OpenPgpContentElement}.
065     * @param metadata Metadata about the encryption.
066     */
067    public OpenPgpMessage(String content, State state, OpenPgpMetadata metadata) {
068        this.metadata = Objects.requireNonNull(metadata);
069        this.state = Objects.requireNonNull(state);
070        this.element = Objects.requireNonNull(content);
071    }
072
073    /**
074     * Constructor.
075     *
076     * @param bytes bytes of the XML representation of the decrypted {@link OpenPgpContentElement}.
077     * @param state {@link State} of the {@link OpenPgpContentElement}.
078     * @param metadata metadata about the encryption.
079     */
080    public OpenPgpMessage(byte[] bytes, State state, OpenPgpMetadata metadata) {
081        this(new String(Objects.requireNonNull(bytes), Charset.forName("UTF-8")), state, metadata);
082    }
083
084    /**
085     * Return the decrypted {@link OpenPgpContentElement} of this message.
086     * To determine, whether the element is a {@link SignElement}, {@link CryptElement} or {@link SigncryptElement},
087     * please consult {@link #getState()}.
088     *
089     * @return {@link OpenPgpContentElement}
090     * @throws XmlPullParserException if the parser encounters an error.
091     * @throws IOException if the parser encounters an error.
092     */
093    public OpenPgpContentElement getOpenPgpContentElement() throws XmlPullParserException, IOException {
094        ensureOpenPgpContentElementSet();
095
096        return openPgpContentElement;
097    }
098
099    private void ensureOpenPgpContentElementSet() throws XmlPullParserException, IOException {
100        if (openPgpContentElement != null)
101            return;
102
103        openPgpContentElement = OpenPgpContentElementProvider.parseOpenPgpContentElement(element);
104        if (openPgpContentElement == null) {
105            return;
106        }
107
108        // Determine the state of the content element.
109        if (openPgpContentElement instanceof SigncryptElement) {
110            if (state != State.signcrypt) {
111                throw new IllegalStateException("OpenPgpContentElement was signed and encrypted, but is not a SigncryptElement.");
112            }
113        } else if (openPgpContentElement instanceof SignElement) {
114            if (state != State.sign) {
115                throw new IllegalStateException("OpenPgpContentElement was signed and unencrypted, but is not a SignElement.");
116            }
117        } else if (openPgpContentElement instanceof CryptElement) {
118            if (state != State.crypt) {
119                throw new IllegalStateException("OpenPgpContentElement was unsigned and encrypted, but is not a CryptElement.");
120            }
121        }
122    }
123
124    /**
125     * Return the state of the message. This value determines, whether the message was a {@link SignElement},
126     * {@link CryptElement} or {@link SigncryptElement}.
127     *
128     * @return state of the content element.
129     * @throws IOException if the parser encounters an error.
130     * @throws XmlPullParserException if the parser encounters and error.
131     */
132    public State getState() throws IOException, XmlPullParserException {
133        ensureOpenPgpContentElementSet();
134        return state;
135    }
136
137    /**
138     * Return metadata about the encrypted message.
139     *
140     * @return metadata
141     */
142    public OpenPgpMetadata getMetadata() {
143        return metadata;
144    }
145}