This is an automated email from the git hooks/post-receive script. ebourg-guest pushed a commit to branch master in repository taglibs-standard.
commit 3aebde786ab0604f6b9bb0ee31891b1e25aa7c99 Author: Emmanuel Bourg <[email protected]> Date: Wed Jul 20 17:29:38 2016 +0200 Removed CVE-2015-0254.patch (fixed upstream) --- debian/changelog | 4 +- debian/patches/CVE-2015-0254.patch | 2239 ------------------------------------ debian/patches/series | 1 - 3 files changed, 3 insertions(+), 2241 deletions(-) diff --git a/debian/changelog b/debian/changelog index e657db6..43b51c0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,8 @@ -taglibs-standard (1.1.2-5) UNRELEASED; urgency=medium +taglibs-standard (1.2.5-1) UNRELEASED; urgency=medium * Team upload. + * New upstream release + - Removed CVE-2015-0254.patch (fixed upstream) * Renamed the package to taglibs-standard * Standards-Version updated to 3.9.8 * Updated debian/watch to track the releases >= 1.2 diff --git a/debian/patches/CVE-2015-0254.patch b/debian/patches/CVE-2015-0254.patch deleted file mode 100644 index fe46e41..0000000 --- a/debian/patches/CVE-2015-0254.patch +++ /dev/null @@ -1,2239 +0,0 @@ -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 -+++ b/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. -+ } -+ } -+ } -+} ---- a/standard/src/javax/servlet/jsp/jstl/tlv/PermittedTaglibsTLV.java -+++ b/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 @@ - //********************************************************************* - // 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 @@ - 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( ---- a/standard/src/javax/servlet/jsp/jstl/tlv/ScriptFreeTLV.java -+++ b/standard/src/javax/servlet/jsp/jstl/tlv/ScriptFreeTLV.java -@@ -24,7 +24,6 @@ - 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 @@ - * @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. ---- a/standard/src/org/apache/taglibs/standard/tag/common/core/ImportSupport.java -+++ b/standard/src/org/apache/taglibs/standard/tag/common/core/ImportSupport.java -@@ -45,6 +45,7 @@ - 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 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 @@ - 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 -@@ -500,43 +485,10 @@ - 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) ---- a/standard/src/org/apache/taglibs/standard/tag/common/core/RedirectSupport.java -+++ b/standard/src/org/apache/taglibs/standard/tag/common/core/RedirectSupport.java -@@ -22,6 +22,8 @@ - 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 @@ - 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) ---- a/standard/src/org/apache/taglibs/standard/tag/common/core/UrlSupport.java -+++ b/standard/src/org/apache/taglibs/standard/tag/common/core/UrlSupport.java -@@ -24,6 +24,7 @@ - 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 @@ - 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 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 -+++ b/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(); -+ } -+ } -+} ---- a/standard/src/org/apache/taglibs/standard/tag/common/xml/ParseSupport.java -+++ b/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 @@ - 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 @@ - 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 @@ - // 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); - } - - //********************************************************************* ---- a/standard/src/org/apache/taglibs/standard/tag/common/xml/TransformSupport.java -+++ b/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 @@ - // 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 @@ - 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 @@ - // 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 @@ - 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 @@ - // 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 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 -+++ b/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 -+++ b/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); -+ } -+ } -+} ---- a/standard/src/org/apache/taglibs/standard/tlv/JstlBaseTLV.java -+++ b/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.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 @@ - 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 @@ - - } catch (SAXException ex) { - return vmFromString(ex.toString()); -- } catch (ParserConfigurationException ex) { -- return vmFromString(ex.toString()); - } catch (IOException ex) { - return vmFromString(ex.toString()); - } ---- /dev/null -+++ b/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 -+++ b/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 -+++ b/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 56884ca..d668276 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,3 +1,2 @@ 01-jdbc-compatibility.patch 02-servlet-api-compatibility.patch -CVE-2015-0254.patch -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/taglibs-standard.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

