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