1 /*
2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package com.sun.org.apache.xml.internal.utils;
22 
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.StringTokenizer;
26 import javax.xml.transform.Source;
27 import javax.xml.transform.TransformerException;
28 import javax.xml.transform.URIResolver;
29 import javax.xml.transform.sax.SAXSource;
30 import org.xml.sax.Attributes;
31 import org.xml.sax.InputSource;
32 import org.xml.sax.helpers.DefaultHandler;
33 
34 /**
35  * Search for the xml-stylesheet processing instructions in an XML document.
36  * @see <a href="http://www.w3.org/TR/xml-stylesheet/">
37  * Associating Style Sheets with XML documents, Version 1.0</a>
38  *
39  * @LastModified: Oct 2017
40  */
41 public class StylesheetPIHandler extends DefaultHandler
42 {
43   /** The baseID of the document being processed.  */
44   String m_baseID;
45 
46   /** The desired media criteria. */
47   String m_media;
48 
49   /** The desired title criteria.  */
50   String m_title;
51 
52   /** The desired character set criteria.   */
53   String m_charset;
54 
55   /** A list of SAXSource objects that match the criteria.  */
56   List<Source> m_stylesheets = new ArrayList<>();
57 
58   // Add code to use a URIResolver. Patch from Dmitri Ilyin.
59 
60   /**
61    * The object that implements the URIResolver interface,
62    * or null.
63    */
64   URIResolver m_uriResolver;
65 
66   /**
67    * Get the object that will be used to resolve URIs in href
68    * in xml-stylesheet processing instruction.
69    *
70    * @param resolver An object that implements the URIResolver interface,
71    * or null.
72    */
setURIResolver(URIResolver resolver)73   public void setURIResolver(URIResolver resolver)
74   {
75     m_uriResolver = resolver;
76   }
77 
78   /**
79    * Get the object that will be used to resolve URIs in href
80    * in xml-stylesheet processing instruction.
81    *
82    * @return The URIResolver that was set with setURIResolver.
83    */
getURIResolver()84   public URIResolver getURIResolver()
85   {
86     return m_uriResolver;
87   }
88 
89   /**
90    * Construct a StylesheetPIHandler instance that will search
91    * for xml-stylesheet PIs based on the given criteria.
92    *
93    * @param baseID The base ID of the XML document, needed to resolve
94    *               relative IDs.
95    * @param media The desired media criteria.
96    * @param title The desired title criteria.
97    * @param charset The desired character set criteria.
98    */
StylesheetPIHandler(String baseID, String media, String title, String charset)99   public StylesheetPIHandler(String baseID, String media, String title,
100                              String charset)
101   {
102 
103     m_baseID = baseID;
104     m_media = media;
105     m_title = title;
106     m_charset = charset;
107   }
108 
109   /**
110    * Return the last stylesheet found that match the constraints.
111    *
112    * @return Source object that references the last stylesheet reference
113    *         that matches the constraints.
114    */
getAssociatedStylesheet()115   public Source getAssociatedStylesheet()
116   {
117 
118     int sz = m_stylesheets.size();
119 
120     if (sz > 0)
121     {
122       Source source = m_stylesheets.get(sz-1);
123       return source;
124     }
125     else
126       return null;
127   }
128 
129   /**
130    * Handle the xml-stylesheet processing instruction.
131    *
132    * @param target The processing instruction target.
133    * @param data The processing instruction data, or null if
134    *             none is supplied.
135    * @throws org.xml.sax.SAXException Any SAX exception, possibly
136    *            wrapping another exception.
137    * @see org.xml.sax.ContentHandler#processingInstruction
138    * @see <a href="http://www.w3.org/TR/xml-stylesheet/">
139    * Associating Style Sheets with XML documents, Version 1.0</a>
140    */
processingInstruction(String target, String data)141   public void processingInstruction(String target, String data)
142           throws org.xml.sax.SAXException
143   {
144 
145     if (target.equals("xml-stylesheet"))
146     {
147       String href = null;  // CDATA #REQUIRED
148       String type = null;  // CDATA #REQUIRED
149       String title = null;  // CDATA #IMPLIED
150       String media = null;  // CDATA #IMPLIED
151       String charset = null;  // CDATA #IMPLIED
152       boolean alternate = false;  // (yes|no) "no"
153       StringTokenizer tokenizer = new StringTokenizer(data, " \t=\n", true);
154       boolean lookedAhead = false;
155       Source source = null;
156 
157       String token = "";
158       while (tokenizer.hasMoreTokens())
159       {
160         if (!lookedAhead)
161           token = tokenizer.nextToken();
162         else
163           lookedAhead = false;
164         if (tokenizer.hasMoreTokens() &&
165                (token.equals(" ") || token.equals("\t") || token.equals("=")))
166           continue;
167 
168         String name = token;
169         if (name.equals("type"))
170         {
171           token = tokenizer.nextToken();
172           while (tokenizer.hasMoreTokens() &&
173                (token.equals(" " ) || token.equals("\t") || token.equals("=")))
174             token = tokenizer.nextToken();
175           type = token.substring(1, token.length() - 1);
176 
177         }
178         else if (name.equals("href"))
179         {
180           token = tokenizer.nextToken();
181           while (tokenizer.hasMoreTokens() &&
182                (token.equals(" " ) || token.equals("\t") || token.equals("=")))
183             token = tokenizer.nextToken();
184           href = token;
185           if (tokenizer.hasMoreTokens())
186           {
187             token = tokenizer.nextToken();
188             // If the href value has parameters to be passed to a
189             // servlet(something like "foobar?id=12..."),
190             // we want to make sure we get them added to
191             // the href value. Without this check, we would move on
192             // to try to process another attribute and that would be
193             // wrong.
194             // We need to set lookedAhead here to flag that we
195             // already have the next token.
196             while ( token.equals("=") && tokenizer.hasMoreTokens())
197             {
198               href = href + token + tokenizer.nextToken();
199               if (tokenizer.hasMoreTokens())
200               {
201                 token = tokenizer.nextToken();
202                 lookedAhead = true;
203               }
204               else
205               {
206                 break;
207               }
208             }
209           }
210           href = href.substring(1, href.length() - 1);
211           try
212           {
213             // Add code to use a URIResolver. Patch from Dmitri Ilyin.
214             if (m_uriResolver != null)
215             {
216               source = m_uriResolver.resolve(href, m_baseID);
217             }
218            else
219             {
220               href = SystemIDResolver.getAbsoluteURI(href, m_baseID);
221               source = new SAXSource(new InputSource(href));
222             }
223           }
224           catch(TransformerException te)
225           {
226             throw new org.xml.sax.SAXException(te);
227           }
228         }
229         else if (name.equals("title"))
230         {
231           token = tokenizer.nextToken();
232           while (tokenizer.hasMoreTokens() &&
233                (token.equals(" " ) || token.equals("\t") || token.equals("=")))
234             token = tokenizer.nextToken();
235           title = token.substring(1, token.length() - 1);
236         }
237         else if (name.equals("media"))
238         {
239           token = tokenizer.nextToken();
240           while (tokenizer.hasMoreTokens() &&
241                (token.equals(" " ) || token.equals("\t") || token.equals("=")))
242             token = tokenizer.nextToken();
243           media = token.substring(1, token.length() - 1);
244         }
245         else if (name.equals("charset"))
246         {
247           token = tokenizer.nextToken();
248           while (tokenizer.hasMoreTokens() &&
249               (token.equals(" " ) || token.equals("\t") || token.equals("=")))
250             token = tokenizer.nextToken();
251           charset = token.substring(1, token.length() - 1);
252         }
253         else if (name.equals("alternate"))
254         {
255           token = tokenizer.nextToken();
256           while (tokenizer.hasMoreTokens() &&
257                (token.equals(" " ) || token.equals("\t") || token.equals("=")))
258             token = tokenizer.nextToken();
259           alternate = token.substring(1, token.length()
260                                              - 1).equals("yes");
261         }
262 
263       }
264 
265       if ((null != type)
266           && (type.equals("text/xsl") || type.equals("text/xml") || type.equals("application/xml+xslt"))
267           && (null != href))
268       {
269         if (null != m_media)
270         {
271           if (null != media)
272           {
273             if (!media.equals(m_media))
274               return;
275           }
276           else
277             return;
278         }
279 
280         if (null != m_charset)
281         {
282           if (null != charset)
283           {
284             if (!charset.equals(m_charset))
285               return;
286           }
287           else
288             return;
289         }
290 
291         if (null != m_title)
292         {
293           if (null != title)
294           {
295             if (!title.equals(m_title))
296               return;
297           }
298           else
299             return;
300         }
301 
302         m_stylesheets.add(source);
303       }
304     }
305   }
306 
307 
308   /**
309    * The spec notes that "The xml-stylesheet processing instruction is allowed only in the prolog of an XML document.",
310    * so, at least for right now, I'm going to go ahead an throw a TransformerException
311    * in order to stop the parse.
312    *
313    * @param namespaceURI The Namespace URI, or an empty string.
314    * @param localName The local name (without prefix), or empty string if not namespace processing.
315    * @param qName The qualified name (with prefix).
316    * @param atts  The specified or defaulted attributes.
317    *
318    * @throws StopParseException since there can be no valid xml-stylesheet processing
319    *                            instructions past the first element.
320    */
startElement( String namespaceURI, String localName, String qName, Attributes atts)321   public void startElement(
322           String namespaceURI, String localName, String qName, Attributes atts)
323             throws org.xml.sax.SAXException
324   {
325     throw new StopParseException();
326   }
327 
328   /**
329     * Added additional getter and setter methods for the Base Id
330     * to fix bugzilla bug 24187
331     *
332     */
setBaseId(String baseId)333    public void setBaseId(String baseId) {
334        m_baseID = baseId;
335 
336    }
getBaseId()337    public String  getBaseId() {
338        return m_baseID ;
339    }
340 
341 }
342