[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