1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.sun.org.apache.xml.internal.resolver; 19 20import java.io.IOException; 21import java.io.InputStream; 22import java.io.FileNotFoundException; 23import java.util.Enumeration; 24import java.util.Vector; 25import java.net.URL; 26import java.net.URLConnection; 27import java.net.MalformedURLException; 28import javax.xml.parsers.SAXParserFactory; 29import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; 30import com.sun.org.apache.xerces.internal.utils.SecuritySupport; 31import com.sun.org.apache.xml.internal.resolver.readers.SAXCatalogReader; 32import com.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader; 33import com.sun.org.apache.xml.internal.resolver.readers.TR9401CatalogReader; 34 35/** 36 * An extension to OASIS Open Catalog files, this class supports 37 * suffix-based matching and an external RFC2483 resolver. 38 * 39 * @see Catalog 40 * @deprecated The JDK internal Catalog API in package 41 * {@code com.sun.org.apache.xml.internal.resolver} 42 * is encapsulated in JDK 9. The entire implementation under the package is now 43 * deprecated and subject to removal in a future release. Users of the API 44 * should migrate to the {@linkplain javax.xml.catalog new public API}. 45 * <p> 46 * The new Catalog API is supported throughout the JDK XML Processors, which allows 47 * the use of Catalog by simply setting a path to a Catalog file as a property. 48 * 49 * @author Norman Walsh 50 * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a> 51 * 52 * @version 1.0 53 */ 54@Deprecated(since="9", forRemoval=true) 55public class Resolver extends Catalog { 56 /** 57 * The URISUFFIX Catalog Entry type. 58 * 59 * <p>URI suffix entries match URIs that end in a specified suffix.</p> 60 */ 61 public static final int URISUFFIX = CatalogEntry.addEntryType("URISUFFIX", 2); 62 63 /** 64 * The SYSTEMSUFFIX Catalog Entry type. 65 * 66 * <p>System suffix entries match system identifiers that end in a 67 * specified suffix.</p> 68 */ 69 public static final int SYSTEMSUFFIX = CatalogEntry.addEntryType("SYSTEMSUFFIX", 2); 70 71 /** 72 * The RESOLVER Catalog Entry type. 73 * 74 * <p>A hook for providing support for web-based backup resolvers.</p> 75 */ 76 public static final int RESOLVER = CatalogEntry.addEntryType("RESOLVER", 1); 77 78 /** 79 * The SYSTEMREVERSE Catalog Entry type. 80 * 81 * <p>This is a bit of a hack. There's no actual SYSTEMREVERSE entry, 82 * but this entry type is used to indicate that a reverse lookup is 83 * being performed. (This allows the Resolver to implement 84 * RFC2483 I2N and I2NS.) 85 */ 86 public static final int SYSTEMREVERSE 87 = CatalogEntry.addEntryType("SYSTEMREVERSE", 1); 88 89 /** 90 * Setup readers. 91 */ 92 public void setupReaders() { 93 SAXParserFactory spf = catalogManager.useServicesMechanism() ? 94 SAXParserFactory.newInstance() : new SAXParserFactoryImpl(); 95 spf.setNamespaceAware(true); 96 spf.setValidating(false); 97 98 SAXCatalogReader saxReader = new SAXCatalogReader(spf); 99 100 saxReader.setCatalogParser(null, "XCatalog", 101 "com.sun.org.apache.xml.internal.resolver.readers.XCatalogReader"); 102 103 saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName, 104 "catalog", 105 "com.sun.org.apache.xml.internal.resolver.readers.ExtendedXMLCatalogReader"); 106 107 addReader("application/xml", saxReader); 108 109 TR9401CatalogReader textReader = new TR9401CatalogReader(); 110 addReader("text/plain", textReader); 111 } 112 113 /** 114 * Cleanup and process a Catalog entry. 115 * 116 * <p>This method processes each Catalog entry, changing mapped 117 * relative system identifiers into absolute ones (based on the current 118 * base URI), and maintaining other information about the current 119 * catalog.</p> 120 * 121 * @param entry The CatalogEntry to process. 122 */ 123 public void addEntry(CatalogEntry entry) { 124 int type = entry.getEntryType(); 125 126 if (type == URISUFFIX) { 127 String suffix = normalizeURI(entry.getEntryArg(0)); 128 String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); 129 130 entry.setEntryArg(1, fsi); 131 132 catalogManager.debug.message(4, "URISUFFIX", suffix, fsi); 133 } else if (type == SYSTEMSUFFIX) { 134 String suffix = normalizeURI(entry.getEntryArg(0)); 135 String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); 136 137 entry.setEntryArg(1, fsi); 138 139 catalogManager.debug.message(4, "SYSTEMSUFFIX", suffix, fsi); 140 } 141 142 super.addEntry(entry); 143 } 144 145 /** 146 * Return the applicable URI. 147 * 148 * <p>If a URI entry exists in the Catalog 149 * for the URI specified, return the mapped value.</p> 150 * 151 * <p>In the Resolver (as opposed to the Catalog) class, if the 152 * URI isn't found by the usual algorithm, URISUFFIX entries are 153 * considered.</p> 154 * 155 * <p>URI comparison is case sensitive.</p> 156 * 157 * @param uri The URI to locate in the catalog. 158 * 159 * @return The resolved URI. 160 * 161 * @throws MalformedURLException The system identifier of a 162 * subordinate catalog cannot be turned into a valid URL. 163 * @throws IOException Error reading subordinate catalog file. 164 */ 165 public String resolveURI(String uri) 166 throws MalformedURLException, IOException { 167 168 String resolved = super.resolveURI(uri); 169 if (resolved != null) { 170 return resolved; 171 } 172 173 Enumeration en = catalogEntries.elements(); 174 while (en.hasMoreElements()) { 175 CatalogEntry e = (CatalogEntry) en.nextElement(); 176 if (e.getEntryType() == RESOLVER) { 177 resolved = resolveExternalSystem(uri, e.getEntryArg(0)); 178 if (resolved != null) { 179 return resolved; 180 } 181 } else if (e.getEntryType() == URISUFFIX) { 182 String suffix = e.getEntryArg(0); 183 String result = e.getEntryArg(1); 184 185 if (suffix.length() <= uri.length() 186 && uri.substring(uri.length()-suffix.length()).equals(suffix)) { 187 return result; 188 } 189 } 190 } 191 192 // Otherwise, look in the subordinate catalogs 193 return resolveSubordinateCatalogs(Catalog.URI, 194 null, 195 null, 196 uri); 197 } 198 199 /** 200 * Return the applicable SYSTEM system identifier, resorting 201 * to external RESOLVERs if necessary. 202 * 203 * <p>If a SYSTEM entry exists in the Catalog 204 * for the system ID specified, return the mapped value.</p> 205 * 206 * <p>In the Resolver (as opposed to the Catalog) class, if the 207 * URI isn't found by the usual algorithm, SYSTEMSUFFIX entries are 208 * considered.</p> 209 * 210 * <p>On Windows-based operating systems, the comparison between 211 * the system identifier provided and the SYSTEM entries in the 212 * Catalog is case-insensitive.</p> 213 * 214 * @param systemId The system ID to locate in the catalog. 215 * 216 * @return The system identifier to use for systemId. 217 * 218 * @throws MalformedURLException The formal system identifier of a 219 * subordinate catalog cannot be turned into a valid URL. 220 * @throws IOException Error reading subordinate catalog file. 221 */ 222 public String resolveSystem(String systemId) 223 throws MalformedURLException, IOException { 224 225 String resolved = super.resolveSystem(systemId); 226 if (resolved != null) { 227 return resolved; 228 } 229 230 Enumeration en = catalogEntries.elements(); 231 while (en.hasMoreElements()) { 232 CatalogEntry e = (CatalogEntry) en.nextElement(); 233 if (e.getEntryType() == RESOLVER) { 234 resolved = resolveExternalSystem(systemId, e.getEntryArg(0)); 235 if (resolved != null) { 236 return resolved; 237 } 238 } else if (e.getEntryType() == SYSTEMSUFFIX) { 239 String suffix = e.getEntryArg(0); 240 String result = e.getEntryArg(1); 241 242 if (suffix.length() <= systemId.length() 243 && systemId.substring(systemId.length()-suffix.length()).equals(suffix)) { 244 return result; 245 } 246 } 247 } 248 249 return resolveSubordinateCatalogs(Catalog.SYSTEM, 250 null, 251 null, 252 systemId); 253 } 254 255 /** 256 * Return the applicable PUBLIC or SYSTEM identifier, resorting 257 * to external resolvers if necessary. 258 * 259 * <p>This method searches the Catalog and returns the system 260 * identifier specified for the given system or 261 * public identifiers. If 262 * no appropriate PUBLIC or SYSTEM entry is found in the Catalog, 263 * null is returned.</p> 264 * 265 * <p>Note that a system or public identifier in the current catalog 266 * (or subordinate catalogs) will be used in preference to an 267 * external resolver. Further, if a systemId is present, the external 268 * resolver(s) will be queried for that before the publicId.</p> 269 * 270 * @param publicId The public identifier to locate in the catalog. 271 * Public identifiers are normalized before comparison. 272 * @param systemId The nominal system identifier for the entity 273 * in question (as provided in the source document). 274 * 275 * @throws MalformedURLException The formal system identifier of a 276 * subordinate catalog cannot be turned into a valid URL. 277 * @throws IOException Error reading subordinate catalog file. 278 * 279 * @return The system identifier to use. 280 * Note that the nominal system identifier is not returned if a 281 * match is not found in the catalog, instead null is returned 282 * to indicate that no match was found. 283 */ 284 public String resolvePublic(String publicId, String systemId) 285 throws MalformedURLException, IOException { 286 287 String resolved = super.resolvePublic(publicId, systemId); 288 if (resolved != null) { 289 return resolved; 290 } 291 292 Enumeration en = catalogEntries.elements(); 293 while (en.hasMoreElements()) { 294 CatalogEntry e = (CatalogEntry) en.nextElement(); 295 if (e.getEntryType() == RESOLVER) { 296 if (systemId != null) { 297 resolved = resolveExternalSystem(systemId, 298 e.getEntryArg(0)); 299 if (resolved != null) { 300 return resolved; 301 } 302 } 303 resolved = resolveExternalPublic(publicId, e.getEntryArg(0)); 304 if (resolved != null) { 305 return resolved; 306 } 307 } 308 } 309 310 return resolveSubordinateCatalogs(Catalog.PUBLIC, 311 null, 312 publicId, 313 systemId); 314 } 315 316 /** 317 * Query an external RFC2483 resolver for a system identifier. 318 * 319 * @param systemId The system ID to locate. 320 * @param resolver The name of the resolver to use. 321 * 322 * @return The system identifier to use for the systemId. 323 */ 324 protected String resolveExternalSystem(String systemId, String resolver) 325 throws MalformedURLException, IOException { 326 Resolver r = queryResolver(resolver, "i2l", systemId, null); 327 if (r != null) { 328 return r.resolveSystem(systemId); 329 } else { 330 return null; 331 } 332 } 333 334 /** 335 * Query an external RFC2483 resolver for a public identifier. 336 * 337 * @param publicId The system ID to locate. 338 * @param resolver The name of the resolver to use. 339 * 340 * @return The system identifier to use for the systemId. 341 */ 342 protected String resolveExternalPublic(String publicId, String resolver) 343 throws MalformedURLException, IOException { 344 Resolver r = queryResolver(resolver, "fpi2l", publicId, null); 345 if (r != null) { 346 return r.resolvePublic(publicId, null); 347 } else { 348 return null; 349 } 350 } 351 352 /** 353 * Query an external RFC2483 resolver. 354 * 355 * @param resolver The URL of the RFC2483 resolver. 356 * @param command The command to send the resolver. 357 * @param arg1 The first argument to the resolver. 358 * @param arg2 The second argument to the resolver, usually null. 359 * 360 * @return The Resolver constructed. 361 */ 362 protected Resolver queryResolver(String resolver, 363 String command, 364 String arg1, 365 String arg2) { 366 InputStream iStream = null; 367 String RFC2483 = resolver + "?command=" + command 368 + "&format=tr9401&uri=" + arg1 369 + "&uri2=" + arg2; 370 String line = null; 371 372 try { 373 URL url = new URL(RFC2483); 374 375 URLConnection urlCon = url.openConnection(); 376 377 urlCon.setUseCaches(false); 378 379 Resolver r = (Resolver) newCatalog(); 380 381 String cType = urlCon.getContentType(); 382 383 // I don't care about the character set or subtype 384 if (cType.indexOf(";") > 0) { 385 cType = cType.substring(0, cType.indexOf(";")); 386 } 387 388 r.parseCatalog(cType, urlCon.getInputStream()); 389 390 return r; 391 } catch (CatalogException cex) { 392 if (cex.getExceptionType() == CatalogException.UNPARSEABLE) { 393 catalogManager.debug.message(1, "Unparseable catalog: " + RFC2483); 394 } else if (cex.getExceptionType() 395 == CatalogException.UNKNOWN_FORMAT) { 396 catalogManager.debug.message(1, "Unknown catalog format: " + RFC2483); 397 } 398 return null; 399 } catch (MalformedURLException mue) { 400 catalogManager.debug.message(1, "Malformed resolver URL: " + RFC2483); 401 return null; 402 } catch (IOException ie) { 403 catalogManager.debug.message(1, "I/O Exception opening resolver: " + RFC2483); 404 return null; 405 } 406 } 407 408 /** 409 * Append two vectors, returning the result. 410 * 411 * @param vec The first vector 412 * @param appvec The vector to be appended 413 * @return The vector vec, with appvec's elements appended to it 414 */ 415 private Vector appendVector(Vector vec, Vector appvec) { 416 if (appvec != null) { 417 for (int count = 0; count < appvec.size(); count++) { 418 vec.addElement(appvec.elementAt(count)); 419 } 420 } 421 return vec; 422 } 423 424 /** 425 * Find the URNs for a given system identifier in all catalogs. 426 * 427 * @param systemId The system ID to locate. 428 * 429 * @return A vector of URNs that map to the systemId. 430 */ 431 public Vector resolveAllSystemReverse(String systemId) 432 throws MalformedURLException, IOException { 433 Vector resolved = new Vector(); 434 435 // If there's a SYSTEM entry in this catalog, use it 436 if (systemId != null) { 437 Vector localResolved = resolveLocalSystemReverse(systemId); 438 resolved = appendVector(resolved, localResolved); 439 } 440 441 // Otherwise, look in the subordinate catalogs 442 Vector subResolved = resolveAllSubordinateCatalogs(SYSTEMREVERSE, 443 null, 444 null, 445 systemId); 446 447 return appendVector(resolved, subResolved); 448 } 449 450 /** 451 * Find the URN for a given system identifier. 452 * 453 * @param systemId The system ID to locate. 454 * 455 * @return A (single) URN that maps to the systemId. 456 */ 457 public String resolveSystemReverse(String systemId) 458 throws MalformedURLException, IOException { 459 Vector resolved = resolveAllSystemReverse(systemId); 460 if (resolved != null && resolved.size() > 0) { 461 return (String) resolved.elementAt(0); 462 } else { 463 return null; 464 } 465 } 466 467 /** 468 * Return the applicable SYSTEM system identifiers. 469 * 470 * <p>If one or more SYSTEM entries exists in the Catalog 471 * for the system ID specified, return the mapped values.</p> 472 * 473 * <p>The caller is responsible for doing any necessary 474 * normalization of the system identifier before calling 475 * this method. For example, a relative system identifier in 476 * a document might be converted to an absolute system identifier 477 * before attempting to resolve it.</p> 478 * 479 * <p>Note that this function will force all subordinate catalogs 480 * to be loaded.</p> 481 * 482 * <p>On Windows-based operating systems, the comparison between 483 * the system identifier provided and the SYSTEM entries in the 484 * Catalog is case-insensitive.</p> 485 * 486 * @param systemId The system ID to locate in the catalog. 487 * 488 * @return The system identifier to use for the notation. 489 * 490 * @throws MalformedURLException The formal system identifier of a 491 * subordinate catalog cannot be turned into a valid URL. 492 * @throws IOException Error reading subordinate catalog file. 493 */ 494 public Vector resolveAllSystem(String systemId) 495 throws MalformedURLException, IOException { 496 Vector resolutions = new Vector(); 497 498 // If there are SYSTEM entries in this catalog, start with them 499 if (systemId != null) { 500 Vector localResolutions = resolveAllLocalSystem(systemId); 501 resolutions = appendVector(resolutions, localResolutions); 502 } 503 504 // Then look in the subordinate catalogs 505 Vector subResolutions = resolveAllSubordinateCatalogs(SYSTEM, 506 null, 507 null, 508 systemId); 509 resolutions = appendVector(resolutions, subResolutions); 510 511 if (resolutions.size() > 0) { 512 return resolutions; 513 } else { 514 return null; 515 } 516 } 517 518 /** 519 * Return all applicable SYSTEM system identifiers in this 520 * catalog. 521 * 522 * <p>If one or more SYSTEM entries exists in the catalog file 523 * for the system ID specified, return the mapped values.</p> 524 * 525 * @param systemId The system ID to locate in the catalog 526 * 527 * @return A vector of the mapped system identifiers or null 528 */ 529 private Vector resolveAllLocalSystem(String systemId) { 530 Vector map = new Vector(); 531 String osname = SecuritySupport.getSystemProperty("os.name"); 532 boolean windows = (osname.indexOf("Windows") >= 0); 533 Enumeration en = catalogEntries.elements(); 534 while (en.hasMoreElements()) { 535 CatalogEntry e = (CatalogEntry) en.nextElement(); 536 if (e.getEntryType() == SYSTEM 537 && (e.getEntryArg(0).equals(systemId) 538 || (windows 539 && e.getEntryArg(0).equalsIgnoreCase(systemId)))) { 540 map.addElement(e.getEntryArg(1)); 541 } 542 } 543 if (map.size() == 0) { 544 return null; 545 } else { 546 return map; 547 } 548 } 549 550 /** 551 * Find the URNs for a given system identifier in the current catalog. 552 * 553 * @param systemId The system ID to locate. 554 * 555 * @return A vector of URNs that map to the systemId. 556 */ 557 private Vector resolveLocalSystemReverse(String systemId) { 558 Vector map = new Vector(); 559 String osname = SecuritySupport.getSystemProperty("os.name"); 560 boolean windows = (osname.indexOf("Windows") >= 0); 561 Enumeration en = catalogEntries.elements(); 562 while (en.hasMoreElements()) { 563 CatalogEntry e = (CatalogEntry) en.nextElement(); 564 if (e.getEntryType() == SYSTEM 565 && (e.getEntryArg(1).equals(systemId) 566 || (windows 567 && e.getEntryArg(1).equalsIgnoreCase(systemId)))) { 568 map.addElement(e.getEntryArg(0)); 569 } 570 } 571 if (map.size() == 0) { 572 return null; 573 } else { 574 return map; 575 } 576 } 577 578 /** 579 * Search the subordinate catalogs, in order, looking for all 580 * match. 581 * 582 * <p>This method searches the Catalog and returns all of the system 583 * identifiers specified for the given entity type with the given 584 * name, public, and system identifiers. In some contexts, these 585 * may be null.</p> 586 * 587 * @param entityType The CatalogEntry type for which this query is 588 * being conducted. This is necessary in order to do the approprate 589 * query on a subordinate catalog. 590 * @param entityName The name of the entity being searched for, if 591 * appropriate. 592 * @param publicId The public identifier of the entity in question 593 * (as provided in the source document). 594 * @param systemId The nominal system identifier for the entity 595 * in question (as provided in the source document). 596 * 597 * @throws MalformedURLException The formal system identifier of a 598 * delegated catalog cannot be turned into a valid URL. 599 * @throws IOException Error reading delegated catalog file. 600 * 601 * @return The system identifier to use. 602 * Note that the nominal system identifier is not returned if a 603 * match is not found in the catalog, instead null is returned 604 * to indicate that no match was found. 605 */ 606 private synchronized Vector resolveAllSubordinateCatalogs(int entityType, 607 String entityName, 608 String publicId, 609 String systemId) 610 throws MalformedURLException, IOException { 611 612 Vector resolutions = new Vector(); 613 614 for (int catPos = 0; catPos < catalogs.size(); catPos++) { 615 Resolver c = null; 616 617 try { 618 c = (Resolver) catalogs.elementAt(catPos); 619 } catch (ClassCastException e) { 620 String catfile = (String) catalogs.elementAt(catPos); 621 c = (Resolver) newCatalog(); 622 623 try { 624 c.parseCatalog(catfile); 625 } catch (MalformedURLException mue) { 626 catalogManager.debug.message(1, "Malformed Catalog URL", catfile); 627 } catch (FileNotFoundException fnfe) { 628 catalogManager.debug.message(1, "Failed to load catalog, file not found", 629 catfile); 630 } catch (IOException ioe) { 631 catalogManager.debug.message(1, "Failed to load catalog, I/O error", catfile); 632 } 633 634 catalogs.setElementAt(c, catPos); 635 } 636 637 String resolved = null; 638 639 // Ok, now what are we supposed to call here? 640 if (entityType == DOCTYPE) { 641 resolved = c.resolveDoctype(entityName, 642 publicId, 643 systemId); 644 if (resolved != null) { 645 // Only find one DOCTYPE resolution 646 resolutions.addElement(resolved); 647 return resolutions; 648 } 649 } else if (entityType == DOCUMENT) { 650 resolved = c.resolveDocument(); 651 if (resolved != null) { 652 // Only find one DOCUMENT resolution 653 resolutions.addElement(resolved); 654 return resolutions; 655 } 656 } else if (entityType == ENTITY) { 657 resolved = c.resolveEntity(entityName, 658 publicId, 659 systemId); 660 if (resolved != null) { 661 // Only find one ENTITY resolution 662 resolutions.addElement(resolved); 663 return resolutions; 664 } 665 } else if (entityType == NOTATION) { 666 resolved = c.resolveNotation(entityName, 667 publicId, 668 systemId); 669 if (resolved != null) { 670 // Only find one NOTATION resolution 671 resolutions.addElement(resolved); 672 return resolutions; 673 } 674 } else if (entityType == PUBLIC) { 675 resolved = c.resolvePublic(publicId, systemId); 676 if (resolved != null) { 677 // Only find one PUBLIC resolution 678 resolutions.addElement(resolved); 679 return resolutions; 680 } 681 } else if (entityType == SYSTEM) { 682 Vector localResolutions = c.resolveAllSystem(systemId); 683 resolutions = appendVector(resolutions, localResolutions); 684 break; 685 } else if (entityType == SYSTEMREVERSE) { 686 Vector localResolutions = c.resolveAllSystemReverse(systemId); 687 resolutions = appendVector(resolutions, localResolutions); 688 } 689 } 690 691 if (resolutions != null) { 692 return resolutions; 693 } else { 694 return null; 695 } 696 } 697} 698