19
19
import java .net .URL ;
20
20
import java .net .URLConnection ;
21
21
import java .nio .charset .StandardCharsets ;
22
- import java .security .AccessControlException ;
23
22
import java .security .PrivilegedActionException ;
24
23
import java .util .HashMap ;
25
24
import java .util .HashSet ;
@@ -218,6 +217,46 @@ public InputSource getExternalSubset(String name, String baseURI) throws SAXExce
218
217
return null ;
219
218
}
220
219
220
+ /**
221
+ * Register an internal classpath filename to retrieve a DTD {@code SystemId}.
222
+ *
223
+ * @param systemId the {@code SystemId}.
224
+ * @param filename the internal filename.
225
+ * @return {@code true} if the new {@code SystemId} was successfully registered,
226
+ * {@code false} if it was already registered.
227
+ * @throws IllegalArgumentException if the {@code filename} is considered
228
+ * invalid by
229
+ * {@link #isInvalidInternalPath(String)}.
230
+ */
231
+ protected boolean registerSystemIdFilename (String systemId , String filename ) {
232
+ if (filename == null || systemId == null ) {
233
+ throw new NullPointerException ("Null SystemId or filename." );
234
+ }
235
+ if (isInvalidInternalPath (filename )) {
236
+ throw new IllegalArgumentException ("Bad DTD filename." );
237
+ }
238
+ String ret ;
239
+ synchronized (systemIdToFilename ) {
240
+ ret = systemIdToFilename .putIfAbsent (systemId , filename );
241
+ }
242
+ return ret == null ;
243
+ }
244
+
245
+ /**
246
+ * Determine if the given pathname is an invalid internal path.
247
+ * <p>
248
+ * The pathname must contain {@code /dtd/} and be a valid path according to
249
+ * {@link #isInvalidPath(String)}.
250
+ * </p>
251
+ *
252
+ * @param pathname the classpath pathname to check. It is assumed to be
253
+ * non-{@code null}.
254
+ * @return {@code true} if the pathname is invalid.
255
+ */
256
+ protected boolean isInvalidInternalPath (String pathname ) {
257
+ return isInvalidPath (pathname ) || !pathname .contains ("/dtd/" );
258
+ }
259
+
221
260
@ Override
222
261
public final InputSource resolveEntity (String name , String publicId , String baseURI , String systemId )
223
262
throws SAXException , IOException {
@@ -296,6 +335,15 @@ private String getSystemIdFromPublicId(String publicId) {
296
335
return null ;
297
336
}
298
337
338
+ /**
339
+ * Determine if the given path is considered invalid for a DTD.
340
+ * <p>
341
+ * To be valid, must end with {@code .dtd}, {@code .ent} or {@code .mod}.
342
+ * </p>
343
+ *
344
+ * @param path the path to check.
345
+ * @return {@code true} if the path is invalid for a DTD, {@code false} otherwise.
346
+ */
299
347
protected boolean isInvalidPath (String path ) {
300
348
int len = path .length ();
301
349
String ext ;
@@ -314,6 +362,9 @@ protected boolean isWhitelistEnabled() {
314
362
315
363
/**
316
364
* Is the given protocol not supported by this resolver ?
365
+ * <p>
366
+ * Only {@code http} and {@code https} are valid.
367
+ * </p>
317
368
*
318
369
* @param protocol
319
370
* the protocol.
@@ -411,8 +462,7 @@ private Reader loadDTDfromClasspath(final String dtdFilename) {
411
462
buf .append ('/' ).append (pkgPath ).append ('/' ).append (dtdFilename );
412
463
resPath = buf .toString ();
413
464
} else {
414
- // All filenames must be relative
415
- throw new AccessControlException ("Attempt to read " + dtdFilename );
465
+ resPath = dtdFilename ;
416
466
}
417
467
InputStream is = java .security .AccessController .doPrivileged (new java .security .PrivilegedAction <InputStream >() {
418
468
@ Override
0 commit comments