package org.geowebcache.service.wms;
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.*;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.easymock.classextension.EasyMock;
import org.geowebcache.config.legends.LegendRawInfo;
import org.geowebcache.config.legends.LegendsRawInfo;
import org.geowebcache.config.meta.ServiceInformation;
import org.geowebcache.filter.parameters.StringParameterFilter;
import org.geowebcache.grid.GridSetBroker;
import org.geowebcache.grid.GridSubset;
import org.geowebcache.grid.GridSubsetFactory;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.layer.TileLayerDispatcher;
import org.geowebcache.layer.wms.WMSLayer;
import org.geowebcache.util.NullURLMangler;
import org.geowebcache.util.URLMangler;
import org.hamcrest.xml.HasXPath;
import org.junit.Test;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
public class WMSGetCapabilitiesTest {
@Test
public void testEscapeXMLChars() throws Exception {
TileLayerDispatcher tld = createMock(TileLayerDispatcher.class);
HttpServletRequest servReq = createMock(HttpServletRequest.class);
HttpServletResponse response = createMock(HttpServletResponse.class);
String baseUrl = "http://example.com/geowebcache/";
String contextPath = "service/";
URLMangler urlMangler = new NullURLMangler();
ServiceInformation servInfo = createMock(ServiceInformation.class);
Map<String, String[]> parameterMap = new HashMap<>();
parameterMap.put("SERVICE", new String[]{"WMS"});
parameterMap.put("VERSION", new String[]{"1.1.1"});
parameterMap.put("REQUEST", new String[]{"getcapabilities"});
parameterMap.put("TILED", new String[]{"true"});
expect(servReq.getParameterMap()).andStubReturn(Collections.unmodifiableMap(parameterMap));
expect(servReq.getCharacterEncoding()).andStubReturn("UTF-8");
expect(servInfo.getTitle()).andStubReturn("Title & \"stuff\"");
expect(servInfo.getDescription()).andStubReturn("This \"description\" contains <characters> which & should be \'escaped\'.");
expect(servInfo.getKeywords()).andStubReturn(null);
expect(servInfo.getServiceProvider()).andStubReturn(null);
expect(servInfo.getFees()).andStubReturn("NONE");
expect(servInfo.getAccessConstraints()).andStubReturn("NONE");
expect(tld.getServiceInformation()).andStubReturn(servInfo);
// creating some styles for the advertised layer
StringParameterFilter stylesParameterFilter = new StringParameterFilter();
stylesParameterFilter.setKey("STYLES");
stylesParameterFilter.setValues(Arrays.asList("style1", "style2"));
// create grid sets for this layer
Map<String, GridSubset> subSets = new HashMap<>();
GridSubset gridSubSet = GridSubsetFactory.createGridSubSet(new GridSetBroker(true, true).get("EPSG:4326"));
subSets.put(gridSubSet.getName(), gridSubSet);
// create the layer
WMSLayer advertisedLayer = new WMSLayer("testAdv", null, "style,style2", null,
null, subSets, Collections.singletonList(stylesParameterFilter), null, null, false, null);
advertisedLayer.setEnabled(true);
advertisedLayer.setAdvertised(true);
// add legends info to the advertised layer
LegendsRawInfo legendsRawInfo = new LegendsRawInfo();
legendsRawInfo.setDefaultWidth(50);
legendsRawInfo.setDefaultHeight(100);
legendsRawInfo.setDefaultFormat("image/png");
LegendRawInfo legendRawInfo1 = new LegendRawInfo();
legendRawInfo1.setStyle("style1");
legendRawInfo1.setUrl("htp://localhost:8080/geoserver");
LegendRawInfo legendRawInfo2 = new LegendRawInfo();
legendRawInfo2.setStyle("style2");
legendRawInfo2.setUrl("htp://localhost:8080/geoserver");
// tie legend information together
legendsRawInfo.addLegendRawInfo(legendRawInfo1);
legendsRawInfo.addLegendRawInfo(legendRawInfo2);
advertisedLayer.setLegends(legendsRawInfo);
TileLayer unAdvertisedLayer = new WMSLayer("testNotAdv", null, null, null,
null, subSets, null, null, null, false, null);
unAdvertisedLayer.setEnabled(true);
unAdvertisedLayer.setAdvertised(false);
expect(tld.getLayerList()).andStubReturn(Arrays.asList(advertisedLayer, unAdvertisedLayer));
replay(tld, servReq, response, servInfo);
WMSGetCapabilities capabilities = new WMSGetCapabilities(tld, servReq, baseUrl, contextPath, urlMangler);
String xml = capabilities.generateGetCapabilities(StandardCharsets.UTF_8);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xml));
Document document = builder.parse(is);
assertThat(document.getDocumentElement(), HasXPath.hasXPath("/WMT_MS_Capabilities/Service/Title[text()='Title & \"stuff\"']"));
assertThat(document.getDocumentElement(), HasXPath.hasXPath("/WMT_MS_Capabilities/Service/Abstract[text()="+xpathString("This \"description\" contains <characters> which & should be \'escaped\'.")+"]"));
// Be extra strict
assertThat(xml, not(containsString("& ")));
assertThat(xml, not(containsString("<characters>")));
assertThat(xml, not(containsString("'escaped'")));
assertThat(xml, not(containsString("\"description\"")));
assertThat(xml, containsString("testAdv"));
assertThat(xml, not(containsString("testNotAdv")));
// check for legends URL for style 1
assertThat(document.getDocumentElement(), HasXPath.hasXPath("/WMT_MS_Capabilities/Capability/VendorSpecificCapabilities/" +
"TileSet/Styles/Style[Identifier='style1']/LegendURL[@width='50'][@height='100'][Format='image/png']" +
"/OnlineResource[@type='simple'][@href='htp://localhost:8080/geoserver?service=WMS&request=GetLegendGraphic&" +
"format=image/png&width=50&height=100&layer=testAdv&style=style1']"));
// check for legends URL for style 2
assertThat(document.getDocumentElement(), HasXPath.hasXPath("/WMT_MS_Capabilities/Capability/VendorSpecificCapabilities/" +
"TileSet/Styles/Style[Identifier='style2']/LegendURL[@width='50'][@height='100'][Format='image/png']" +
"/OnlineResource[@type='simple'][@href='htp://localhost:8080/geoserver?service=WMS&request=GetLegendGraphic&" +
"format=image/png&width=50&height=100&layer=testAdv&style=style2']"));
EasyMock.verify(tld, servReq, response, servInfo);
}
/**
* Returns an XPath expression equivalent to the given string which can safely include both " and ' characters.
* @param s
* @return
*/
String xpathString(String s) {
StringBuilder b = new StringBuilder();
int len = s.length();
b.append("concat('");
for(int i = 0; i<len; i++) {
char c = s.charAt(i);
if(c=='\'') {
b.append("',\"'\",'");
} else {
b.append(c);
}
}
b.append("')");
return b.toString();
}
}