[jakarta-taglibs-standard] 02/02: Upload to unstable
Miguel Landaeta
nomadium at moszumanska.debian.org
Sun Mar 15 04:56:16 UTC 2015
This is an automated email from the git hooks/post-receive script.
nomadium pushed a commit to branch master
in repository jakarta-taglibs-standard.
commit f987407b2556d34642e8c4009604182225a2c784
Author: Miguel Landaeta <nomadium at debian.org>
Date: Sat Mar 14 22:46:53 2015 -0300
Upload to unstable
---
debian/ant.properties | 4 +-
debian/changelog | 10 +
debian/patches/CVE-2015-0254.patch | 2239 ++++++++++++++++++++++++++++++++++++
debian/patches/series | 1 +
4 files changed, 2252 insertions(+), 2 deletions(-)
diff --git a/debian/ant.properties b/debian/ant.properties
index 837ff82..a89b6a8 100644
--- a/debian/ant.properties
+++ b/debian/ant.properties
@@ -1,7 +1,7 @@
build.dir=build
dist.dir=dist
-ant.build.javac.source=1.4
-ant.build.javac.target=1.4
+ant.build.javac.source=1.5
+ant.build.javac.target=1.5
servlet24.jar=/usr/share/java/servlet-api-2.5.jar
jsp20.jar=/usr/share/java/jsp-api-2.1.jar
xalan.jar=/usr/share/java/xalan2.jar
diff --git a/debian/changelog b/debian/changelog
index 98990bb..c7493ed 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+jakarta-taglibs-standard (1.1.2-3) unstable; urgency=high
+
+ * Team upload.
+ * Fix CVE-2015-0254 XXE and RCE via XSL extension in JSTL XML tags:
+ - Introduce new patch: d/patches/CVE-2015-0254.patch.
+ - Adjust source and target JVM parameters to 1.5.
+ (Closes: #779621).
+
+ -- Miguel Landaeta <nomadium at debian.org> Sat, 14 Mar 2015 22:46:07 -0300
+
jakarta-taglibs-standard (1.1.2-2) unstable; urgency=low
[ James Page ]
diff --git a/debian/patches/CVE-2015-0254.patch b/debian/patches/CVE-2015-0254.patch
new file mode 100644
index 0000000..cc2a804
--- /dev/null
+++ b/debian/patches/CVE-2015-0254.patch
@@ -0,0 +1,2239 @@
+Description: Fix CVE-2015-0254 XXE and RCE via XSL extension in JSTL XML tags
+ When an application uses <x:parse> or <x:transform> tags to process
+ untrusted XML documents, a request may utilize external entity
+ references to access resources on the host system or utilize XSLT
+ extensions that may allow remote execution. For more information, just go
+ to: http://www.securityfocus.com/archive/1/534772.
+Author: The Apache Software Foundation
+Bug-Debian: https://bugs.debian.org/779621
+Origin: upstream, http://svn.apache.org/r1642442, http://svn.apache.org/r1642613
+Forwarded: not-needed
+Last-Update: 2015-03-14
+
+--- /dev/null
++++ jakarta-taglibs-standard-1.1.2/standard/src/javax/servlet/jsp/jstl/tlv/ParserUtil.java
+@@ -0,0 +1,86 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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 javax.servlet.jsp.jstl.tlv;
++
++import java.io.IOException;
++import java.io.InputStream;
++import java.security.AccessController;
++import java.security.PrivilegedAction;
++
++import javax.servlet.jsp.tagext.PageData;
++import javax.xml.XMLConstants;
++import javax.xml.parsers.ParserConfigurationException;
++import javax.xml.parsers.SAXParser;
++import javax.xml.parsers.SAXParserFactory;
++
++import org.xml.sax.SAXException;
++import org.xml.sax.SAXNotRecognizedException;
++import org.xml.sax.SAXNotSupportedException;
++import org.xml.sax.helpers.DefaultHandler;
++
++/**
++ * Support class for working with the SAX Parser.
++ */
++class ParserUtil {
++
++ private static final SAXParserFactory PARSER_FACTORY;
++ static {
++ PARSER_FACTORY = AccessController.doPrivileged(new PrivilegedAction<SAXParserFactory>() {
++ public SAXParserFactory run() {
++ ClassLoader original = Thread.currentThread().getContextClassLoader();
++ ClassLoader ours = ParserUtil.class.getClassLoader();
++ try {
++ if (original != ours) {
++ Thread.currentThread().setContextClassLoader(ours);
++ }
++ return SAXParserFactory.newInstance();
++ } finally {
++ if (original != ours) {
++ Thread.currentThread().setContextClassLoader(original);
++ }
++ }
++ }
++ });
++ try {
++ PARSER_FACTORY.setValidating(true);
++ PARSER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
++ } catch (ParserConfigurationException e) {
++ throw new ExceptionInInitializerError(e);
++ } catch (SAXNotRecognizedException e) {
++ throw new ExceptionInInitializerError(e);
++ } catch (SAXNotSupportedException e) {
++ throw new ExceptionInInitializerError(e);
++ }
++ }
++
++ private ParserUtil() {
++ }
++
++ static void parse(PageData pageData, DefaultHandler handler) throws ParserConfigurationException, SAXException, IOException {
++ SAXParser parser = PARSER_FACTORY.newSAXParser();
++ InputStream is = pageData.getInputStream();
++ try {
++ parser.parse(is, handler);
++ } finally {
++ try {
++ is.close();
++ } catch (IOException e) {
++ // Suppress.
++ }
++ }
++ }
++}
+--- jakarta-taglibs-standard-1.1.2.orig/standard/src/javax/servlet/jsp/jstl/tlv/PermittedTaglibsTLV.java
++++ jakarta-taglibs-standard-1.1.2/standard/src/javax/servlet/jsp/jstl/tlv/PermittedTaglibsTLV.java
+@@ -17,6 +17,7 @@
+ package javax.servlet.jsp.jstl.tlv;
+
+ import java.io.IOException;
++import java.io.InputStream;
+ import java.util.HashSet;
+ import java.util.Set;
+ import java.util.StringTokenizer;
+@@ -92,8 +93,7 @@ public class PermittedTaglibsTLV extends
+ //*********************************************************************
+ // Validation entry point
+
+- public synchronized ValidationMessage[] validate(
+- String prefix, String uri, PageData page) {
++ public synchronized ValidationMessage[] validate(String prefix, String uri, PageData page) {
+ try {
+
+ // initialize
+@@ -104,10 +104,7 @@ public class PermittedTaglibsTLV extends
+ DefaultHandler h = new PermittedTaglibsHandler();
+
+ // parse the page
+- SAXParserFactory f = SAXParserFactory.newInstance();
+- f.setValidating(true);
+- SAXParser p = f.newSAXParser();
+- p.parse(page.getInputStream(), h);
++ ParserUtil.parse(page, h);
+
+ if (failed)
+ return vmFromString(
+--- jakarta-taglibs-standard-1.1.2.orig/standard/src/javax/servlet/jsp/jstl/tlv/ScriptFreeTLV.java
++++ jakarta-taglibs-standard-1.1.2/standard/src/javax/servlet/jsp/jstl/tlv/ScriptFreeTLV.java
+@@ -24,7 +24,6 @@ import javax.servlet.jsp.tagext.PageData
+ import javax.servlet.jsp.tagext.TagLibraryValidator;
+ import javax.servlet.jsp.tagext.ValidationMessage;
+ import javax.xml.parsers.ParserConfigurationException;
+-import javax.xml.parsers.SAXParser;
+ import javax.xml.parsers.SAXParserFactory;
+
+ import org.xml.sax.Attributes;
+@@ -100,32 +99,19 @@ public class ScriptFreeTLV extends TagLi
+ * @return null, if the page is valid; otherwise, a ValidationMessage[]
+ * containing one or more messages indicating why the page is not valid.
+ */
+- public ValidationMessage[] validate
+- (String prefix, String uri, PageData page) {
+- InputStream in = null;
+- SAXParser parser;
+- MyContentHandler handler = new MyContentHandler();
+- try {
+- synchronized (factory) {
+- parser = factory.newSAXParser();
+- }
+- in = page.getInputStream();
+- parser.parse(in, handler);
++ public ValidationMessage[] validate(String prefix, String uri, PageData page) {
++ try {
++ MyContentHandler handler = new MyContentHandler();
++ ParserUtil.parse(page, handler);
++ return handler.reportResults();
++ } catch (ParserConfigurationException e) {
++ return vmFromString(e.toString());
++ } catch (SAXException e) {
++ return vmFromString(e.toString());
++ } catch (IOException e) {
++ return vmFromString(e.toString());
++ }
+ }
+- catch (ParserConfigurationException e) {
+- return vmFromString(e.toString());
+- }
+- catch (SAXException e) {
+- return vmFromString(e.toString());
+- }
+- catch (IOException e) {
+- return vmFromString(e.toString());
+- }
+- finally {
+- if (in != null) try { in.close(); } catch (IOException e) {}
+- }
+- return handler.reportResults();
+- }
+
+ /**
+ * Handler for SAX events.
+--- jakarta-taglibs-standard-1.1.2.orig/standard/src/org/apache/taglibs/standard/tag/common/core/ImportSupport.java
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/tag/common/core/ImportSupport.java
+@@ -45,6 +45,7 @@ import javax.servlet.jsp.tagext.BodyTagS
+ import javax.servlet.jsp.tagext.TryCatchFinally;
+
+ import org.apache.taglibs.standard.resources.Resources;
++import org.apache.taglibs.standard.util.UrlUtil;
+
+ /**
+ * <p>Support for tag handlers for <import>, the general-purpose
+@@ -60,22 +61,6 @@ public abstract class ImportSupport exte
+ //*********************************************************************
+ // Public constants
+
+- /** <p>Valid characters in a scheme.</p>
+- * <p>RFC 1738 says the following:</p>
+- * <blockquote>
+- * Scheme names consist of a sequence of characters. The lower
+- * case letters "a"--"z", digits, and the characters plus ("+"),
+- * period ("."), and hyphen ("-") are allowed. For resiliency,
+- * programs interpreting URLs should treat upper case letters as
+- * equivalent to lower case in scheme names (e.g., allow "HTTP" as
+- * well as "http").
+- * </blockquote>
+- * <p>We treat as absolute any URL that begins with such a scheme name,
+- * followed by a colon.</p>
+- */
+- public static final String VALID_SCHEME_CHARS =
+- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-";
+-
+ /** Default character encoding for response. */
+ public static final String DEFAULT_ENCODING = "ISO-8859-1";
+
+@@ -133,7 +118,7 @@ public abstract class ImportSupport exte
+ throw new NullAttributeException("import", "url");
+
+ // Record whether our URL is absolute or relative
+- isAbsoluteUrl = isAbsoluteUrl();
++ isAbsoluteUrl = UrlUtil.isAbsoluteUrl(url);
+
+ try {
+ // If we need to expose a Reader, we've got to do it right away
+@@ -494,43 +479,10 @@ public abstract class ImportSupport exte
+ return urlWithParams;
+ }
+
+- /**
+- * Returns <tt>true</tt> if our current URL is absolute,
+- * <tt>false</tt> otherwise.
+- */
+- private boolean isAbsoluteUrl() throws JspTagException {
+- return isAbsoluteUrl(url);
+- }
+-
+-
+ //*********************************************************************
+ // Public utility methods
+
+ /**
+- * Returns <tt>true</tt> if our current URL is absolute,
+- * <tt>false</tt> otherwise.
+- */
+- public static boolean isAbsoluteUrl(String url) {
+- // a null URL is not absolute, by our definition
+- if (url == null)
+- return false;
+-
+- // do a fast, simple check first
+- int colonPos;
+- if ((colonPos = url.indexOf(":")) == -1)
+- return false;
+-
+- // if we DO have a colon, make sure that every character
+- // leading up to it is a valid scheme character
+- for (int i = 0; i < colonPos; i++)
+- if (VALID_SCHEME_CHARS.indexOf(url.charAt(i)) == -1)
+- return false;
+-
+- // if so, we've got an absolute url
+- return true;
+- }
+-
+- /**
+ * Strips a servlet session ID from <tt>url</tt>. The session ID
+ * is encoded as a URL "path parameter" beginning with "jsessionid=".
+ * We thus remove anything we find between ";jsessionid=" (inclusive)
+--- jakarta-taglibs-standard-1.1.2.orig/standard/src/org/apache/taglibs/standard/tag/common/core/RedirectSupport.java
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/tag/common/core/RedirectSupport.java
+@@ -22,6 +22,8 @@ import javax.servlet.jsp.JspTagException
+ import javax.servlet.jsp.PageContext;
+ import javax.servlet.jsp.tagext.BodyTagSupport;
+
++import org.apache.taglibs.standard.util.UrlUtil;
++
+ /**
+ * <p>Support for tag handlers for <redirect>, JSTL 1.0's tag
+ * for redirecting to a new URL (with optional query parameters).</p>
+@@ -90,29 +92,30 @@ public abstract class RedirectSupport ex
+ return EVAL_BODY_BUFFERED;
+ }
+
+-
+ // gets the right value, encodes it, and prints or stores it
++
+ public int doEndTag() throws JspException {
+- String result; // the eventual result
++ String result; // the eventual result
+
+- // add (already encoded) parameters
++ // add (already encoded) parameters
+ String baseUrl = UrlSupport.resolveUrl(url, context, pageContext);
+ result = params.aggregateParams(baseUrl);
+
+ // if the URL is relative, rewrite it with 'redirect' encoding rules
+ HttpServletResponse response =
+- ((HttpServletResponse) pageContext.getResponse());
+- if (!ImportSupport.isAbsoluteUrl(result))
++ ((HttpServletResponse) pageContext.getResponse());
++ if (!UrlUtil.isAbsoluteUrl(result)) {
+ result = response.encodeRedirectURL(result);
++ }
+
+- // redirect!
+- try {
+- response.sendRedirect(result);
+- } catch (java.io.IOException ex) {
+- throw new JspTagException(ex.toString(), ex);
+- }
++ // redirect!
++ try {
++ response.sendRedirect(result);
++ } catch (java.io.IOException ex) {
++ throw new JspTagException(ex.toString(), ex);
++ }
+
+- return SKIP_PAGE;
++ return SKIP_PAGE;
+ }
+
+ // Releases any resources we may have (or inherit)
+--- jakarta-taglibs-standard-1.1.2.orig/standard/src/org/apache/taglibs/standard/tag/common/core/UrlSupport.java
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/tag/common/core/UrlSupport.java
+@@ -24,6 +24,7 @@ import javax.servlet.jsp.PageContext;
+ import javax.servlet.jsp.tagext.BodyTagSupport;
+
+ import org.apache.taglibs.standard.resources.Resources;
++import org.apache.taglibs.standard.util.UrlUtil;
+
+ /**
+ * <p>Support for tag handlers for <url>, the URL creation
+@@ -104,7 +105,7 @@ public abstract class UrlSupport extends
+ result = params.aggregateParams(baseUrl);
+
+ // if the URL is relative, rewrite it
+- if (!ImportSupport.isAbsoluteUrl(result)) {
++ if (!UrlUtil.isAbsoluteUrl(result)) {
+ HttpServletResponse response =
+ ((HttpServletResponse) pageContext.getResponse());
+ result = response.encodeURL(result);
+@@ -134,29 +135,32 @@ public abstract class UrlSupport extends
+
+ public static String resolveUrl(
+ String url, String context, PageContext pageContext)
+- throws JspException {
+- // don't touch absolute URLs
+- if (ImportSupport.isAbsoluteUrl(url))
+- return url;
+-
+- // normalize relative URLs against a context root
+- HttpServletRequest request =
+- (HttpServletRequest) pageContext.getRequest();
+- if (context == null) {
+- if (url.startsWith("/"))
+- return (request.getContextPath() + url);
+- else
+- return url;
+- } else {
++ throws JspException {
++ // don't touch absolute URLs
++ if (UrlUtil.isAbsoluteUrl(url)) {
++ return url;
++ }
++
++ // normalize relative URLs against a context root
++ HttpServletRequest request =
++ (HttpServletRequest) pageContext.getRequest();
++ if (context == null) {
++ if (url.startsWith("/")) {
++ return (request.getContextPath() + url);
++ } else {
++ return url;
++ }
++ } else {
+ if (!context.startsWith("/") || !url.startsWith("/")) {
+ throw new JspTagException(
+- Resources.getMessage("IMPORT_BAD_RELATIVE"));
++ Resources.getMessage("IMPORT_BAD_RELATIVE"));
+ }
+- if (context.equals("/")) {
++ if (context.endsWith("/") && url.startsWith("/")) {
+ // Don't produce string starting with '//', many
+ // browsers interpret this as host name, not as
+- // path on same host.
+- return url;
++ // path on same host. Bug 22860
++ // Also avoid // inside the url. Bug 34109
++ return (context.substring(0, context.length() - 1) + url);
+ } else {
+ return (context + url);
+ }
+--- /dev/null
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/tag/common/xml/JSTLVariableStack.java
+@@ -0,0 +1,132 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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 org.apache.taglibs.standard.tag.common.xml;
++
++import java.util.HashMap;
++import java.util.Map;
++
++import javax.servlet.http.Cookie;
++import javax.servlet.http.HttpServletRequest;
++import javax.servlet.jsp.PageContext;
++import javax.xml.transform.TransformerException;
++
++import org.apache.taglibs.standard.resources.Resources;
++import org.apache.xml.utils.QName;
++import org.apache.xpath.VariableStack;
++import org.apache.xpath.XPathContext;
++import org.apache.xpath.objects.XObject;
++import org.apache.xpath.objects.XObjectFactory;
++
++/**
++ */
++public class JSTLVariableStack extends VariableStack {
++
++ private static enum Scope {
++ PARAM,
++ HEADER,
++ COOKIE,
++ INITPARAM,
++ PAGE,
++ REQUEST,
++ SESSION,
++ APPLICATION
++ }
++
++ // Prefixes for JSTL implicit variables
++ private static final String PARAM_PREFIX = "param";
++ private static final String HEADER_PREFIX = "header";
++ private static final String COOKIE_PREFIX = "cookie";
++ private static final String INITPARAM_PREFIX = "initParam";
++ private static final String PAGE_PREFIX = "pageScope";
++ private static final String REQUEST_PREFIX = "requestScope";
++ private static final String SESSION_PREFIX = "sessionScope";
++ private static final String APP_PREFIX = "applicationScope";
++
++ // map prefixes to scopes
++ private static final Map<String, Scope> SCOPES;
++ static {
++ SCOPES = new HashMap<String, Scope>(8);
++ SCOPES.put(PARAM_PREFIX, Scope.PARAM);
++ SCOPES.put(HEADER_PREFIX, Scope.HEADER);
++ SCOPES.put(COOKIE_PREFIX, Scope.COOKIE);
++ SCOPES.put(INITPARAM_PREFIX, Scope.INITPARAM);
++ SCOPES.put(PAGE_PREFIX, Scope.PAGE);
++ SCOPES.put(REQUEST_PREFIX, Scope.REQUEST);
++ SCOPES.put(SESSION_PREFIX, Scope.SESSION);
++ SCOPES.put(APP_PREFIX, Scope.APPLICATION);
++ }
++
++ private final PageContext pageContext;
++
++ public JSTLVariableStack(PageContext pageContext) {
++ super(2);
++ this.pageContext = pageContext;
++ }
++
++ @Override
++ public XObject getVariableOrParam(XPathContext xctxt, QName qname) throws TransformerException {
++ String prefix = qname.getNamespaceURI();
++ String name = qname.getLocalPart();
++ Object value = getValue(prefix, name);
++ if (value == null) {
++ StringBuilder var = new StringBuilder();
++ var.append('$');
++ if (prefix != null) {
++ var.append(prefix);
++ var.append(':');
++ }
++ var.append(name);
++ throw new TransformerException(Resources.getMessage("XPATH_UNABLE_TO_RESOLVE_VARIABLE", var.toString()));
++ }
++ return XObjectFactory.create(value, xctxt);
++ }
++
++ private Object getValue(String prefix, String name) {
++ if (prefix == null) {
++ return pageContext.findAttribute(name);
++ }
++ Scope scope = SCOPES.get(prefix);
++ switch (scope) {
++ case PARAM:
++ return pageContext.getRequest().getParameter(name);
++ case HEADER:
++ return ((HttpServletRequest) pageContext.getRequest()).getHeader(name);
++ case COOKIE:
++ Cookie[] cookies = ((HttpServletRequest) pageContext.getRequest()).getCookies();
++ if (cookies != null) {
++ for (Cookie cookie : cookies) {
++ if (cookie.getName().equals(name)) {
++ return cookie.getValue();
++ }
++ }
++ }
++ return null;
++ case INITPARAM:
++ return pageContext.getServletContext().getInitParameter(name);
++ case PAGE:
++ return pageContext.getAttribute(name, PageContext.PAGE_SCOPE);
++ case REQUEST:
++ return pageContext.getAttribute(name, PageContext.REQUEST_SCOPE);
++ case SESSION:
++ return pageContext.getAttribute(name, PageContext.SESSION_SCOPE);
++ case APPLICATION:
++ return pageContext.getAttribute(name, PageContext.APPLICATION_SCOPE);
++ default:
++ throw new AssertionError();
++ }
++ }
++}
+--- jakarta-taglibs-standard-1.1.2.orig/standard/src/org/apache/taglibs/standard/tag/common/xml/ParseSupport.java
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/tag/common/xml/ParseSupport.java
+@@ -16,36 +16,26 @@
+
+ package org.apache.taglibs.standard.tag.common.xml;
+
+-import java.io.FileNotFoundException;
+ import java.io.IOException;
+-import java.io.InputStream;
+ import java.io.Reader;
+ import java.io.StringReader;
+
+-import javax.servlet.http.HttpServletRequest;
+ import javax.servlet.jsp.JspException;
+ import javax.servlet.jsp.JspTagException;
+ import javax.servlet.jsp.PageContext;
+ import javax.servlet.jsp.tagext.BodyTagSupport;
+ import javax.xml.parsers.DocumentBuilder;
+-import javax.xml.parsers.DocumentBuilderFactory;
+-import javax.xml.parsers.ParserConfigurationException;
+ import javax.xml.transform.TransformerConfigurationException;
+-import javax.xml.transform.TransformerFactory;
+ import javax.xml.transform.dom.DOMResult;
+-import javax.xml.transform.sax.SAXTransformerFactory;
+ import javax.xml.transform.sax.TransformerHandler;
+
+ import org.apache.taglibs.standard.resources.Resources;
+-import org.apache.taglibs.standard.tag.common.core.ImportSupport;
+ import org.apache.taglibs.standard.tag.common.core.Util;
+ import org.w3c.dom.Document;
+-import org.xml.sax.EntityResolver;
+ import org.xml.sax.InputSource;
+ import org.xml.sax.SAXException;
+ import org.xml.sax.XMLFilter;
+ import org.xml.sax.XMLReader;
+-import org.xml.sax.helpers.XMLReaderFactory;
+
+ /**
+ * <p>Support for tag handlers for <parse>, the XML parsing tag.</p>
+@@ -68,12 +58,7 @@ public abstract class ParseSupport exten
+ private String varDom; // 'varDom' attribute
+ private int scope; // processed 'scope' attr
+ private int scopeDom; // processed 'scopeDom' attr
+-
+- // state in support of XML parsing...
+- private DocumentBuilderFactory dbf;
+- private DocumentBuilder db;
+- private TransformerFactory tf;
+- private TransformerHandler th;
++ private XmlUtil.JstlEntityResolver entityResolver;
+
+
+ //*********************************************************************
+@@ -89,76 +74,50 @@ public abstract class ParseSupport exten
+ xml = null;
+ systemId = null;
+ filter = null;
+- dbf = null;
+- db = null;
+- tf = null;
+- th = null;
+ scope = PageContext.PAGE_SCOPE;
+ scopeDom = PageContext.PAGE_SCOPE;
+ }
+
+-
+ //*********************************************************************
+ // Tag logic
+
+ // parse 'source' or body, storing result in 'var'
+ public int doEndTag() throws JspException {
+- try {
+-
+- // set up our DocumentBuilder
+- if (dbf == null) {
+- dbf = DocumentBuilderFactory.newInstance();
+- dbf.setNamespaceAware(true);
+- dbf.setValidating(false);
++ // produce a Document by parsing whatever the attributes tell us to use
++ Object xmlText = this.xml;
++ if (xmlText == null) {
++ // if the attribute was specified, use the body as 'xml'
++ if (bodyContent != null && bodyContent.getString() != null) {
++ xmlText = bodyContent.getString().trim();
++ } else {
++ xmlText = "";
++ }
++ }
++ if (xmlText instanceof String) {
++ xmlText = new StringReader((String) xmlText);
++ }
++ if (!(xmlText instanceof Reader)) {
++ throw new JspTagException(Resources.getMessage("PARSE_INVALID_SOURCE"));
++ }
++ InputSource source = XmlUtil.newInputSource(((Reader) xmlText), systemId);
++
++ Document d;
++ if (filter != null) {
++ d = parseInputSourceWithFilter(source, filter);
++ } else {
++ d = parseInputSource(source);
++ }
++
++ // we've got a Document object; store it out as appropriate
++ // (let any exclusivity or other constraints be enforced by TEI/TLV)
++ if (var != null) {
++ pageContext.setAttribute(var, d, scope);
++ }
++ if (varDom != null) {
++ pageContext.setAttribute(varDom, d, scopeDom);
+ }
+- db = dbf.newDocumentBuilder();
+
+- // if we've gotten a filter, set up a transformer to support it
+- if (filter != null) {
+- if (tf == null)
+- tf = TransformerFactory.newInstance();
+- if (!tf.getFeature(SAXTransformerFactory.FEATURE))
+- throw new JspTagException(
+- Resources.getMessage("PARSE_NO_SAXTRANSFORMER"));
+- SAXTransformerFactory stf = (SAXTransformerFactory) tf;
+- th = stf.newTransformerHandler();
+- }
+-
+- // produce a Document by parsing whatever the attributes tell us to use
+- Document d;
+- Object xmlText = this.xml;
+- if (xmlText == null) {
+- // if the attribute was specified, use the body as 'xml'
+- if (bodyContent != null && bodyContent.getString() != null)
+- xmlText = bodyContent.getString().trim();
+- else
+- xmlText = "";
+- }
+- if (xmlText instanceof String)
+- d = parseStringWithFilter((String) xmlText, filter);
+- else if (xmlText instanceof Reader)
+- d = parseReaderWithFilter((Reader) xmlText, filter);
+- else
+- throw new JspTagException(
+- Resources.getMessage("PARSE_INVALID_SOURCE"));
+-
+- // we've got a Document object; store it out as appropriate
+- // (let any exclusivity or other constraints be enforced by TEI/TLV)
+- if (var != null)
+- pageContext.setAttribute(var, d, scope);
+- if (varDom != null)
+- pageContext.setAttribute(varDom, d, scopeDom);
+-
+- return EVAL_PAGE;
+- } catch (SAXException ex) {
+- throw new JspException(ex);
+- } catch (IOException ex) {
+- throw new JspException(ex);
+- } catch (ParserConfigurationException ex) {
+- throw new JspException(ex);
+- } catch (TransformerConfigurationException ex) {
+- throw new JspException(ex);
+- }
++ return EVAL_PAGE;
+ }
+
+ // Releases any resources we may have (or inherit)
+@@ -171,126 +130,48 @@ public abstract class ParseSupport exten
+ // Private utility methods
+
+ /** Parses the given InputSource after, applying the given XMLFilter. */
+- private Document parseInputSourceWithFilter(InputSource s, XMLFilter f)
+- throws SAXException, IOException {
+- if (f != null) {
+- // prepare an output Document
+- Document o = db.newDocument();
+-
+- // use TrAX to adapt SAX events to a Document object
+- th.setResult(new DOMResult(o));
+- XMLReader xr = XMLReaderFactory.createXMLReader();
+- xr.setEntityResolver(new JstlEntityResolver(pageContext));
++ private Document parseInputSourceWithFilter(InputSource s, XMLFilter f) throws JspException {
++ try {
++ XMLReader xr = XmlUtil.newXMLReader(entityResolver);
+ // (note that we overwrite the filter's parent. this seems
+ // to be expected usage. we could cache and reset the old
+ // parent, but you can't setParent(null), so this wouldn't
+ // be perfect.)
+ f.setParent(xr);
+- f.setContentHandler(th);
+- f.parse(s);
+- return o;
+- } else
+- return parseInputSource(s);
+- }
+
+- /** Parses the given Reader after applying the given XMLFilter. */
+- private Document parseReaderWithFilter(Reader r, XMLFilter f)
+- throws SAXException, IOException {
+- return parseInputSourceWithFilter(new InputSource(r), f);
+- }
++ TransformerHandler th = XmlUtil.newTransformerHandler();
++ Document o = XmlUtil.newEmptyDocument();
++ th.setResult(new DOMResult(o));
+
+- /** Parses the given String after applying the given XMLFilter. */
+- private Document parseStringWithFilter(String s, XMLFilter f)
+- throws SAXException, IOException {
+- StringReader r = new StringReader(s);
+- return parseReaderWithFilter(r, f);
+- }
++ f.setContentHandler(th);
+
+- /** Parses the given Reader after applying the given XMLFilter. */
+- private Document parseURLWithFilter(String url, XMLFilter f)
+- throws SAXException, IOException {
+- return parseInputSourceWithFilter(new InputSource(url), f);
++ f.parse(s);
++ return o;
++ } catch (IOException e) {
++ throw new JspException(e);
++ } catch (SAXException e) {
++ throw new JspException(e);
++ } catch (TransformerConfigurationException e) {
++ throw new JspException(e);
++ }
+ }
+
+ /** Parses the given InputSource into a Document. */
+- private Document parseInputSource(InputSource s)
+- throws SAXException, IOException {
+- db.setEntityResolver(new JstlEntityResolver(pageContext));
+-
+- // normalize URIs so they can be processed consistently by resolver
+- if (systemId == null)
+- s.setSystemId("jstl:");
+- else if (ImportSupport.isAbsoluteUrl(systemId))
+- s.setSystemId(systemId);
+- else
+- s.setSystemId("jstl:" + systemId);
+- return db.parse(s);
+- }
+-
+- /** Parses the given Reader into a Document. */
+- private Document parseReader(Reader r) throws SAXException, IOException {
+- return parseInputSource(new InputSource(r));
+- }
+-
+- /** Parses the given String into a Document. */
+- private Document parseString(String s) throws SAXException, IOException {
+- StringReader r = new StringReader(s);
+- return parseReader(r);
+- }
+-
+- /** Parses the URL (passed as a String) into a Document. */
+- private Document parseURL(String url) throws SAXException, IOException {
+- return parseInputSource(new InputSource(url));
+- }
+-
+- //*********************************************************************
+- // JSTL-specific EntityResolver class
+-
+- /** Lets us resolve relative external entities. */
+- public static class JstlEntityResolver implements EntityResolver {
+- private final PageContext ctx;
+- public JstlEntityResolver(PageContext ctx) {
+- this.ctx = ctx;
++ private Document parseInputSource(InputSource s) throws JspException {
++ try {
++ DocumentBuilder db = XmlUtil.newDocumentBuilder();
++ db.setEntityResolver(entityResolver);
++ return db.parse(s);
++ } catch (SAXException e) {
++ throw new JspException(e);
++ } catch (IOException e) {
++ throw new JspException(e);
+ }
+- public InputSource resolveEntity(String publicId, String systemId)
+- throws FileNotFoundException {
++ }
+
+- // pass if we don't have a systemId
+- if (systemId == null)
+- return null;
+-
+- // strip leading "jstl:" off URL if applicable
+- if (systemId.startsWith("jstl:"))
+- systemId = systemId.substring(5);
+-
+- // we're only concerned with relative URLs
+- if (ImportSupport.isAbsoluteUrl(systemId))
+- return null;
+-
+- // for relative URLs, load and wrap the resource.
+- // don't bother checking for 'null' since we specifically want
+- // the parser to fail if the resource doesn't exist
+- InputStream s;
+- if (systemId.startsWith("/")) {
+- s = ctx.getServletContext().getResourceAsStream(systemId);
+- if (s == null)
+- throw new FileNotFoundException(
+- Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY",
+- systemId));
+- } else {
+- String pagePath =
+- ((HttpServletRequest) ctx.getRequest()).getServletPath();
+- String basePath =
+- pagePath.substring(0, pagePath.lastIndexOf("/"));
+- s = ctx.getServletContext().getResourceAsStream(
+- basePath + "/" + systemId);
+- if (s == null)
+- throw new FileNotFoundException(
+- Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY",
+- systemId));
+- }
+- return new InputSource(s);
+- }
++ public void setPageContext(PageContext pageContext) {
++ super.setPageContext(pageContext);
++ entityResolver = pageContext == null ? null: new XmlUtil.JstlEntityResolver(pageContext);
+ }
+
+ //*********************************************************************
+--- jakarta-taglibs-standard-1.1.2.orig/standard/src/org/apache/taglibs/standard/tag/common/xml/TransformSupport.java
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/tag/common/xml/TransformSupport.java
+@@ -16,43 +16,29 @@
+
+ package org.apache.taglibs.standard.tag.common.xml;
+
+-import java.io.IOException;
+-import java.io.InputStream;
+ import java.io.Reader;
+ import java.io.StringReader;
+-import java.io.Writer;
+ import java.util.List;
+
+-import javax.servlet.http.HttpServletRequest;
+ import javax.servlet.jsp.JspException;
+ import javax.servlet.jsp.JspTagException;
+ import javax.servlet.jsp.PageContext;
+ import javax.servlet.jsp.tagext.BodyTagSupport;
+-import javax.xml.parsers.DocumentBuilder;
+-import javax.xml.parsers.DocumentBuilderFactory;
+-import javax.xml.parsers.ParserConfigurationException;
+ import javax.xml.transform.Result;
+ import javax.xml.transform.Source;
+ import javax.xml.transform.Transformer;
+ import javax.xml.transform.TransformerConfigurationException;
+ import javax.xml.transform.TransformerException;
+-import javax.xml.transform.TransformerFactory;
+-import javax.xml.transform.URIResolver;
+ import javax.xml.transform.dom.DOMResult;
+ import javax.xml.transform.dom.DOMSource;
+-import javax.xml.transform.sax.SAXSource;
+ import javax.xml.transform.stream.StreamResult;
+-import javax.xml.transform.stream.StreamSource;
+
+ import org.apache.taglibs.standard.resources.Resources;
+-import org.apache.taglibs.standard.tag.common.core.ImportSupport;
+ import org.apache.taglibs.standard.tag.common.core.Util;
++import org.apache.taglibs.standard.util.UnclosableWriter;
+ import org.w3c.dom.Document;
+ import org.w3c.dom.Node;
+-import org.xml.sax.InputSource;
+ import org.xml.sax.SAXException;
+-import org.xml.sax.XMLReader;
+-import org.xml.sax.helpers.XMLReaderFactory;
+
+ /**
+ * <p>Support for tag handlers for <transform>, the XML transformation
+@@ -66,6 +52,7 @@ public abstract class TransformSupport e
+ // Protected state
+
+ protected Object xml; // attribute
++ protected boolean xmlSpecified; // true if xml attribute was specified
+ protected String xmlSystemId; // attribute
+ protected Object xslt; // attribute
+ protected String xsltSystemId; // attribute
+@@ -77,25 +64,22 @@ public abstract class TransformSupport e
+ private String var; // 'var' attribute
+ private int scope; // processed 'scope' attr
+ private Transformer t; // actual Transformer
+- private TransformerFactory tf; // reusable factory
+- private DocumentBuilder db; // reusable factory
+- private DocumentBuilderFactory dbf; // reusable factory
+-
++ private XmlUtil.JstlEntityResolver entityResolver;
++ private XmlUtil.JstlUriResolver uriResolver;
+
+ //*********************************************************************
+ // Constructor and initialization
+
+ public TransformSupport() {
+- super();
+ init();
+ }
+
+ private void init() {
+ xml = xslt = null;
++ xmlSpecified = false;
+ xmlSystemId = xsltSystemId = null;
+ var = null;
+ result = null;
+- tf = null;
+ scope = PageContext.PAGE_SCOPE;
+ }
+
+@@ -104,107 +88,70 @@ public abstract class TransformSupport e
+ // Tag logic
+
+ public int doStartTag() throws JspException {
+- /*
+- * We can set up our Transformer here, so we do so, and we let
+- * it receive parameters directly from subtags (instead of
+- * caching them.
+- */
+- try {
+-
+- //************************************
+- // Initialize
+-
+- // set up our DocumentBuilderFactory if necessary
+- if (dbf == null) {
+- dbf = DocumentBuilderFactory.newInstance();
+- dbf.setNamespaceAware(true);
+- dbf.setValidating(false);
+- }
+- if (db == null)
+- db = dbf.newDocumentBuilder();
+-
+- // set up the TransformerFactory if necessary
+- if (tf == null)
+- tf = TransformerFactory.newInstance();
+-
+- //************************************
+- // Produce transformer
+-
+- Source s;
+- if (xslt != null) {
+- if (!(xslt instanceof String) && !(xslt instanceof Reader)
+- && !(xslt instanceof javax.xml.transform.Source))
+- throw new JspTagException(
+- Resources.getMessage("TRANSFORM_XSLT_UNRECOGNIZED"));
+- s = getSource(xslt, xsltSystemId);
+- } else {
+- throw new JspTagException(
+- Resources.getMessage("TRANSFORM_NO_TRANSFORMER"));
+- }
+- tf.setURIResolver(new JstlUriResolver(pageContext));
+- t = tf.newTransformer(s);
+-
+- return EVAL_BODY_BUFFERED;
+-
+- } catch (SAXException ex) {
+- throw new JspException(ex);
+- } catch (ParserConfigurationException ex) {
+- throw new JspException(ex);
+- } catch (IOException ex) {
+- throw new JspException(ex);
+- } catch (TransformerConfigurationException ex) {
+- throw new JspException(ex);
+- }
++ // set up transformer in the start tag so that nested <param> tags can set parameters directly
++ if (xslt == null) {
++ throw new JspTagException(Resources.getMessage("TRANSFORM_XSLT_IS_NULL"));
++ }
++
++ Source source;
++ try {
++ if (xslt instanceof Source) {
++ source = (Source) xslt;
++ } else if (xslt instanceof String) {
++ String s = (String) xslt;
++ s = s.trim();
++ if (s.length() == 0) {
++ throw new JspTagException(Resources.getMessage("TRANSFORM_XSLT_IS_EMPTY"));
++ }
++ source = XmlUtil.newSAXSource(new StringReader(s), xsltSystemId, entityResolver);
++ } else if (xslt instanceof Reader) {
++ source = XmlUtil.newSAXSource((Reader) xslt, xsltSystemId, entityResolver);
++ } else {
++ throw new JspTagException(Resources.getMessage("TRANSFORM_XSLT_UNSUPPORTED_TYPE", xslt.getClass()));
++ }
++ } catch (SAXException e) {
++ throw new JspException(e);
++ }
++
++ try {
++ t = XmlUtil.newTransformer(source);
++ t.setURIResolver(uriResolver);
++ } catch (TransformerConfigurationException e) {
++ throw new JspTagException(e);
++ } catch (RuntimeException e) {
++ throw e;
++ }
++ return EVAL_BODY_BUFFERED;
+ }
+
+- // parse 'xml' or body, transform via our Transformer,
+- // and store as 'var' or through 'result'
+ public int doEndTag() throws JspException {
+- try {
+
+- //************************************
+- // Determine source XML
++ try {
++ Source source = xmlSpecified ? getSourceFromXmlAttribute() : getSourceFromBodyContent();
+
+- // if we haven't gotten a source, use the body (which may be empty)
+- Object xml = this.xml;
+- if (xml == null) // still equal
+- if (bodyContent != null && bodyContent.getString() != null)
+- xml = bodyContent.getString().trim();
+- else
+- xml = "";
+-
+- // let the Source be with you
+- Source source = getSource(xml, xmlSystemId);
+-
+- //************************************
+- // Conduct the transformation
+-
+- // we can assume at most one of 'var' or 'result' is specified
+- if (result != null)
+- // we can write directly to the Result
+- t.transform(source, result);
+- else if (var != null) {
+- // we need a Document
+- Document d = db.newDocument();
+- Result doc = new DOMResult(d);
+- t.transform(source, doc);
+- pageContext.setAttribute(var, d, scope);
+- } else {
+- Result page =
+- new StreamResult(new SafeWriter(pageContext.getOut()));
+- t.transform(source, page);
+- }
+-
+- return EVAL_PAGE;
+- } catch (SAXException ex) {
+- throw new JspException(ex);
+- } catch (ParserConfigurationException ex) {
+- throw new JspException(ex);
+- } catch (IOException ex) {
+- throw new JspException(ex);
+- } catch (TransformerException ex) {
+- throw new JspException(ex);
+- }
++ // Conduct the transformation
++ if (var != null) {
++ // Save the result to var.
++ Document d = XmlUtil.newEmptyDocument();
++ Result doc = new DOMResult(d);
++ t.transform(source, doc);
++ pageContext.setAttribute(var, d, scope);
++ } else {
++ // Write to out if result is not specified.
++ Result out = result;
++ if (out == null) {
++ out = new StreamResult(new UnclosableWriter(pageContext.getOut()));
++ }
++ t.transform(source, out);
++ }
++ return EVAL_PAGE;
++ } catch (TransformerException ex) {
++ throw new JspException(ex);
++ } catch (SAXException e) {
++ throw new JspException(e);
++ } finally {
++ t = null;
++ }
+ }
+
+ // Releases any resources we may have (or inherit)
+@@ -212,6 +159,11 @@ public abstract class TransformSupport e
+ init();
+ }
+
++ public void setPageContext(PageContext pageContext) {
++ super.setPageContext(pageContext);
++ uriResolver = pageContext == null ? null : new XmlUtil.JstlUriResolver(pageContext);
++ entityResolver = pageContext == null ? null : new XmlUtil.JstlEntityResolver(pageContext);
++ }
+
+ //*********************************************************************
+ // Public methods for subtags
+@@ -226,64 +178,67 @@ public abstract class TransformSupport e
+ // Utility methods
+
+ /**
+- * Wraps systemId with a "jstl:" prefix to prevent the parser from
+- * thinking that the URI is truly relative and resolving it against
+- * the current directory in the filesystem.
++ * Return the Source for a document specified in the "doc" or "xml" attribute.
++ *
++ * @return the document Source
++ * @throws JspTagException if there is a problem with the attribute
+ */
+- private static String wrapSystemId(String systemId) {
+- if (systemId == null)
+- return "jstl:";
+- else if (ImportSupport.isAbsoluteUrl(systemId))
+- return systemId;
+- else
+- return ("jstl:" + systemId);
++ Source getSourceFromXmlAttribute() throws JspTagException, SAXException {
++ Object xml = this.xml;
++ if (xml == null) {
++ throw new JspTagException(Resources.getMessage("TRANSFORM_XML_IS_NULL"));
++ }
++
++ // other JSTL XML tags may produce a list
++ if (xml instanceof List) {
++ List<?> list = (List<?>) xml;
++ if (list.size() != 1) {
++ throw new JspTagException(Resources.getMessage("TRANSFORM_XML_LIST_SIZE"));
++ }
++ xml = list.get(0);
++ }
++
++ if (xml instanceof Source) {
++ return (Source) xml;
++ }
++ if (xml instanceof String) {
++ String s = (String) xml;
++ s = s.trim();
++ if (s.length() == 0) {
++ throw new JspTagException(Resources.getMessage("TRANSFORM_XML_IS_EMPTY"));
++ }
++ return XmlUtil.newSAXSource(new StringReader(s), xmlSystemId, entityResolver);
++ }
++ if (xml instanceof Reader) {
++ return XmlUtil.newSAXSource((Reader) xml, xmlSystemId, entityResolver);
++ }
++ if (xml instanceof Node) {
++ return new DOMSource((Node) xml, xmlSystemId);
++ }
++ throw new JspTagException(Resources.getMessage("TRANSFORM_XML_UNSUPPORTED_TYPE", xml.getClass()));
+ }
+
+ /**
+- * Retrieves a Source from the given Object, whether it be a String,
+- * Reader, Node, or other supported types (even a Source already).
+- * If 'url' is true, then we must be passed a String and will interpret
+- * it as a URL. A null input always results in a null output.
++ * Return the Source for a document specified as body content.
++ *
++ * @return the document Source
++ * @throws JspTagException if there is a problem with the body content
+ */
+- private Source getSource(Object o, String systemId)
+- throws SAXException, ParserConfigurationException, IOException {
+- if (o == null)
+- return null;
+- else if (o instanceof Source) {
+- return (Source) o;
+- } else if (o instanceof String) {
+- // if we've got a string, chain to Reader below
+- return getSource(new StringReader((String) o), systemId);
+- } else if (o instanceof Reader) {
+- // explicitly go through SAX to maintain control
+- // over how relative external entities resolve
+- XMLReader xr = XMLReaderFactory.createXMLReader();
+- xr.setEntityResolver(
+- new ParseSupport.JstlEntityResolver(pageContext));
+- InputSource s = new InputSource((Reader) o);
+- s.setSystemId(wrapSystemId(systemId));
+- Source result = new SAXSource(xr, s);
+- result.setSystemId(wrapSystemId(systemId));
+- return result;
+- } else if (o instanceof Node) {
+- return new DOMSource((Node) o);
+- } else if (o instanceof List) {
+- // support 1-item List because our XPath processor outputs them
+- List l = (List) o;
+- if (l.size() == 1) {
+- return getSource(l.get(0), systemId); // unwrap List
+- } else {
+- throw new IllegalArgumentException(
+- Resources.getMessage("TRANSFORM_SOURCE_INVALID_LIST"));
+- }
+- } else {
+- throw new IllegalArgumentException(
+- Resources.getMessage("TRANSFORM_SOURCE_UNRECOGNIZED")
+- + o.getClass());
+- }
++ Source getSourceFromBodyContent() throws JspTagException, SAXException {
++ if (bodyContent == null) {
++ throw new JspTagException(Resources.getMessage("TRANSFORM_BODY_IS_NULL"));
++ }
++ String s = bodyContent.getString();
++ if (s == null) {
++ throw new JspTagException(Resources.getMessage("TRANSFORM_BODY_CONTENT_IS_NULL"));
++ }
++ s = s.trim();
++ if (s.length() == 0) {
++ throw new JspTagException(Resources.getMessage("TRANSFORM_BODY_IS_EMPTY"));
++ }
++ return XmlUtil.newSAXSource(new StringReader(s), xmlSystemId, entityResolver);
+ }
+
+-
+ //*********************************************************************
+ // Tag attributes
+
+@@ -294,88 +249,4 @@ public abstract class TransformSupport e
+ public void setScope(String scope) {
+ this.scope = Util.getScope(scope);
+ }
+-
+-
+- //*********************************************************************
+- // Private utility classes
+-
+- /**
+- * A Writer based on a wrapped Writer but ignoring requests to
+- * close() and flush() it. (Someone must have wrapped the
+- * toilet in my office similarly...)
+- */
+- private static class SafeWriter extends Writer {
+- private Writer w;
+- public SafeWriter(Writer w) { this.w = w; }
+- public void close() { }
+- public void flush() { }
+- public void write(char[] cbuf, int off, int len) throws IOException {
+- w.write(cbuf, off, len);
+- }
+- }
+-
+- //*********************************************************************
+- // JSTL-specific URIResolver class
+-
+- /** Lets us resolve relative external entities. */
+- private static class JstlUriResolver implements URIResolver {
+- private final PageContext ctx;
+- public JstlUriResolver(PageContext ctx) {
+- this.ctx = ctx;
+- }
+- public Source resolve(String href, String base)
+- throws TransformerException {
+-
+- // pass if we don't have a systemId
+- if (href == null)
+- return null;
+-
+- // remove "jstl" marker from 'base'
+- // NOTE: how 'base' is determined varies among different Xalan
+- // xsltc implementations
+- int index;
+- if (base != null && (index = base.indexOf("jstl:")) != -1) {
+- base = base.substring(index + 5);
+- }
+-
+- // we're only concerned with relative URLs
+- if (ImportSupport.isAbsoluteUrl(href)
+- || (base != null && ImportSupport.isAbsoluteUrl(base)))
+- return null;
+-
+- // base is relative; remove everything after trailing '/'
+- if (base == null || base.lastIndexOf("/") == -1)
+- base = "";
+- else
+- base = base.substring(0, base.lastIndexOf("/") + 1);
+-
+- // concatenate to produce the real URL we're interested in
+- String target = base + href;
+-
+- // for relative URLs, load and wrap the resource.
+- // don't bother checking for 'null' since we specifically want
+- // the parser to fail if the resource doesn't exist
+- InputStream s;
+- if (target.startsWith("/")) {
+- s = ctx.getServletContext().getResourceAsStream(target);
+- if (s == null)
+- throw new TransformerException(
+- Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY",
+- href));
+- } else {
+- String pagePath =
+- ((HttpServletRequest) ctx.getRequest()).getServletPath();
+- String basePath =
+- pagePath.substring(0, pagePath.lastIndexOf("/"));
+- s = ctx.getServletContext().getResourceAsStream(
+- basePath + "/" + target);
+- if (s == null)
+- throw new TransformerException(
+- Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY",
+- href));
+- }
+- return new StreamSource(s);
+- }
+- }
+-
+ }
+--- /dev/null
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/tag/common/xml/XalanUtil.java
+@@ -0,0 +1,90 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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 org.apache.taglibs.standard.tag.common.xml;
++
++import javax.servlet.jsp.PageContext;
++import javax.servlet.jsp.tagext.Tag;
++import javax.servlet.jsp.tagext.TagSupport;
++import javax.xml.transform.TransformerException;
++
++import org.apache.xpath.VariableStack;
++import org.apache.xpath.XPathContext;
++import org.apache.xpath.objects.XBoolean;
++import org.apache.xpath.objects.XNodeSet;
++import org.apache.xpath.objects.XNumber;
++import org.apache.xpath.objects.XObject;
++import org.apache.xpath.objects.XString;
++import org.w3c.dom.NodeList;
++
++/**
++ */
++public class XalanUtil {
++ /**
++ * Return the XPathContext to be used for evaluating expressions.
++ *
++ * If the child is nested withing a forEach tag its iteration context is used.
++ * Otherwise, a new context is created based on an empty Document.
++ *
++ * @param child the tag whose context should be returned
++ * @param pageContext the current page context
++ * @return the XPath evaluation context
++ */
++ public static XPathContext getContext(Tag child, PageContext pageContext) {
++ // if within a forEach tag, use its context
++ ForEachTag forEachTag = (ForEachTag) TagSupport.findAncestorWithClass(child, ForEachTag.class);
++ if (forEachTag != null) {
++ throw new UnsupportedOperationException("getContext: not implemented method in org.apache.taglibs.standard.tag.common.xml.ForEachTag class!");
++ //return forEachTag.getContext();
++ }
++
++ // otherwise, create a new context referring to an empty document
++ XPathContext context = new XPathContext(false);
++ VariableStack variableStack = new JSTLVariableStack(pageContext);
++ context.setVarStack(variableStack);
++ int dtm = context.getDTMHandleFromNode(XmlUtil.newEmptyDocument());
++ context.pushCurrentNodeAndExpression(dtm, dtm);
++ return context;
++ }
++
++ /**
++ * Return the Java value corresponding to an XPath result.
++ *
++ * @param xo the XPath type
++ * @return the corresponding Java value per the JSTL mapping rules
++ * @throws TransformerException if there was a problem converting the type
++ */
++ static Object coerceToJava(XObject xo) throws TransformerException {
++ if (xo instanceof XBoolean) {
++ return xo.bool();
++ } else if (xo instanceof XNumber) {
++ return xo.num();
++ } else if (xo instanceof XString) {
++ return xo.str();
++ } else if (xo instanceof XNodeSet) {
++ NodeList nodes = xo.nodelist();
++ // if there is only one node in the nodeset return it rather than the list
++ if (nodes.getLength() == 1) {
++ return nodes.item(0);
++ } else {
++ return nodes;
++ }
++ } else {
++ // unexpected result type
++ throw new AssertionError();
++ }
++ }
++}
+--- /dev/null
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/tag/common/xml/XmlUtil.java
+@@ -0,0 +1,279 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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 org.apache.taglibs.standard.tag.common.xml;
++
++import java.io.FileNotFoundException;
++import java.io.InputStream;
++import java.io.Reader;
++
++import javax.servlet.http.HttpServletRequest;
++import javax.servlet.jsp.PageContext;
++import javax.xml.XMLConstants;
++import javax.xml.parsers.DocumentBuilder;
++import javax.xml.parsers.DocumentBuilderFactory;
++import javax.xml.parsers.ParserConfigurationException;
++import javax.xml.transform.Source;
++import javax.xml.transform.Transformer;
++import javax.xml.transform.TransformerConfigurationException;
++import javax.xml.transform.TransformerException;
++import javax.xml.transform.TransformerFactory;
++import javax.xml.transform.URIResolver;
++import javax.xml.transform.sax.SAXSource;
++import javax.xml.transform.sax.SAXTransformerFactory;
++import javax.xml.transform.sax.TransformerHandler;
++import javax.xml.transform.stream.StreamSource;
++
++import org.apache.taglibs.standard.resources.Resources;
++import org.apache.taglibs.standard.util.UrlUtil;
++import org.w3c.dom.Document;
++import org.xml.sax.EntityResolver;
++import org.xml.sax.InputSource;
++import org.xml.sax.SAXException;
++import org.xml.sax.XMLReader;
++import org.xml.sax.helpers.XMLReaderFactory;
++
++/**
++ * Utilities for working with JAXP and SAX.
++ */
++public class XmlUtil {
++ private static final DocumentBuilderFactory dbf;
++ private static final SAXTransformerFactory stf;
++
++ static {
++ // from Java5 on DocumentBuilderFactory is thread safe and hence can be cached
++ dbf = DocumentBuilderFactory.newInstance();
++ dbf.setNamespaceAware(true);
++ dbf.setValidating(false);
++ try {
++ dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
++ } catch (ParserConfigurationException e) {
++ throw new AssertionError("Parser does not support secure processing");
++ }
++
++ TransformerFactory tf = TransformerFactory.newInstance();
++ if (!(tf instanceof SAXTransformerFactory)) {
++ throw new AssertionError("TransformerFactory does not support SAX");
++ }
++ try {
++ tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
++ } catch (TransformerConfigurationException e) {
++ throw new AssertionError("TransformerFactory does not support secure processing");
++ }
++ stf = (SAXTransformerFactory) tf;
++ }
++
++
++ /**
++ * Create a new empty document.
++ *
++ * This method always allocates a new document as its root node might be
++ * exposed to other tags and potentially be mutated.
++ *
++ * @return a new empty document
++ */
++ static Document newEmptyDocument() {
++ return newDocumentBuilder().newDocument();
++ }
++
++ /**
++ * Create a new DocumentBuilder configured for namespaces but not validating.
++ *
++ * @return a new, configured DocumentBuilder
++ */
++ static DocumentBuilder newDocumentBuilder() {
++ try {
++ return dbf.newDocumentBuilder();
++ } catch (ParserConfigurationException e) {
++ throw new AssertionError();
++ }
++ }
++
++ /**
++ * Create a new TransformerHandler.
++ * @return a new TransformerHandler
++ */
++ static TransformerHandler newTransformerHandler() throws TransformerConfigurationException {
++ return stf.newTransformerHandler();
++ }
++
++ static Transformer newTransformer(Source source) throws TransformerConfigurationException {
++ Transformer transformer = stf.newTransformer(source);
++ if (transformer == null) {
++ throw new TransformerConfigurationException("newTransformer returned null");
++ }
++ return transformer;
++ }
++
++ /**
++ * Create an InputSource from a Reader.
++ *
++ * The systemId will be wrapped for use with JSTL's EntityResolver and UriResolver.
++ *
++ * @param reader the source of the XML
++ * @param systemId the system id
++ * @return a configured InputSource
++ */
++ static InputSource newInputSource(Reader reader, String systemId) {
++ InputSource source = new InputSource(reader);
++ source.setSystemId(wrapSystemId(systemId));
++ return source;
++ }
++
++ /**
++ * Create an XMLReader that resolves entities using JSTL semantics.
++ * @param entityResolver for resolving using JSTL semamtics
++ * @return a new XMLReader
++ * @throws SAXException if there was a problem creating the reader
++ */
++ static XMLReader newXMLReader(JstlEntityResolver entityResolver) throws SAXException {
++ XMLReader xmlReader = XMLReaderFactory.createXMLReader();
++ xmlReader.setEntityResolver(entityResolver);
++ return xmlReader;
++ }
++
++ /**
++ * Create a SAXSource from a Reader. Any entities will be resolved using JSTL semantics.
++ *
++ * @param reader the source of the XML
++ * @param systemId the system id
++ * @param entityResolver for resolving using JSTL semamtics
++ * @return a new SAXSource
++ * @throws SAXException if there was a problem creating the source
++ */
++ static SAXSource newSAXSource(Reader reader, String systemId, JstlEntityResolver entityResolver) throws SAXException {
++ SAXSource source = new SAXSource(newXMLReader(entityResolver), new InputSource(reader));
++ source.setSystemId(wrapSystemId(systemId));
++ return source;
++ }
++
++ /**
++ * Wraps systemId with a "jstl:" prefix to prevent the parser from
++ * thinking that the URI is truly relative and resolving it against
++ * the current directory in the filesystem.
++ */
++ private static String wrapSystemId(String systemId) {
++ if (systemId == null) {
++ return "jstl:";
++ } else if (UrlUtil.isAbsoluteUrl(systemId)) {
++ return systemId;
++ } else {
++ return ("jstl:" + systemId);
++ }
++ }
++
++ /**
++ * JSTL-specific implementation of EntityResolver.
++ */
++ static class JstlEntityResolver implements EntityResolver {
++ private final PageContext ctx;
++
++ public JstlEntityResolver(PageContext ctx) {
++ this.ctx = ctx;
++ }
++
++ public InputSource resolveEntity(String publicId, String systemId) throws FileNotFoundException {
++
++ // pass if we don't have a systemId
++ if (systemId == null) {
++ return null;
++ }
++
++ // strip leading "jstl:" off URL if applicable
++ if (systemId.startsWith("jstl:")) {
++ systemId = systemId.substring(5);
++ }
++
++ // we're only concerned with relative URLs
++ if (UrlUtil.isAbsoluteUrl(systemId)) {
++ return null;
++ }
++
++ // for relative URLs, load and wrap the resource.
++ // don't bother checking for 'null' since we specifically want
++ // the parser to fail if the resource doesn't exist
++ String path = systemId;
++ if (!path.startsWith("/")) {
++ String pagePath = ((HttpServletRequest) ctx.getRequest()).getServletPath();
++ String basePath = pagePath.substring(0, pagePath.lastIndexOf("/"));
++ path = basePath + "/" + systemId;
++ }
++
++ InputStream s = ctx.getServletContext().getResourceAsStream(path);
++ if (s == null) {
++ throw new FileNotFoundException(Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY", systemId));
++ }
++ return new InputSource(s);
++ }
++ }
++
++ /**
++ * JSTL-specific implementation of URIResolver.
++ */
++ static class JstlUriResolver implements URIResolver {
++ private final PageContext ctx;
++
++ public JstlUriResolver(PageContext ctx) {
++ this.ctx = ctx;
++ }
++
++ public Source resolve(String href, String base) throws TransformerException {
++
++ // pass if we don't have a systemId
++ if (href == null) {
++ return null;
++ }
++
++ // remove "jstl" marker from 'base'
++ // NOTE: how 'base' is determined varies among different Xalan
++ // xsltc implementations
++ int index;
++ if (base != null && (index = base.indexOf("jstl:")) != -1) {
++ base = base.substring(index + 5);
++ }
++
++ // we're only concerned with relative URLs
++ if (UrlUtil.isAbsoluteUrl(href)
++ || (base != null && UrlUtil.isAbsoluteUrl(base))) {
++ return null;
++ }
++
++ // base is relative; remove everything after trailing '/'
++ if (base == null || base.lastIndexOf("/") == -1) {
++ base = "";
++ } else {
++ base = base.substring(0, base.lastIndexOf("/") + 1);
++ }
++
++ // concatenate to produce the real URL we're interested in
++ String target = base + href;
++
++ // for relative URLs, load and wrap the resource.
++ // don't bother checking for 'null' since we specifically want
++ // the parser to fail if the resource doesn't exist
++ if (!target.startsWith("/")) {
++ String pagePath = ((HttpServletRequest) ctx.getRequest()).getServletPath();
++ String basePath = pagePath.substring(0, pagePath.lastIndexOf("/"));
++ target = basePath + "/" + target;
++ }
++ InputStream s = ctx.getServletContext().getResourceAsStream(target);
++ if (s == null) {
++ throw new TransformerException(Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY", href));
++ }
++ return new StreamSource(s);
++ }
++ }
++}
+--- jakarta-taglibs-standard-1.1.2.orig/standard/src/org/apache/taglibs/standard/tlv/JstlBaseTLV.java
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/tlv/JstlBaseTLV.java
+@@ -17,6 +17,7 @@
+ package org.apache.taglibs.standard.tlv;
+
+ import java.io.IOException;
++import java.io.InputStream;
+ import java.util.HashMap;
+ import java.util.HashSet;
+ import java.util.Map;
+@@ -30,15 +31,15 @@ import javax.servlet.jsp.tagext.PageData
+ import javax.servlet.jsp.tagext.TagData;
+ import javax.servlet.jsp.tagext.TagLibraryValidator;
+ import javax.servlet.jsp.tagext.ValidationMessage;
+-import javax.xml.parsers.ParserConfigurationException;
+-import javax.xml.parsers.SAXParser;
+-import javax.xml.parsers.SAXParserFactory;
+
+ import org.apache.taglibs.standard.lang.support.ExpressionEvaluator;
+ import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
+ import org.apache.taglibs.standard.resources.Resources;
++import org.apache.taglibs.standard.util.XmlUtil;
+ import org.xml.sax.Attributes;
++import org.xml.sax.InputSource;
+ import org.xml.sax.SAXException;
++import org.xml.sax.XMLReader;
+ import org.xml.sax.helpers.DefaultHandler;
+
+ /**
+@@ -149,11 +150,19 @@ public abstract class JstlBaseTLV extend
+ DefaultHandler h = getHandler();
+
+ // parse the page
+- SAXParserFactory f = SAXParserFactory.newInstance();
+- f.setValidating(false);
+- f.setNamespaceAware(true);
+- SAXParser p = f.newSAXParser();
+- p.parse(page.getInputStream(), h);
++ XMLReader xmlReader = XmlUtil.newXMLReader(null);
++ xmlReader.setContentHandler(h);
++ InputStream inputStream = page.getInputStream();
++ try {
++ xmlReader.parse(new InputSource(inputStream));
++ } finally {
++ try {
++ inputStream.close();
++ } catch (IOException e) {
++ // Suppressed.
++ }
++ }
++
+
+ if (messageVector.size() == 0)
+ return null;
+@@ -162,8 +171,6 @@ public abstract class JstlBaseTLV extend
+
+ } catch (SAXException ex) {
+ return vmFromString(ex.toString());
+- } catch (ParserConfigurationException ex) {
+- return vmFromString(ex.toString());
+ } catch (IOException ex) {
+ return vmFromString(ex.toString());
+ }
+--- /dev/null
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/util/UnclosableWriter.java
+@@ -0,0 +1,44 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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 org.apache.taglibs.standard.util;
++
++import java.io.IOException;
++import java.io.Writer;
++
++/**
++ * A Writer based on a wrapped Writer but ignoring requests to
++ * close() and flush() it. (Someone must have wrapped the
++ * toilet in my office similarly...)
++ */
++public class UnclosableWriter extends Writer {
++ // TODO: shouldn't we be delegating all methods?
++ private Writer w;
++
++ public UnclosableWriter(Writer w) {
++ this.w = w;
++ }
++
++ public void close() {
++ }
++
++ public void flush() {
++ }
++
++ public void write(char[] cbuf, int off, int len) throws IOException {
++ w.write(cbuf, off, len);
++ }
++}
+--- /dev/null
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/util/UrlUtil.java
+@@ -0,0 +1,80 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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 org.apache.taglibs.standard.util;
++
++import java.util.BitSet;
++
++/**
++ * Utilities for working with URLs.
++ */
++public class UrlUtil {
++ /**
++ * <p>Valid characters in a scheme.</p>
++ * <p>RFC 1738 says the following:</p>
++ * <blockquote>
++ * Scheme names consist of a sequence of characters. The lower
++ * case letters "a"--"z", digits, and the characters plus ("+"),
++ * period ("."), and hyphen ("-") are allowed. For resiliency,
++ * programs interpreting URLs should treat upper case letters as
++ * equivalent to lower case in scheme names (e.g., allow "HTTP" as
++ * well as "http").
++ * </blockquote>
++ * <p>We treat as absolute any URL that begins with such a scheme name,
++ * followed by a colon.</p>
++ */
++/*
++ private static final String VALID_SCHEME_CHARS =
++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-";
++*/
++ private static final BitSet VALID_SCHEME_CHARS;
++ static {
++ VALID_SCHEME_CHARS = new BitSet(128);
++ VALID_SCHEME_CHARS.set('A', 'Z' + 1);
++ VALID_SCHEME_CHARS.set('a', 'z' + 1);
++ VALID_SCHEME_CHARS.set('0', '9' + 1);
++ VALID_SCHEME_CHARS.set('+');
++ VALID_SCHEME_CHARS.set('.');
++ VALID_SCHEME_CHARS.set('-');
++ }
++
++ /**
++ * Determine if a URL is absolute by JSTL's definition.
++ */
++ public static boolean isAbsoluteUrl(String url) {
++ // a null URL is not absolute, by our definition
++ if (url == null) {
++ return false;
++ }
++
++ // do a fast, simple check first
++ int colonPos = url.indexOf(":");
++ if (colonPos == -1) {
++ return false;
++ }
++
++ // if we DO have a colon, make sure that every character
++ // leading up to it is a valid scheme character
++ for (int i = 0; i < colonPos; i++) {
++ if (!VALID_SCHEME_CHARS.get(url.charAt(i))) {
++ return false;
++ }
++ }
++
++ // if so, we've got an absolute url
++ return true;
++ }
++}
+--- /dev/null
++++ jakarta-taglibs-standard-1.1.2/standard/src/org/apache/taglibs/standard/util/XmlUtil.java
+@@ -0,0 +1,345 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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 org.apache.taglibs.standard.util;
++
++import java.io.FileNotFoundException;
++import java.io.InputStream;
++import java.io.Reader;
++import java.security.AccessController;
++import java.security.PrivilegedActionException;
++import java.security.PrivilegedExceptionAction;
++import java.util.concurrent.Callable;
++
++import javax.servlet.http.HttpServletRequest;
++import javax.servlet.jsp.PageContext;
++import javax.xml.XMLConstants;
++import javax.xml.parsers.DocumentBuilder;
++import javax.xml.parsers.DocumentBuilderFactory;
++import javax.xml.parsers.ParserConfigurationException;
++import javax.xml.transform.Source;
++import javax.xml.transform.Transformer;
++import javax.xml.transform.TransformerConfigurationException;
++import javax.xml.transform.TransformerException;
++import javax.xml.transform.TransformerFactory;
++import javax.xml.transform.URIResolver;
++import javax.xml.transform.sax.SAXSource;
++import javax.xml.transform.sax.SAXTransformerFactory;
++import javax.xml.transform.sax.TransformerHandler;
++import javax.xml.transform.stream.StreamSource;
++
++import org.apache.taglibs.standard.resources.Resources;
++import org.w3c.dom.Document;
++import org.xml.sax.EntityResolver;
++import org.xml.sax.InputSource;
++import org.xml.sax.SAXException;
++import org.xml.sax.XMLReader;
++import org.xml.sax.helpers.XMLReaderFactory;
++
++/**
++ * Utilities for working with JAXP and SAX.
++ */
++public class XmlUtil {
++ /* Cache factory classes when this class is initialized (since Java1.5 factories are required
++ * to be thread safe).
++ *
++ * As JavaEE 5 requires JSTL to be provided by the container we use our ClassLoader to locate
++ * the implementations rather than the application's. As we don't know the actual implementation
++ * class in use we can't use the newInstance() variant that allows the ClassLoader to be
++ * specified so we use the no-arg form and coerce the TCCL (which may be restricted by the
++ * AccessController).
++ */
++ private static final DocumentBuilderFactory PARSER_FACTORY;
++ private static final SAXTransformerFactory TRANSFORMER_FACTORY;
++ static {
++ try {
++ PARSER_FACTORY = runWithOurClassLoader(new Callable<DocumentBuilderFactory>() {
++ public DocumentBuilderFactory call() throws ParserConfigurationException {
++ return DocumentBuilderFactory.newInstance();
++ }
++ }, ParserConfigurationException.class);
++ PARSER_FACTORY.setNamespaceAware(true);
++ PARSER_FACTORY.setValidating(false);
++ PARSER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
++ } catch (ParserConfigurationException e) {
++ throw new ExceptionInInitializerError(e);
++ }
++ try {
++ TRANSFORMER_FACTORY = runWithOurClassLoader(new Callable<SAXTransformerFactory>() {
++ public SAXTransformerFactory call() throws TransformerConfigurationException {
++ TransformerFactory tf = TransformerFactory.newInstance();
++ if (!(tf instanceof SAXTransformerFactory)) {
++ throw new TransformerConfigurationException("TransformerFactory does not support SAX");
++ }
++ return (SAXTransformerFactory) tf;
++ }
++ }, TransformerConfigurationException.class);
++ TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
++ } catch (TransformerConfigurationException e) {
++ throw new ExceptionInInitializerError(e);
++ }
++ }
++
++ /**
++ * Create a new empty document.
++ *
++ * @return a new empty document
++ */
++ public static Document newEmptyDocument() {
++ return newDocumentBuilder().newDocument();
++ }
++
++ /**
++ * Create a new DocumentBuilder configured for namespaces but not validating.
++ *
++ * @return a new, configured DocumentBuilder
++ */
++ public static DocumentBuilder newDocumentBuilder() {
++ try {
++ return PARSER_FACTORY.newDocumentBuilder();
++ } catch (ParserConfigurationException e) {
++ throw (Error) new AssertionError().initCause(e);
++ }
++ }
++
++ /**
++ * Create a new TransformerHandler.
++ * @return a new TransformerHandler
++ */
++ public static TransformerHandler newTransformerHandler() throws TransformerConfigurationException {
++ return TRANSFORMER_FACTORY.newTransformerHandler();
++ }
++
++ /**
++ * Create a new Transformer from an XSLT.
++ * @param source the source of the XSLT.
++ * @return a new Transformer
++ * @throws TransformerConfigurationException if there was a problem creating the Transformer from the XSLT
++ */
++ public static Transformer newTransformer(Source source) throws TransformerConfigurationException {
++ Transformer transformer = TRANSFORMER_FACTORY.newTransformer(source);
++ // Although newTansformer() is not allowed to return null, Xalan does.
++ // Trap that here by throwing the expected TransformerConfigurationException.
++ if (transformer == null) {
++ throw new TransformerConfigurationException("newTransformer returned null. XSLT may be invalid.");
++ }
++ return transformer;
++ }
++
++ /**
++ * Create an InputSource from a Reader.
++ *
++ * The systemId will be wrapped for use with JSTL's EntityResolver and UriResolver.
++ *
++ * @param reader the source of the XML
++ * @param systemId the system id
++ * @return a configured InputSource
++ */
++ public static InputSource newInputSource(Reader reader, String systemId) {
++ InputSource source = new InputSource(reader);
++ source.setSystemId(wrapSystemId(systemId));
++ return source;
++ }
++
++ /**
++ * Create an XMLReader that resolves entities using JSTL semantics.
++ * @param entityResolver for resolving using JSTL semamtics
++ * @return a new XMLReader
++ * @throws SAXException if there was a problem creating the reader
++ */
++ public static XMLReader newXMLReader(JstlEntityResolver entityResolver) throws SAXException {
++ XMLReader xmlReader = runWithOurClassLoader(new Callable<XMLReader>() {
++ public XMLReader call() throws SAXException {
++ return XMLReaderFactory.createXMLReader();
++ }
++ }, SAXException.class);
++ xmlReader.setEntityResolver(entityResolver);
++ xmlReader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
++ return xmlReader;
++ }
++
++ /**
++ * Create a SAXSource from a Reader. Any entities will be resolved using JSTL semantics.
++ *
++ * @param reader the source of the XML
++ * @param systemId the system id
++ * @param entityResolver for resolving using JSTL semamtics
++ * @return a new SAXSource
++ * @throws SAXException if there was a problem creating the source
++ */
++ public static SAXSource newSAXSource(Reader reader, String systemId, JstlEntityResolver entityResolver) throws SAXException {
++ SAXSource source = new SAXSource(newXMLReader(entityResolver), new InputSource(reader));
++ source.setSystemId(wrapSystemId(systemId));
++ return source;
++ }
++
++ /**
++ * Wraps systemId with a "jstl:" prefix to prevent the parser from
++ * thinking that the URI is truly relative and resolving it against
++ * the current directory in the filesystem.
++ */
++ private static String wrapSystemId(String systemId) {
++ if (systemId == null) {
++ return "jstl:";
++ } else if (UrlUtil.isAbsoluteUrl(systemId)) {
++ return systemId;
++ } else {
++ return ("jstl:" + systemId);
++ }
++ }
++
++ /**
++ * JSTL-specific implementation of EntityResolver.
++ */
++ public static class JstlEntityResolver implements EntityResolver {
++ private final PageContext ctx;
++
++ public JstlEntityResolver(PageContext ctx) {
++ this.ctx = ctx;
++ }
++
++ public InputSource resolveEntity(String publicId, String systemId) throws FileNotFoundException {
++
++ // pass if we don't have a systemId
++ if (systemId == null) {
++ return null;
++ }
++
++ // strip leading "jstl:" off URL if applicable
++ if (systemId.startsWith("jstl:")) {
++ systemId = systemId.substring(5);
++ }
++
++ // we're only concerned with relative URLs
++ if (UrlUtil.isAbsoluteUrl(systemId)) {
++ return null;
++ }
++
++ // for relative URLs, load and wrap the resource.
++ // don't bother checking for 'null' since we specifically want
++ // the parser to fail if the resource doesn't exist
++ String path = systemId;
++ if (!path.startsWith("/")) {
++ String pagePath = ((HttpServletRequest) ctx.getRequest()).getServletPath();
++ String basePath = pagePath.substring(0, pagePath.lastIndexOf("/"));
++ path = basePath + "/" + systemId;
++ }
++
++ InputStream s = ctx.getServletContext().getResourceAsStream(path);
++ if (s == null) {
++ throw new FileNotFoundException(Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY", systemId));
++ }
++ return new InputSource(s);
++ }
++ }
++
++ /**
++ * JSTL-specific implementation of URIResolver.
++ */
++ public static class JstlUriResolver implements URIResolver {
++ private final PageContext ctx;
++
++ public JstlUriResolver(PageContext ctx) {
++ this.ctx = ctx;
++ }
++
++ public Source resolve(String href, String base) throws TransformerException {
++
++ // pass if we don't have a systemId
++ if (href == null) {
++ return null;
++ }
++
++ // remove "jstl" marker from 'base'
++ // NOTE: how 'base' is determined varies among different Xalan
++ // xsltc implementations
++ int index;
++ if (base != null && (index = base.indexOf("jstl:")) != -1) {
++ base = base.substring(index + 5);
++ }
++
++ // we're only concerned with relative URLs
++ if (UrlUtil.isAbsoluteUrl(href)
++ || (base != null && UrlUtil.isAbsoluteUrl(base))) {
++ return null;
++ }
++
++ // base is relative; remove everything after trailing '/'
++ if (base == null || base.lastIndexOf("/") == -1) {
++ base = "";
++ } else {
++ base = base.substring(0, base.lastIndexOf("/") + 1);
++ }
++
++ // concatenate to produce the real URL we're interested in
++ String target = base + href;
++
++ // for relative URLs, load and wrap the resource.
++ // don't bother checking for 'null' since we specifically want
++ // the parser to fail if the resource doesn't exist
++ if (!target.startsWith("/")) {
++ String pagePath = ((HttpServletRequest) ctx.getRequest()).getServletPath();
++ String basePath = pagePath.substring(0, pagePath.lastIndexOf("/"));
++ target = basePath + "/" + target;
++ }
++ InputStream s = ctx.getServletContext().getResourceAsStream(target);
++ if (s == null) {
++ throw new TransformerException(Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY", href));
++ }
++ return new StreamSource(s);
++ }
++ }
++
++ /**
++ * Performs an action using this Class's ClassLoader as the Thread context ClassLoader.
++ *
++ * @param action the action to perform
++ * @param allowed an Exception that might be thrown by the action
++ * @param <T> the type of the result
++ * @param <E> the type of the allowed Exception
++ * @return the result of the action
++ * @throws E if the action threw the allowed Exception
++ */
++ private static <T, E extends Exception> T runWithOurClassLoader(final Callable<T> action, Class<E> allowed) throws E {
++ PrivilegedExceptionAction<T> actionWithClassloader = new PrivilegedExceptionAction<T>() {
++ public T run() throws Exception {
++ ClassLoader original = Thread.currentThread().getContextClassLoader();
++ ClassLoader ours = XmlUtil.class.getClassLoader();
++ // Don't override the TCCL if it is not needed.
++ if (original == ours) {
++ return action.call();
++ } else {
++ try {
++ Thread.currentThread().setContextClassLoader(ours);
++ return action.call();
++ } finally {
++ Thread.currentThread().setContextClassLoader(original);
++ }
++ }
++ }
++ };
++ try {
++ return AccessController.doPrivileged(actionWithClassloader);
++ } catch (PrivilegedActionException e) {
++ Throwable cause = e.getCause();
++ if (allowed.isInstance(cause)) {
++ throw allowed.cast(cause);
++ } else {
++ throw (Error) new AssertionError().initCause(cause);
++ }
++ }
++ }
++}
diff --git a/debian/patches/series b/debian/patches/series
index 2c100b1..88a1f4b 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1,3 @@
01_fix_build.diff
java7-compat.patch
+CVE-2015-0254.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jakarta-taglibs-standard.git
More information about the pkg-java-commits
mailing list