Commits

Shlomi Fish committed 86e4071

Add a global external entity resolver.

Fixing https://rt.cpan.org/Ticket/Display.html?id=69166 .

Comments (0)

Files changed (6)

     - Change "a XML::LibXML::*" to "an XML::LibXML::*" in the documentation.
     - Document XML::LibXML::NamedNodeMap :
         - https://rt.cpan.org/Ticket/Display.html?id=57652 .
-
+    - Add an external entity resolver (for XSLT/etc.):
+        - Fixing https://rt.cpan.org/Ticket/Display.html?id=69166 .
+    
 1.76            Thu Jun 30 20:58:46 IDT 2011
-     - Cleaned up t/28new_callbacks_multiple.t - convert to a Counter
+    - Cleaned up t/28new_callbacks_multiple.t - convert to a Counter
      and Stacker class.
         - After that, the regression test for was added:
             - https://rt.cpan.org/Ticket/Display.html?id=51086
 # callback functions                                                      #
 #-------------------------------------------------------------------------#
 
+sub externalEntityLoader(&)
+{
+    return _externalEntityLoader($_[0]);
+}
+
 sub input_callbacks {
     my $self     = shift;
     my $icbclass = shift;
 /* this should keep the default */
 static xmlExternalEntityLoader LibXML_old_ext_ent_loader = NULL;
 
+/* global external entity loader */
+SV *EXTERNAL_ENTITY_LOADER_FUNC = (SV *)NULL;
+
 SV* PROXY_NODE_REGISTRY_MUTEX = NULL;
 
 /* ****************************************************************
         const char * ID,
         xmlParserCtxtPtr ctxt)
 {
-    SV * self;
-    HV * real_obj;
     SV ** func;
     int count;
     SV * results;
     const char * results_pv;
     xmlParserInputBufferPtr input_buf;
 
-    if (ctxt->_private == NULL) {
+    if (ctxt->_private == NULL && EXTERNAL_ENTITY_LOADER_FUNC == NULL)
+    {
         return xmlNewInputFromFile(ctxt, URL);
     }
 
         ID = "";
     }
 
-    self = (SV *)ctxt->_private;
-    real_obj = (HV *)SvRV(self);
-    func = hv_fetch(real_obj, "ext_ent_handler", 15, 0);
-
-    if (func != NULL  && SvTRUE(*func)) {
+    /* fetch entity loader function */
+    if(EXTERNAL_ENTITY_LOADER_FUNC != NULL)
+    {
+       func = &EXTERNAL_ENTITY_LOADER_FUNC;
+    }
+    else
+    {
+       SV * self;
+       HV * real_obj;
+
+       self = (SV *)ctxt->_private;
+       real_obj = (HV *)SvRV(self);
+       func = hv_fetch(real_obj, "ext_ent_handler", 15, 0);
+    }
+
+    if (func != NULL && SvTRUE(*func)) {
         dTHX;
         dSP;
 
             if (ctxt) ctxt->linenumbers = 0;
         }
 
-        item = hv_fetch(real_obj, "ext_ent_handler", 15, 0);
-        if ( item != NULL  && SvTRUE(*item)) {
-            LibXML_old_ext_ent_loader =  xmlGetExternalEntityLoader();
-            xmlSetExternalEntityLoader( (xmlExternalEntityLoader)LibXML_load_external_entity );
-        }
-        else {
-            if (parserOptions & XML_PARSE_NONET) {
+       if(EXTERNAL_ENTITY_LOADER_FUNC == NULL)
+       {
+            item = hv_fetch(real_obj, "ext_ent_handler", 15, 0);
+            if (item != NULL  && SvTRUE(*item)) {
                 LibXML_old_ext_ent_loader =  xmlGetExternalEntityLoader();
-                xmlSetExternalEntityLoader( xmlNoNetExternalEntityLoader );
-            }
-            /* LibXML_old_ext_ent_loader =  NULL; */
-        }
+                xmlSetExternalEntityLoader( (xmlExternalEntityLoader)LibXML_load_external_entity );
+            }
+            else 
+             {
+                if (parserOptions & XML_PARSE_NONET)
+                {
+                    LibXML_old_ext_ent_loader = xmlGetExternalEntityLoader();
+                    xmlSetExternalEntityLoader( xmlNoNetExternalEntityLoader );
+                }
+                /* LibXML_old_ext_ent_loader =  NULL; */
+            }
+       }
     }
 
     return real_obj;
 #ifndef WITH_SERRORS
     xmlGetWarningsDefaultValue = 0;
 #endif
-    if (LibXML_old_ext_ent_loader != NULL ) {
+    if (EXTERNAL_ENTITY_LOADER_FUNC == NULL && LibXML_old_ext_ent_loader != NULL)
+    {
         xmlSetExternalEntityLoader( (xmlExternalEntityLoader)LibXML_old_ext_ent_loader );
     }
 }
     OUTPUT:
         RETVAL
 
+SV*
+_externalEntityLoader( loader )
+        SV* loader
+    CODE:
+        {
+            RETVAL = EXTERNAL_ENTITY_LOADER_FUNC;
+            if(EXTERNAL_ENTITY_LOADER_FUNC == NULL)
+            {
+                EXTERNAL_ENTITY_LOADER_FUNC = newSVsv(loader);
+            }
+
+            if (LibXML_old_ext_ent_loader == NULL )
+            {
+                LibXML_old_ext_ent_loader = xmlGetExternalEntityLoader();
+                xmlSetExternalEntityLoader((xmlExternalEntityLoader)LibXML_load_external_entity);
+            }
+        }
+    OUTPUT:
+        RETVAL
+
 MODULE = XML::LibXML         PACKAGE = XML::LibXML::HashTable
 
 xmlHashTablePtr
 #endif
     OUTPUT:
         RETVAL
-
 t/47load_xml_callbacks.t
 t/48_rt55000.t
 t/49_load_html.t
+t/49global_extent.t
 t/60struct_error.t
 t/61error.t
 t/80registryleak.t
                     <funcsynopsisinfo>$parser-&gt;load_catalog( $catalog_file );</funcsynopsisinfo>
                   </funcsynopsis>
 		  <para>Loads the XML catalog file $catalog_file.</para>
+          <programlisting>
+# Global external entity loader (similar to ext_ent_handler option
+# but this works really globally, also in XML::LibXSLT include etc..)
+
+XML::LibXML::externalEntityLoader(\&amp;my_loader);
+          </programlisting>
 		</listitem>
 	      </varlistentry>
 		</variablelist>

t/49global_extent.t

+use strict;
+use warnings;
+
+use Test::More tests => 1;
+use XML::LibXML;
+
+if (XML::LibXML::LIBXML_VERSION() < 20627) {
+    skip_all("skipping for libxml2 < 2.6.27");
+}
+
+sub handler {
+  return "ENTITY:" . join(",",@_);
+}
+
+# global entity loader
+XML::LibXML::externalEntityLoader(\&handler);
+
+my $parser = XML::LibXML->new({
+  expand_entities => 1,
+});
+
+my $xml = <<'EOF';
+<?xml version="1.0"?>
+<!DOCTYPE foo [
+<!ENTITY a PUBLIC "//foo/bar/b" "file:/dev/null">
+<!ENTITY b SYSTEM "file:///dev/null">
+]>
+<root>
+  <a>&a;</a>
+  <b>&b;</b>
+</root>
+EOF
+my $xml_out = $xml;
+$xml_out =~ s{&a;}{ENTITY:file:/dev/null,//foo/bar/b};
+$xml_out =~ s{&b;}{ENTITY:file:///dev/null,};
+
+my $doc = $parser->parse_string($xml);
+
+# TEST
+is( $doc->toString(), $xml_out );