Commit 85e145e0 authored by Dave Cridland's avatar Dave Cridland Committed by GitHub

Merge pull request #902 from guusdk/OF-1335_Do-not-strip-all-namespaces

OF-1335: Don't strip namespaces everywhere.
parents 9cf89f04 fd3e847e
...@@ -16,6 +16,8 @@ import org.xmlpull.v1.XmlPullParserFactory; ...@@ -16,6 +16,8 @@ import org.xmlpull.v1.XmlPullParserFactory;
import java.io.*; import java.io.*;
import java.net.URL; import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
/** /**
* <p><code>XMPPPacketReader</code> is a Reader of DOM4J documents that * <p><code>XMPPPacketReader</code> is a Reader of DOM4J documents that
...@@ -54,6 +56,15 @@ public class XMPPPacketReader { ...@@ -54,6 +56,15 @@ public class XMPPPacketReader {
*/ */
private long lastActive = System.currentTimeMillis(); private long lastActive = System.currentTimeMillis();
/**
* Stream of various endpoints (eg: s2s, c2s) use different default namespaces. To be able to use a stanza that's
* parsed on one type of endpoint in the context of another endpoint, we explicitly ignore these namespaced. This
* allows us to forward, for instance, a stanza received via C2S (which has the "jabber:client" default namespace)
* on a S2S stream (which has the "jabber:server" default namespace).
*
* @see <a href="https://xmpp.org/rfcs/rfc6120.html#streams-ns-xmpp">RFC 6120, 4.8.3. XMPP Content Namespaces</a>
*/
public static final Collection<String> IGNORED_NAMESPACE_ON_STANZA = Arrays.asList( "jabber:client", "jabber:server", "jabber:connectionmanager", "jabber:component:accept", "http://jabber.org/protocol/httpbind" );
public XMPPPacketReader() { public XMPPPacketReader() {
} }
...@@ -364,15 +375,13 @@ public class XMPPPacketReader { ...@@ -364,15 +375,13 @@ public class XMPPPacketReader {
} }
case XmlPullParser.START_TAG: { case XmlPullParser.START_TAG: {
QName qname = (pp.getPrefix() == null) ? df.createQName(pp.getName(), pp.getNamespace()) : df.createQName(pp.getName(), pp.getPrefix(), pp.getNamespace()); QName qname = (pp.getPrefix() == null) ? df.createQName(pp.getName(), pp.getNamespace()) : df.createQName(pp.getName(), pp.getPrefix(), pp.getNamespace());
Element newElement = null; Element newElement;
// Do not include the namespace if this is the start tag of a new packet
// This avoids including "jabber:client", "jabber:server" or // Do not qualify stanza element with certain namespaces. This makes those stanzas re-usable between,
// "jabber:component:accept" // for example, c2s and s2s. This code prevents such qualification only for elements that have no
if ("jabber:client".equals(qname.getNamespaceURI()) || // default namespace declared
"jabber:server".equals(qname.getNamespaceURI()) || final boolean defaultNamespaceDeclared = parent == null || !parent.getNamespaceForPrefix("").getURI().equals("");
"jabber:connectionmanager".equals(qname.getNamespaceURI()) || if ( !defaultNamespaceDeclared && IGNORED_NAMESPACE_ON_STANZA.contains( qname.getNamespaceURI() ) ) {
"jabber:component:accept".equals(qname.getNamespaceURI()) ||
"http://jabber.org/protocol/httpbind".equals(qname.getNamespaceURI())) {
newElement = df.createElement(pp.getName()); newElement = df.createElement(pp.getName());
} }
else { else {
...@@ -381,9 +390,13 @@ public class XMPPPacketReader { ...@@ -381,9 +390,13 @@ public class XMPPPacketReader {
int nsStart = pp.getNamespaceCount(pp.getDepth() - 1); int nsStart = pp.getNamespaceCount(pp.getDepth() - 1);
int nsEnd = pp.getNamespaceCount(pp.getDepth()); int nsEnd = pp.getNamespaceCount(pp.getDepth());
for (int i = nsStart; i < nsEnd; i++) { for (int i = nsStart; i < nsEnd; i++) {
if (pp.getNamespacePrefix(i) != null) { final String namespacePrefix = pp.getNamespacePrefix( i );
newElement final String namespaceUri = pp.getNamespaceUri( i );
.addNamespace(pp.getNamespacePrefix(i), pp.getNamespaceUri(i)); if ( namespacePrefix != null ) {
newElement.addNamespace( namespacePrefix, namespaceUri );
} else if ( !( pp.getDepth() <= 2 && IGNORED_NAMESPACE_ON_STANZA.contains(namespaceUri) ) ) {
// Do not include certain default namespace on the root-element ('stream') or stanza level. This makes stanzas re-usable between, for example, c2s and s2s.
newElement.addNamespace( "", namespaceUri );
} }
} }
for (int i = 0; i < pp.getAttributeCount(); i++) { for (int i = 0; i < pp.getAttributeCount(); i++) {
......
/*
* Copyright (C) 2017 Ignite Realtime Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package dom.io;
import org.dom4j.Document;
import org.dom4j.io.XMPPPacketReader;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.StringReader;
/**
* Unit tests that verify the functionality of {@link XMPPPacketReader}
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class XMPPPacketReaderTest
{
private XMPPPacketReader packetReader;
@Before
public void setup()
{
packetReader = new XMPPPacketReader();
}
/**
* Check if the 'jabber:client' default namespace declaration is stripped from a stream tag.
*
* Openfire strips this namespace (among others) to make the resulting XML 're-usable', in context of the
* implementation note in RFC 6120, section 4.8.3.
*
* @see <a href="https://xmpp.org/rfcs/rfc6120.html#streams-ns-xmpp">RFC 6120, 4.8.3. XMPP Content Namespaces</a>
*/
@Test
public void testStripContentNamespace() throws Exception
{
// Setup fixture.
final String input = "<stream:stream to='example.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'><message from='juliet@example.com' to='romeo@example.net' xml:lang='en'><body>Art thou not Romeo, and a Montague?</body></message></stream:stream>";
// Execute system under test.
final Document result = packetReader.read( new StringReader( input ) );
// Verify result.
Assert.assertFalse( result.asXML().contains( "jabber:client" ) );
}
/**
* Check if the 'jabber:client' default namespace declaration is stripped from a stanza.
*
* Openfire strips this namespace (among others) to make the resulting XML 're-usable', in context of the
* implementation note in RFC 6120, section 4.8.3.
*
* @see <a href="https://xmpp.org/rfcs/rfc6120.html#streams-ns-xmpp">RFC 6120, 4.8.3. XMPP Content Namespaces</a>
*/
@Test
public void testStripPrefixFreeCanonicalization() throws Exception
{
// Setup fixture.
final String input = "<stream:stream to='example.com' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'><message xmlns='jabber:client' from='juliet@example.com' to='romeo@example.net' xml:lang='en'><body>Art thou not Romeo, and a Montague?</body></message></stream:stream>";
// Execute system under test.
final Document result = packetReader.read( new StringReader( input ) );
// Verify result.
Assert.assertFalse( result.asXML().contains( "jabber:client" ) );
}
/**
* Check that the 'jabber:client' default namespace declaration is explicitly _not_ stripped from a child element
* of a stanza.
*
* Openfire strips this namespace (among others) to make the resulting XML 're-usable', in context of the
* implementation note in RFC 6120, section 4.8.3.
*
* @see <a href="https://issues.igniterealtime.org/browse/OF-1335">Issue OF-1335: Forwarded messages rewritten to default namespace over S2S</a>
* @see <a href="https://xmpp.org/rfcs/rfc6120.html#streams-ns-xmpp">RFC 6120, 4.8.3. XMPP Content Namespaces</a>
*/
@Test
public void testAvoidStrippingInternalContentNamespace() throws Exception
{
// Setup fixture
final String input =
"<stream:stream xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>" +
" <message xmlns='jabber:client'>" +
" <other xmlns='something:else'>" +
" <message xmlns='jabber:client'/>" +
" </other>" +
" </message>" +
"</stream:stream>";
final Document result = packetReader.read( new StringReader( input ) );
// Verify result.
Assert.assertFalse( "'jabber:client' should not occur before 'something:else'", result.asXML().substring( 0, result.asXML().indexOf("something:else") ).contains( "jabber:client" ) );
Assert.assertTrue( "'jabber:client' should occur after 'something:else'", result.asXML().substring( result.asXML().indexOf("something:else") ).contains( "jabber:client" ) );
}
/**
* Check that the 'jabber:client' default namespace declaration is explicitly _not_ stripped from a child element
* of a stanza.
*
* Openfire strips this namespace (among others) to make the resulting XML 're-usable', in context of the
* implementation note in RFC 6120, section 4.8.3.
*
* @see <a href="https://issues.igniterealtime.org/browse/OF-1335">Issue OF-1335: Forwarded messages rewritten to default namespace over S2S</a>
* @see <a href="https://xmpp.org/rfcs/rfc6120.html#streams-ns-xmpp">RFC 6120, 4.8.3. XMPP Content Namespaces</a>
*/
@Test
public void testAvoidStrippingPrefixFreeCanonicalization() throws Exception
{
// Setup fixture
final String input =
"<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>" +
" <message>" +
" <other xmlns='something:else'>" +
" <message xmlns='jabber:client'/>" +
" </other>" +
" </message>" +
"</stream:stream>";
final Document result = packetReader.read( new StringReader( input ) );
// Verify result.
Assert.assertFalse( "'jabber:client' should not occur before 'something:else'", result.asXML().substring( 0, result.asXML().indexOf("something:else") ).contains( "jabber:client" ) );
Assert.assertTrue( "'jabber:client' should occur after 'something:else'", result.asXML().substring( result.asXML().indexOf("something:else") ).contains( "jabber:client" ) );
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment