Updates to handle entity resolution for libxml2-2.13.?

This commit is contained in:
rfm 2025-01-15 09:23:59 +00:00
parent da9dc23a89
commit b5e0ef1514
3 changed files with 86 additions and 107 deletions

View file

@ -199,6 +199,7 @@ GS_EXPORT_CLASS
void *lib; /* parser context */ void *lib; /* parser context */
GSSAXHandler *saxHandler; /* handler for parsing */ GSSAXHandler *saxHandler; /* handler for parsing */
NSMutableString *messages; /* append messages here */ NSMutableString *messages; /* append messages here */
BOOL resolve;
} }
+ (NSString*) loadEntity: (NSString*)publicId at: (NSString*)location; + (NSString*) loadEntity: (NSString*)publicId at: (NSString*)location;
+ (GSXMLParser*) parser; + (GSXMLParser*) parser;

View file

@ -205,12 +205,10 @@ static xmlParserInputPtr
loadEntityFunction(const unsigned char *url, const unsigned char *eid, loadEntityFunction(const unsigned char *url, const unsigned char *eid,
void *ctx); void *ctx);
static xmlParserInputPtr static xmlParserInputPtr
resolveEntityFunction(void *ctx, const unsigned char *eid, resolveEntityFunction(void *ctx,
const unsigned char *url); const unsigned char *eid, const unsigned char *url);
static xmlEntityPtr static xmlEntityPtr
getEntityIgnoreExternal(void *ctx, const xmlChar *name); getEntityDefault(void *ctx, const xmlChar *name);
static xmlEntityPtr
getEntityResolveExternal(void *ctx, const xmlChar *name);
@interface GSXPathObject(Private) @interface GSXPathObject(Private)
+ (id) _newWithNativePointer: (xmlXPathObject *)lib + (id) _newWithNativePointer: (xmlXPathObject *)lib
@ -234,6 +232,7 @@ getEntityResolveExternal(void *ctx, const xmlChar *name);
- (BOOL) _initLibXML; - (BOOL) _initLibXML;
- (NSMutableString*) _messages; - (NSMutableString*) _messages;
- (void) _parseChunk: (NSData*)data; - (void) _parseChunk: (NSData*)data;
- (BOOL) _resolves;
@end @end
@interface GSSAXHandler (Private) @interface GSSAXHandler (Private)
@ -2402,28 +2401,9 @@ static NSString *endMarker = @"At end of incremental parse";
- (BOOL) resolveEntities: (BOOL)yesno - (BOOL) resolveEntities: (BOOL)yesno
{ {
BOOL old; BOOL old = resolve;;
if (yesno) yesno = YES; resolve = yesno;
if ((((xmlParserCtxtPtr)lib)->sax)->getEntity
== (void*)getEntityIgnoreExternal)
{
old = NO;
}
else
{
old = YES;
}
if (YES == yesno)
{
(((xmlParserCtxtPtr)lib)->sax)->getEntity
= (void*)getEntityResolveExternal;
}
else
{
(((xmlParserCtxtPtr)lib)->sax)->getEntity
= (void*)getEntityIgnoreExternal;
}
return old; return old;
} }
@ -2434,14 +2414,11 @@ static NSString *endMarker = @"At end of incremental parse";
*/ */
- (BOOL) substituteEntities: (BOOL)yesno - (BOOL) substituteEntities: (BOOL)yesno
{ {
xmlParserCtxtPtr context = (xmlParserCtxtPtr)lib;
BOOL old; BOOL old;
if (yesno) yesno = YES; old = context->replaceEntities ? YES : NO;
old = (((xmlParserCtxtPtr)lib)->replaceEntities) ? YES : NO; context->replaceEntities = (yesno ? 1 : 0);
if (old != yesno)
{
((xmlParserCtxtPtr)lib)->replaceEntities = (yesno ? 1 : 0);
}
return old; return old;
} }
@ -2494,16 +2471,15 @@ static NSString *endMarker = @"At end of incremental parse";
} }
else else
{ {
/* /* Put saxHandler address in _private member, so we can retrieve
* Put saxHandler address in _private member, so we can retrieve
* the GSSAXHandler to use in our SAX C Functions. * the GSSAXHandler to use in our SAX C Functions.
*/ */
((xmlParserCtxtPtr)lib)->_private = saxHandler; ((xmlParserCtxtPtr)lib)->_private = saxHandler;
/* /* Set the entity loading function for this parser to be our one.
* Set the entity loading function for this parser to be our one.
*/ */
((xmlParserCtxtPtr)lib)->sax->resolveEntity = resolveEntityFunction; ((xmlParserCtxtPtr)lib)->sax->resolveEntity = resolveEntityFunction;
[self resolveEntities: NO]; // Off by default
} }
return YES; return YES;
} }
@ -2523,6 +2499,10 @@ static NSString *endMarker = @"At end of incremental parse";
xmlParseChunk(lib, [data bytes], [data length], data == nil); xmlParseChunk(lib, [data bytes], [data length], data == nil);
} }
- (BOOL) _resolves
{
return resolve;
}
@end @end
/** /**
@ -2623,11 +2603,12 @@ static NSString *endMarker = @"At end of incremental parse";
#define HANDLER ((GSSAXHandler*)(((xmlParserCtxtPtr)ctx)->_private)) #define HANDLER ((GSSAXHandler*)(((xmlParserCtxtPtr)ctx)->_private))
static xmlEntityPtr static xmlEntityPtr
getEntityDefault(void *ctx, const xmlChar *name, BOOL resolve) getEntityDefault(void *ctx, const xmlChar *name)
{ {
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
xmlEntityPtr ret = NULL; xmlEntityPtr ret = NULL;
// NSLog(@"getEntityDefault called for '%s'", name);
if (ctx != 0) if (ctx != 0)
{ {
if (0 == ctxt->inSubset) if (0 == ctxt->inSubset)
@ -2667,12 +2648,13 @@ getEntityDefault(void *ctx, const xmlChar *name, BOOL resolve)
{ {
ret = xmlGetDocEntity(ctxt->myDoc, name); ret = xmlGetDocEntity(ctxt->myDoc, name);
} }
#if LIBXML_VERSION < 21300
/* In older versions we may need to parse the entity content.
*/
if ((ret != NULL) if ((ret != NULL)
&& ((ctxt->validate) || (ctxt->replaceEntities)) && ((ctxt->validate) || (ctxt->replaceEntities))
&& (ret->children == NULL) && (ret->children == NULL)
&& (ret->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY)) && (ret->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY))
{
if (YES == resolve)
{ {
xmlNodePtr children; xmlNodePtr children;
int val; int val;
@ -2697,39 +2679,23 @@ getEntityDefault(void *ctx, const xmlChar *name, BOOL resolve)
} }
ret->owner = 1; ret->owner = 1;
#if LIBXML_VERSION < 21100 #if LIBXML_VERSION < 21100
/* Set old flag ... not prent/needed in cewlater versions.
*/
if (ret->checked == 0) if (ret->checked == 0)
{ {
ret->checked = 1; ret->checked = 1;
} }
#endif #endif /* LIBXML_VERSION < 21100 */
}
} }
#endif /* LIBXML_VERSION < 21300 */
} }
return ret; return ret;
} }
static xmlEntityPtr
getEntityIgnoreExternal(void *ctx, const xmlChar *name)
{
return getEntityDefault(ctx, name, NO);
}
static xmlEntityPtr
getEntityResolveExternal(void *ctx, const xmlChar *name)
{
return getEntityDefault(ctx, name, YES);
}
/* WARNING ... as far as I can tell libxml2 never uses the resolveEntity /* WARNING ... as far as I can tell libxml2 never uses the resolveEntity
* callback, so this function is never called via that route. * callback, so this function is never called via that route.
* We therefore also set this as the global default entity loading * We therefore also set this as the global default entity loading
* function (in [GSXMLParser+initialize] and [GSSAXHandler+initialize]). * function (in [GSXMLParser+initialize] and [GSSAXHandler+initialize]).
*
* To implement the -resolveEntities method we must permit/deny any attempt
* to load an entity (before the function to resolve is even called),
* We therefore intercept the getEntity callback (using getEntityDefault()),
* re-implementing some of the code inside libxml2 to avoid attempts to
* load/parse external entities unless we have specifically enabled it.
*/ */
static xmlParserInputPtr static xmlParserInputPtr
loadEntityFunction(const unsigned char *url, loadEntityFunction(const unsigned char *url,
@ -2741,6 +2707,7 @@ loadEntityFunction(const unsigned char *url,
NSString *location; NSString *location;
NSArray *components; NSArray *components;
NSMutableString *local; NSMutableString *local;
GSXMLParser *parser;
unsigned count; unsigned count;
unsigned index; unsigned index;
@ -2748,6 +2715,12 @@ loadEntityFunction(const unsigned char *url,
if (url == NULL) if (url == NULL)
return NULL; return NULL;
parser = [HANDLER parser];
if (NO == [parser _resolves])
{
return xmlNewStringInputStream(ctx, (const xmlChar *)"");
}
entityId = (eid != NULL) ? (id)UTF8Str(eid) : nil; entityId = (eid != NULL) ? (id)UTF8Str(eid) : nil;
location = UTF8Str(url); location = UTF8Str(url);
components = [location pathComponents]; components = [location pathComponents];
@ -2942,6 +2915,7 @@ static xmlParserInputPtr
resolveEntityFunction(void *ctx, resolveEntityFunction(void *ctx,
const unsigned char *eid, const unsigned char *url) const unsigned char *eid, const unsigned char *url)
{ {
NSLog(@"resolveEntityFunction called for %s %s", url, eid);
return loadEntityFunction(url, eid, ctx); return loadEntityFunction(url, eid, ctx);
} }
@ -3729,7 +3703,7 @@ fatalErrorFunction(void *ctx, const unsigned char *msg, ...)
LIB->hasInternalSubset = (void*) hasInternalSubsetFunction; LIB->hasInternalSubset = (void*) hasInternalSubsetFunction;
LIB->hasExternalSubset = (void*) hasExternalSubsetFunction; LIB->hasExternalSubset = (void*) hasExternalSubsetFunction;
LIB->resolveEntity = (void*) resolveEntityFunction; LIB->resolveEntity = (void*) resolveEntityFunction;
LIB->getEntity = (void*) getEntityIgnoreExternal; LIB->getEntity = (void*) getEntityDefault;
LIB->entityDecl = (void*) entityDeclFunction; LIB->entityDecl = (void*) entityDeclFunction;
LIB->notationDecl = (void*) notationDeclFunction; LIB->notationDecl = (void*) notationDeclFunction;
LIB->attributeDecl = (void*) attributeDeclFunction; LIB->attributeDecl = (void*) attributeDeclFunction;
@ -3852,7 +3826,7 @@ fatalErrorFunction(void *ctx, const unsigned char *msg, ...)
SETCB(getEntity, getEntity:); SETCB(getEntity, getEntity:);
if (LIB->getEntity != getEntityFunction) if (LIB->getEntity != getEntityFunction)
{ {
LIB->getEntity = getEntityIgnoreExternal; LIB->getEntity = getEntityDefault;
} }
SETCB(entityDecl, entityDecl:type:public:system:content:); SETCB(entityDecl, entityDecl:type:public:system:content:);
SETCB(notationDecl, notationDecl:public:system:); SETCB(notationDecl, notationDecl:public:system:);
@ -3898,7 +3872,11 @@ fatalErrorFunction(void *ctx, const unsigned char *msg, ...)
} }
else else
{ {
#if LIBXML_VERSION < 21100
memcpy(lib, &htmlDefaultSAXHandler, sizeof(htmlSAXHandler)); memcpy(lib, &htmlDefaultSAXHandler, sizeof(htmlSAXHandler));
#else
xmlSAX2InitHtmlDefaultSAXHandler(lib);
#endif
#define LIB ((htmlSAXHandlerPtr)lib) #define LIB ((htmlSAXHandlerPtr)lib)
LIB->internalSubset = (void*)internalSubsetFunction; LIB->internalSubset = (void*)internalSubsetFunction;

View file

@ -15,6 +15,7 @@ int main()
NSMutableArray *oparams; NSMutableArray *oparams;
GSXMLNode *node; GSXMLNode *node;
GSXMLRPC *rpc; GSXMLRPC *rpc;
NSString *xml;
NSString *str; NSString *str;
NSString *testPath; NSString *testPath;
NSString *absolutePath; NSString *absolutePath;
@ -155,44 +156,43 @@ int main()
stringByAppendingPathComponent: @"GNUmakefile"]; stringByAppendingPathComponent: @"GNUmakefile"];
absolutePath = [[NSURL fileURLWithPath: testPath] absoluteString]; absolutePath = [[NSURL fileURLWithPath: testPath] absoluteString];
str = [NSString stringWithFormat: xml = [NSString stringWithFormat:
@"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" @"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
@"<!DOCTYPE foo [\n" @"<!DOCTYPE foo [\n"
@"<!ENTITY foo SYSTEM \"%@\">\n" @"<!ENTITY foo SYSTEM \"%@\">\n"
@"]>\n" @"]>\n"
@"<file>&amp;&foo;&#65;</file>", absolutePath]; @"<file>&amp;&foo;&#65;</file>", absolutePath];
dat = [xml dataUsingEncoding: NSUTF8StringEncoding];
parser = [GSXMLParser parserWithData: parser = [GSXMLParser parserWithData: dat];
[str dataUsingEncoding: NSUTF8StringEncoding]];
[parser substituteEntities: YES]; [parser substituteEntities: YES];
[parser parse]; [parser parse];
PASS_EQUAL([[[parser document] root] content], @"&A", str = [[[parser document] root] content];
"external entity is ignored") PASS_EQUAL(str, @"&A", "external entity is ignored")
parser = [GSXMLParser parserWithData: parser = [GSXMLParser parserWithData: dat];
[str dataUsingEncoding: NSUTF8StringEncoding]];
[parser substituteEntities: YES]; [parser substituteEntities: YES];
[parser resolveEntities: YES]; [parser resolveEntities: YES];
[parser parse]; [parser parse];
str = [[[parser document] root] content]; str = [[[parser document] root] content];
PASS([str rangeOfString: @"MAKEFILES"].length > 0, PASS(str != nil && [str rangeOfString: @"MAKEFILES"].length > 0,
"external entity is resolved") "external entity is resolved")
str = @"<!DOCTYPE plist PUBLIC \"-//GNUstep//DTD plist 0.9//EN\"" xml = @"<!DOCTYPE plist PUBLIC \"-//GNUstep//DTD plist 0.9//EN\""
@" \"http://www.gnustep.org/plist-0_9.xml\">\n" @" \"http://www.gnustep.org/plist-0_9.xml\">\n"
@"<plist></plist>"; @"<plist></plist>";
parser = [GSXMLParser parserWithData: dat = [xml dataUsingEncoding: NSUTF8StringEncoding];
[str dataUsingEncoding: NSUTF8StringEncoding]]; parser = [GSXMLParser parserWithData: dat];
[parser substituteEntities: YES]; [parser substituteEntities: YES];
[parser resolveEntities: YES]; [parser resolveEntities: YES];
[parser doValidityChecking: YES]; [parser doValidityChecking: YES];
PASS([parser parse] == NO, "empty plist is not valid") PASS([parser parse] == NO, "empty plist is not valid")
str = @"<!DOCTYPE plist PUBLIC \"-//GNUstep//DTD plist 0.9//EN\"" xml = @"<!DOCTYPE plist PUBLIC \"-//GNUstep//DTD plist 0.9//EN\""
@" \"http://www.gnustep.org/plist-0_9.xml\">\n" @" \"http://www.gnustep.org/plist-0_9.xml\">\n"
@"<plist><string>xxx</string></plist>"; @"<plist><string>xxx</string></plist>";
parser = [GSXMLParser parserWithData: dat = [xml dataUsingEncoding: NSUTF8StringEncoding];
[str dataUsingEncoding: NSUTF8StringEncoding]]; parser = [GSXMLParser parserWithData: dat];
[parser substituteEntities: YES]; [parser substituteEntities: YES];
[parser resolveEntities: YES]; [parser resolveEntities: YES];
[parser doValidityChecking: YES]; [parser doValidityChecking: YES];