/* * 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 com.sun.org.apache.xml.internal.resolver; import java.io.IOException; import java.io.InputStream; import java.io.FileNotFoundException; import java.util.Enumeration; import java.util.Vector; import java.net.URL; import java.net.URLConnection; import java.net.MalformedURLException; import javax.xml.parsers.SAXParserFactory; import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; import com.sun.org.apache.xerces.internal.utils.SecuritySupport; import com.sun.org.apache.xml.internal.resolver.readers.SAXCatalogReader; import com.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader; import com.sun.org.apache.xml.internal.resolver.readers.TR9401CatalogReader; /** * An extension to OASIS Open Catalog files, this class supports * suffix-based matching and an external RFC2483 resolver. * * @see Catalog * * @author Norman Walsh * Norman.Walsh@Sun.COM * * @version 1.0 */ public class Resolver extends Catalog { /** * The URISUFFIX Catalog Entry type. * *

URI suffix entries match URIs that end in a specified suffix.

*/ public static final int URISUFFIX = CatalogEntry.addEntryType("URISUFFIX", 2); /** * The SYSTEMSUFFIX Catalog Entry type. * *

System suffix entries match system identifiers that end in a * specified suffix.

*/ public static final int SYSTEMSUFFIX = CatalogEntry.addEntryType("SYSTEMSUFFIX", 2); /** * The RESOLVER Catalog Entry type. * *

A hook for providing support for web-based backup resolvers.

*/ public static final int RESOLVER = CatalogEntry.addEntryType("RESOLVER", 1); /** * The SYSTEMREVERSE Catalog Entry type. * *

This is a bit of a hack. There's no actual SYSTEMREVERSE entry, * but this entry type is used to indicate that a reverse lookup is * being performed. (This allows the Resolver to implement * RFC2483 I2N and I2NS.) */ public static final int SYSTEMREVERSE = CatalogEntry.addEntryType("SYSTEMREVERSE", 1); /** * Setup readers. */ public void setupReaders() { SAXParserFactory spf = catalogManager.useServicesMechanism() ? SAXParserFactory.newInstance() : new SAXParserFactoryImpl(); spf.setNamespaceAware(true); spf.setValidating(false); SAXCatalogReader saxReader = new SAXCatalogReader(spf); saxReader.setCatalogParser(null, "XCatalog", "com.sun.org.apache.xml.internal.resolver.readers.XCatalogReader"); saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName, "catalog", "com.sun.org.apache.xml.internal.resolver.readers.ExtendedXMLCatalogReader"); addReader("application/xml", saxReader); TR9401CatalogReader textReader = new TR9401CatalogReader(); addReader("text/plain", textReader); } /** * Cleanup and process a Catalog entry. * *

This method processes each Catalog entry, changing mapped * relative system identifiers into absolute ones (based on the current * base URI), and maintaining other information about the current * catalog.

* * @param entry The CatalogEntry to process. */ public void addEntry(CatalogEntry entry) { int type = entry.getEntryType(); if (type == URISUFFIX) { String suffix = normalizeURI(entry.getEntryArg(0)); String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); entry.setEntryArg(1, fsi); catalogManager.debug.message(4, "URISUFFIX", suffix, fsi); } else if (type == SYSTEMSUFFIX) { String suffix = normalizeURI(entry.getEntryArg(0)); String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); entry.setEntryArg(1, fsi); catalogManager.debug.message(4, "SYSTEMSUFFIX", suffix, fsi); } super.addEntry(entry); } /** * Return the applicable URI. * *

If a URI entry exists in the Catalog * for the URI specified, return the mapped value.

* *

In the Resolver (as opposed to the Catalog) class, if the * URI isn't found by the usual algorithm, URISUFFIX entries are * considered.

* *

URI comparison is case sensitive.

* * @param uri The URI to locate in the catalog. * * @return The resolved URI. * * @throws MalformedURLException The system identifier of a * subordinate catalog cannot be turned into a valid URL. * @throws IOException Error reading subordinate catalog file. */ public String resolveURI(String uri) throws MalformedURLException, IOException { String resolved = super.resolveURI(uri); if (resolved != null) { return resolved; } Enumeration en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.nextElement(); if (e.getEntryType() == RESOLVER) { resolved = resolveExternalSystem(uri, e.getEntryArg(0)); if (resolved != null) { return resolved; } } else if (e.getEntryType() == URISUFFIX) { String suffix = e.getEntryArg(0); String result = e.getEntryArg(1); if (suffix.length() <= uri.length() && uri.substring(uri.length()-suffix.length()).equals(suffix)) { return result; } } } // Otherwise, look in the subordinate catalogs return resolveSubordinateCatalogs(Catalog.URI, null, null, uri); } /** * Return the applicable SYSTEM system identifier, resorting * to external RESOLVERs if necessary. * *

