mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-09-10 18:59:41 +02:00
Move Packet 'properties' from core to extensions
Code for Jive's packet properties should not be in 'core'. Also de-serializing input from network causes some security implications, it is therefore disabled as default.
This commit is contained in:
parent
c2b214f8d8
commit
6e08a10186
11 changed files with 516 additions and 281 deletions
|
@ -20,29 +20,21 @@ package org.jivesoftware.smack.packet;
|
|||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Base class for XMPP packets. Every packet has a unique ID (which is automatically
|
||||
* generated, but can be overriden). Optionally, the "to" and "from" fields can be set,
|
||||
* as well as an arbitrary number of properties.
|
||||
*
|
||||
* Properties provide an easy mechanism for clients to share data. Each property has a
|
||||
* String name, and a value that is a Java primitive (int, long, float, double, boolean)
|
||||
* or any Serializable object (a Java object is Serializable when it implements the
|
||||
* Serializable interface).
|
||||
* generated, but can be overridden). Optionally, the "to" and "from" fields can be set.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public abstract class Packet {
|
||||
private static final Logger LOGGER = Logger.getLogger(Packet.class.getName());
|
||||
|
||||
|
||||
protected static final String DEFAULT_LANGUAGE =
|
||||
java.util.Locale.getDefault().getLanguage().toLowerCase(Locale.US);
|
||||
|
||||
|
@ -88,7 +80,6 @@ public abstract class Packet {
|
|||
private final List<PacketExtension> packetExtensions
|
||||
= new CopyOnWriteArrayList<PacketExtension>();
|
||||
|
||||
private final Map<String,Object> properties = new HashMap<String, Object>();
|
||||
private XMPPError error = null;
|
||||
|
||||
public Packet() {
|
||||
|
@ -292,60 +283,6 @@ public abstract class Packet {
|
|||
packetExtensions.remove(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packet property with the specified name or <tt>null</tt> if the
|
||||
* property doesn't exist. Property values that were originally primitives will
|
||||
* be returned as their object equivalent. For example, an int property will be
|
||||
* returned as an Integer, a double as a Double, etc.
|
||||
*
|
||||
* @param name the name of the property.
|
||||
* @return the property, or <tt>null</tt> if the property doesn't exist.
|
||||
*/
|
||||
public synchronized Object getProperty(String name) {
|
||||
if (properties == null) {
|
||||
return null;
|
||||
}
|
||||
return properties.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a property with an Object as the value. The value must be Serializable
|
||||
* or an IllegalArgumentException will be thrown.
|
||||
*
|
||||
* @param name the name of the property.
|
||||
* @param value the value of the property.
|
||||
*/
|
||||
public synchronized void setProperty(String name, Object value) {
|
||||
if (!(value instanceof Serializable)) {
|
||||
throw new IllegalArgumentException("Value must be serialiazble");
|
||||
}
|
||||
properties.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a property.
|
||||
*
|
||||
* @param name the name of the property to delete.
|
||||
*/
|
||||
public synchronized void deleteProperty(String name) {
|
||||
if (properties == null) {
|
||||
return;
|
||||
}
|
||||
properties.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable collection of all the property names that are set.
|
||||
*
|
||||
* @return all property names.
|
||||
*/
|
||||
public synchronized Collection<String> getPropertyNames() {
|
||||
if (properties == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return Collections.unmodifiableSet(new HashSet<String>(properties.keySet()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packet as XML. Every concrete extension of Packet must implement
|
||||
* this method. In addition to writing out packet-specific data, every sub-class
|
||||
|
@ -368,88 +305,6 @@ public abstract class Packet {
|
|||
for (PacketExtension extension : getExtensions()) {
|
||||
xml.append(extension.toXML());
|
||||
}
|
||||
// Add in packet properties.
|
||||
if (properties != null && !properties.isEmpty()) {
|
||||
xml.halfOpenElement("properties").xmlnsAttribute("http://www.jivesoftware.com/xmlns/xmpp/properties");
|
||||
xml.rightAngelBracket();
|
||||
// Loop through all properties and write them out.
|
||||
for (String name : getPropertyNames()) {
|
||||
Object value = getProperty(name);
|
||||
xml.openElement("property");
|
||||
xml.element("name", name);
|
||||
xml.halfOpenElement("value");
|
||||
|
||||
String type;
|
||||
String valueStr;
|
||||
if (value instanceof Integer) {
|
||||
type = "integer";
|
||||
valueStr = Integer.toString((Integer)value);
|
||||
}
|
||||
else if (value instanceof Long) {
|
||||
type = "long";
|
||||
valueStr = Long.toString((Long) value);
|
||||
}
|
||||
else if (value instanceof Float) {
|
||||
type = "float";
|
||||
valueStr = Float.toString((Float) value);
|
||||
}
|
||||
else if (value instanceof Double) {
|
||||
type = "double";
|
||||
valueStr = Double.toString((Double) value);
|
||||
}
|
||||
else if (value instanceof Boolean) {
|
||||
type = "boolean";
|
||||
valueStr = Boolean.toString((Boolean) value);
|
||||
}
|
||||
else if (value instanceof String) {
|
||||
type = "string";
|
||||
valueStr = (String) value;
|
||||
}
|
||||
// Otherwise, it's a generic Serializable object. Serialized objects are in
|
||||
// a binary format, which won't work well inside of XML. Therefore, we base-64
|
||||
// encode the binary data before adding it.
|
||||
else {
|
||||
ByteArrayOutputStream byteStream = null;
|
||||
ObjectOutputStream out = null;
|
||||
try {
|
||||
byteStream = new ByteArrayOutputStream();
|
||||
out = new ObjectOutputStream(byteStream);
|
||||
out.writeObject(value);
|
||||
type ="java-object";
|
||||
valueStr = StringUtils.encodeBase64(byteStream.toByteArray());
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, "Error encoding java object", e);
|
||||
type ="java-object";
|
||||
valueStr = "Serializing error: " + e.getMessage();
|
||||
}
|
||||
finally {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
if (byteStream != null) {
|
||||
try {
|
||||
byteStream.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
xml.attribute("type", type);
|
||||
xml.rightAngelBracket();
|
||||
xml.escape(valueStr);
|
||||
xml.closeElement("value");
|
||||
xml.closeElement("property");
|
||||
}
|
||||
xml.closeElement("properties");
|
||||
}
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
@ -479,10 +334,6 @@ public abstract class Packet {
|
|||
if (packetID != null ? !packetID.equals(packet.packetID) : packet.packetID != null) {
|
||||
return false;
|
||||
}
|
||||
if (properties != null ? !properties.equals(packet.properties)
|
||||
: packet.properties != null) {
|
||||
return false;
|
||||
}
|
||||
if (to != null ? !to.equals(packet.to) : packet.to != null) { return false; }
|
||||
return !(xmlns != null ? !xmlns.equals(packet.xmlns) : packet.xmlns != null);
|
||||
}
|
||||
|
@ -495,7 +346,6 @@ public abstract class Packet {
|
|||
result = 31 * result + (to != null ? to.hashCode() : 0);
|
||||
result = 31 * result + (from != null ? from.hashCode() : 0);
|
||||
result = 31 * result + packetExtensions.hashCode();
|
||||
result = 31 * result + properties.hashCode();
|
||||
result = 31 * result + (error != null ? error.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -16,9 +16,7 @@
|
|||
*/
|
||||
package org.jivesoftware.smack.util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
@ -55,12 +53,6 @@ import org.xmlpull.v1.XmlPullParserException;
|
|||
*/
|
||||
public class PacketParserUtils {
|
||||
private static final Logger LOGGER = Logger.getLogger(PacketParserUtils.class.getName());
|
||||
|
||||
/**
|
||||
* Namespace used to store packet properties.
|
||||
*/
|
||||
private static final String PROPERTIES_NAMESPACE =
|
||||
"http://www.jivesoftware.com/xmlns/xmpp/properties";
|
||||
|
||||
/**
|
||||
* Parses a message packet.
|
||||
|
@ -69,7 +61,7 @@ public class PacketParserUtils {
|
|||
* @return a Message packet.
|
||||
* @throws Exception if an exception occurs while parsing the packet.
|
||||
*/
|
||||
public static Packet parseMessage(XmlPullParser parser) throws Exception {
|
||||
public static Message parseMessage(XmlPullParser parser) throws Exception {
|
||||
Message message = new Message();
|
||||
String id = parser.getAttributeValue("", "id");
|
||||
message.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
|
||||
|
@ -93,7 +85,6 @@ public class PacketParserUtils {
|
|||
// in arbitrary sub-elements.
|
||||
boolean done = false;
|
||||
String thread = null;
|
||||
Map<String, Object> properties = null;
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
|
@ -131,11 +122,6 @@ public class PacketParserUtils {
|
|||
else if (elementName.equals("error")) {
|
||||
message.setError(parseError(parser));
|
||||
}
|
||||
else if (elementName.equals("properties") &&
|
||||
namespace.equals(PROPERTIES_NAMESPACE))
|
||||
{
|
||||
properties = parseProperties(parser);
|
||||
}
|
||||
// Otherwise, it must be a packet extension.
|
||||
else {
|
||||
message.addExtension(
|
||||
|
@ -150,12 +136,6 @@ public class PacketParserUtils {
|
|||
}
|
||||
|
||||
message.setThread(thread);
|
||||
// Set packet properties.
|
||||
if (properties != null) {
|
||||
for (String name : properties.keySet()) {
|
||||
message.setProperty(name, properties.get(name));
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
|
@ -246,15 +226,6 @@ public class PacketParserUtils {
|
|||
else if (elementName.equals("error")) {
|
||||
presence.setError(parseError(parser));
|
||||
}
|
||||
else if (elementName.equals("properties") &&
|
||||
namespace.equals(PROPERTIES_NAMESPACE))
|
||||
{
|
||||
Map<String,Object> properties = parseProperties(parser);
|
||||
// Set packet properties.
|
||||
for (String name : properties.keySet()) {
|
||||
presence.setProperty(name, properties.get(name));
|
||||
}
|
||||
}
|
||||
// Otherwise, it must be a packet extension.
|
||||
else {
|
||||
try {
|
||||
|
@ -549,87 +520,6 @@ public class PacketParserUtils {
|
|||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a properties sub-packet. If any errors occur while de-serializing Java object
|
||||
* properties, an exception will be printed and not thrown since a thrown
|
||||
* exception will shut down the entire connection. ClassCastExceptions will occur
|
||||
* when both the sender and receiver of the packet don't have identical versions
|
||||
* of the same class.
|
||||
*
|
||||
* @param parser the XML parser, positioned at the start of a properties sub-packet.
|
||||
* @return a map of the properties.
|
||||
* @throws Exception if an error occurs while parsing the properties.
|
||||
*/
|
||||
public static Map<String, Object> parseProperties(XmlPullParser parser) throws Exception {
|
||||
Map<String, Object> properties = new HashMap<String, Object>();
|
||||
while (true) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG && parser.getName().equals("property")) {
|
||||
// Parse a property
|
||||
boolean done = false;
|
||||
String name = null;
|
||||
String type = null;
|
||||
String valueText = null;
|
||||
Object value = null;
|
||||
while (!done) {
|
||||
eventType = parser.next();
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String elementName = parser.getName();
|
||||
if (elementName.equals("name")) {
|
||||
name = parser.nextText();
|
||||
}
|
||||
else if (elementName.equals("value")) {
|
||||
type = parser.getAttributeValue("", "type");
|
||||
valueText = parser.nextText();
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("property")) {
|
||||
if ("integer".equals(type)) {
|
||||
value = Integer.valueOf(valueText);
|
||||
}
|
||||
else if ("long".equals(type)) {
|
||||
value = Long.valueOf(valueText);
|
||||
}
|
||||
else if ("float".equals(type)) {
|
||||
value = Float.valueOf(valueText);
|
||||
}
|
||||
else if ("double".equals(type)) {
|
||||
value = Double.valueOf(valueText);
|
||||
}
|
||||
else if ("boolean".equals(type)) {
|
||||
value = Boolean.valueOf(valueText);
|
||||
}
|
||||
else if ("string".equals(type)) {
|
||||
value = valueText;
|
||||
}
|
||||
else if ("java-object".equals(type)) {
|
||||
try {
|
||||
byte [] bytes = StringUtils.decodeBase64(valueText);
|
||||
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
|
||||
value = in.readObject();
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, "Error parsing java object", e);
|
||||
}
|
||||
}
|
||||
if (name != null && value != null) {
|
||||
properties.put(name, value);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG) {
|
||||
if (parser.getName().equals("properties")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses SASL authentication error packets.
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue