1 /******************************************************************/
2 /*  Implementation of XML document processing using libxml2       */
3 /*  Author: Olivier Bertrand                2007-2016             */
4 /******************************************************************/
5 #include "my_global.h"
6 #include <string.h>
7 #include <stdio.h>
8 #include <libxml/parser.h>
9 #include <libxml/tree.h>
10 #include <libxml/xpath.h>
11 #include <libxml/xpathInternals.h>
12 #include <libxml/catalog.h>
13 #include <libxml/xmlschemastypes.h>
14 #include <libxml/relaxng.h>
15 
16 #if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED)
17 #error "tree support not compiled in"
18 #endif
19 
20 #if !defined(LIBXML_XPATH_ENABLED) || !defined(LIBXML_SAX1_ENABLED)
21 #error "XPath not supported"
22 #endif
23 
24 #include "global.h"
25 #include "plgdbsem.h"
26 #include "xobject.h"
27 #include "libdoc.h"
28 
29 #include "sql_string.h"
30 
31 /******************************************************************/
32 /*  Declaration of XML document processing using libxml2          */
33 /*  Author: Olivier Bertrand                2007-2012             */
34 /******************************************************************/
35 #include "plgxml.h"
36 
37 typedef class LIBXMLDOC    *PXDOC2;
38 typedef class XML2NODE     *PNODE2;
39 typedef class XML2ATTR     *PATTR2;
40 typedef class XML2NODELIST *PLIST2;
41 
42 /******************************************************************/
43 /* XML2 block. Must have the same layout than FBLOCK up to Type.  */
44 /******************************************************************/
45 typedef struct _x2block {         /* Loaded XML file block        */
46   struct _x2block   *Next;
47   LPCSTR             Fname;       /* Point on file name           */
48   size_t             Length;      /* Used to tell if read mode    */
49   short              Count;       /* Nb of times file is used     */
50   short              Type;        /* TYPE_FB_XML                  */
51   int                Retcode;     /* Return code from Load        */
52   xmlDocPtr          Docp;        /* Document interface pointer   */
53   } X2BLOCK, *PX2BLOCK;
54 
55 /******************************************************************/
56 /*  Declaration of libxml2 document.                              */
57 /******************************************************************/
58 class LIBXMLDOC : public XMLDOCUMENT {
59   friend class XML2NODE;
60   friend class XML2ATTR;
61  public:
62   // Constructor
63   LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp);
64 
65   // Properties
GetDocType(void)66   virtual short  GetDocType(void) {return TYPE_FB_XML2;}
GetDocPtr(void)67   virtual void  *GetDocPtr(void) {return Docp;}
SetNofree(bool b)68   virtual void   SetNofree(bool b) {Nofreelist = b;}
69 
70   // Methods
71 	virtual bool    Initialize(PGLOBAL g, PCSZ entry, bool zipped);
72   virtual bool    ParseFile(PGLOBAL g, char *fn);
73   virtual bool    NewDoc(PGLOBAL g, PCSZ ver);
74   virtual void    AddComment(PGLOBAL g, char *com);
75   virtual PXNODE  GetRoot(PGLOBAL g);
76   virtual PXNODE  NewRoot(PGLOBAL g, char *name);
77   virtual PXNODE  NewPnode(PGLOBAL g, char *name);
78   virtual PXATTR  NewPattr(PGLOBAL g);
79   virtual PXLIST  NewPlist(PGLOBAL g);
80   virtual int     DumpDoc(PGLOBAL g, char *ofn);
81   virtual void    CloseDoc(PGLOBAL g, PFBLOCK xp);
82   virtual PFBLOCK LinkXblock(PGLOBAL g, MODE m, int rc, char *fn);
83 
84  protected:
85 //        bool     CheckDocument(FILE *of, xmlNodePtr np);
86           xmlNodeSetPtr GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp);
87           int      Decode(xmlChar *cnt, char *buf, int n);
88           xmlChar *Encode(PGLOBAL g, char *txt);
89 
90   // Members
91   xmlDocPtr          Docp;
92   xmlNodeSetPtr      Nlist;
93   xmlXPathContextPtr Ctxp;
94   xmlXPathObjectPtr  Xop;
95   xmlXPathObjectPtr  NlXop;
96   xmlErrorPtr        Xerr;
97   char              *Buf;                  // Temporary
98   bool               Nofreelist;
99 }; // end of class LIBXMLDOC
100 
101 /******************************************************************/
102 /*  Declaration of libxml2 node.                                  */
103 /******************************************************************/
104 class XML2NODE : public XMLNODE {
105   friend class LIBXMLDOC;
106   friend class XML2NODELIST;
107  public:
108   // Properties
GetName(PGLOBAL g)109   virtual char  *GetName(PGLOBAL g) {return (char*)Nodep->name;}
110   virtual int    GetType(void);
111   virtual PXNODE GetNext(PGLOBAL g);
112   virtual PXNODE GetChild(PGLOBAL g);
113 
114   // Methods
115   virtual RCODE  GetContent(PGLOBAL g, char *buf, int len);
116   virtual bool   SetContent(PGLOBAL g, char *txtp, int len);
117   virtual PXNODE Clone(PGLOBAL g, PXNODE np);
118   virtual PXLIST GetChildElements(PGLOBAL g, char *xp, PXLIST lp);
119   virtual PXLIST SelectNodes(PGLOBAL g, char *xp, PXLIST lp);
120   virtual PXNODE SelectSingleNode(PGLOBAL g, char *xp, PXNODE np);
121   virtual PXATTR GetAttribute(PGLOBAL g, char *name, PXATTR ap);
122   virtual PXNODE AddChildNode(PGLOBAL g, PCSZ name, PXNODE np);
123   virtual PXATTR AddProperty(PGLOBAL g, char *name, PXATTR ap);
124   virtual void   AddText(PGLOBAL g, PCSZ txtp);
125   virtual void   DeleteChild(PGLOBAL g, PXNODE dnp);
126 
127  protected:
128   // Constructor
129   XML2NODE(PXDOC dp, xmlNodePtr np);
130 
131   // Members
132   xmlDocPtr  Docp;
133   xmlChar   *Content;
134   xmlNodePtr Nodep;
135 }; // end of class XML2NODE
136 
137 /******************************************************************/
138 /*  Declaration of libxml2 node list.                             */
139 /******************************************************************/
140 class XML2NODELIST : public XMLNODELIST {
141   friend class LIBXMLDOC;
142   friend class XML2NODE;
143  public:
144   // Methods
145   virtual int    GetLength(void);
146   virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np);
147   virtual bool   DropItem(PGLOBAL g, int n);
148 
149  protected:
150   // Constructor
151   XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp);
152 
153   // Members
154   xmlNodeSetPtr Listp;
155 }; // end of class XML2NODELIST
156 
157 /******************************************************************/
158 /*  Declaration of libxml2 attribute.                             */
159 /******************************************************************/
160 class XML2ATTR : public XMLATTRIBUTE {
161   friend class LIBXMLDOC;
162   friend class XML2NODE;
163  public:
164   // Properties
GetName(PGLOBAL g)165   virtual char  *GetName(PGLOBAL g) {return (char*)Atrp->name;}
166   virtual PXATTR GetNext(PGLOBAL g);
167 
168   // Methods
169   virtual RCODE  GetText(PGLOBAL g, char *bufp, int len);
170   virtual bool   SetText(PGLOBAL g, char *txtp, int len);
171 
172  protected:
173   // Constructor
174   XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np);
175 
176   // Members
177   xmlAttrPtr Atrp;
178   xmlNodePtr Parent;
179 }; // end of class XML2ATTR
180 
181 
182 
183 extern "C" {
184 extern char version[];
185 } //   "C"
186 
187 #if defined(MEMORY_TRACE)
188 static int  m = 0;
189 static char s[500];
190 /**************************************************************************/
191 /*  Tracing output function.                                              */
192 /**************************************************************************/
xtrc(char const * fmt,...)193 void xtrc(char const *fmt, ...)
194   {
195   va_list ap;
196   va_start (ap, fmt);
197 																							;
198 //vfprintf(stderr, fmt, ap);
199   vsprintf(s, fmt, ap);
200   if (s[strlen(s)-1] == '\n')
201       s[strlen(s)-1] = 0;
202   va_end (ap);
203   } // end of htrc
204 
205 static xmlFreeFunc Free;
206 static xmlMallocFunc Malloc;
207 static xmlMallocFunc MallocA;
208 static xmlReallocFunc Realloc;
209 static xmlStrdupFunc Strdup;
210 
xmlMyFree(void * mem)211 void xmlMyFree(void *mem)
212 {
213   if (trace(1)) {
214     htrc("%.4d Freeing          at %p   %s\n", ++m, mem, s);
215     *s = 0;
216     } // endif trace
217   Free(mem);
218 } // end of xmlMyFree
219 
xmlMyMalloc(size_t size)220 void *xmlMyMalloc(size_t size)
221 {
222   void *p = Malloc(size);
223   if (trace(1)) {
224     htrc("%.4d Allocating %.5d at %p   %s\n", ++m, size, p, s);
225     *s = 0;
226     } // endif trace
227   return p;
228 } // end of xmlMyMalloc
229 
xmlMyMallocAtomic(size_t size)230 void *xmlMyMallocAtomic(size_t size)
231 {
232   void *p = MallocA(size);
233   if (trace(1)) {
234     htrc("%.4d Atom alloc %.5d at %p   %s\n", ++m, size, p, s);
235     *s = 0;
236     } // endif trace
237   return p;
238 } // end of xmlMyMallocAtomic
239 
xmlMyRealloc(void * mem,size_t size)240 void *xmlMyRealloc(void *mem, size_t size)
241 {
242   void *p = Realloc(mem, size);
243   if (trace(1)) {
244     htrc("%.4d ReAlloc    %.5d to %p from %p   %s\n", ++m, size, p, mem, s);
245     *s = 0;
246     } // endif trace
247   return p;
248 } // end of xmlMyRealloc
249 
xmlMyStrdup(const char * str)250 char *xmlMyStrdup(const char *str)
251 {
252   char *p = Strdup(str);
253   if (trace(1)) {
254     htrc("%.4d Duplicating      to %p from %p %s   %s\n", ++m, p, str, str, s);
255     *s = 0;
256     } // endif trace
257   return p;
258 } // end of xmlMyStrdup
259 #define htrc xtrc
260 #endif   // MEMORY_TRACE
261 
262 /******************************************************************/
263 /*  Return a LIBXMLDOC as a XMLDOC.                               */
264 /******************************************************************/
GetLibxmlDoc(PGLOBAL g,char * nsl,char * nsdf,char * enc,PFBLOCK fp)265 PXDOC GetLibxmlDoc(PGLOBAL g, char *nsl, char *nsdf,
266                                          char *enc, PFBLOCK fp)
267   {
268   return (PXDOC) new(g) LIBXMLDOC(nsl, nsdf, enc, fp);
269   } // end of GetLibxmlDoc
270 
271 /******************************************************************/
272 /*  XML library initialization function.                          */
273 /******************************************************************/
XmlInitParserLib(void)274 void XmlInitParserLib(void)
275   {
276 #if defined(MEMORY_TRACE)
277 int	rc = xmlGcMemGet(&Free, &Malloc, &MallocA, &Realloc, &Strdup);
278 
279 if (!rc)
280   rc = xmlGcMemSetup(xmlMyFree,
281                      xmlMyMalloc,
282                      xmlMyMallocAtomic,
283                      xmlMyRealloc,
284                      xmlMyStrdup);
285 
286 #endif   // MEMORY_TRACE
287   xmlInitParser();
288   } // end of XmlInitParserLib
289 
290 /******************************************************************/
291 /*  XML library cleanup function.                                 */
292 /******************************************************************/
293 /*
294   This is a copy of xmlCleanupParser() from the libxml2 sources
295   with xmlResetLastError() commented.
296 
297   xmlResetLastError() called from the original xmlCleanupParser() causes
298   valgrind to report memory leaks. This happens because
299   ha_initialize_handlerton() is called from the main thread in mysqld.cc,
300   while ha_finalize_handlerton() is called from a non-main thread.
301   libxml2 gets confused because of xmlInitParser() and xmlCleanupParser()
302   being called from the different threads.
303 
304   Perhaps the code in mysqld.cc should eventually be modified
305   to shutdown plugins from the main thread.
306 */
307 static void
xmlCleanupParser_replacement(void)308 xmlCleanupParser_replacement(void)
309   {
310   xmlCleanupCharEncodingHandlers();
311 #ifdef LIBXML_CATALOG_ENABLED
312   xmlCatalogCleanup();
313 #endif
314   xmlDictCleanup();
315   xmlCleanupInputCallbacks();
316 #ifdef LIBXML_OUTPUT_ENABLED
317   xmlCleanupOutputCallbacks();
318 #endif
319 #ifdef LIBXML_SCHEMAS_ENABLED
320   xmlSchemaCleanupTypes();
321   xmlRelaxNGCleanupTypes();
322 #endif
323   //xmlResetLastError();
324   xmlCleanupGlobals();
325   xmlCleanupThreads(); /* must be last if called not from the main thread */
326   xmlCleanupMemory();
327   }
328 
329 
XmlCleanupParserLib(void)330 void XmlCleanupParserLib(void)
331   {
332   xmlCleanupParser_replacement();
333   } // end of XmlCleanupParserLib
334 
335 /******************************************************************/
336 /*  Close a loaded libxml2 XML file.                              */
337 /******************************************************************/
CloseXML2File(PGLOBAL g,PFBLOCK fp,bool all)338 void CloseXML2File(PGLOBAL g, PFBLOCK fp, bool all)
339   {
340   PX2BLOCK xp = (PX2BLOCK)fp;
341 
342   if (trace(1))
343     htrc("CloseXML2File: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0);
344 
345   if (xp && xp->Count > 1 && !all) {
346     xp->Count--;
347   } else if (xp && xp->Count > 0) {
348     xmlFreeDoc(xp->Docp);
349     xp->Count = 0;
350   } // endif
351 
352   } // end of CloseXML2File
353 
354 /* ---------------------- class LIBXMLDOC ----------------------- */
355 
356 /******************************************************************/
357 /*  LIBXMLDOC constructor.                                        */
358 /******************************************************************/
LIBXMLDOC(char * nsl,char * nsdf,char * enc,PFBLOCK fp)359 LIBXMLDOC::LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp)
360          : XMLDOCUMENT(nsl, nsdf, enc)
361   {
362   assert (!fp || fp->Type == TYPE_FB_XML2);
363   Docp = (fp) ? ((PX2BLOCK)fp)->Docp : NULL;
364   Nlist = NULL;
365   Ctxp = NULL;
366   Xop = NULL;
367   NlXop = NULL;
368   Xerr = NULL;
369   Buf = NULL;
370   Nofreelist = false;
371   } // end of LIBXMLDOC constructor
372 
373 /******************************************************************/
374 /*  Initialize XML parser and check library compatibility.        */
375 /******************************************************************/
Initialize(PGLOBAL g,PCSZ entry,bool zipped)376 bool LIBXMLDOC::Initialize(PGLOBAL g, PCSZ entry, bool zipped)
377 {
378 	if (zipped && InitZip(g, entry))
379 		return true;
380 
381   xmlKeepBlanksDefault(1);
382   return MakeNSlist(g);
383 } // end of Initialize
384 
385 /******************************************************************/
386 /* Parse the XML file and construct node tree in memory.          */
387 /******************************************************************/
ParseFile(PGLOBAL g,char * fn)388 bool LIBXMLDOC::ParseFile(PGLOBAL g, char *fn)
389   {
390   if (trace(1))
391     htrc("ParseFile\n");
392 
393 	if (zip) {
394 		// Parse an in memory document
395 		char *xdoc = GetMemDoc(g, fn);
396 
397 		Docp = (xdoc) ? xmlParseDoc((const xmlChar *)xdoc) : NULL;
398 	} else
399 		Docp = xmlParseFile(fn);
400 
401 	if (Docp) {
402 		if (Docp->encoding)
403       Encoding = (char*)Docp->encoding;
404 
405     return false;
406   } else if ((Xerr = xmlGetLastError()))
407     xmlResetError(Xerr);
408 
409   return true;
410   } // end of ParseFile
411 
412 /******************************************************************/
413 /* Create or reuse an Xblock for this document.                   */
414 /******************************************************************/
LinkXblock(PGLOBAL g,MODE m,int rc,char * fn)415 PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
416   {
417   PDBUSER  dup = (PDBUSER)g->Activityp->Aptr;
418   PX2BLOCK xp = (PX2BLOCK)PlugSubAlloc(g, NULL, sizeof(X2BLOCK));
419 
420   memset(xp, 0, sizeof(X2BLOCK));
421   xp->Next = (PX2BLOCK)dup->Openlist;
422   dup->Openlist = (PFBLOCK)xp;
423   xp->Type = TYPE_FB_XML2;
424   xp->Fname = (LPCSTR)PlugDup(g, fn);
425   xp->Count = 1;
426   xp->Length = (m == MODE_READ) ? 1 : 0;
427   xp->Retcode = rc;
428   xp->Docp = Docp;
429 
430   // Return xp as a fp
431   return (PFBLOCK)xp;
432   } // end of LinkXblock
433 
434 /******************************************************************/
435 /* Construct and add the XML processing instruction node.         */
436 /******************************************************************/
NewDoc(PGLOBAL g,PCSZ ver)437 bool LIBXMLDOC::NewDoc(PGLOBAL g, PCSZ ver)
438   {
439   if (trace(1))
440     htrc("NewDoc\n");
441 
442   return ((Docp = xmlNewDoc(BAD_CAST ver)) == NULL);
443   } // end of NewDoc
444 
445 /******************************************************************/
446 /*  Add a new comment node to the document.                       */
447 /******************************************************************/
AddComment(PGLOBAL g,char * txtp)448 void LIBXMLDOC::AddComment(PGLOBAL g, char *txtp)
449   {
450   if (trace(1))
451     htrc("AddComment: %s\n", txtp);
452 
453   xmlNodePtr cp = xmlNewDocComment(Docp, BAD_CAST txtp);
454   xmlAddChild((xmlNodePtr)Docp, cp);
455   } // end of AddText
456 
457 /******************************************************************/
458 /* Return the node class of the root of the document.             */
459 /******************************************************************/
GetRoot(PGLOBAL g)460 PXNODE LIBXMLDOC::GetRoot(PGLOBAL g)
461   {
462   if (trace(1))
463     htrc("GetRoot\n");
464 
465   xmlNodePtr root = xmlDocGetRootElement(Docp);
466 
467   if (!root)
468     return NULL;
469 
470   return new(g) XML2NODE(this, root);
471   } // end of GetRoot
472 
473 /******************************************************************/
474 /* Create a new root element and return its class node.           */
475 /******************************************************************/
NewRoot(PGLOBAL g,char * name)476 PXNODE LIBXMLDOC::NewRoot(PGLOBAL g, char *name)
477   {
478   if (trace(1))
479     htrc("NewRoot: %s\n", name);
480 
481   xmlNodePtr root = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL);
482 
483   if (root) {
484     xmlDocSetRootElement(Docp, root);
485     return new(g) XML2NODE(this, root);
486   } else
487     return NULL;
488 
489   } // end of NewRoot
490 
491 /******************************************************************/
492 /* Return a void XML2NODE node class.                             */
493 /******************************************************************/
NewPnode(PGLOBAL g,char * name)494 PXNODE LIBXMLDOC::NewPnode(PGLOBAL g, char *name)
495   {
496   if (trace(1))
497     htrc("NewNode: %s\n", name);
498 
499   xmlNodePtr nop;
500 
501   if (name) {
502     nop = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL);
503 
504     if (nop == NULL)
505       return NULL;
506 
507   } else
508     nop = NULL;
509 
510   return new(g) XML2NODE(this, nop);
511   } // end of NewPnode
512 
513 /******************************************************************/
514 /* Return a void XML2ATTR node class.                             */
515 /******************************************************************/
NewPattr(PGLOBAL g)516 PXATTR LIBXMLDOC::NewPattr(PGLOBAL g)
517   {
518   return new(g) XML2ATTR(this, NULL, NULL);
519   } // end of NewPattr
520 
521 /******************************************************************/
522 /* Return a void XML2ATTR node class.                             */
523 /******************************************************************/
NewPlist(PGLOBAL g)524 PXLIST LIBXMLDOC::NewPlist(PGLOBAL g)
525   {
526   return new(g) XML2NODELIST(this, NULL);
527   } // end of NewPlist
528 
529 /******************************************************************/
530 /* Dump the node tree to a new XML file.                          */
531 /******************************************************************/
DumpDoc(PGLOBAL g,char * ofn)532 int LIBXMLDOC::DumpDoc(PGLOBAL g, char *ofn)
533   {
534   int   rc = 0;
535   FILE *of;
536 
537   if (trace(1))
538     htrc("DumpDoc: %s\n", ofn);
539 
540   if (!(of= global_fopen(g, MSGID_CANNOT_OPEN, ofn, "w")))
541     return -1;
542 
543 #if 1
544   // This function does not crash (
545   if (xmlSaveFormatFileEnc((const char *)ofn, Docp, Encoding, 0) < 0) {
546     xmlErrorPtr err = xmlGetLastError();
547     strcpy(g->Message, (err) ? err->message : "Error saving XML doc");
548     xmlResetError(Xerr);
549     rc = -1;
550     } // endif Save
551 //  rc = xmlDocDump(of, Docp);
552 #else // 0
553   // Until this function is fixed, do the job ourself
554   xmlNodePtr Rootp;
555 
556   // Save the modified document
557   fprintf(of, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", Encoding);
558   fprintf(of, "<!-- Created by CONNECT %s -->\n", version);
559 
560   if (!(Rootp = xmlDocGetRootElement(Docp)))
561     return 1;
562 
563   Buf = (char*)PlugSubAlloc(g, NULL, 1024);
564   rc = iconv_close(Cd2);
565   Cd2 = iconv_open(Encoding, "UTF-8");
566   rc = CheckDocument(of, Rootp);
567 #endif // 0
568 
569   fclose(of);
570   return rc;
571   } // end of Dump
572 
573 /******************************************************************/
574 /* Free the document, cleanup the XML library, and                */
575 /* debug memory for regression tests.                             */
576 /******************************************************************/
CloseDoc(PGLOBAL g,PFBLOCK xp)577 void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp)
578   {
579   if (trace(1))
580     htrc("CloseDoc: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0);
581 
582 //if (xp && xp->Count == 1) {
583   if (xp) {
584     if (Nlist) {
585       xmlXPathFreeNodeSet(Nlist);
586 
587       if ((Xerr = xmlGetLastError()))
588         xmlResetError(Xerr);
589 
590       Nlist = NULL;
591       } // endif Nlist
592 
593     if (Xop) {
594       xmlXPathFreeObject(Xop);
595 
596       if ((Xerr = xmlGetLastError()))
597         xmlResetError(Xerr);
598 
599       Xop = NULL;
600       } // endif Xop
601 
602     if (NlXop) {
603       xmlXPathFreeObject(NlXop);
604 
605       if ((Xerr = xmlGetLastError()))
606         xmlResetError(Xerr);
607 
608       NlXop = NULL;
609       } // endif NlXop
610 
611     if (Ctxp) {
612       xmlXPathFreeContext(Ctxp);
613 
614       if ((Xerr = xmlGetLastError()))
615         xmlResetError(Xerr);
616 
617       Ctxp = NULL;
618       } // endif Ctxp
619 
620     } // endif xp
621 
622   CloseXML2File(g, xp, false);
623 	CloseZip();
624   } // end of Close
625 
626 /******************************************************************/
627 /* Evaluate the passed Xpath from the passed context node.        */
628 /******************************************************************/
GetNodeList(PGLOBAL g,xmlNodePtr np,char * xp)629 xmlNodeSetPtr LIBXMLDOC::GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp)
630   {
631   xmlNodeSetPtr nl;
632 
633   if (trace(1))
634     htrc("GetNodeList: %s np=%p\n", xp, np);
635 
636   if (!Ctxp) {
637     // Init Xpath
638     if (trace(1))
639       htrc("Calling xmlPathInit\n");
640 
641     xmlXPathInit();
642 
643     if (trace(1))
644       htrc("Calling xmlXPathNewContext Docp=%p\n", Docp);
645 
646     // Create xpath evaluation context
647     if (!(Ctxp = xmlXPathNewContext(Docp))) {
648       strcpy(g->Message, MSG(XPATH_CNTX_ERR));
649 
650       if (trace(1))
651         htrc("Context error: %s\n", g->Message);
652 
653       return NULL;
654       } // endif xpathCtx
655 
656     // Register namespaces from list (if any)
657     for (PNS nsp = Namespaces; nsp; nsp = nsp->Next) {
658       if (trace(1))
659         htrc("Calling xmlXPathRegisterNs Prefix=%s Uri=%s\n",
660              nsp->Prefix, nsp->Uri);
661 
662       if (xmlXPathRegisterNs(Ctxp, BAD_CAST nsp->Prefix,
663                                    BAD_CAST nsp->Uri)) {
664         sprintf(g->Message, MSG(REGISTER_ERR), nsp->Prefix, nsp->Uri);
665 
666         if (trace(1))
667           htrc("Ns error: %s\n", g->Message);
668 
669         return NULL;
670         } // endif Registering
671 
672       } // endfor nsp
673 
674     } // endif Ctxp
675 
676   if (Xop) {
677     if (trace(1))
678       htrc("Calling xmlXPathFreeNodeSetList Xop=%p NOFREE=%d\n",
679                                             Xop, Nofreelist);
680 
681     if (Nofreelist) {
682       // Making Nlist that must not be freed yet
683 //    xmlXPathFreeNodeSetList(Xop);       // Caused memory leak
684       assert(!NlXop);
685       NlXop = Xop;                        // Freed on closing
686       Nofreelist = false;
687     } else
688       xmlXPathFreeObject(Xop);            // Caused node not found
689 
690     if ((Xerr = xmlGetLastError())) {
691       strcpy(g->Message, Xerr->message);
692       xmlResetError(Xerr);
693       return NULL;
694       } // endif Xerr
695 
696     } // endif Xop
697 
698   // Set the context to the calling node
699   Ctxp->node = np;
700 
701   if (trace(1))
702     htrc("Calling xmlXPathEval %s Ctxp=%p\n", xp, Ctxp);
703 
704   // Evaluate table xpath
705   if (!(Xop = xmlXPathEval(BAD_CAST xp, Ctxp))) {
706     sprintf(g->Message, MSG(XPATH_EVAL_ERR), xp);
707 
708     if (trace(1))
709       htrc("Path error: %s\n", g->Message);
710 
711     return NULL;
712   } else
713     nl = Xop->nodesetval;
714 
715   if (trace(1))
716     htrc("GetNodeList nl=%p n=%p\n", nl, (nl) ? nl->nodeNr : 0);
717 
718   return nl;
719   } // end of GetNodeList
720 
721 #if 0                                // Not used anymore
722 /******************************************************************/
723 /*  CheckDocument: check if the document is ok to dump.           */
724 /*  Currently this does the dumping of the document.              */
725 /******************************************************************/
726 bool LIBXMLDOC::CheckDocument(FILE *of, xmlNodePtr np)
727   {
728   int  n;
729   bool b;
730 
731   if (!np)
732     return true;
733 
734   if (np->type == XML_ELEMENT_NODE) {
735     n = fprintf(of, "<%s", np->name);
736     b = CheckDocument(of, (xmlNodePtr)np->properties);
737 
738     if (np->children)
739       n = fprintf(of, ">");
740     else
741       n = fprintf(of, "/>");
742 
743   } else if (np->type == XML_ATTRIBUTE_NODE)
744     n = fprintf(of, " %s=\"", np->name);
745   else if (np->type == XML_TEXT_NODE)
746     n = fprintf(of, "%s", Encode(NULL, (char*)np->content));
747   else if (np->type == XML_COMMENT_NODE)
748     n = fprintf(of, "%s", Encode(NULL, (char*)np->content));
749 
750   b = CheckDocument(of, np->children);
751 
752   if (np->type == XML_ATTRIBUTE_NODE)
753     n = fprintf(of, "\"");
754   else if (!b && np->type == XML_ELEMENT_NODE)
755     n = fprintf(of, "</%s>", np->name);
756 
757   b = CheckDocument(of, np->next);
758   return false;
759   } // end of CheckDocument
760 
761 /******************************************************************/
762 /*  Convert node or attribute content to latin characters.        */
763 /******************************************************************/
764 int LIBXMLDOC::Decode(xmlChar *cnt, char *buf, int n)
765   {
766   const char *txt = (const char *)cnt;
767   uint   dummy_errors;
768   uint32 len= copy_and_convert(buf, n, &my_charset_utf8_general_ci, txt,
769                                strlen(txt), &my_charset_utf8_general_ci,
770                                &dummy_errors);
771   buf[len]= '\0';
772   return 0;
773   } // end of Decode
774 
775 /******************************************************************/
776 /*  Convert node or attribute content to latin characters.        */
777 /******************************************************************/
778 xmlChar *LIBXMLDOC::Encode(PGLOBAL g, char *txt)
779   {
780   const CHARSET_INFO *ics= &my_charset_utf8_general_ci;
781   const CHARSET_INFO *ocs= &my_charset_utf8_general_ci;
782   size_t      i = strlen(txt);
783   size_t      o = i * ocs->mbmaxlen / ics->mbmaxlen + 1;
784   char        *buf;
785   if (g) {
786     buf = (char*)PlugSubAlloc(g, NULL, o);
787   } else {
788     o = 1024;
789     buf = Buf;
790   } // endif g
791   uint dummy_errors;
792   uint32 len= copy_and_convert(buf, o, ocs,
793                                txt, i, ics,
794                                &dummy_errors);
795   buf[len]= '\0';
796   return BAD_CAST buf;
797   } // end of Encode
798 #endif // 0
799 
800 /* ---------------------- class XML2NODE ------------------------ */
801 
802 /******************************************************************/
803 /*  XML2NODE constructor.                                         */
804 /******************************************************************/
XML2NODE(PXDOC dp,xmlNodePtr np)805 XML2NODE::XML2NODE(PXDOC dp, xmlNodePtr np) : XMLNODE(dp)
806   {
807   Docp = ((PXDOC2)dp)->Docp;
808   Content = NULL;
809   Nodep = np;
810   } // end of XML2NODE constructor
811 
GetType(void)812 int XML2NODE::GetType(void)
813   {
814   if (trace(1))
815     htrc("GetType type=%d\n", Nodep->type);
816 
817   return Nodep->type;
818   }  // end of GetType
819 
820 /******************************************************************/
821 /*  Return the node class of next sibling of the node.            */
822 /******************************************************************/
GetNext(PGLOBAL g)823 PXNODE XML2NODE::GetNext(PGLOBAL g)
824   {
825   if (trace(1))
826     htrc("GetNext\n");
827 
828   if (!Nodep->next)
829     Next = NULL;
830   else // if (!Next)
831     Next = new(g) XML2NODE(Doc, Nodep->next);
832 
833   return Next;
834   } // end of GetNext
835 
836 /******************************************************************/
837 /*  Return the node class of first children of the node.          */
838 /******************************************************************/
GetChild(PGLOBAL g)839 PXNODE XML2NODE::GetChild(PGLOBAL g)
840   {
841   if (trace(1))
842     htrc("GetChild\n");
843 
844   if (!Nodep->children)
845     Children = NULL;
846   else // if (!Children)
847     Children = new(g) XML2NODE(Doc, Nodep->children);
848 
849   return Children;
850   } // end of GetChild
851 
852 /******************************************************************/
853 /*  Return the content of a node and subnodes.                    */
854 /******************************************************************/
GetContent(PGLOBAL g,char * buf,int len)855 RCODE XML2NODE::GetContent(PGLOBAL g, char *buf, int len)
856   {
857   RCODE rc = RC_OK;
858 
859   if (trace(1))
860     htrc("GetContent\n");
861 
862   if (Content)
863     xmlFree(Content);
864 
865   if ((Content = xmlNodeGetContent(Nodep))) {
866     char *p1 = (char*)Content, *p2 = buf;
867     bool  b = false;
868 
869     // Copy content eliminating extra characters
870     for (; *p1; p1++)
871       if ((p2 - buf) < len) {
872         if (strchr(" \t\r\n", *p1)) {
873           if (b) {
874             // This to have one blank between sub-nodes
875             *p2++ = ' ';
876             b = false;
877             }  // endif b
878 
879         } else {
880           *p2++ = *p1;
881           b = true;
882         } // endif p1
883 
884       } else {
885         sprintf(g->Message, "Truncated %s content", Nodep->name);
886         rc = RC_INFO;
887       } // endif len
888 
889     *p2 = 0;
890 
891     if (trace(1))
892       htrc("GetText buf='%s' len=%d\n", buf, len);
893 
894     xmlFree(Content);
895     Content = NULL;
896   } else
897     *buf = '\0';
898 
899   if (trace(1))
900     htrc("GetContent: %s\n", buf);
901 
902   return rc;
903   } // end of GetContent
904 
905 /******************************************************************/
906 /*  Set the content of a node.                                    */
907 /******************************************************************/
SetContent(PGLOBAL g,char * txtp,int len)908 bool XML2NODE::SetContent(PGLOBAL g, char *txtp, int len)
909   {
910   if (trace(1))
911     htrc("SetContent: %s\n", txtp);
912 
913   xmlChar *buf = xmlEncodeEntitiesReentrant(Docp, BAD_CAST txtp);
914 
915   if (trace(1))
916     htrc("SetContent: %s -> %s\n", txtp, buf);
917 
918   xmlNodeSetContent(Nodep, buf);
919   xmlFree(buf);
920   return false;
921   } // end of SetContent
922 
923 /******************************************************************/
924 /*  Return a clone of this node.                                  */
925 /******************************************************************/
Clone(PGLOBAL g,PXNODE np)926 PXNODE XML2NODE::Clone(PGLOBAL g, PXNODE np)
927   {
928   if (trace(1))
929     htrc("Clone: np=%p\n", np);
930 
931   if (np) {
932     ((PNODE2)np)->Nodep = Nodep;
933     return np;
934   } else
935     return new(g) XML2NODE(Doc, Nodep);
936 
937   } // end of Clone
938 
939 /******************************************************************/
940 /*  Return the list of all or matching children that are elements.*/
941 /******************************************************************/
GetChildElements(PGLOBAL g,char * xp,PXLIST lp)942 PXLIST XML2NODE::GetChildElements(PGLOBAL g, char *xp, PXLIST lp)
943   {
944   if (trace(1))
945     htrc("GetChildElements: %s\n", xp);
946 
947   return SelectNodes(g, (xp) ? xp : (char*)"*", lp);
948   } // end of GetChildElements
949 
950 /******************************************************************/
951 /*  Return the list of nodes verifying the passed Xpath.          */
952 /******************************************************************/
SelectNodes(PGLOBAL g,char * xp,PXLIST lp)953 PXLIST XML2NODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp)
954   {
955   if (trace(1))
956     htrc("SelectNodes: %s\n", xp);
957 
958   xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp);
959 
960   if (lp) {
961     ((PLIST2)lp)->Listp = nl;
962     return lp;
963   } else
964     return new(g) XML2NODELIST(Doc, nl);
965 
966   } // end of SelectNodes
967 
968 /******************************************************************/
969 /*  Return the first node verifying the passed Xapth.             */
970 /******************************************************************/
SelectSingleNode(PGLOBAL g,char * xp,PXNODE np)971 PXNODE XML2NODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np)
972   {
973   if (trace(1))
974     htrc("SelectSingleNode: %s\n", xp);
975 
976   xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp);
977 
978   if (nl && nl->nodeNr) {
979     if (np) {
980       ((PNODE2)np)->Nodep = nl->nodeTab[0];
981       return np;
982     } else
983       return new(g) XML2NODE(Doc, nl->nodeTab[0]);
984 
985   } else
986     return NULL;
987 
988   } // end of SelectSingleNode
989 
990 /******************************************************************/
991 /*  Return the node attribute with the specified name.            */
992 /******************************************************************/
GetAttribute(PGLOBAL g,char * name,PXATTR ap)993 PXATTR XML2NODE::GetAttribute(PGLOBAL g, char *name, PXATTR ap)
994   {
995   xmlAttrPtr atp;
996 
997   if (trace(1))
998     htrc("GetAttribute: %s\n", SVP(name));
999 
1000   if (name)
1001     atp = xmlHasProp(Nodep, BAD_CAST name);
1002   else
1003     atp = Nodep->properties;
1004 
1005 
1006   if (atp) {
1007     if (ap) {
1008       ((PATTR2)ap)->Atrp = atp;
1009       ((PATTR2)ap)->Parent = Nodep;
1010       return ap;
1011     } else
1012       return new(g) XML2ATTR(Doc, atp, Nodep);
1013 
1014   } else
1015     return NULL;
1016 
1017   } // end of GetAttribute
1018 
1019 /******************************************************************/
1020 /*  Add a new child node to this node and return it.              */
1021 /******************************************************************/
AddChildNode(PGLOBAL g,PCSZ name,PXNODE np)1022 PXNODE XML2NODE::AddChildNode(PGLOBAL g, PCSZ name, PXNODE np)
1023   {
1024   char *p, *pn, *pf = NULL, *nmp = PlugDup(g, name);
1025 
1026   if (trace(1))
1027     htrc("AddChildNode: %s\n", name);
1028 
1029   // Is a prefix specified
1030   if ((pn = strchr(nmp, ':'))) {
1031     pf = nmp;
1032     *pn++ = '\0';                    // Separate name from prefix
1033   } else
1034     pn = nmp;
1035 
1036   // If name has the format m[n] only m is taken as node name
1037   if ((p = strchr(pn, '[')))
1038     p = BufAlloc(g, pn, int(p - pn));
1039   else
1040     p = pn;
1041 
1042   xmlNodePtr nop = xmlNewChild(Nodep, NULL, BAD_CAST p, NULL);
1043 
1044   if (!nop)
1045     return NULL;
1046 
1047   if (pf) {
1048     // Prefixed name, is it the default NS prefix?
1049     if (Doc->DefNs && !strcmp(pf, Doc->DefNs))
1050       pf = NULL;                        // Default namespace
1051 
1052     xmlNsPtr nsp = xmlSearchNs(Docp, nop, BAD_CAST pf);
1053 
1054     if (!nsp)
1055       nsp = xmlNewNs(nop, NULL, BAD_CAST pf);
1056 
1057     // Set node namespace
1058     nop->ns = nsp;
1059     *(--p) = ':';                       // Restore Xname
1060   } else if (Doc->DefNs && xmlSearchNs(Docp, nop, NULL))
1061     // Not in default namespace
1062     nop->ns = xmlNewNs(nop, BAD_CAST "", NULL);
1063 
1064   if (np)
1065     ((PNODE2)np)->Nodep = nop;
1066   else
1067     np = new(g) XML2NODE(Doc, nop);
1068 
1069   return NewChild(np);
1070   } // end of AddChildNode
1071 
1072 /******************************************************************/
1073 /*  Add a new property to this node and return it.                */
1074 /******************************************************************/
AddProperty(PGLOBAL g,char * name,PXATTR ap)1075 PXATTR XML2NODE::AddProperty(PGLOBAL g, char *name, PXATTR ap)
1076   {
1077   if (trace(1))
1078     htrc("AddProperty: %s\n", name);
1079 
1080   xmlAttrPtr atp = xmlNewProp(Nodep, BAD_CAST name, NULL);
1081 
1082   if (atp) {
1083     if (ap) {
1084       ((PATTR2)ap)->Atrp = atp;
1085       ((PATTR2)ap)->Parent = Nodep;
1086       return ap;
1087     } else
1088       return new(g) XML2ATTR(Doc, atp, Nodep);
1089 
1090   } else
1091     return NULL;
1092 
1093   } // end of AddProperty
1094 
1095 /******************************************************************/
1096 /*  Add a new text node to this node.                             */
1097 /******************************************************************/
AddText(PGLOBAL g,PCSZ txtp)1098 void XML2NODE::AddText(PGLOBAL g, PCSZ txtp)
1099   {
1100   if (trace(1))
1101     htrc("AddText: %s\n", txtp);
1102 
1103   // This is to avoid a blank line when inserting a new line
1104   xmlNodePtr np = xmlGetLastChild(Nodep);
1105 
1106   if (np && np->type == XML_TEXT_NODE) {
1107     xmlUnlinkNode(np);
1108     xmlFreeNode(np);
1109     } // endif type
1110 
1111   // Add the new text
1112   xmlAddChild(Nodep, xmlNewText(BAD_CAST txtp));
1113   } // end of AddText
1114 
1115 /******************************************************************/
1116 /*  Remove a child node from this node.                           */
1117 /******************************************************************/
DeleteChild(PGLOBAL g,PXNODE dnp)1118 void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp)
1119   {
1120   xmlErrorPtr xerr;
1121 
1122   if (trace(1))
1123     htrc("DeleteChild: node=%p\n", dnp);
1124 
1125   xmlNodePtr np = ((PNODE2)dnp)->Nodep;
1126   xmlNodePtr text = np->next;
1127 
1128   // This is specific to row nodes
1129   if (text && text->type == XML_TEXT_NODE) {
1130     xmlUnlinkNode(text);
1131 
1132     if ((xerr = xmlGetLastError()))
1133       goto err;
1134 
1135     xmlFreeNode(text);
1136 
1137     if ((xerr = xmlGetLastError()))
1138       goto err;
1139 
1140     } // endif type
1141 
1142   xmlUnlinkNode(np);
1143 
1144   if ((xerr = xmlGetLastError()))
1145     goto err;
1146 
1147   xmlFreeNode(np);
1148 
1149   if ((xerr = xmlGetLastError()))
1150     goto err;
1151 
1152   Delete(dnp);
1153 
1154   if ((xerr = xmlGetLastError()))
1155     goto err;
1156 
1157   return;
1158 
1159 err:
1160   if (trace(1))
1161     htrc("DeleteChild: errmsg=%s\n", xerr->message);
1162 
1163   xmlResetError(xerr);
1164   } // end of DeleteChild
1165 
1166 /* -------------------- class XML2NODELIST ---------------------- */
1167 
1168 /******************************************************************/
1169 /*  XML2NODELIST constructor.                                     */
1170 /******************************************************************/
XML2NODELIST(PXDOC dp,xmlNodeSetPtr lp)1171 XML2NODELIST::XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp)
1172             : XMLNODELIST(dp)
1173   {
1174   Listp = lp;
1175   } // end of XML2NODELIST constructor
1176 
1177 /******************************************************************/
1178 /*  Return the length of the list.                                */
1179 /******************************************************************/
GetLength(void)1180 int XML2NODELIST::GetLength(void)
1181   {
1182   return (Listp) ? Listp->nodeNr : 0;
1183   } // end of GetLength
1184 
1185 /******************************************************************/
1186 /*  Return the nth element of the list.                           */
1187 /******************************************************************/
GetItem(PGLOBAL g,int n,PXNODE np)1188 PXNODE XML2NODELIST::GetItem(PGLOBAL g, int n, PXNODE np)
1189   {
1190   if (trace(1))
1191     htrc("GetItem: %d\n", n);
1192 
1193   if (!Listp || Listp->nodeNr <= n)
1194     return NULL;
1195 
1196   if (np) {
1197     ((PNODE2)np)->Nodep = Listp->nodeTab[n];
1198     return np;
1199   } else
1200     return new(g) XML2NODE(Doc, Listp->nodeTab[n]);
1201 
1202   } // end of GetItem
1203 
1204 /******************************************************************/
1205 /*  Reset the pointer on the deleted item.                        */
1206 /******************************************************************/
DropItem(PGLOBAL g,int n)1207 bool XML2NODELIST::DropItem(PGLOBAL g, int n)
1208   {
1209   if (trace(1))
1210     htrc("DropItem: n=%d\n", n);
1211 
1212   // We should do something here
1213   if (!Listp || Listp->nodeNr <= n)
1214     return true;
1215 
1216   Listp->nodeTab[n] = NULL;   // This was causing Valgrind warning
1217   return false;
1218   } // end of DropItem
1219 
1220 /* ---------------------- class XML2ATTR ------------------------ */
1221 
1222 /******************************************************************/
1223 /*  XML2ATTR constructor.                                         */
1224 /******************************************************************/
XML2ATTR(PXDOC dp,xmlAttrPtr ap,xmlNodePtr np)1225 XML2ATTR::XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np)
1226         : XMLATTRIBUTE(dp)
1227   {
1228   Atrp = ap;
1229   Parent = np;
1230   } // end of XML2ATTR constructor
1231 
1232 /******************************************************************/
1233 /*  Return the next sibling of the attribute.                     */
1234 /******************************************************************/
GetNext(PGLOBAL g)1235 PXATTR XML2ATTR::GetNext(PGLOBAL g)
1236   {
1237   if (trace(1))
1238     htrc("Attr GetNext\n");
1239 
1240   if (!Atrp->next)
1241     return NULL;
1242   else
1243     return new(g) XML2ATTR(Doc, Atrp->next, Atrp->parent);
1244 
1245   } // end of GetNext
1246 
1247 /******************************************************************/
1248 /*  Return the text of an attribute.                              */
1249 /******************************************************************/
GetText(PGLOBAL g,char * buf,int len)1250 RCODE XML2ATTR::GetText(PGLOBAL g, char *buf, int len)
1251   {
1252   RCODE    rc = RC_OK;
1253   xmlChar *txt;
1254 
1255   if (trace(1))
1256     htrc("GetText\n");
1257 
1258   if ((txt = xmlGetProp(Atrp->parent, Atrp->name))) {
1259     // Copy the text to the buffer
1260     if (strlen((char*)txt) >= (unsigned)len) {
1261       memcpy(buf, txt, len - 1);
1262       buf[len - 1] = 0;
1263       sprintf(g->Message, "Truncated %s content", Atrp->name);
1264       rc = RC_INFO;
1265     } else
1266       strcpy(buf, (const char*)txt);
1267 
1268     xmlFree(txt);
1269   } else
1270     *buf = '\0';
1271 
1272   if (trace(1))
1273     htrc("GetText: %s\n", buf);
1274 
1275   return rc;
1276   } // end of GetText
1277 
1278 /******************************************************************/
1279 /*  Set the content of an attribute.                              */
1280 /******************************************************************/
SetText(PGLOBAL g,char * txtp,int len)1281 bool XML2ATTR::SetText(PGLOBAL g, char *txtp, int len)
1282   {
1283   if (trace(1))
1284     htrc("SetText: %s %d\n", txtp, len);
1285 
1286   xmlSetProp(Parent, Atrp->name, BAD_CAST txtp);
1287   return false;
1288   } // end of SetText
1289