001/**
002 *
003 * Copyright 2017 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 static org.xmlpull.v1.XmlPullParser.END_TAG;
020import static org.xmlpull.v1.XmlPullParser.START_TAG;
021
022import java.io.IOException;
023import java.util.Date;
024import java.util.HashSet;
025import java.util.LinkedList;
026import java.util.List;
027import java.util.Set;
028import java.util.logging.Level;
029import java.util.logging.Logger;
030
031import org.jivesoftware.smack.packet.ExtensionElement;
032import org.jivesoftware.smack.provider.ExtensionElementProvider;
033import org.jivesoftware.smack.provider.ProviderManager;
034import org.jivesoftware.smack.util.PacketParserUtils;
035import org.jivesoftware.smackx.ox.element.CryptElement;
036import org.jivesoftware.smackx.ox.element.EncryptedOpenPgpContentElement;
037import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
038import org.jivesoftware.smackx.ox.element.SignElement;
039import org.jivesoftware.smackx.ox.element.SigncryptElement;
040
041import org.jxmpp.jid.Jid;
042import org.jxmpp.jid.impl.JidCreate;
043import org.jxmpp.util.XmppDateTime;
044import org.xmlpull.v1.XmlPullParser;
045import org.xmlpull.v1.XmlPullParserException;
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) throws Exception;
082
083    protected static OpenPgpContentElementData parseOpenPgpContentElementData(XmlPullParser parser, int initialDepth)
084            throws Exception {
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            int tag = parser.next();
092            String name = parser.getName();
093            switch (tag) {
094                case START_TAG:
095                    switch (name) {
096
097                        case OpenPgpContentElement.ELEM_TIME:
098                            String stamp = parser.getAttributeValue("", OpenPgpContentElement.ATTR_STAMP);
099                            timestamp = XmppDateTime.parseDate(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                                int ptag = parser.next();
114                                String pname = parser.getName();
115                                String pns = parser.getNamespace();
116                                switch (ptag) {
117
118                                    case START_TAG:
119                                        ExtensionElementProvider<ExtensionElement> provider =
120                                                ProviderManager.getExtensionProvider(pname, pns);
121                                        if (provider == null) {
122                                            LOGGER.log(Level.INFO, "No provider found for " + pname + " " + pns);
123                                            continue innerloop;
124                                        }
125                                        payload.add(provider.parse(parser));
126                                        break;
127
128                                    case END_TAG:
129                                        break innerloop;
130                                }
131                            }
132                            break;
133                    }
134                    break;
135
136                case END_TAG:
137                    switch (name) {
138                        case CryptElement.ELEMENT:
139                        case SigncryptElement.ELEMENT:
140                        case SignElement.ELEMENT:
141                            break outerloop;
142                    }
143                    break;
144            }
145        }
146
147        return new OpenPgpContentElementData(to, timestamp, rpad, payload);
148    }
149
150    protected static final class OpenPgpContentElementData {
151        protected final Set<Jid> to;
152        protected final Date timestamp;
153        protected final String rpad;
154        protected final List<ExtensionElement> payload;
155
156        private OpenPgpContentElementData(Set<Jid> to, Date timestamp, String rpad, List<ExtensionElement> payload) {
157            this.to = to;
158            this.timestamp = timestamp;
159            this.rpad = rpad;
160            this.payload = payload;
161        }
162    }
163}