/**
* WS-Attacker - A Modular Web Services Penetration Testing Framework Copyright
* (C) 2010 Christian Mainka
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package wsattacker.util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import wsattacker.library.xmlutilities.dom.DomUtilities;
public class SoapUtilities
{
private static final Logger log = Logger.getLogger( SoapUtilities.class );
public static final String SOAP_11_NAMESPACE_URL = "http://schemas.xmlsoap.org/soap/envelope/";
public static final String SOAP_12_NAMESPACE_URL = "http://www.w3.org/2003/05/soap-envelope";
/**
* Converts an XML String to a javax.xml.soap.SOAPMessage Detects SOAP Protocol Version 1.1 and 1.2 via SOAP
* Namespace
*
* @param soapString
* @return
* @throws SOAPException
*/
public static SOAPMessage stringToSoap( String soapString )
throws SOAPException
{
byte[] bytes;
try
{
bytes = soapString.getBytes( "UTF-8" );
}
catch ( UnsupportedEncodingException e )
{
log.fatal( "### Error - this should never happen: " + e.getMessage() );
bytes = "".getBytes();
}
String protocol;
// detecting soap protocoll version
if ( soapString.contains( SOAP_11_NAMESPACE_URL ) )
{
protocol = javax.xml.soap.SOAPConstants.SOAP_1_1_PROTOCOL;
}
else if ( soapString.contains( SOAP_12_NAMESPACE_URL ) )
{
protocol = javax.xml.soap.SOAPConstants.SOAP_1_2_PROTOCOL;
}
else
{
throw new SOAPException( "Could't not detect SOAP protocol Version" );
}
SOAPMessage sm;
try
{
// create new SOAPMessage with null header and xml content
sm = MessageFactory.newInstance( protocol ).createMessage( null, new ByteArrayInputStream( bytes ) );
}
catch ( IOException e )
{
log.fatal( "### Error - this should never happen: " + e.getMessage() );
sm = MessageFactory.newInstance().createMessage(); // return new
// empty message
}
return sm;
}
/**
* Converts a javax.xml.soap.SOAPElement to a String Can be used for soapUI requests Be carefull: To convert a
* SOAPMessage sm, you must use sm.getSOAPPart().getEnvelope();
*
* @param element
* @return
*/
public static String soapToString( SOAPElement element )
{
// use the dom hepler function
return DomUtilities.domToString( element.getOwnerDocument() );
}
/**
* Converts a String to a DOM. Sometimes, you might prefer DOM to SOAPElement. No namespace prefixes are used by
* default.
*
* @param xmlString
* @return
* @throws SAXException
*/
public static Document stringToDom( String xmlString )
throws SAXException
{
return DomUtilities.stringToDom( xmlString, false );
}
/**
* Finds all SOAP Childs of an SOAPElement (non recursive) This is useful, as getChildElements() will also contain
* Text Nodes. This method only returns instances of SOAPElement
*
* @param ele
* @param namespace : if null, all childs will be returned
* @return
*/
public static List<SOAPElement> getSoapChilds( SOAPElement ele, QName name )
{
List<SOAPElement> list = new ArrayList<SOAPElement>();
Iterator<?> ei;
if ( name == null )
{
ei = ele.getChildElements();
}
else
{
ei = ele.getChildElements( name );
}
while ( ei.hasNext() )
{
Object o = ei.next();
if ( o instanceof SOAPElement )
{
SOAPElement e = (SOAPElement) o;
log.trace( "Found Child Element // " + e.getNodeName() );
list.add( e );
}
}
return list;
}
/**
* Finds all SOAP Childs of an SOAPElement (non recursive) This is useful, as getChildElements() will also contain
* Text Nodes. This method only returns instances of SOAPElement
*
* @param ele
* @return
*/
public static List<SOAPElement> getSoapChilds( SOAPElement ele )
{
return getSoapChilds( ele, null );
}
/**
* Finds recursive all leafs of a SOAPElement as they are expected to need text content. Excludes elements of
* soap-env and soap namespace (Envelop, Header Body)
*
* @param ele
* @return
*/
public static List<SOAPElement> inputNeeded( SOAPElement ele )
{
List<SOAPElement> l = new ArrayList<SOAPElement>();
log.trace( "Starting Input-Needed Lookup for " + ele.getNodeName() );
inputNeeded( ele, l );
log.trace( "Input-Needed Lookup done: " + l );
return l;
}
private static void inputNeeded( SOAPElement ele, List<SOAPElement> l )
{
// input is needed for leafs or for existing text-nods
if ( !ele.hasChildNodes()
|| ( ele.getChildNodes().getLength() == 1 && ( ele.getFirstChild().getNodeType() == Node.TEXT_NODE ) ) )
{
// check if element is a special element that should never get input
String namespace = ele.getNamespaceURI();
// only add to list if element has no namspace or namespace is no
// soap namespace
if ( namespace == null
|| ( !namespace.equals( SOAP_11_NAMESPACE_URL ) && !namespace.equals( SOAP_12_NAMESPACE_URL ) ) )
{
log.trace( "Adding node " + ele.getNodeName() );
l.add( ele );
}
}
for ( SOAPElement e : getSoapChilds( ele ) )
{
inputNeeded( e, l );
}
}
/**
* Returns a list of all namespaces below an element (recursive)
*
* @param ele
* @return
*/
public static Map<String, String> allNamespaces( SOAPElement ele )
{
Map<String, String> nsList = new TreeMap<String, String>();
log.trace( "Starting Namespace Lookup in " + ele.getNodeName() );
allNamespaces( ele, nsList );
log.trace( "Namespace Lookup done: " + nsList );
return nsList;
}
private static void allNamespaces( SOAPElement ele, Map<String, String> nsList )
{
Iterator<?> i;
// Loop through namespaces
i = ele.getNamespacePrefixes();
while ( i.hasNext() )
{
String prefix = (String) i.next();
String uri = ele.getNamespaceURI( prefix );
log.trace( "Found Namespace // " + prefix + ":" + uri );
nsList.put( prefix, uri );
}
// Loop through childs
for ( SOAPElement e : getSoapChilds( ele ) )
{
allNamespaces( e, nsList );
}
}
/**
* Returns a list of all parents of a SOAPElement The first element of the list is the father of e, the second the
* grand-father and so on. Last element is always the root.
*
* @param e
* @return
*/
public static List<SOAPElement> getParents( SOAPElement e )
{
// searching all parent nodes
List<SOAPElement> parents = new ArrayList<SOAPElement>();
SOAPElement zwerg = e.getParentElement();
while ( zwerg != null )
{
parents.add( zwerg );
zwerg = zwerg.getParentElement();
}
return parents;
}
/**
* Returns the root element of e
*
* @param e
* @return
*/
public static SOAPElement getRoot( SOAPElement e )
{
List<SOAPElement> parents = getParents( e );
int size = parents.size();
if ( size > 0 )
{
return parents.get( size - 1 );
}
else
{
return e;
}
}
}