If a SYSTEM entry exists in the Catalog * for the system ID specified, return the mapped value.

* *

In the Resolver (as opposed to the Catalog) class, if the * URI isn't found by the usual algorithm, SYSTEMSUFFIX entries are * considered.

* *

On Windows-based operating systems, the comparison between * the system identifier provided and the SYSTEM entries in the * Catalog is case-insensitive.

* * @param systemId The system ID to locate in the catalog. * * @return The system identifier to use for systemId. * * @throws MalformedURLException The formal system identifier of a * subordinate catalog cannot be turned into a valid URL. * @throws IOException Error reading subordinate catalog file. */ public String resolveSystem(String systemId) throws MalformedURLException, IOException { String resolved = super.resolveSystem(systemId); if (resolved != null) { return resolved; } Enumeration en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.nextElement(); if (e.getEntryType() == RESOLVER) { resolved = resolveExternalSystem(systemId, e.getEntryArg(0)); if (resolved != null) { return resolved; } } else if (e.getEntryType() == SYSTEMSUFFIX) { String suffix = e.getEntryArg(0); String result = e.getEntryArg(1); if (suffix.length() <= systemId.length() && systemId.substring(systemId.length()-suffix.length()).equals(suffix)) { return result; } } } return resolveSubordinateCatalogs(Catalog.SYSTEM, null, null, systemId); } /** * Return the applicable PUBLIC or SYSTEM identifier, resorting * to external resolvers if necessary. * *

This method searches the Catalog and returns the system * identifier specified for the given system or * public identifiers. If * no appropriate PUBLIC or SYSTEM entry is found in the Catalog, * null is returned.

* *

Note that a system or public identifier in the current catalog * (or subordinate catalogs) will be used in preference to an * external resolver. Further, if a systemId is present, the external * resolver(s) will be queried for that before the publicId.

* * @param publicId The public identifier to locate in the catalog. * Public identifiers are normalized before comparison. * @param systemId The nominal system identifier for the entity * in question (as provided in the source document). * * @throws MalformedURLException The formal system identifier of a * subordinate catalog cannot be turned into a valid URL. * @throws IOException Error reading subordinate catalog file. * * @return The system identifier to use. * Note that the nominal system identifier is not returned if a * match is not found in the catalog, instead null is returned * to indicate that no match was found. */ public String resolvePublic(String publicId, String systemId) throws MalformedURLException, IOException { String resolved = super.resolvePublic(publicId, systemId); if (resolved != null) { return resolved; } Enumeration en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.nextElement(); if (e.getEntryType() == RESOLVER) { if (systemId != null) { resolved = resolveExternalSystem(systemId, e.getEntryArg(0)); if (resolved != null) { return resolved; } } resolved = resolveExternalPublic(publicId, e.getEntryArg(0)); if (resolved != null) { return resolved; } } } return resolveSubordinateCatalogs(Catalog.PUBLIC, null, publicId, systemId); } /** * Query an external RFC2483 resolver for a system identifier. * * @param systemId The system ID to locate. * @param resolver The name of the resolver to use. * * @return The system identifier to use for the systemId. */ protected String resolveExternalSystem(String systemId, String resolver) throws MalformedURLException, IOException { Resolver r = queryResolver(resolver, "i2l", systemId, null); if (r != null) { return r.resolveSystem(systemId); } else { return null; } } /** * Query an external RFC2483 resolver for a public identifier. * * @param publicId The system ID to locate. * @param resolver The name of the resolver to use. * * @return The system identifier to use for the systemId. */ protected String resolveExternalPublic(String publicId, String resolver) throws MalformedURLException, IOException { Resolver r = queryResolver(resolver, "fpi2l", publicId, null); if (r != null) { return r.resolvePublic(publicId, null); } else { return null; } } /** * Query an external RFC2483 resolver. * * @param resolver The URL of the RFC2483 resolver. * @param command The command to send the resolver. * @param arg1 The first argument to the resolver. * @param arg2 The second argument to the resolver, usually null. * * @return The Resolver constructed. */ protected Resolver queryResolver(String resolver, String command, String arg1, String arg2) { InputStream iStream = null; String RFC2483 = resolver + "?command=" + command + "&format=tr9401&uri=" + arg1 + "&uri2=" + arg2; String line = null; try { URL url = new URL(RFC2483); URLConnection urlCon = url.openConnection(); urlCon.setUseCaches(false); Resolver r = (Resolver) newCatalog(); String cType = urlCon.getContentType(); // I don't care about the character set or subtype if (cType.indexOf(";") > 0) { cType = cType.substring(0, cType.indexOf(";")); } r.parseCatalog(cType, urlCon.getInputStream()); return r; } catch (CatalogException cex) { if (cex.getExceptionType() == CatalogException.UNPARSEABLE) { catalogManager.debug.message(1, "Unparseable catalog: " + RFC2483); } else if (cex.getExceptionType() == CatalogException.UNKNOWN_FORMAT) { catalogManager.debug.message(1, "Unknown catalog format: " + RFC2483); } return null; } catch (MalformedURLException mue) { catalogManager.debug.message(1, "Malformed resolver URL: " + RFC2483); return null; } catch (IOException ie) { catalogManager.debug.message(1, "I/O Exception opening resolver: " + RFC2483); return null; } } /** * Append two vectors, returning the result. * * @param vec The first vector * @param appvec The vector to be appended * @return The vector vec, with appvec's elements appended to it */ private Vector appendVector(Vector vec, Vector appvec) { if (appvec != null) { for (int count = 0; count < appvec.size(); count++) { vec.addElement(appvec.elementAt(count)); } } return vec; } /** * Find the URNs for a given system identifier in all catalogs. * * @param systemId The system ID to locate. * * @return A vector of URNs that map to the systemId. */ public Vector resolveAllSystemReverse(String systemId) throws MalformedURLException, IOException { Vector resolved = new Vector(); // If there's a SYSTEM entry in this catalog, use it if (systemId != null) { Vector localResolved = resolveLocalSystemReverse(systemId); resolved = appendVector(resolved, localResolved); } // Otherwise, look in the subordinate catalogs Vector subResolved = resolveAllSubordinateCatalogs(SYSTEMREVERSE, null, null, systemId); return appendVector(resolved, subResolved); } /** * Find the URN for a given system identifier. * * @param systemId The system ID to locate. * * @return A (single) URN that maps to the systemId. */ public String resolveSystemReverse(String systemId) throws MalformedURLException, IOException { Vector resolved = resolveAllSystemReverse(systemId); if (resolved != null && resolved.size() > 0) { return (String) resolved.elementAt(0); } else { return null; } } /** * Return the applicable SYSTEM system identifiers. * *

If one or more SYSTEM entries exists in the Catalog * for the system ID specified, return the mapped values.

* *

The caller is responsible for doing any necessary * normalization of the system identifier before calling * this method. For example, a relative system identifier in * a document might be converted to an absolute system identifier * before attempting to resolve it.

* *

Note that this function will force all subordinate catalogs * to be loaded.

* *

On Windows-based operating systems, the comparison between * the system identifier provided and the SYSTEM entries in the * Catalog is case-insensitive.

* * @param systemId The system ID to locate in the catalog. * * @return The system identifier to use for the notation. * * @throws MalformedURLException The formal system identifier of a * subordinate catalog cannot be turned into a valid URL. * @throws IOException Error reading subordinate catalog file. */ public Vector resolveAllSystem(String systemId) throws MalformedURLException, IOException { Vector resolutions = new Vector(); // If there are SYSTEM entries in this catalog, start with them if (systemId != null) { Vector localResolutions = resolveAllLocalSystem(systemId); resolutions = appendVector(resolutions, localResolutions); } // Then look in the subordinate catalogs Vector subResolutions = resolveAllSubordinateCatalogs(SYSTEM, null, null, systemId); resolutions = appendVector(resolutions, subResolutions); if (resolutions.size() > 0) { return resolutions; } else { return null; } } /** * Return all applicable SYSTEM system identifiers in this * catalog. * *

If one or more SYSTEM entries exists in the catalog file * for the system ID specified, return the mapped values.

* * @param systemId The system ID to locate in the catalog * * @return A vector of the mapped system identifiers or null */ private Vector resolveAllLocalSystem(String systemId) { Vector map = new Vector(); String osname = SecuritySupport.getSystemProperty("os.name"); boolean windows = (osname.indexOf("Windows") >= 0); Enumeration en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.nextElement(); if (e.getEntryType() == SYSTEM && (e.getEntryArg(0).equals(systemId) || (windows && e.getEntryArg(0).equalsIgnoreCase(systemId)))) { map.addElement(e.getEntryArg(1)); } } if (map.size() == 0) { return null; } else { return map; } } /** * Find the URNs for a given system identifier in the current catalog. * * @param systemId The system ID to locate. * * @return A vector of URNs that map to the systemId. */ private Vector resolveLocalSystemReverse(String systemId) { Vector map = new Vector(); String osname = SecuritySupport.getSystemProperty("os.name"); boolean windows = (osname.indexOf("Windows") >= 0); Enumeration en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.nextElement(); if (e.getEntryType() == SYSTEM && (e.getEntryArg(1).equals(systemId) || (windows && e.getEntryArg(1).equalsIgnoreCase(systemId)))) { map.addElement(e.getEntryArg(0)); } } if (map.size() == 0) { return null; } else { return map; } } /** * Search the subordinate catalogs, in order, looking for all * match. * *

This method searches the Catalog and returns all of the system * identifiers specified for the given entity type with the given * name, public, and system identifiers. In some contexts, these * may be null.

* * @param entityType The CatalogEntry type for which this query is * being conducted. This is necessary in order to do the approprate * query on a subordinate catalog. * @param entityName The name of the entity being searched for, if * appropriate. * @param publicId The public identifier of the entity in question * (as provided in the source document). * @param systemId The nominal system identifier for the entity * in question (as provided in the source document). * * @throws MalformedURLException The formal system identifier of a * delegated catalog cannot be turned into a valid URL. * @throws IOException Error reading delegated catalog file. * * @return The system identifier to use. * Note that the nominal system identifier is not returned if a * match is not found in the catalog, instead null is returned * to indicate that no match was found. */ private synchronized Vector resolveAllSubordinateCatalogs(int entityType, String entityName, String publicId, String systemId) throws MalformedURLException, IOException { Vector resolutions = new Vector(); for (int catPos = 0; catPos < catalogs.size(); catPos++) { Resolver c = null; try { c = (Resolver) catalogs.elementAt(catPos); } catch (ClassCastException e) { String catfile = (String) catalogs.elementAt(catPos); c = (Resolver) newCatalog(); try { c.parseCatalog(catfile); } catch (MalformedURLException mue) { catalogManager.debug.message(1, "Malformed Catalog URL", catfile); } catch (FileNotFoundException fnfe) { catalogManager.debug.message(1, "Failed to load catalog, file not found", catfile); } catch (IOException ioe) { catalogManager.debug.message(1, "Failed to load catalog, I/O error", catfile); } catalogs.setElementAt(c, catPos); } String resolved = null; // Ok, now what are we supposed to call here? if (entityType == DOCTYPE) { resolved = c.resolveDoctype(entityName, publicId, systemId); if (resolved != null) { // Only find one DOCTYPE resolution resolutions.addElement(resolved); return resolutions; } } else if (entityType == DOCUMENT) { resolved = c.resolveDocument(); if (resolved != null) { // Only find one DOCUMENT resolution resolutions.addElement(resolved); return resolutions; } } else if (entityType == ENTITY) { resolved = c.resolveEntity(entityName, publicId, systemId); if (resolved != null) { // Only find one ENTITY resolution resolutions.addElement(resolved); return resolutions; } } else if (entityType == NOTATION) { resolved = c.resolveNotation(entityName, publicId, systemId); if (resolved != null) { // Only find one NOTATION resolution resolutions.addElement(resolved); return resolutions; } } else if (entityType == PUBLIC) { resolved = c.resolvePublic(publicId, systemId); if (resolved != null) { // Only find one PUBLIC resolution resolutions.addElement(resolved); return resolutions; } } else if (entityType == SYSTEM) { Vector localResolutions = c.resolveAllSystem(systemId); resolutions = appendVector(resolutions, localResolutions); break; } else if (entityType == SYSTEMREVERSE) { Vector localResolutions = c.resolveAllSystemReverse(systemId); resolutions = appendVector(resolutions, localResolutions); } } if (resolutions != null) { return resolutions; } else { return null; } } }