Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
import org.apache.synapse.mediators.AbstractMediator;
import org.apache.synapse.util.xpath.SynapseXPath;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -59,6 +62,17 @@ public class PayloadFactoryMediator extends AbstractMediator {
*/
private Pattern pattern = Pattern.compile("\\$(\\d)+");

/**
* Parser used only to decide whether an argument value is already a standalone XML fragment.
* DTD and external entity support are off: the values tested here come from the message.
*/
private static final XMLInputFactory XML_PROBE_FACTORY = XMLInputFactory.newInstance();

static {
XML_PROBE_FACTORY.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
XML_PROBE_FACTORY.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
}

/**
* Replaces the existing payload with a new payload as defined by the format and the argument list
* @param synCtx the current message for mediation
Expand Down Expand Up @@ -105,7 +119,13 @@ private void transformPayload(StringBuffer result, MessageContext synCtx) {
while (matcher.find()) {
String matchSeq = matcher.group();
int argIndex = Integer.parseInt(matchSeq.substring(1));
matcher.appendReplacement(result, argValues[argIndex - 1].toString());
if (argIndex < 1 || argIndex > argValues.length) {
handleException("Invalid argument index " + matchSeq + " in the payloadFactory " +
"format; " + argValues.length + " argument(s) are configured", synCtx);
}
// quoteReplacement keeps a literal '$' or '\' from being read as replacement syntax.
matcher.appendReplacement(result,
Matcher.quoteReplacement(argValues[argIndex - 1].toString()));
}
matcher.appendTail(result);
}
Expand All @@ -125,7 +145,8 @@ private Object[] getArgValues(MessageContext synCtx) {
} else if (arg.getExpression() != null) {
String value = arg.getExpression().stringValueOf(synCtx);
if (value != null) {
argValues[i] = value;
// Escape the value unless it is already a well-formed XML fragment.
argValues[i] = isXML(value) ? value : escapeXmlText(value);
} else {
argValues[i] = "";
}
Expand All @@ -137,6 +158,85 @@ private Object[] getArgValues(MessageContext synCtx) {
return argValues;
}

/**
* Reports whether the value is already a standalone, well-formed XML fragment.
*
* @param value argument value to test.
* @return true if the value parses on its own as XML.
*/
private static boolean isXML(String value) {

String trimmed = value.trim();
if (trimmed.length() < 4 || !trimmed.endsWith(">")) {
return false;
}
try {
XMLStreamReader reader = XML_PROBE_FACTORY.createXMLStreamReader(new StringReader(trimmed));
while (reader.hasNext()) {
reader.next();
}
reader.close();
return true;
} catch (Exception e) {
// Broad on purpose: malformed markup can surface as an unchecked exception here.
return false;
}
}

/**
* Escapes the characters that would otherwise be read as markup once the completed format is
* parsed, and drops the characters XML 1.0 does not permit at all.
*
* @param value argument value to escape.
* @return the value as XML character data.
*/
private static String escapeXmlText(String value) {

StringBuilder escaped = new StringBuilder(value.length());
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
switch (c) {
case '&':
escaped.append("&amp;");
break;
case '<':
escaped.append("&lt;");
break;
case '>':
escaped.append("&gt;");
break;
case '"':
escaped.append("&quot;");
break;
case '\'':
// Numeric rather than &apos;, which predates XML in some consumers.
escaped.append("&#39;");
break;
default:
if (Character.isHighSurrogate(c) && i + 1 < value.length()
&& Character.isLowSurrogate(value.charAt(i + 1))) {
// Keep a valid surrogate pair together so the filter below can't split it.
escaped.append(c).append(value.charAt(++i));
} else if (isValidXmlCharacter(c)) {
escaped.append(c);
}
break;
}
}
return escaped.toString();
}

/**
* @param c character to test.
* @return true if XML 1.0 permits the character in a document.
*/
private static boolean isValidXmlCharacter(char c) {

return c == 0x9 || c == 0xA || c == 0xD
|| (c >= 0x20 && c <= 0xD7FF)
|| (c >= 0xE000 && c <= 0xFFFD);
}

public String getFormat() {
return format;
}
Expand Down
Loading