001/* 002 * 003 * Copyright 2017-2025 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.JxmppContext; 046import org.jxmpp.jid.Jid; 047import org.jxmpp.jid.impl.JidCreate; 048 049/** 050 * Abstract {@link ExtensionElementProvider} implementation for the also abstract {@link OpenPgpContentElement}. 051 * 052 * @param <O> Specialized subclass of {@link OpenPgpContentElement}. 053 */ 054public abstract class OpenPgpContentElementProvider<O extends OpenPgpContentElement> extends ExtensionElementProvider<O> { 055 056 private static final Logger LOGGER = Logger.getLogger(OpenPgpContentElementProvider.class.getName()); 057 058 public static OpenPgpContentElement parseOpenPgpContentElement(String element) 059 throws XmlPullParserException, IOException { 060 XmlPullParser parser = PacketParserUtils.getParserFor(element); 061 return parseOpenPgpContentElement(parser); 062 } 063 064 public static OpenPgpContentElement parseOpenPgpContentElement(XmlPullParser parser) 065 throws XmlPullParserException { 066 try { 067 switch (parser.getName()) { 068 case SigncryptElement.ELEMENT: 069 return SigncryptElementProvider.INSTANCE.parse(parser); 070 case SignElement.ELEMENT: 071 return SignElementProvider.INSTANCE.parse(parser); 072 case CryptElement.ELEMENT: 073 return CryptElementProvider.INSTANCE.parse(parser); 074 default: throw new XmlPullParserException("Expected <crypt/>, <sign/> or <signcrypt/> element, " + 075 "but got neither of them."); 076 } 077 } catch (Exception e) { 078 throw new XmlPullParserException(e.getMessage()); 079 } 080 } 081 082 @Override 083 public abstract O parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment, JxmppContext jxmppContext) 084 throws XmlPullParserException, IOException, SmackParsingException, ParseException; 085 086 protected static OpenPgpContentElementData parseOpenPgpContentElementData(XmlPullParser parser, int initialDepth) 087 throws XmlPullParserException, IOException, SmackParsingException, ParseException { 088 Set<Jid> to = new HashSet<>(); 089 Date timestamp = null; 090 String rpad = null; 091 List<ExtensionElement> payload = new ArrayList<>(); 092 093 outerloop: while (true) { 094 XmlPullParser.Event tag = parser.next(); 095 String name = parser.getName(); 096 switch (tag) { 097 case START_ELEMENT: 098 switch (name) { 099 100 case OpenPgpContentElement.ELEM_TIME: 101 String stamp = parser.getAttributeValue("", OpenPgpContentElement.ATTR_STAMP); 102 timestamp = ParserUtils.getDateFromXep82String(stamp); 103 break; 104 105 case OpenPgpContentElement.ELEM_TO: 106 String jid = parser.getAttributeValue("", OpenPgpContentElement.ATTR_JID); 107 to.add(JidCreate.bareFrom(jid)); 108 break; 109 110 case EncryptedOpenPgpContentElement.ELEM_RPAD: 111 rpad = parser.nextText(); 112 break; 113 114 case OpenPgpContentElement.ELEM_PAYLOAD: 115 innerloop: while (true) { 116 XmlPullParser.Event ptag = parser.next(); 117 String pname = parser.getName(); 118 String pns = parser.getNamespace(); 119 switch (ptag) { 120 case START_ELEMENT: 121 var provider = 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}