From 20599f0cd5e6e992c05d60f93df16139245e2972d0c8db413fa96bc8011fda03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Schr=C3=B6ter?= Date: Fri, 3 May 2024 13:54:09 +0200 Subject: [PATCH] Sync from SUSE:SLFO:Main jakarta-taglibs-standard revision 41311d75bafc3cb5bb8368d4ceab60d1 --- .gitattributes | 23 + CVE-2015-0254.patch | 2269 +++++++++++++++++ jakarta-taglibs-standard-1.1.2-build.patch | 19 + ...-taglibs-standard-1.1.2-remove-enums.patch | 207 ++ jakarta-taglibs-standard-1.1.2-src.tar.gz | 3 + ...taglibs-standard-java6-compatibility.patch | 16 + jakarta-taglibs-standard-java7.patch | 26 + jakarta-taglibs-standard.changes | 84 + jakarta-taglibs-standard.spec | 99 + 9 files changed, 2746 insertions(+) create mode 100644 .gitattributes create mode 100644 CVE-2015-0254.patch create mode 100644 jakarta-taglibs-standard-1.1.2-build.patch create mode 100644 jakarta-taglibs-standard-1.1.2-remove-enums.patch create mode 100644 jakarta-taglibs-standard-1.1.2-src.tar.gz create mode 100644 jakarta-taglibs-standard-java6-compatibility.patch create mode 100644 jakarta-taglibs-standard-java7.patch create mode 100644 jakarta-taglibs-standard.changes create mode 100644 jakarta-taglibs-standard.spec diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/CVE-2015-0254.patch b/CVE-2015-0254.patch new file mode 100644 index 0000000..8f97ae7 --- /dev/null +++ b/CVE-2015-0254.patch @@ -0,0 +1,2269 @@ +Description: Fix CVE-2015-0254 XXE and RCE via XSL extension in JSTL XML tags + When an application uses or 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 + +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/javax/servlet/jsp/jstl/tlv/ParserUtil.java +=================================================================== +--- /dev/null ++++ jakarta-taglibs-standard-1.1.2-src/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() { ++ 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. ++ } ++ } ++ } ++} +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/javax/servlet/jsp/jstl/tlv/PermittedTaglibsTLV.java +=================================================================== +--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/javax/servlet/jsp/jstl/tlv/PermittedTaglibsTLV.java ++++ jakarta-taglibs-standard-1.1.2-src/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( +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/javax/servlet/jsp/jstl/tlv/ScriptFreeTLV.java +=================================================================== +--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/javax/servlet/jsp/jstl/tlv/ScriptFreeTLV.java ++++ jakarta-taglibs-standard-1.1.2-src/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. +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/core/ImportSupport.java +=================================================================== +--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/core/ImportSupport.java ++++ jakarta-taglibs-standard-1.1.2-src/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; + + /** + *

Support for tag handlers for <import>, the general-purpose +@@ -60,22 +61,6 @@ public abstract class ImportSupport exte + //********************************************************************* + // Public constants + +- /**

Valid characters in a scheme.

+- *

RFC 1738 says the following:

+- *
+- * 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"). +- *
+- *

We treat as absolute any URL that begins with such a scheme name, +- * followed by a colon.

+- */ +- 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 true if our current URL is absolute, +- * false otherwise. +- */ +- private boolean isAbsoluteUrl() throws JspTagException { +- return isAbsoluteUrl(url); +- } +- +- + //********************************************************************* + // Public utility methods + + /** +- * Returns true if our current URL is absolute, +- * false 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 url. The session ID + * is encoded as a URL "path parameter" beginning with "jsessionid=". + * We thus remove anything we find between ";jsessionid=" (inclusive) +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/core/RedirectSupport.java +=================================================================== +--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/core/RedirectSupport.java ++++ jakarta-taglibs-standard-1.1.2-src/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; ++ + /** + *

Support for tag handlers for <redirect>, JSTL 1.0's tag + * for redirecting to a new URL (with optional query parameters).

+@@ -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) +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/core/UrlSupport.java +=================================================================== +--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/core/UrlSupport.java ++++ jakarta-taglibs-standard-1.1.2-src/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; + + /** + *

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); + } +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/JSTLVariableStack.java +=================================================================== +--- /dev/null ++++ jakarta-taglibs-standard-1.1.2-src/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 SCOPES; ++ static { ++ SCOPES = new HashMap(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(); ++ } ++ } ++} +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/ParseSupport.java +=================================================================== +--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/xml/ParseSupport.java ++++ jakarta-taglibs-standard-1.1.2-src/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; + + /** + *

Support for tag handlers for <parse>, the XML parsing tag.

+@@ -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); + } + + //********************************************************************* +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/TransformSupport.java +=================================================================== +--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/xml/TransformSupport.java ++++ jakarta-taglibs-standard-1.1.2-src/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; + + /** + *

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 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); +- } +- } +- + } +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/XalanUtil.java +=================================================================== +--- /dev/null ++++ jakarta-taglibs-standard-1.1.2-src/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(); ++ } ++ } ++} +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/XmlUtil.java +=================================================================== +--- /dev/null ++++ jakarta-taglibs-standard-1.1.2-src/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); ++ } ++ } ++} +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tlv/JstlBaseTLV.java +=================================================================== +--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tlv/JstlBaseTLV.java ++++ jakarta-taglibs-standard-1.1.2-src/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()); + } +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/util/UnclosableWriter.java +=================================================================== +--- /dev/null ++++ jakarta-taglibs-standard-1.1.2-src/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); ++ } ++} +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/util/UrlUtil.java +=================================================================== +--- /dev/null ++++ jakarta-taglibs-standard-1.1.2-src/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 { ++ /** ++ *

Valid characters in a scheme.

++ *

RFC 1738 says the following:

++ *
++ * 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"). ++ *
++ *

We treat as absolute any URL that begins with such a scheme name, ++ * followed by a colon.

++ */ ++/* ++ 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; ++ } ++} +Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/util/XmlUtil.java +=================================================================== +--- /dev/null ++++ jakarta-taglibs-standard-1.1.2-src/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() { ++ 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() { ++ 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() { ++ 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 the type of the result ++ * @param the type of the allowed Exception ++ * @return the result of the action ++ * @throws E if the action threw the allowed Exception ++ */ ++ private static T runWithOurClassLoader(final Callable action, Class allowed) throws E { ++ PrivilegedExceptionAction actionWithClassloader = new PrivilegedExceptionAction() { ++ 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/jakarta-taglibs-standard-1.1.2-build.patch b/jakarta-taglibs-standard-1.1.2-build.patch new file mode 100644 index 0000000..bb767c6 --- /dev/null +++ b/jakarta-taglibs-standard-1.1.2-build.patch @@ -0,0 +1,19 @@ +--- standard/build.xml ++++ standard/build.xml +@@ -164,14 +164,11 @@ + + + +- ++ + T unwrap(Class iface) throws SQLException { ++ throw new SQLException(Resources.getMessage("NOT_SUPPORTED")); ++ } + ++ public boolean isWrapperFor(Class iface) throws SQLException { ++ throw new SQLException(Resources.getMessage("NOT_SUPPORTED")); ++ } ++ + } diff --git a/jakarta-taglibs-standard-java7.patch b/jakarta-taglibs-standard-java7.patch new file mode 100644 index 0000000..644593e --- /dev/null +++ b/jakarta-taglibs-standard-java7.patch @@ -0,0 +1,26 @@ +Index: jakarta-taglibs-standard-1.1.1-src/standard/src/org/apache/taglibs/standard/tag/common/sql/DataSourceWrapper.java +=================================================================== +--- jakarta-taglibs-standard-1.1.1-src.orig/standard/src/org/apache/taglibs/standard/tag/common/sql/DataSourceWrapper.java 2012-06-13 09:25:20.976324079 +0200 ++++ jakarta-taglibs-standard-1.1.1-src/standard/src/org/apache/taglibs/standard/tag/common/sql/DataSourceWrapper.java 2012-06-15 09:24:56.340327868 +0200 +@@ -20,6 +20,10 @@ + import java.sql.Connection; + import java.sql.DriverManager; + import java.sql.SQLException; ++import java.sql.SQLException; ++import java.sql.SQLFeatureNotSupportedException; ++ ++import java.util.logging.Logger; + + import javax.sql.DataSource; + +@@ -119,5 +123,10 @@ + public boolean isWrapperFor(Class iface) throws SQLException { + throw new SQLException(Resources.getMessage("NOT_SUPPORTED")); + } ++ ++ // Java7 Build ++ public Logger getParentLogger() throws SQLFeatureNotSupportedException { ++ throw new SQLFeatureNotSupportedException(Resources.getMessage("NOT_SUPPORTED")); ++ } + + } diff --git a/jakarta-taglibs-standard.changes b/jakarta-taglibs-standard.changes new file mode 100644 index 0000000..82e9081 --- /dev/null +++ b/jakarta-taglibs-standard.changes @@ -0,0 +1,84 @@ +------------------------------------------------------------------- +Sun Mar 20 13:09:59 UTC 2022 - Fridrich Strba + +- Build with source and target levels 8 + +------------------------------------------------------------------- +Tue Dec 28 09:01:48 UTC 2021 - David Anes + +- Update to 1.1.2, a missing released version that went into oblivion. +- Update Source0 to download from apache website. +- Marked LICENSE file as a license. +- Rebased patches: + * CVE-2015-0254.patch + * jakarta-taglibs-standard-1.1.2-build.patch + (replaces jakarta-taglibs-standard-1.1.1-build.patch) + * jakarta-taglibs-standard-1.1.2-remove-enums.patch + (replaces jakarta-taglibs-standard-1.1.1-remove-enums.patch) + +------------------------------------------------------------------- +Mon Sep 18 15:40:25 UTC 2017 - fstrba@suse.com + +- Modified patch: + * jakarta-taglibs-standard-1.1.1-build.patch + + Don't hardcode java source and target levels +- Specify source and target level 1.6 in order to allow building + with jdk9 +- Clean spec file and fix some rpmlint errors + +------------------------------------------------------------------- +Tue Oct 6 09:00:42 UTC 2015 - tchvatal@suse.com + +- Update URL to link to live domain +- Fix bnc#920813 CVE-2015-0254, patch taken from debian: + * CVE-2015-0254.patch + +------------------------------------------------------------------- +Fri Jul 11 09:14:05 UTC 2014 - tchvatal@suse.com + +- Cleanup bit with spec-cleaner. + +------------------------------------------------------------------- +Wed Jun 13 07:13:56 UTC 2012 - mvyskocil@suse.cz + +- fix build with java7 +- use non-versioned javadocdir + +------------------------------------------------------------------- +Tue Aug 5 11:56:52 CEST 2008 - mvyskocil@suse.cz + +- fixed build using openjdk6 (add java6 API and remove enums) +- use bzip2 in source tarball +- use macro name in patches +- use source=1.5 and target=1.5 + +------------------------------------------------------------------- +Sun Sep 17 22:44:30 CEST 2006 - ro@suse.de + +- fix build with java-1.5 + +------------------------------------------------------------------- +Wed Jan 25 21:46:39 CET 2006 - mls@suse.de + +- converted neededforbuild to BuildRequires + +------------------------------------------------------------------- +Thu Jul 28 15:01:10 CEST 2005 - jsmeix@suse.de + +- Adjustments in the spec file. + +------------------------------------------------------------------- +Mon Jul 18 16:34:00 CEST 2005 - jsmeix@suse.de + +- Current version 1.1.1 from JPackage.org + +------------------------------------------------------------------- +Thu Sep 16 14:53:41 CEST 2004 - skh@suse.de + +- Fix prerequires of javadoc subpackage + +------------------------------------------------------------------- +Mon Sep 6 00:24:26 CEST 2004 - skh@suse.de + +- Initial package created with version 1.1.1 (JPackage 1.5) + diff --git a/jakarta-taglibs-standard.spec b/jakarta-taglibs-standard.spec new file mode 100644 index 0000000..c65badf --- /dev/null +++ b/jakarta-taglibs-standard.spec @@ -0,0 +1,99 @@ +# +# spec file for package jakarta-taglibs-standard +# +# Copyright (c) 2022 SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + + +%define short_name taglibs-standard +Name: jakarta-taglibs-standard +Version: 1.1.2 +Release: 0 +Summary: Open Source Implementation of the JSP Standard Tag Library +License: Apache-2.0 +Group: Development/Libraries/Java +URL: http://tomcat.apache.org/taglibs/ +Source0: http://archive.apache.org/dist/jakarta/taglibs/standard/source/%{name}-%{version}-src.tar.gz +Patch0: %{name}-%{version}-build.patch +Patch1: %{name}-java6-compatibility.patch +Patch2: %{name}-%{version}-remove-enums.patch +Patch3: jakarta-taglibs-standard-java7.patch +Patch4: CVE-2015-0254.patch +BuildRequires: ant +BuildRequires: fdupes +BuildRequires: java-devel >= 1.8 +BuildRequires: servletapi5 +BuildRequires: xalan-j2 +Requires: servletapi5 >= 5.0.16 +Requires: xalan-j2 +BuildArch: noarch + +%description +This package contains releases for the 1.1.x versions of the Standard +Tag Library, Jakarta Taglibs's open source implementation of the JSP +Standard Tag Library (JSTL), version 1.1. JSTL is a standard under the +Java Community Process. + +%package javadoc +Summary: Javadoc for jakarta-taglibs-standard +Group: Development/Libraries/Java + +%description javadoc +This package contains the javadoc documentation for Jakarta Taglibs. + +%prep +%setup -q -n %{name}-%{version}-src +%patch0 +%patch1 -b .sav1 +%patch2 -b .sav2 +%patch3 -p1 +%patch4 -p1 + +cat > build.properties <