001/**
002 *
003 * Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub.
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.provider;
018
019import java.io.IOException;
020import java.util.Date;
021import java.util.HashSet;
022import java.util.LinkedList;
023import java.util.List;
024import java.util.Set;
025import java.util.logging.Level;
026import java.util.logging.Logger;
027
028import org.jivesoftware.smack.packet.ExtensionElement;
029import org.jivesoftware.smack.packet.XmlEnvironment;
030import org.jivesoftware.smack.parsing.SmackParsingException;
031import org.jivesoftware.smack.provider.ExtensionElementProvider;
032import org.jivesoftware.smack.provider.ProviderManager;
033import org.jivesoftware.smack.util.PacketParserUtils;
034import org.jivesoftware.smack.util.ParserUtils;
035import org.jivesoftware.smack.xml.XmlPullParser;
036import org.jivesoftware.smack.xml.XmlPullParserException;
037
038import org.jivesoftware.smackx.ox.element.CryptElement;
039import org.jivesoftware.smackx.ox.element.EncryptedOpenPgpContentElement;
040import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
041import org.jivesoftware.smackx.ox.element.SignElement;
042import org.jivesoftware.smackx.ox.element.SigncryptElement;
043
044import org.jxmpp.jid.Jid;
045import org.jxmpp.jid.impl.JidCreate;
046
047/**
048 * Abstract {@link ExtensionElementProvider} implementation for the also abstract {@link OpenPgpContentElement}.
049 *
050 * @param <O> Specialized subclass of {@link OpenPgpContentElement}.
051 */
052public abstract class OpenPgpContentElementProvider<O extends OpenPgpContentElement> extends ExtensionElementProvider<O> {
053
054    private static final Logger LOGGER = Logger.getLogger(OpenPgpContentElementProvider.class.getName());
055
056    public static OpenPgpContentElement parseOpenPgpContentElement(String element)
057            throws XmlPullParserException, IOException {
058        XmlPullParser parser = PacketParserUtils.getParserFor(element);
059        return parseOpenPgpContentElement(parser);
060    }
061
062    public static OpenPgpContentElement parseOpenPgpContentElement(XmlPullParser parser)
063            throws  XmlPullParserException {
064        try {
065            switch (parser.getName()) {
066                case SigncryptElement.ELEMENT:
067                    return SigncryptElementProvider.INSTANCE.parse(parser);
068                case SignElement.ELEMENT:
069                    return SignElementProvider.INSTANCE.parse(parser);
070                case CryptElement.ELEMENT:
071                    return CryptElementProvider.INSTANCE.parse(parser);
072                default: throw new XmlPullParserException("Expected <crypt/>, <sign/> or <signcrypt/> element, " +
073                        "but got neither of them.");
074            }
075        } catch (Exception e) {
076            throw new XmlPullParserException(e.getMessage());
077        }
078    }
079
080    @Override
081    public abstract O parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException;
082
083    protected static OpenPgpContentElementData parseOpenPgpContentElementData(XmlPullParser parser, int initialDepth)
084            throws XmlPullParserException, IOException, SmackParsingException {
085        Set<Jid> to = new HashSet<>();
086        Date timestamp = null;
087        String rpad = null;
088        List<ExtensionElement> payload = new LinkedList<>();
089
090        outerloop: while (true) {
091            XmlPullParser.Event tag = parser.next();
092            String name = parser.getName();
093            switch (tag) {
094                case START_ELEMENT:
095                    switch (name) {
096
097                        case OpenPgpContentElement.ELEM_TIME:
098                            String stamp = parser.getAttributeValue("", OpenPgpContentElement.ATTR_STAMP);
099                            timestamp = ParserUtils.getDateFromXep82String(stamp);
100                            break;
101
102                        case OpenPgpContentElement.ELEM_TO:
103                            String jid = parser.getAttributeValue("", OpenPgpContentElement.ATTR_JID);
104                            to.add(JidCreate.bareFrom(jid));
105                            break;
106
107                        case EncryptedOpenPgpContentElement.ELEM_RPAD:
108                            rpad = parser.nextText();
109                            break;
110
111                        case OpenPgpContentElement.ELEM_PAYLOAD:
112                            innerloop: while (true) {
113                                XmlPullParser.Event ptag = parser.next();
114                                String pname = parser.getName();
115                                String pns = parser.getNamespace();
116                                switch (ptag) {
117                                    case START_ELEMENT:
118                                        ExtensionElementProvider<ExtensionElement> provider =
119                                                ProviderManager.getExtensionProvider(pname, pns);
120                                        if (provider == null) {
121                                            LOGGER.log(Level.INFO, "No provider found for " + pname + " " + pns);
122                                            continue innerloop;
123                                        }
124                                        payload.add(provider.parse(parser));
125                                        break;
126
127                                    case END_ELEMENT:
128                                        break innerloop;
129
130                                    default:
131                                        // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
132                                        break;
133                                }
134                            }
135                            break;
136                    }
137                    break;
138
139                case END_ELEMENT:
140                    switch (name) {
141                        case CryptElement.ELEMENT:
142                        case SigncryptElement.ELEMENT:
143                        case SignElement.ELEMENT:
144                            break outerloop;
145                    }
146                    break;
147
148                default:
149                    // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
150                    break;
151            }
152        }
153
154        return new OpenPgpContentElementData(to, timestamp, rpad, payload);
155    }
156
157    protected static final class OpenPgpContentElementData {
158        protected final Set<Jid> to;
159        protected final Date timestamp;
160        protected final String rpad;
161        protected final List<ExtensionElement> payload;
162
163        private OpenPgpContentElementData(Set<Jid> to, Date timestamp, String rpad, List<ExtensionElement> payload) {
164            this.to = to;
165            this.timestamp = timestamp;
166            this.rpad = rpad;
167            this.payload = payload;
168        }
169    }
170}