1 package de.timroes.axmlrpc; 2 3 import de.timroes.axmlrpc.serializer.SerializerHandler; 4 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.OutputStream; 8 import java.io.OutputStreamWriter; 9 import java.util.Map; 10 11 import javax.xml.parsers.DocumentBuilder; 12 import javax.xml.parsers.DocumentBuilderFactory; 13 import javax.xml.parsers.ParserConfigurationException; 14 import javax.xml.transform.OutputKeys; 15 import javax.xml.transform.Transformer; 16 import javax.xml.transform.TransformerException; 17 import javax.xml.transform.TransformerFactory; 18 import javax.xml.transform.dom.DOMSource; 19 import javax.xml.transform.stream.StreamResult; 20 21 import org.w3c.dom.Document; 22 import org.w3c.dom.Element; 23 24 /** 25 * The ResponseParser parses the response of an XMLRPC server to an object. 26 * 27 * @author Tim Roes 28 */ 29 public class ResponseParser { 30 31 private static final String FAULT_CODE = "faultCode"; 32 private static final String FAULT_STRING = "faultString"; 33 34 /** 35 * The given InputStream must contain the xml response from an xmlrpc server. 36 * This method extract the content of it as an object. 37 * 38 * @param serializerHandler You can inject an arbitrary one if you want to use your own transport protocol. 39 * See the README (section "Using an arbitrary transport") for more info on this feature. 40 * @param response The InputStream of the server response. 41 * @param debugMode This prints data on System.out to make it easy to debug 42 * @return The returned object. 43 * @throws XMLRPCException Will be thrown whenever something fails. 44 * @throws XMLRPCServerException Will be thrown, if the server returns an error. 45 */ parse(SerializerHandler serializerHandler, InputStream response, boolean debugMode)46 public Object parse(SerializerHandler serializerHandler, InputStream response, boolean debugMode) throws XMLRPCException { 47 48 try { 49 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 50 51 // Ensure the xml parser won't allow exploitation of the vuln CWE-611 52 // (described on https://cwe.mitre.org/data/definitions/611.html ) 53 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 54 factory.setExpandEntityReferences(false); 55 factory.setNamespaceAware(true); 56 factory.setXIncludeAware(false); 57 factory.setExpandEntityReferences(false); 58 // End of the configuration of the parser for CWE-611 59 60 DocumentBuilder builder = factory.newDocumentBuilder(); 61 Document dom = builder.parse(response); 62 if (debugMode ){ 63 printDocument(dom, System.out); 64 } 65 Element e = dom.getDocumentElement(); 66 67 68 // Check for root tag 69 if(!e.getNodeName().equals(XMLRPCClient.METHOD_RESPONSE)) { 70 throw new XMLRPCException("MethodResponse root tag is missing."); 71 } 72 73 e = XMLUtil.getOnlyChildElement(e.getChildNodes()); 74 75 if(e.getNodeName().equals(XMLRPCClient.PARAMS)) { 76 77 e = XMLUtil.getOnlyChildElement(e.getChildNodes()); 78 79 if(!e.getNodeName().equals(XMLRPCClient.PARAM)) { 80 throw new XMLRPCException("The params tag must contain a param tag."); 81 } 82 83 return getReturnValueFromElement(serializerHandler, e); 84 85 } else if(e.getNodeName().equals(XMLRPCClient.FAULT)) { 86 87 @SuppressWarnings("unchecked") 88 Map<String,Object> o = (Map<String,Object>)getReturnValueFromElement(serializerHandler, e); 89 90 throw new XMLRPCServerException((String)o.get(FAULT_STRING), (Integer)o.get(FAULT_CODE)); 91 92 } 93 94 throw new XMLRPCException("The methodResponse tag must contain a fault or params tag."); 95 96 } catch(XMLRPCServerException e) { 97 throw e; 98 } catch (Exception ex) { 99 throw new XMLRPCException("Error getting result from server.", ex); 100 } 101 102 } 103 printDocument(Document doc, OutputStream out)104 public static void printDocument(Document doc, OutputStream out) throws IOException, TransformerException { 105 TransformerFactory tf = TransformerFactory.newInstance(); 106 Transformer transformer = tf.newTransformer(); 107 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); 108 transformer.setOutputProperty(OutputKeys.METHOD, "xml"); 109 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 110 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 111 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 112 113 transformer.transform(new DOMSource(doc), 114 new StreamResult(new OutputStreamWriter(out, "UTF-8"))); 115 } 116 117 /** 118 * This method takes an element (must be a param or fault element) and 119 * returns the deserialized object of this param tag. 120 * 121 * @param element An param element. 122 * @return The deserialized object within the given param element. 123 * @throws XMLRPCException Will be thrown when the structure of the document 124 * doesn't match the XML-RPC specification. 125 */ getReturnValueFromElement(SerializerHandler serializerHandler, Element element)126 private Object getReturnValueFromElement(SerializerHandler serializerHandler, Element element) throws XMLRPCException { 127 128 Element childElement = XMLUtil.getOnlyChildElement(element.getChildNodes()); 129 130 return serializerHandler.deserialize(childElement); 131 } 132 133 } 134