From 6e08a101869867b7cf02fcb654aba15c375f770e Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 26 Apr 2014 15:43:58 +0200 Subject: [PATCH] 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. --- .../org/jivesoftware/smack/packet/Packet.java | 164 +------------- .../smack/util/PacketParserUtils.java | 112 +--------- documentation/extensions/intro.html | 5 + .../{ => extensions}/properties.html | 28 ++- documentation/extensions/toc.html | 1 + documentation/index.html | 1 - .../jiveproperties/JivePropertiesManager.java | 77 +++++++ .../packet/JivePropertiesExtension.java | 210 ++++++++++++++++++ .../JivePropertiesExtensionProvider.java | 127 +++++++++++ .../extensions.providers | 6 + .../JivePropertiesExtensionTest.java | 66 ++++++ 11 files changed, 516 insertions(+), 281 deletions(-) rename documentation/{ => extensions}/properties.html (78%) create mode 100644 extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/JivePropertiesManager.java create mode 100644 extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/packet/JivePropertiesExtension.java create mode 100644 extensions/src/main/java/org/jivesoftware/smackx/jiveproperties/provider/JivePropertiesExtensionProvider.java create mode 100644 extensions/src/test/java/org/jivesoftware/smackx/jiveproperties/JivePropertiesExtensionTest.java diff --git a/core/src/main/java/org/jivesoftware/smack/packet/Packet.java b/core/src/main/java/org/jivesoftware/smack/packet/Packet.java index 097afb4e2..02b53ee11 100644 --- a/core/src/main/java/org/jivesoftware/smack/packet/Packet.java +++ b/core/src/main/java/org/jivesoftware/smack/packet/Packet.java @@ -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 packetExtensions = new CopyOnWriteArrayList(); - private final Map properties = new HashMap(); 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 null 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 null 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 getPropertyNames() { - if (properties == null) { - return Collections.emptySet(); - } - return Collections.unmodifiableSet(new HashSet(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; } diff --git a/core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java b/core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java index fbbf6f431..c9b7eb159 100644 --- a/core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java +++ b/core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java @@ -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 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 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 parseProperties(XmlPullParser parser) throws Exception { - Map properties = new HashMap(); - 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. * diff --git a/documentation/extensions/intro.html b/documentation/extensions/intro.html index 8d1a41b0e..811ba9174 100644 --- a/documentation/extensions/intro.html +++ b/documentation/extensions/intro.html @@ -90,6 +90,11 @@ XEP-0332 Allows to transport HTTP communication over XMPP peer-to-peer networks. + + Jive Properties + N/A + TODO + diff --git a/documentation/properties.html b/documentation/extensions/properties.html similarity index 78% rename from documentation/properties.html rename to documentation/extensions/properties.html index 04095a8a1..f15ce76ca 100644 --- a/documentation/properties.html +++ b/documentation/extensions/properties.html @@ -10,10 +10,6 @@ Packet Properties - -

Smack provides an easy mechanism for attaching arbitrary properties to packets. Each property has a String name, and a value that is a Java primitive (int, long, float, double, boolean) or @@ -32,10 +28,13 @@ demonstrates how to set properties:

 Message message = chat.createMessage();
+JivePropertiesExtension jpe = new JivePropertiesExtension();
 // Add a Color object as a property.
-message.setProperty("favoriteColor", new Color(0, 0, 255));
+jpe.setProperty("favoriteColor", new Color(0, 0, 255));
 // Add an int as a property.
-message.setProperty("favoriteNumber", 4);
+jpe.setProperty("favoriteNumber", 4);
+// Add the JivePropertiesExtension to the message packet
+message.addPacketExtension(jpe);
 chat.sendMessage(message);
 
@@ -45,14 +44,23 @@ Getting those same properties would use the following code:
 Message message = chat.nextMessage();
+// Get the JivePropertiesExtension
+JivePropertiesExtension jpe = message.getExtension(JivePropertiesExtension.NAMESPACE);
 // Get a Color object property.
-Color favoriteColor = (Color)message.getProperty("favoriteColor");
+Color favoriteColor = (Color)jpe.getProperty("favoriteColor");
 // Get an int property. Note that properties are always returned as
 // Objects, so we must cast the value to an Integer, then convert
 // it to an int.
-int favoriteNumber = ((Integer)message.getProperty("favoriteNumber")).intValue();
+int favoriteNumber = ((Integer)jpe.getProperty("favoriteNumber")).intValue();
 
+

+For convenience JivePropertiesManager contains two helper +methods namely addProperty(Packet packet, String name, Object +value) and +getProperty(Packet packet, String name). +

+

Objects as Properties

@@ -63,10 +71,6 @@ you should keep the following in mind: