Commits

Anonymous committed 85a4db1

- fix bogus validation results if revalidating a modified document
- added ABI compatibility checks

Comments (0)

Files changed (22)

 package XML::LibXML;
 
 use strict;
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS
+use vars qw($VERSION $ABI_VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS
             $skipDTD $skipXMLDeclaration $setTagCompression
             $MatchCB $ReadCB $OpenCB $CloseCB 
             );
 use IO::Handle; # for FH reads called as methods
 
 BEGIN {
-
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
+$ABI_VERSION = 2;
 require Exporter;
 require DynaLoader;
 @ISA = qw(DynaLoader Exporter);
 
 use vars qw($__PROXY_NODE_REGISTRY $__threads_shared $__PROXY_NODE_REGISTRY_MUTEX $__loaded);
 
+sub VERSION {
+  my $class = shift;
+  my ($caller) = caller;
+  my $req_abi = $ABI_VERSION;
+  if (UNIVERSAL::can($caller,'REQUIRE_XML_LIBXML_ABI_VERSION')) {
+    $req_abi = $caller->REQUIRE_XML_LIBXML_ABI_VERSION();
+  } elsif ($caller eq 'XML::LibXSLT') {
+    # XML::LibXSLT without REQUIRE_XML_LIBXML_ABI_VERSION is an old and incompatible version
+    $req_abi = 1;
+  }
+  unless ($req_abi == $ABI_VERSION) {
+    my $ver = @_ ? ' '.$_[0] : '';
+    die ("This version of $caller requires XML::LibXML$ver (ABI $req_abi), which is incompatible with currently installed XML::LibXML $VERSION (ABI $ABI_VERSION). Please upgrade $caller, XML::LibXML, or both!");
+  }
+  return $class->UNIVERSAL::VERSION(@_)
+}
+
 #-------------------------------------------------------------------------#
 # export information                                                      #
 #-------------------------------------------------------------------------#
         if (encoding!=NULL && strlen(encoding)) {
 	  self->encoding = xmlStrdup( (const xmlChar *)encoding );
 	  charset = (int)xmlParseCharEncoding( (const char*)self->encoding );
-	  if ( charset > 0 ) {
-            ((ProxyNodePtr)self->_private)->encoding = charset;
-	  }
-	  else {
-            ((ProxyNodePtr)self->_private)->encoding = XML_CHAR_ENCODING_ERROR;
+	  if ( charset <= 0 ) {
+            charset = XML_CHAR_ENCODING_ERROR;
 	  }
 	} else {
 	  self->encoding=NULL;
-	  ((ProxyNodePtr)self->_private)->encoding = XML_CHAR_ENCODING_UTF8;
+          charset = XML_CHAR_ENCODING_UTF8;
 	}
+        SetPmmNodeEncoding(self, charset);
+
 
 int
 standalone( self )
         cvp.vstateNr = 0;
         cvp.vstateTab = NULL;
 
+        PmmClearPSVI(self);
+        PmmInvalidatePSVI(self);
         if (items > 1) {
             dtd_sv = ST(1);
             if ( sv_isobject(dtd_sv) && (SvTYPE(SvRV(dtd_sv)) == SVt_PVMG) ) {
         cvp.vstateNr = 0;
         cvp.vstateTab = NULL;
 
+        PmmClearPSVI(self);
+        PmmInvalidatePSVI(self);
+
         if (items > 1) {
             dtd_sv = ST(1);
             if ( sv_isobject(dtd_sv) && (SvTYPE(SvRV(dtd_sv)) == SVt_PVMG) ) {
             doc = self->doc;
 
             if ( doc != NULL ) {
-                xmlSetTreeDoc(ret, doc);
+                xmlSetTreeDoc(ret, doc); /* setting to self, no need to clear psvi */
             }
 
             docfrag = PmmNewFragment( doc );
         if ( path == NULL ) {
             croak( "cannot calculate path for the given node" );
         }
-        RETVAL = nodeC2Sv( path, self );
+        RETVAL = C2Sv( path, NULL );
         xmlFree(path);
     OUTPUT:
         RETVAL
         RETVAL
 
 SV*
-_getAttribute( self, attr_name, doc_enc = 0 )
+_getAttribute( self, attr_name, useDomEncoding = 0 )
         xmlNodePtr self
         SV * attr_name
-        int doc_enc
+        int useDomEncoding
     PREINIT:
         xmlChar * name;
         xmlChar * prefix    = NULL;
         }
         xmlFree(name);
         if ( ret ) {
-            if ( doc_enc == 1 ) {
+            if ( useDomEncoding ) {
                 RETVAL = nodeC2Sv(ret, self);
             }
             else {
         RETVAL
 
 SV *
-_getAttributeNS( self, namespaceURI, attr_name )
+_getAttributeNS( self, namespaceURI, attr_name, useDomEncoding = 0 )
         xmlNodePtr self
         SV * namespaceURI
         SV * attr_name
+        int useDomEncoding
     PREINIT:
         xmlChar * name;
         xmlChar * nsURI;
             xmlFree( nsURI );
         }
         if ( ret ) {
-            RETVAL = nodeC2Sv( ret, self );
+            if (useDomEncoding) {
+                RETVAL = nodeC2Sv( ret, self );
+            } else {
+                RETVAL = C2Sv( ret, NULL );
+            }
             xmlFree( ret );
         }
         else {
     CODE:
         INIT_ERROR_HANDLER;
 
+        if (doc) {
+            PmmClearPSVI(doc);
+            PmmInvalidatePSVI(doc);
+        }
         vctxt  = xmlRelaxNGNewValidCtxt( self );
         if ( vctxt == NULL ) {
             CLEANUP_ERROR_HANDLER;
     CODE:
         INIT_ERROR_HANDLER;
 
+        if (doc) {
+            PmmClearPSVI(doc);
+            PmmInvalidatePSVI(doc);
+        }
         vctxt  = xmlSchemaNewValidCtxt( self );
         if ( vctxt == NULL ) {
             CLEANUP_ERROR_HANDLER;
 	  /* will be decremented in Reader destructor */
 	  PmmREFCNT_inc(SvPROXYNODE(perl_doc));
 	}
+        if (xmlTextReaderGetParserProp(reader,XML_PARSER_VALIDATE))
+            PmmInvalidatePSVI(doc); /* the document may have psvi info */
 
         copy = PmmCloneNode( node, expand );
         if ( copy == NULL ) {
 	doc = xmlTextReaderCurrentDoc(reader);
         if (!doc) XSRETURN_UNDEF;
         RETVAL = PmmNodeToSv((xmlNodePtr)doc, NULL);
+        /* FIXME: taint the document with PmmInvalidatePSVI if the reader did validation */
         if ( PmmREFCNT(SvPROXYNODE(RETVAL))==1 ) {
 	  /* will be decremented in Reader destructor */
 	  PmmREFCNT_inc(SvPROXYNODE(RETVAL));
 	}
+        if (xmlTextReaderGetParserProp(reader,XML_PARSER_VALIDATE))
+            PmmInvalidatePSVI(doc); /* the document may have psvi info */
+
     OUTPUT:
         RETVAL
 
 */
 
 #include "dom.h"
-
+#include "perl-libxml-mm.h"
 
 /* #define warn(string) fprintf(stderr, string) */
 
 #define xs_warn(string)
 #endif
 
+#if LIBXML_VERSION >= 20621
+void
+domClearPSVIInList(xmlNodePtr list);
+
+void
+domClearPSVI(xmlNodePtr tree) {
+    xmlAttrPtr prop;
+
+    if (tree == NULL)
+        return;
+    if (tree->type == XML_ELEMENT_NODE) {
+        tree->psvi = NULL;
+        prop = tree->properties;
+        while (prop != NULL) {
+            if (tree->type == XML_ATTRIBUTE_NODE)
+                ((xmlAttrPtr) prop)->psvi = NULL;
+            domClearPSVIInList(prop->children);
+            prop = prop->next;
+        }
+    } else if (tree->type == XML_DOCUMENT_NODE) {
+        ((xmlDocPtr) tree)->psvi = NULL;
+    }
+    if (tree->children != NULL)
+        domClearPSVIInList(tree->children);
+}
+
+void
+domClearPSVIInList(xmlNodePtr list) {
+    xmlNodePtr cur;
+
+    if (list == NULL)
+        return;
+    cur = list;
+    while (cur != NULL) {
+        domClearPSVI(cur);
+        cur = cur->next;
+    }
+}
+#endif
+
+
+
 /**
  * Name: domReconcileNs
  * Synopsis: void domReconcileNs( xmlNodePtr tree );
 
     /* tell all children about the new boss */ 
     if ( node && node->doc != doc ) {
+        printf("node type: %d\n",return_node->type);
+        PmmClearPSVI(return_node); /* if the old document contained psvi, clear it now */
         xmlSetTreeDoc(return_node, doc);
     }
 
 }
 
 
-void
-domSetParentNode( xmlNodePtr self, xmlNodePtr p ) {
-    /* never set the parent to a node in the own subtree */ 
-    if( self && !domIsParent(self, p)) {
-        if( self->parent != p ){
-            xmlUnlinkNode( self );
-            self->parent = p;
-            if( p->doc != self->doc ) {
-                self->doc = p->doc;
-            }
-        }
-    }
-}
-
 xmlNodeSetPtr
 domGetElementsByTagName( xmlNodePtr n, xmlChar* name ){
     xmlNodeSetPtr rv = NULL;
 void
 domSetNodeValue( xmlNodePtr self, xmlChar* value );
 
-void
-domSetParentNode( xmlNodePtr self, xmlNodePtr newParent );
-
 xmlNodePtr
 domReplaceNode( xmlNodePtr old, xmlNodePtr new );
 

lib/XML/LibXML/Boolean.pm

 
 use vars qw ($VERSION);
 
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 use overload
         '""' => \&value,

lib/XML/LibXML/Common.pm

 
 @ISA = qw(Exporter);
 
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 use XML::LibXML qw(:libxml);
 

lib/XML/LibXML/ErrNo.pm

 use strict;
 use vars qw($VERSION);
 
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 use constant ERR_OK                               => 0;
 use constant ERR_INTERNAL_ERROR                   => 1;

lib/XML/LibXML/Error.pm

 use overload
   '""' => \&as_string;
 
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 use constant XML_ERR_NONE	     => 0;
 use constant XML_ERR_WARNING	     => 1; # A simple warning

lib/XML/LibXML/Literal.pm

 use strict;
 
 use vars qw ($VERSION);
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 use overload 
 		'""' => \&value,

lib/XML/LibXML/NodeList.pm

 use XML::LibXML::Number;
 
 use vars qw ($VERSION);
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 use overload 
 		'""' => \&to_literal,

lib/XML/LibXML/Number.pm

 use strict;
 
 use vars qw ($VERSION);
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 use overload
         '""' => \&value,

lib/XML/LibXML/Reader.pm

 use warnings;
 
 use vars qw ($VERSION);
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 use 5.008_000;
 

lib/XML/LibXML/SAX.pm

 use strict;
 use vars qw($VERSION @ISA);
 
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 use XML::LibXML;
 use XML::SAX::Base;

lib/XML/LibXML/SAX/Builder.pm

   return $XML::LibXML::__threads_shared ? 0 : 1;
 }
 
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 sub new {
     my $class = shift;

lib/XML/LibXML/SAX/Generator.pm

 use XML::LibXML;
 use vars qw ($VERSION);
 
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 sub CLONE_SKIP {
   return $XML::LibXML::__threads_shared ? 0 : 1;

lib/XML/LibXML/SAX/Parser.pm

 use XML::SAX::Base;
 use XML::SAX::DocumentLocator;
 
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 @ISA = ('XML::SAX::Base');
 
 sub CLONE_SKIP {

lib/XML/LibXML/XPathContext.pm

 use XML::LibXML;
 use XML::LibXML::NodeList;
 
-$VERSION = "1.69_2"; # VERSION TEMPLATE: DO NOT CHANGE
+$VERSION = "1.70"; # VERSION TEMPLATE: DO NOT CHANGE
 
 # should LibXML XPath data types be used for simple objects
 # when passing parameters to extension functions (default: no)
     }
 
     if ( node->_private == NULL ) {
-	proxy = (ProxyNodePtr)xmlMalloc(sizeof(struct _ProxyNode));
+        switch ( node->type ) {
+        case XML_DOCUMENT_NODE:
+        case XML_HTML_DOCUMENT_NODE:
+        case XML_DOCB_DOCUMENT_NODE:
+            proxy = (ProxyNodePtr)xmlMalloc(sizeof(struct _DocProxyNode));
+            if (proxy != NULL) {
+                ((DocProxyNodePtr)proxy)->psvi_status = Pmm_NO_PSVI;
+                SetPmmENCODING(proxy, XML_CHAR_ENCODING_NONE);
+            }
+            break;
+        default:
+            proxy = (ProxyNodePtr)xmlMalloc(sizeof(struct _ProxyNode));
+            break;
+        }
         if (proxy != NULL) {
             proxy->node  = node;
             proxy->owner   = NULL;
             proxy->count   = 0;
-            proxy->encoding= 0;
             node->_private = (void*) proxy;
         }
     }
         case XML_HTML_DOCUMENT_NODE:
         case XML_DOCB_DOCUMENT_NODE:
             if ( ((xmlDocPtr)node)->encoding != NULL ) {
-                dfProxy->encoding = (int)xmlParseCharEncoding( (const char*)((xmlDocPtr)node)->encoding );
+                SetPmmENCODING(dfProxy, (int)xmlParseCharEncoding( (const char*)((xmlDocPtr)node)->encoding ));
             }
             break;
         default:
     return retval;
 }
 
-/* This is a little helper, that allows us to set the encoding attr. 
- * after broken transformations 
- * 
- * PP: This function is not used!
- */
-void
-PmmFixProxyEncoding( ProxyNodePtr dfProxy ) 
-{
-    xmlNodePtr node = PmmNODE( dfProxy );
-    
-    if ( node != NULL ) {
-        switch ( node->type ) {
-        case XML_DOCUMENT_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-        case XML_DOCB_DOCUMENT_NODE:
-            if ( ((xmlDocPtr)node)->encoding != NULL ) {
-                dfProxy->encoding = (int)xmlParseCharEncoding( (const char*)((xmlDocPtr)node)->encoding );
-            }
-            break;
-        default:
-            dfProxy->encoding = 1;
-            break;
-        }
-    }
-
-}
 
 xmlNodePtr
 PmmCloneNode( xmlNodePtr node, int recursive )
                values set by XML::LibXSLT */
 
             if ( PmmNodeEncoding(real_doc) == XML_CHAR_ENCODING_NONE ) {
-                PmmNodeEncoding(real_doc) = XML_CHAR_ENCODING_UTF8;
+                SetPmmNodeEncoding(real_doc, XML_CHAR_ENCODING_UTF8);
             }
 
             decoded = PmmFastDecodeString( PmmNodeEncoding(real_doc),
                         /* The following statement is to handle bad
                            values set by XML::LibXSLT */
                         if ( PmmNodeEncoding(real_dom) == XML_CHAR_ENCODING_NONE ) {
-                            PmmNodeEncoding(real_dom) = XML_CHAR_ENCODING_UTF8;
+                            SetPmmNodeEncoding(real_dom, XML_CHAR_ENCODING_UTF8);
                         }
                         /* the following allocates a new string (by xmlStrdup if no conversion is done) */
                         string= PmmFastEncodeString( PmmNodeEncoding(real_dom),
     xmlNodePtr node;
     xmlNodePtr owner;
     int count;
-    int encoding;
 };
 
+struct _DocProxyNode {
+    xmlNodePtr node;
+    xmlNodePtr owner;
+    int count;
+    int encoding; /* only used for proxies of xmlDocPtr */
+    int psvi_status; /* three-state flag for a document */
+};
+
+#define Pmm_NO_PSVI 0
+#define Pmm_PSVI_TAINTED 1
+
 /* helper type for the proxy structure */
+typedef struct _DocProxyNode DocProxyNode;
 typedef struct _ProxyNode ProxyNode;
 
 /* pointer to the proxy structure */
 typedef ProxyNode* ProxyNodePtr;
+typedef DocProxyNode* DocProxyNodePtr;
 
 /* this my go only into the header used by the xs */
 #define SvPROXYNODE(x) (INT2PTR(ProxyNodePtr,SvIV(SvRV(x))))
 #define PmmNODE(xnode)       xnode->node
 #define PmmOWNER(node)       node->owner
 #define PmmOWNERPO(node)     ((node && PmmOWNER(node)) ? (ProxyNodePtr)PmmOWNER(node)->_private : node)
-#define PmmENCODING(node)    node->encoding
-#define PmmNodeEncoding(node) ((ProxyNodePtr)(node->_private))->encoding
-#define PmmDocEncoding(node) (node->charset)
+
+#define PmmENCODING(node)    ((DocProxyNodePtr)(node))->encoding
+#define PmmNodeEncoding(node) ((DocProxyNodePtr)(node->_private))->encoding
+
+#define SetPmmENCODING(node,code) PmmENCODING(node)=(code)
+#define SetPmmNodeEncoding(node,code) PmmNodeEncoding(node)=(code)
+
+#define PmmInvalidatePSVI(node) if (node->_private) ((DocProxyNodePtr)(node->_private))->psvi_status = Pmm_PSVI_TAINTED;
+
+#if LIBXML_VERSION >= 20621
+
+#define PmmClearPSVI(node) if (node && node->doc && node->doc->_private && \
+                               ((DocProxyNodePtr)(node->doc->_private))->psvi_status == Pmm_PSVI_TAINTED) \
+   domClearPSVI((xmlNodePtr) node)
+#endif
 
 #ifndef NO_XML_LIBXML_THREADS
 #ifdef USE_ITHREADS
     use XML::LibXML;
 
     if ( XML::LibXML::LIBXML_VERSION >= 20510 ) {
-        plan tests => 8;
+        plan tests => 13;
     }
     else {
         plan tests => 0;
 my $badfile      = "test/relaxng/badschema.rng";
 my $validfile    = "test/relaxng/demo.xml";
 my $invalidfile  = "test/relaxng/invaliddemo.xml";
-
+my $demo4        = "test/relaxng/demo4.rng";
 
 print "# 1 parse schema from a file\n";
 {
     ok ( $@ );
 }
 
-} # Version >= 20510 test 
+print "# 5 re-validate a modified document\n";
+{
+  my $rng = XML::LibXML::RelaxNG->new(location => $demo4);
+  my $seed_xml = <<'EOXML';
+<?xml version="1.0" encoding="UTF-8"?>
+<root/>
+EOXML
+
+  my $doc = $xmlparser->parse_string($seed_xml);
+  my $rootElem = $doc->documentElement;
+  my $bogusElem = $doc->createElement('bogus-element');
+
+  eval{$rng->validate($doc);};
+  ok ($@);
+
+  $rootElem->setAttribute('name', 'rootElem');
+  eval{ $rng->validate($doc); };
+  ok (!$@);
+
+  $rootElem->appendChild($bogusElem);
+  eval{$rng->validate($doc);};
+  ok ($@);
+
+  $bogusElem->unlinkNode();
+  eval{$rng->validate($doc);};
+  ok (!$@);
+
+  $rootElem->removeAttribute('name');
+  eval{$rng->validate($doc);};
+  ok ($@);
+
+}
+
+
+} # Version >= 20510 test 

test/relaxng/demo4.rng

+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+  <!--
+    
+    The master container.
+    
+  -->
+  <start>
+    <element name="root">
+      <ref name="attribute.name"/>
+      <optional>
+        <ref name="element.onedown"/>
+      </optional>
+    </element>
+  </start>
+  <define name="attribute.name">
+    <attribute name="name"/>
+  </define>
+  <define name="element.onedown">
+    <element name="onedown">
+      <optional>
+        <ref name="attribute.name"/>
+      </optional>
+      <zeroOrMore>
+        <ref name="element.twodown"/>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="element.twodown">
+    <element name="twodown">
+      <ref name="attribute.name"/>
+    </element>
+  </define>
+</grammar>
                 froot = froot->parent;
             }
             xmlAddChild((xmlNodePtr)tdoc, froot);
-
-            refNode->doc = tdoc;
+            xmlSetTreeDoc(froot, tdoc); /* probably no need to clean psvi */
+	    froot->doc = tdoc;
+            /* refNode->doc = tdoc; */
         }
 
         /* prepare the xpath context */
         if ( tdoc != NULL ) {
             /* after looking through a fragment, we need to drop the
                fake document again */
-            xmlSetTreeDoc(froot, NULL);
+            xmlSetTreeDoc(froot, NULL); /* probably no need to clean psvi */
 	    froot->doc = NULL;
 	    froot->parent = NULL;
             tdoc->children = NULL;
                 froot = froot->parent;
             }
             xmlAddChild((xmlNodePtr)tdoc, froot);
-	    ctxt->node->doc = tdoc;
+	    xmlSetTreeDoc(froot,tdoc);  /* probably no need to clean psvi */
+            froot->doc = tdoc;
+	    /* ctxt->node->doc = tdoc; */
         }
 	if (to_bool) {
 #if LIBXML_VERSION >= 20627
         if ( tdoc != NULL ) {
             /* after looking through a fragment, we need to drop the
                fake document again */
-	    xmlSetTreeDoc(froot,NULL);
+	    xmlSetTreeDoc(froot,NULL); /* probably no need to clean psvi */
             froot->doc = NULL;
             froot->parent  = NULL;
             tdoc->children = NULL;