• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * catalog.c: set of generic Catalog related routines
3  *
4  * Reference:  SGML Open Technical Resolution TR9401:1997.
5  *             http://www.jclark.com/sp/catalog.htm
6  *
7  *             XML Catalogs Working Draft 06 August 2001
8  *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
9  *
10  * See Copyright for the status of this software.
11  *
12  * Daniel.Veillard@imag.fr
13  */
14 
15 #define IN_LIBXML
16 #include "libxml.h"
17 
18 #ifdef LIBXML_CATALOG_ENABLED
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 
26 #ifdef _WIN32
27   #include <io.h>
28 #else
29   #include <unistd.h>
30 #endif
31 
32 #include <libxml/xmlmemory.h>
33 #include <libxml/hash.h>
34 #include <libxml/uri.h>
35 #include <libxml/parserInternals.h>
36 #include <libxml/catalog.h>
37 #include <libxml/xmlerror.h>
38 #include <libxml/threads.h>
39 
40 #include "private/cata.h"
41 #include "private/buf.h"
42 #include "private/error.h"
43 #include "private/memory.h"
44 #include "private/parser.h"
45 #include "private/threads.h"
46 
47 #define MAX_DELEGATE	50
48 #define MAX_CATAL_DEPTH	50
49 
50 #ifdef _WIN32
51 # define PATH_SEPARATOR ';'
52 #else
53 # define PATH_SEPARATOR ':'
54 #endif
55 
56 #define XML_URN_PUBID "urn:publicid:"
57 #define XML_CATAL_BREAK ((xmlChar *) -1)
58 #ifndef XML_XML_DEFAULT_CATALOG
59 #define XML_XML_DEFAULT_CATALOG "file://" XML_SYSCONFDIR "/xml/catalog"
60 #endif
61 #ifndef XML_SGML_DEFAULT_CATALOG
62 #define XML_SGML_DEFAULT_CATALOG "file://" XML_SYSCONFDIR "/sgml/catalog"
63 #endif
64 
65 static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID);
66 static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
67 
68 /************************************************************************
69  *									*
70  *			Types, all private				*
71  *									*
72  ************************************************************************/
73 
74 typedef enum {
75     XML_CATA_REMOVED = -1,
76     XML_CATA_NONE = 0,
77     XML_CATA_CATALOG,
78     XML_CATA_BROKEN_CATALOG,
79     XML_CATA_NEXT_CATALOG,
80     XML_CATA_GROUP,
81     XML_CATA_PUBLIC,
82     XML_CATA_SYSTEM,
83     XML_CATA_REWRITE_SYSTEM,
84     XML_CATA_DELEGATE_PUBLIC,
85     XML_CATA_DELEGATE_SYSTEM,
86     XML_CATA_URI,
87     XML_CATA_REWRITE_URI,
88     XML_CATA_DELEGATE_URI,
89     SGML_CATA_SYSTEM,
90     SGML_CATA_PUBLIC,
91     SGML_CATA_ENTITY,
92     SGML_CATA_PENTITY,
93     SGML_CATA_DOCTYPE,
94     SGML_CATA_LINKTYPE,
95     SGML_CATA_NOTATION,
96     SGML_CATA_DELEGATE,
97     SGML_CATA_BASE,
98     SGML_CATA_CATALOG,
99     SGML_CATA_DOCUMENT,
100     SGML_CATA_SGMLDECL
101 } xmlCatalogEntryType;
102 
103 typedef struct _xmlCatalogEntry xmlCatalogEntry;
104 typedef xmlCatalogEntry *xmlCatalogEntryPtr;
105 struct _xmlCatalogEntry {
106     struct _xmlCatalogEntry *next;
107     struct _xmlCatalogEntry *parent;
108     struct _xmlCatalogEntry *children;
109     xmlCatalogEntryType type;
110     xmlChar *name;
111     xmlChar *value;
112     xmlChar *URL;  /* The expanded URL using the base */
113     xmlCatalogPrefer prefer;
114     int dealloc;
115     int depth;
116     struct _xmlCatalogEntry *group;
117 };
118 
119 typedef enum {
120     XML_XML_CATALOG_TYPE = 1,
121     XML_SGML_CATALOG_TYPE
122 } xmlCatalogType;
123 
124 #define XML_MAX_SGML_CATA_DEPTH 10
125 struct _xmlCatalog {
126     xmlCatalogType type;	/* either XML or SGML */
127 
128     /*
129      * SGML Catalogs are stored as a simple hash table of catalog entries
130      * Catalog stack to check against overflows when building the
131      * SGML catalog
132      */
133     char *catalTab[XML_MAX_SGML_CATA_DEPTH];	/* stack of catals */
134     int          catalNr;	/* Number of current catal streams */
135     int          catalMax;	/* Max number of catal streams */
136     xmlHashTablePtr sgml;
137 
138     /*
139      * XML Catalogs are stored as a tree of Catalog entries
140      */
141     xmlCatalogPrefer prefer;
142     xmlCatalogEntryPtr xml;
143 };
144 
145 /************************************************************************
146  *									*
147  *			Global variables				*
148  *									*
149  ************************************************************************/
150 
151 /*
152  * Those are preferences
153  */
154 static int xmlDebugCatalogs = 0;   /* used for debugging */
155 static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
156 static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
157 
158 /*
159  * Hash table containing all the trees of XML catalogs parsed by
160  * the application.
161  */
162 static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
163 
164 /*
165  * The default catalog in use by the application
166  */
167 static xmlCatalogPtr xmlDefaultCatalog = NULL;
168 
169 /*
170  * A mutex for modifying the shared global catalog(s)
171  * xmlDefaultCatalog tree.
172  * It also protects xmlCatalogXMLFiles
173  * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
174  */
175 static xmlRMutex xmlCatalogMutex;
176 
177 /*
178  * Whether the default system catalog was initialized.
179  */
180 static int xmlCatalogInitialized = 0;
181 
182 /************************************************************************
183  *									*
184  *			Catalog error handlers				*
185  *									*
186  ************************************************************************/
187 
188 /**
189  * xmlCatalogErrMemory:
190  * @extra:  extra information
191  *
192  * Handle an out of memory condition
193  */
194 static void
xmlCatalogErrMemory(void)195 xmlCatalogErrMemory(void)
196 {
197     xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_CATALOG, NULL);
198 }
199 
200 /**
201  * xmlCatalogErr:
202  * @catal: the Catalog entry
203  * @node: the context node
204  * @msg:  the error message
205  * @extra:  extra information
206  *
207  * Handle a catalog error
208  */
209 static void LIBXML_ATTR_FORMAT(4,0)
xmlCatalogErr(xmlCatalogEntryPtr catal,xmlNodePtr node,int error,const char * msg,const xmlChar * str1,const xmlChar * str2,const xmlChar * str3)210 xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,
211                const char *msg, const xmlChar *str1, const xmlChar *str2,
212 	       const xmlChar *str3)
213 {
214     int res;
215 
216     res = xmlRaiseError(NULL, NULL, NULL, catal, node,
217                         XML_FROM_CATALOG, error, XML_ERR_ERROR, NULL, 0,
218                         (const char *) str1, (const char *) str2,
219                         (const char *) str3, 0, 0,
220                         msg, str1, str2, str3);
221     if (res < 0)
222         xmlCatalogErrMemory();
223 }
224 
225 static void
xmlCatalogPrintDebug(const char * fmt,...)226 xmlCatalogPrintDebug(const char *fmt, ...) {
227     va_list ap;
228 
229     va_start(ap, fmt);
230     xmlVPrintErrorMessage(fmt, ap);
231     va_end(ap);
232 }
233 
234 /************************************************************************
235  *									*
236  *			Allocation and Freeing				*
237  *									*
238  ************************************************************************/
239 
240 /**
241  * xmlNewCatalogEntry:
242  * @type:  type of entry
243  * @name:  name of the entry
244  * @value:  value of the entry
245  * @prefer:  the PUBLIC vs. SYSTEM current preference value
246  * @group:  for members of a group, the group entry
247  *
248  * create a new Catalog entry, this type is shared both by XML and
249  * SGML catalogs, but the acceptable types values differs.
250  *
251  * Returns the xmlCatalogEntryPtr or NULL in case of error
252  */
253 static xmlCatalogEntryPtr
xmlNewCatalogEntry(xmlCatalogEntryType type,const xmlChar * name,const xmlChar * value,const xmlChar * URL,xmlCatalogPrefer prefer,xmlCatalogEntryPtr group)254 xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
255 	   const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,
256 	   xmlCatalogEntryPtr group) {
257     xmlCatalogEntryPtr ret;
258     xmlChar *normid = NULL;
259 
260     ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
261     if (ret == NULL) {
262         xmlCatalogErrMemory();
263 	return(NULL);
264     }
265     ret->next = NULL;
266     ret->parent = NULL;
267     ret->children = NULL;
268     ret->type = type;
269     if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {
270         normid = xmlCatalogNormalizePublic(name);
271         if (normid != NULL)
272             name = (*normid != 0 ? normid : NULL);
273     }
274     if (name != NULL)
275 	ret->name = xmlStrdup(name);
276     else
277 	ret->name = NULL;
278     if (normid != NULL)
279         xmlFree(normid);
280     if (value != NULL)
281 	ret->value = xmlStrdup(value);
282     else
283 	ret->value = NULL;
284     if (URL == NULL)
285 	URL = value;
286     if (URL != NULL)
287 	ret->URL = xmlStrdup(URL);
288     else
289 	ret->URL = NULL;
290     ret->prefer = prefer;
291     ret->dealloc = 0;
292     ret->depth = 0;
293     ret->group = group;
294     return(ret);
295 }
296 
297 static void
298 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
299 
300 /**
301  * xmlFreeCatalogEntry:
302  * @payload:  a Catalog entry
303  *
304  * Free the memory allocated to a Catalog entry
305  */
306 static void
xmlFreeCatalogEntry(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)307 xmlFreeCatalogEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
308     xmlCatalogEntryPtr ret = (xmlCatalogEntryPtr) payload;
309     if (ret == NULL)
310 	return;
311     /*
312      * Entries stored in the file hash must be deallocated
313      * only by the file hash cleaner !
314      */
315     if (ret->dealloc == 1)
316 	return;
317 
318     if (xmlDebugCatalogs) {
319 	if (ret->name != NULL)
320 	    xmlCatalogPrintDebug(
321 		    "Free catalog entry %s\n", ret->name);
322 	else if (ret->value != NULL)
323 	    xmlCatalogPrintDebug(
324 		    "Free catalog entry %s\n", ret->value);
325 	else
326 	    xmlCatalogPrintDebug(
327 		    "Free catalog entry\n");
328     }
329 
330     if (ret->name != NULL)
331 	xmlFree(ret->name);
332     if (ret->value != NULL)
333 	xmlFree(ret->value);
334     if (ret->URL != NULL)
335 	xmlFree(ret->URL);
336     xmlFree(ret);
337 }
338 
339 /**
340  * xmlFreeCatalogEntryList:
341  * @ret:  a Catalog entry list
342  *
343  * Free the memory allocated to a full chained list of Catalog entries
344  */
345 static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret)346 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
347     xmlCatalogEntryPtr next;
348 
349     while (ret != NULL) {
350 	next = ret->next;
351 	xmlFreeCatalogEntry(ret, NULL);
352 	ret = next;
353     }
354 }
355 
356 /**
357  * xmlFreeCatalogHashEntryList:
358  * @payload:  a Catalog entry list
359  *
360  * Free the memory allocated to list of Catalog entries from the
361  * catalog file hash.
362  */
363 static void
xmlFreeCatalogHashEntryList(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)364 xmlFreeCatalogHashEntryList(void *payload,
365                             const xmlChar *name ATTRIBUTE_UNUSED) {
366     xmlCatalogEntryPtr catal = (xmlCatalogEntryPtr) payload;
367     xmlCatalogEntryPtr children, next;
368 
369     if (catal == NULL)
370 	return;
371 
372     children = catal->children;
373     while (children != NULL) {
374 	next = children->next;
375 	children->dealloc = 0;
376 	children->children = NULL;
377 	xmlFreeCatalogEntry(children, NULL);
378 	children = next;
379     }
380     catal->dealloc = 0;
381     xmlFreeCatalogEntry(catal, NULL);
382 }
383 
384 /**
385  * xmlCreateNewCatalog:
386  * @type:  type of catalog
387  * @prefer:  the PUBLIC vs. SYSTEM current preference value
388  *
389  * create a new Catalog, this type is shared both by XML and
390  * SGML catalogs, but the acceptable types values differs.
391  *
392  * Returns the xmlCatalogPtr or NULL in case of error
393  */
394 static xmlCatalogPtr
xmlCreateNewCatalog(xmlCatalogType type,xmlCatalogPrefer prefer)395 xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
396     xmlCatalogPtr ret;
397 
398     ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
399     if (ret == NULL) {
400         xmlCatalogErrMemory();
401 	return(NULL);
402     }
403     memset(ret, 0, sizeof(xmlCatalog));
404     ret->type = type;
405     ret->catalNr = 0;
406     ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
407     ret->prefer = prefer;
408     if (ret->type == XML_SGML_CATALOG_TYPE)
409 	ret->sgml = xmlHashCreate(10);
410     return(ret);
411 }
412 
413 /**
414  * xmlFreeCatalog:
415  * @catal:  a Catalog
416  *
417  * Free the memory allocated to a Catalog
418  */
419 void
xmlFreeCatalog(xmlCatalogPtr catal)420 xmlFreeCatalog(xmlCatalogPtr catal) {
421     if (catal == NULL)
422 	return;
423     if (catal->xml != NULL)
424 	xmlFreeCatalogEntryList(catal->xml);
425     if (catal->sgml != NULL)
426 	xmlHashFree(catal->sgml, xmlFreeCatalogEntry);
427     xmlFree(catal);
428 }
429 
430 /************************************************************************
431  *									*
432  *			Serializing Catalogs				*
433  *									*
434  ************************************************************************/
435 
436 #ifdef LIBXML_OUTPUT_ENABLED
437 /**
438  * xmlCatalogDumpEntry:
439  * @entry:  the catalog entry
440  * @out:  the file.
441  *
442  * Serialize an SGML Catalog entry
443  */
444 static void
xmlCatalogDumpEntry(void * payload,void * data,const xmlChar * name ATTRIBUTE_UNUSED)445 xmlCatalogDumpEntry(void *payload, void *data,
446                     const xmlChar *name ATTRIBUTE_UNUSED) {
447     xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
448     FILE *out = (FILE *) data;
449     if ((entry == NULL) || (out == NULL))
450 	return;
451     switch (entry->type) {
452 	case SGML_CATA_ENTITY:
453 	    fprintf(out, "ENTITY "); break;
454 	case SGML_CATA_PENTITY:
455 	    fprintf(out, "ENTITY %%"); break;
456 	case SGML_CATA_DOCTYPE:
457 	    fprintf(out, "DOCTYPE "); break;
458 	case SGML_CATA_LINKTYPE:
459 	    fprintf(out, "LINKTYPE "); break;
460 	case SGML_CATA_NOTATION:
461 	    fprintf(out, "NOTATION "); break;
462 	case SGML_CATA_PUBLIC:
463 	    fprintf(out, "PUBLIC "); break;
464 	case SGML_CATA_SYSTEM:
465 	    fprintf(out, "SYSTEM "); break;
466 	case SGML_CATA_DELEGATE:
467 	    fprintf(out, "DELEGATE "); break;
468 	case SGML_CATA_BASE:
469 	    fprintf(out, "BASE "); break;
470 	case SGML_CATA_CATALOG:
471 	    fprintf(out, "CATALOG "); break;
472 	case SGML_CATA_DOCUMENT:
473 	    fprintf(out, "DOCUMENT "); break;
474 	case SGML_CATA_SGMLDECL:
475 	    fprintf(out, "SGMLDECL "); break;
476 	default:
477 	    return;
478     }
479     switch (entry->type) {
480 	case SGML_CATA_ENTITY:
481 	case SGML_CATA_PENTITY:
482 	case SGML_CATA_DOCTYPE:
483 	case SGML_CATA_LINKTYPE:
484 	case SGML_CATA_NOTATION:
485 	    fprintf(out, "%s", (const char *) entry->name); break;
486 	case SGML_CATA_PUBLIC:
487 	case SGML_CATA_SYSTEM:
488 	case SGML_CATA_SGMLDECL:
489 	case SGML_CATA_DOCUMENT:
490 	case SGML_CATA_CATALOG:
491 	case SGML_CATA_BASE:
492 	case SGML_CATA_DELEGATE:
493 	    fprintf(out, "\"%s\"", entry->name); break;
494 	default:
495 	    break;
496     }
497     switch (entry->type) {
498 	case SGML_CATA_ENTITY:
499 	case SGML_CATA_PENTITY:
500 	case SGML_CATA_DOCTYPE:
501 	case SGML_CATA_LINKTYPE:
502 	case SGML_CATA_NOTATION:
503 	case SGML_CATA_PUBLIC:
504 	case SGML_CATA_SYSTEM:
505 	case SGML_CATA_DELEGATE:
506 	    fprintf(out, " \"%s\"", entry->value); break;
507 	default:
508 	    break;
509     }
510     fprintf(out, "\n");
511 }
512 
513 /**
514  * xmlDumpXMLCatalogNode:
515  * @catal:  top catalog entry
516  * @catalog: pointer to the xml tree
517  * @doc: the containing document
518  * @ns: the current namespace
519  * @cgroup: group node for group members
520  *
521  * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively
522  * for group entries
523  */
xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal,xmlNodePtr catalog,xmlDocPtr doc,xmlNsPtr ns,xmlCatalogEntryPtr cgroup)524 static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,
525 		    xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {
526     xmlNodePtr node;
527     xmlCatalogEntryPtr cur;
528     /*
529      * add all the catalog entries
530      */
531     cur = catal;
532     while (cur != NULL) {
533         if (cur->group == cgroup) {
534 	    switch (cur->type) {
535 	        case XML_CATA_REMOVED:
536 		    break;
537 	        case XML_CATA_BROKEN_CATALOG:
538 	        case XML_CATA_CATALOG:
539 		    if (cur == catal) {
540 			cur = cur->children;
541 		        continue;
542 		    }
543 		    break;
544 		case XML_CATA_NEXT_CATALOG:
545 		    node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
546 		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
547 		    xmlAddChild(catalog, node);
548                     break;
549 		case XML_CATA_NONE:
550 		    break;
551 		case XML_CATA_GROUP:
552 		    node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);
553 		    xmlSetProp(node, BAD_CAST "id", cur->name);
554 		    if (cur->value != NULL) {
555 		        xmlNsPtr xns;
556 			xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);
557 			if (xns != NULL)
558 			    xmlSetNsProp(node, xns, BAD_CAST "base",
559 					 cur->value);
560 		    }
561 		    switch (cur->prefer) {
562 			case XML_CATA_PREFER_NONE:
563 		            break;
564 			case XML_CATA_PREFER_PUBLIC:
565 		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");
566 			    break;
567 			case XML_CATA_PREFER_SYSTEM:
568 		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");
569 			    break;
570 		    }
571 		    xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);
572 		    xmlAddChild(catalog, node);
573 	            break;
574 		case XML_CATA_PUBLIC:
575 		    node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
576 		    xmlSetProp(node, BAD_CAST "publicId", cur->name);
577 		    xmlSetProp(node, BAD_CAST "uri", cur->value);
578 		    xmlAddChild(catalog, node);
579 		    break;
580 		case XML_CATA_SYSTEM:
581 		    node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
582 		    xmlSetProp(node, BAD_CAST "systemId", cur->name);
583 		    xmlSetProp(node, BAD_CAST "uri", cur->value);
584 		    xmlAddChild(catalog, node);
585 		    break;
586 		case XML_CATA_REWRITE_SYSTEM:
587 		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
588 		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
589 		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
590 		    xmlAddChild(catalog, node);
591 		    break;
592 		case XML_CATA_DELEGATE_PUBLIC:
593 		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
594 		    xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
595 		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
596 		    xmlAddChild(catalog, node);
597 		    break;
598 		case XML_CATA_DELEGATE_SYSTEM:
599 		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
600 		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
601 		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
602 		    xmlAddChild(catalog, node);
603 		    break;
604 		case XML_CATA_URI:
605 		    node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
606 		    xmlSetProp(node, BAD_CAST "name", cur->name);
607 		    xmlSetProp(node, BAD_CAST "uri", cur->value);
608 		    xmlAddChild(catalog, node);
609 		    break;
610 		case XML_CATA_REWRITE_URI:
611 		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
612 		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
613 		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
614 		    xmlAddChild(catalog, node);
615 		    break;
616 		case XML_CATA_DELEGATE_URI:
617 		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
618 		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
619 		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
620 		    xmlAddChild(catalog, node);
621 		    break;
622 		case SGML_CATA_SYSTEM:
623 		case SGML_CATA_PUBLIC:
624 		case SGML_CATA_ENTITY:
625 		case SGML_CATA_PENTITY:
626 		case SGML_CATA_DOCTYPE:
627 		case SGML_CATA_LINKTYPE:
628 		case SGML_CATA_NOTATION:
629 		case SGML_CATA_DELEGATE:
630 		case SGML_CATA_BASE:
631 		case SGML_CATA_CATALOG:
632 		case SGML_CATA_DOCUMENT:
633 		case SGML_CATA_SGMLDECL:
634 		    break;
635 	    }
636         }
637 	cur = cur->next;
638     }
639 }
640 
641 static int
xmlDumpXMLCatalog(FILE * out,xmlCatalogEntryPtr catal)642 xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
643     int ret;
644     xmlDocPtr doc;
645     xmlNsPtr ns;
646     xmlDtdPtr dtd;
647     xmlNodePtr catalog;
648     xmlOutputBufferPtr buf;
649 
650     /*
651      * Rebuild a catalog
652      */
653     doc = xmlNewDoc(NULL);
654     if (doc == NULL)
655 	return(-1);
656     dtd = xmlNewDtd(doc, BAD_CAST "catalog",
657 	       BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
658 BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
659 
660     xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
661 
662     ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
663     if (ns == NULL) {
664 	xmlFreeDoc(doc);
665 	return(-1);
666     }
667     catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
668     if (catalog == NULL) {
669 	xmlFreeNs(ns);
670 	xmlFreeDoc(doc);
671 	return(-1);
672     }
673     catalog->nsDef = ns;
674     xmlAddChild((xmlNodePtr) doc, catalog);
675 
676     xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
677 
678     /*
679      * reserialize it
680      */
681     buf = xmlOutputBufferCreateFile(out, NULL);
682     if (buf == NULL) {
683 	xmlFreeDoc(doc);
684 	return(-1);
685     }
686     ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);
687 
688     /*
689      * Free it
690      */
691     xmlFreeDoc(doc);
692 
693     return(ret);
694 }
695 #endif /* LIBXML_OUTPUT_ENABLED */
696 
697 /************************************************************************
698  *									*
699  *			Converting SGML Catalogs to XML			*
700  *									*
701  ************************************************************************/
702 
703 /**
704  * xmlCatalogConvertEntry:
705  * @entry:  the entry
706  * @catal:  pointer to the catalog being converted
707  *
708  * Convert one entry from the catalog
709  */
710 static void
xmlCatalogConvertEntry(void * payload,void * data,const xmlChar * name ATTRIBUTE_UNUSED)711 xmlCatalogConvertEntry(void *payload, void *data,
712                        const xmlChar *name ATTRIBUTE_UNUSED) {
713     xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
714     xmlCatalogPtr catal = (xmlCatalogPtr) data;
715     if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
716 	(catal->xml == NULL))
717 	return;
718     switch (entry->type) {
719 	case SGML_CATA_ENTITY:
720 	    entry->type = XML_CATA_PUBLIC;
721 	    break;
722 	case SGML_CATA_PENTITY:
723 	    entry->type = XML_CATA_PUBLIC;
724 	    break;
725 	case SGML_CATA_DOCTYPE:
726 	    entry->type = XML_CATA_PUBLIC;
727 	    break;
728 	case SGML_CATA_LINKTYPE:
729 	    entry->type = XML_CATA_PUBLIC;
730 	    break;
731 	case SGML_CATA_NOTATION:
732 	    entry->type = XML_CATA_PUBLIC;
733 	    break;
734 	case SGML_CATA_PUBLIC:
735 	    entry->type = XML_CATA_PUBLIC;
736 	    break;
737 	case SGML_CATA_SYSTEM:
738 	    entry->type = XML_CATA_SYSTEM;
739 	    break;
740 	case SGML_CATA_DELEGATE:
741 	    entry->type = XML_CATA_DELEGATE_PUBLIC;
742 	    break;
743 	case SGML_CATA_CATALOG:
744 	    entry->type = XML_CATA_CATALOG;
745 	    break;
746 	default:
747 	    xmlHashRemoveEntry(catal->sgml, entry->name, xmlFreeCatalogEntry);
748 	    return;
749     }
750     /*
751      * Conversion successful, remove from the SGML catalog
752      * and add it to the default XML one
753      */
754     xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
755     entry->parent = catal->xml;
756     entry->next = NULL;
757     if (catal->xml->children == NULL)
758 	catal->xml->children = entry;
759     else {
760 	xmlCatalogEntryPtr prev;
761 
762 	prev = catal->xml->children;
763 	while (prev->next != NULL)
764 	    prev = prev->next;
765 	prev->next = entry;
766     }
767 }
768 
769 /**
770  * xmlConvertSGMLCatalog:
771  * @catal: the catalog
772  *
773  * Convert all the SGML catalog entries as XML ones
774  *
775  * Returns the number of entries converted if successful, -1 otherwise
776  */
777 int
xmlConvertSGMLCatalog(xmlCatalogPtr catal)778 xmlConvertSGMLCatalog(xmlCatalogPtr catal) {
779 
780     if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
781 	return(-1);
782 
783     if (xmlDebugCatalogs) {
784 	xmlCatalogPrintDebug(
785 		"Converting SGML catalog to XML\n");
786     }
787     xmlHashScan(catal->sgml, xmlCatalogConvertEntry, &catal);
788     return(0);
789 }
790 
791 /************************************************************************
792  *									*
793  *			Helper function					*
794  *									*
795  ************************************************************************/
796 
797 /**
798  * xmlCatalogUnWrapURN:
799  * @urn:  an "urn:publicid:" to unwrap
800  *
801  * Expand the URN into the equivalent Public Identifier
802  *
803  * Returns the new identifier or NULL, the string must be deallocated
804  *         by the caller.
805  */
806 static xmlChar *
xmlCatalogUnWrapURN(const xmlChar * urn)807 xmlCatalogUnWrapURN(const xmlChar *urn) {
808     xmlChar result[2000];
809     unsigned int i = 0;
810 
811     if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
812 	return(NULL);
813     urn += sizeof(XML_URN_PUBID) - 1;
814 
815     while (*urn != 0) {
816 	if (i > sizeof(result) - 4)
817 	    break;
818 	if (*urn == '+') {
819 	    result[i++] = ' ';
820 	    urn++;
821 	} else if (*urn == ':') {
822 	    result[i++] = '/';
823 	    result[i++] = '/';
824 	    urn++;
825 	} else if (*urn == ';') {
826 	    result[i++] = ':';
827 	    result[i++] = ':';
828 	    urn++;
829 	} else if (*urn == '%') {
830 	    if ((urn[1] == '2') && (urn[2] == 'B'))
831 		result[i++] = '+';
832 	    else if ((urn[1] == '3') && (urn[2] == 'A'))
833 		result[i++] = ':';
834 	    else if ((urn[1] == '2') && (urn[2] == 'F'))
835 		result[i++] = '/';
836 	    else if ((urn[1] == '3') && (urn[2] == 'B'))
837 		result[i++] = ';';
838 	    else if ((urn[1] == '2') && (urn[2] == '7'))
839 		result[i++] = '\'';
840 	    else if ((urn[1] == '3') && (urn[2] == 'F'))
841 		result[i++] = '?';
842 	    else if ((urn[1] == '2') && (urn[2] == '3'))
843 		result[i++] = '#';
844 	    else if ((urn[1] == '2') && (urn[2] == '5'))
845 		result[i++] = '%';
846 	    else {
847 		result[i++] = *urn;
848 		urn++;
849 		continue;
850 	    }
851 	    urn += 3;
852 	} else {
853 	    result[i++] = *urn;
854 	    urn++;
855 	}
856     }
857     result[i] = 0;
858 
859     return(xmlStrdup(result));
860 }
861 
862 /**
863  * xmlParseCatalogFile:
864  * @filename:  the filename
865  *
866  * parse an XML file and build a tree. It's like xmlParseFile()
867  * except it bypass all catalog lookups.
868  *
869  * Returns the resulting document tree or NULL in case of error
870  */
871 
872 xmlDocPtr
xmlParseCatalogFile(const char * filename)873 xmlParseCatalogFile(const char *filename) {
874     xmlDocPtr ret;
875     xmlParserCtxtPtr ctxt;
876     xmlParserInputPtr inputStream;
877     xmlParserInputBufferPtr buf;
878 
879     ctxt = xmlNewParserCtxt();
880     if (ctxt == NULL) {
881         xmlCatalogErrMemory();
882 	return(NULL);
883     }
884 
885     buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
886     if (buf == NULL) {
887 	xmlFreeParserCtxt(ctxt);
888 	return(NULL);
889     }
890 
891     inputStream = xmlNewInputStream(ctxt);
892     if (inputStream == NULL) {
893 	xmlFreeParserInputBuffer(buf);
894 	xmlFreeParserCtxt(ctxt);
895 	return(NULL);
896     }
897 
898     inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
899     inputStream->buf = buf;
900     xmlBufResetInput(buf->buffer, inputStream);
901 
902     if (xmlCtxtPushInput(ctxt, inputStream) < 0) {
903         xmlFreeInputStream(inputStream);
904         xmlFreeParserCtxt(ctxt);
905         return(NULL);
906     }
907 
908     ctxt->valid = 0;
909     ctxt->validate = 0;
910     ctxt->loadsubset = 0;
911     ctxt->pedantic = 0;
912     ctxt->dictNames = 1;
913 
914     xmlParseDocument(ctxt);
915 
916     if (ctxt->wellFormed)
917 	ret = ctxt->myDoc;
918     else {
919         ret = NULL;
920         xmlFreeDoc(ctxt->myDoc);
921         ctxt->myDoc = NULL;
922     }
923     xmlFreeParserCtxt(ctxt);
924 
925     return(ret);
926 }
927 
928 /**
929  * xmlLoadFileContent:
930  * @filename:  a file path
931  *
932  * Load a file content into memory.
933  *
934  * Returns a pointer to the 0 terminated string or NULL in case of error
935  */
936 static xmlChar *
xmlLoadFileContent(const char * filename)937 xmlLoadFileContent(const char *filename)
938 {
939     int fd;
940     int len;
941     long size;
942 
943     struct stat info;
944     xmlChar *content;
945 
946     if (filename == NULL)
947         return (NULL);
948 
949     if (stat(filename, &info) < 0)
950         return (NULL);
951 
952     fd = open(filename, O_RDONLY);
953     if (fd  < 0)
954     {
955         return (NULL);
956     }
957     size = info.st_size;
958     content = xmlMalloc(size + 10);
959     if (content == NULL) {
960         xmlCatalogErrMemory();
961 	close(fd);
962         return (NULL);
963     }
964     len = read(fd, content, size);
965     close(fd);
966     if (len < 0) {
967         xmlFree(content);
968         return (NULL);
969     }
970     content[len] = 0;
971 
972     return(content);
973 }
974 
975 /**
976  * xmlCatalogNormalizePublic:
977  * @pubID:  the public ID string
978  *
979  *  Normalizes the Public Identifier
980  *
981  * Implements 6.2. Public Identifier Normalization
982  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
983  *
984  * Returns the new string or NULL, the string must be deallocated
985  *         by the caller.
986  */
987 static xmlChar *
xmlCatalogNormalizePublic(const xmlChar * pubID)988 xmlCatalogNormalizePublic(const xmlChar *pubID)
989 {
990     int ok = 1;
991     int white;
992     const xmlChar *p;
993     xmlChar *ret;
994     xmlChar *q;
995 
996     if (pubID == NULL)
997         return(NULL);
998 
999     white = 1;
1000     for (p = pubID;*p != 0 && ok;p++) {
1001         if (!xmlIsBlank_ch(*p))
1002             white = 0;
1003         else if (*p == 0x20 && !white)
1004             white = 1;
1005         else
1006             ok = 0;
1007     }
1008     if (ok && !white)	/* is normalized */
1009         return(NULL);
1010 
1011     ret = xmlStrdup(pubID);
1012     q = ret;
1013     white = 0;
1014     for (p = pubID;*p != 0;p++) {
1015         if (xmlIsBlank_ch(*p)) {
1016             if (q != ret)
1017                 white = 1;
1018         } else {
1019             if (white) {
1020                 *(q++) = 0x20;
1021                 white = 0;
1022             }
1023             *(q++) = *p;
1024         }
1025     }
1026     *q = 0;
1027     return(ret);
1028 }
1029 
1030 /************************************************************************
1031  *									*
1032  *			The XML Catalog parser				*
1033  *									*
1034  ************************************************************************/
1035 
1036 static xmlCatalogEntryPtr
1037 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
1038 static void
1039 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1040 	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
1041 static xmlChar *
1042 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1043 	              const xmlChar *sysID);
1044 static xmlChar *
1045 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
1046 
1047 
1048 /**
1049  * xmlGetXMLCatalogEntryType:
1050  * @name:  the name
1051  *
1052  * lookup the internal type associated to an XML catalog entry name
1053  *
1054  * Returns the type associated with that name
1055  */
1056 static xmlCatalogEntryType
xmlGetXMLCatalogEntryType(const xmlChar * name)1057 xmlGetXMLCatalogEntryType(const xmlChar *name) {
1058     xmlCatalogEntryType type = XML_CATA_NONE;
1059     if (xmlStrEqual(name, (const xmlChar *) "system"))
1060 	type = XML_CATA_SYSTEM;
1061     else if (xmlStrEqual(name, (const xmlChar *) "public"))
1062 	type = XML_CATA_PUBLIC;
1063     else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
1064 	type = XML_CATA_REWRITE_SYSTEM;
1065     else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
1066 	type = XML_CATA_DELEGATE_PUBLIC;
1067     else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
1068 	type = XML_CATA_DELEGATE_SYSTEM;
1069     else if (xmlStrEqual(name, (const xmlChar *) "uri"))
1070 	type = XML_CATA_URI;
1071     else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
1072 	type = XML_CATA_REWRITE_URI;
1073     else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
1074 	type = XML_CATA_DELEGATE_URI;
1075     else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
1076 	type = XML_CATA_NEXT_CATALOG;
1077     else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
1078 	type = XML_CATA_CATALOG;
1079     return(type);
1080 }
1081 
1082 /**
1083  * xmlParseXMLCatalogOneNode:
1084  * @cur:  the XML node
1085  * @type:  the type of Catalog entry
1086  * @name:  the name of the node
1087  * @attrName:  the attribute holding the value
1088  * @uriAttrName:  the attribute holding the URI-Reference
1089  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1090  * @cgroup:  the group which includes this node
1091  *
1092  * Finishes the examination of an XML tree node of a catalog and build
1093  * a Catalog entry from it.
1094  *
1095  * Returns the new Catalog entry node or NULL in case of error.
1096  */
1097 static xmlCatalogEntryPtr
xmlParseXMLCatalogOneNode(xmlNodePtr cur,xmlCatalogEntryType type,const xmlChar * name,const xmlChar * attrName,const xmlChar * uriAttrName,xmlCatalogPrefer prefer,xmlCatalogEntryPtr cgroup)1098 xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
1099 			  const xmlChar *name, const xmlChar *attrName,
1100 			  const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
1101 			  xmlCatalogEntryPtr cgroup) {
1102     int ok = 1;
1103     xmlChar *uriValue;
1104     xmlChar *nameValue = NULL;
1105     xmlChar *base = NULL;
1106     xmlChar *URL = NULL;
1107     xmlCatalogEntryPtr ret = NULL;
1108 
1109     if (attrName != NULL) {
1110 	nameValue = xmlGetProp(cur, attrName);
1111 	if (nameValue == NULL) {
1112 	    xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1113 			  "%s entry lacks '%s'\n", name, attrName, NULL);
1114 	    ok = 0;
1115 	}
1116     }
1117     uriValue = xmlGetProp(cur, uriAttrName);
1118     if (uriValue == NULL) {
1119 	xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1120 		"%s entry lacks '%s'\n", name, uriAttrName, NULL);
1121 	ok = 0;
1122     }
1123     if (!ok) {
1124 	if (nameValue != NULL)
1125 	    xmlFree(nameValue);
1126 	if (uriValue != NULL)
1127 	    xmlFree(uriValue);
1128 	return(NULL);
1129     }
1130 
1131     base = xmlNodeGetBase(cur->doc, cur);
1132     URL = xmlBuildURI(uriValue, base);
1133     if (URL != NULL) {
1134 	if (xmlDebugCatalogs > 1) {
1135 	    if (nameValue != NULL)
1136 		xmlCatalogPrintDebug(
1137 			"Found %s: '%s' '%s'\n", name, nameValue, URL);
1138 	    else
1139 		xmlCatalogPrintDebug(
1140 			"Found %s: '%s'\n", name, URL);
1141 	}
1142 	ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
1143     } else {
1144 	xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
1145 		"%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
1146     }
1147     if (nameValue != NULL)
1148 	xmlFree(nameValue);
1149     if (uriValue != NULL)
1150 	xmlFree(uriValue);
1151     if (base != NULL)
1152 	xmlFree(base);
1153     if (URL != NULL)
1154 	xmlFree(URL);
1155     return(ret);
1156 }
1157 
1158 /**
1159  * xmlParseXMLCatalogNode:
1160  * @cur:  the XML node
1161  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1162  * @parent:  the parent Catalog entry
1163  * @cgroup:  the group which includes this node
1164  *
1165  * Examines an XML tree node of a catalog and build
1166  * a Catalog entry from it adding it to its parent. The examination can
1167  * be recursive.
1168  */
1169 static void
xmlParseXMLCatalogNode(xmlNodePtr cur,xmlCatalogPrefer prefer,xmlCatalogEntryPtr parent,xmlCatalogEntryPtr cgroup)1170 xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
1171 	               xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
1172 {
1173     xmlChar *base = NULL;
1174     xmlCatalogEntryPtr entry = NULL;
1175 
1176     if (cur == NULL)
1177         return;
1178     if (xmlStrEqual(cur->name, BAD_CAST "group")) {
1179         xmlChar *prop;
1180 	xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
1181 
1182         prop = xmlGetProp(cur, BAD_CAST "prefer");
1183         if (prop != NULL) {
1184             if (xmlStrEqual(prop, BAD_CAST "system")) {
1185                 prefer = XML_CATA_PREFER_SYSTEM;
1186             } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1187                 prefer = XML_CATA_PREFER_PUBLIC;
1188             } else {
1189 		xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
1190                               "Invalid value for prefer: '%s'\n",
1191 			      prop, NULL, NULL);
1192             }
1193             xmlFree(prop);
1194 	    pref = prefer;
1195         }
1196 	prop = xmlGetProp(cur, BAD_CAST "id");
1197 	base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
1198 	entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
1199 	xmlFree(prop);
1200     } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
1201 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
1202 		BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
1203     } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
1204 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
1205 		BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
1206     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
1207 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
1208 		BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
1209 		BAD_CAST "rewritePrefix", prefer, cgroup);
1210     } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
1211 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
1212 		BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
1213 		BAD_CAST "catalog", prefer, cgroup);
1214     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
1215 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
1216 		BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
1217 		BAD_CAST "catalog", prefer, cgroup);
1218     } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
1219 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
1220 		BAD_CAST "uri", BAD_CAST "name",
1221 		BAD_CAST "uri", prefer, cgroup);
1222     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
1223 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
1224 		BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
1225 		BAD_CAST "rewritePrefix", prefer, cgroup);
1226     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
1227 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
1228 		BAD_CAST "delegateURI", BAD_CAST "uriStartString",
1229 		BAD_CAST "catalog", prefer, cgroup);
1230     } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
1231 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
1232 		BAD_CAST "nextCatalog", NULL,
1233 		BAD_CAST "catalog", prefer, cgroup);
1234     }
1235     if (entry != NULL) {
1236         if (parent != NULL) {
1237 	    entry->parent = parent;
1238 	    if (parent->children == NULL)
1239 		parent->children = entry;
1240 	    else {
1241 		xmlCatalogEntryPtr prev;
1242 
1243 		prev = parent->children;
1244 		while (prev->next != NULL)
1245 		    prev = prev->next;
1246 		prev->next = entry;
1247 	    }
1248 	}
1249 	if (entry->type == XML_CATA_GROUP) {
1250 	    /*
1251 	     * Recurse to propagate prefer to the subtree
1252 	     * (xml:base handling is automated)
1253 	     */
1254             xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
1255 	}
1256     }
1257     if (base != NULL)
1258 	xmlFree(base);
1259 }
1260 
1261 /**
1262  * xmlParseXMLCatalogNodeList:
1263  * @cur:  the XML node list of siblings
1264  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1265  * @parent:  the parent Catalog entry
1266  * @cgroup:  the group which includes this list
1267  *
1268  * Examines a list of XML sibling nodes of a catalog and build
1269  * a list of Catalog entry from it adding it to the parent.
1270  * The examination will recurse to examine node subtrees.
1271  */
1272 static void
xmlParseXMLCatalogNodeList(xmlNodePtr cur,xmlCatalogPrefer prefer,xmlCatalogEntryPtr parent,xmlCatalogEntryPtr cgroup)1273 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1274 	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
1275     while (cur != NULL) {
1276 	if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
1277 	    (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1278 	    xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
1279 	}
1280 	cur = cur->next;
1281     }
1282     /* TODO: sort the list according to REWRITE lengths and prefer value */
1283 }
1284 
1285 /**
1286  * xmlParseXMLCatalogFile:
1287  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1288  * @filename:  the filename for the catalog
1289  *
1290  * Parses the catalog file to extract the XML tree and then analyze the
1291  * tree to build a list of Catalog entries corresponding to this catalog
1292  *
1293  * Returns the resulting Catalog entries list
1294  */
1295 static xmlCatalogEntryPtr
xmlParseXMLCatalogFile(xmlCatalogPrefer prefer,const xmlChar * filename)1296 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
1297     xmlDocPtr doc;
1298     xmlNodePtr cur;
1299     xmlChar *prop;
1300     xmlCatalogEntryPtr parent = NULL;
1301 
1302     if (filename == NULL)
1303         return(NULL);
1304 
1305     doc = xmlParseCatalogFile((const char *) filename);
1306     if (doc == NULL) {
1307 	if (xmlDebugCatalogs)
1308 	    xmlCatalogPrintDebug(
1309 		    "Failed to parse catalog %s\n", filename);
1310 	return(NULL);
1311     }
1312 
1313     if (xmlDebugCatalogs)
1314 	xmlCatalogPrintDebug(
1315 		"%d Parsing catalog %s\n", xmlGetThreadId(), filename);
1316 
1317     cur = xmlDocGetRootElement(doc);
1318     if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
1319 	(cur->ns != NULL) && (cur->ns->href != NULL) &&
1320 	(xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1321 
1322 	parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
1323 				    (const xmlChar *)filename, NULL, prefer, NULL);
1324         if (parent == NULL) {
1325 	    xmlFreeDoc(doc);
1326 	    return(NULL);
1327 	}
1328 
1329 	prop = xmlGetProp(cur, BAD_CAST "prefer");
1330 	if (prop != NULL) {
1331 	    if (xmlStrEqual(prop, BAD_CAST "system")) {
1332 		prefer = XML_CATA_PREFER_SYSTEM;
1333 	    } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1334 		prefer = XML_CATA_PREFER_PUBLIC;
1335 	    } else {
1336 		xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,
1337 			      "Invalid value for prefer: '%s'\n",
1338 			      prop, NULL, NULL);
1339 	    }
1340 	    xmlFree(prop);
1341 	}
1342 	cur = cur->children;
1343 	xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);
1344     } else {
1345 	xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,
1346 		      "File %s is not an XML Catalog\n",
1347 		      filename, NULL, NULL);
1348 	xmlFreeDoc(doc);
1349 	return(NULL);
1350     }
1351     xmlFreeDoc(doc);
1352     return(parent);
1353 }
1354 
1355 /**
1356  * xmlFetchXMLCatalogFile:
1357  * @catal:  an existing but incomplete catalog entry
1358  *
1359  * Fetch and parse the subcatalog referenced by an entry
1360  *
1361  * Returns 0 in case of success, -1 otherwise
1362  */
1363 static int
xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal)1364 xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
1365     xmlCatalogEntryPtr doc;
1366 
1367     if (catal == NULL)
1368 	return(-1);
1369     if (catal->URL == NULL)
1370 	return(-1);
1371 
1372     /*
1373      * lock the whole catalog for modification
1374      */
1375     xmlRMutexLock(&xmlCatalogMutex);
1376     if (catal->children != NULL) {
1377 	/* Okay someone else did it in the meantime */
1378 	xmlRMutexUnlock(&xmlCatalogMutex);
1379 	return(0);
1380     }
1381 
1382     if (xmlCatalogXMLFiles != NULL) {
1383 	doc = (xmlCatalogEntryPtr)
1384 	    xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1385 	if (doc != NULL) {
1386 	    if (xmlDebugCatalogs)
1387 		xmlCatalogPrintDebug(
1388 		    "Found %s in file hash\n", catal->URL);
1389 
1390 	    if (catal->type == XML_CATA_CATALOG)
1391 		catal->children = doc->children;
1392 	    else
1393 		catal->children = doc;
1394 	    catal->dealloc = 0;
1395 	    xmlRMutexUnlock(&xmlCatalogMutex);
1396 	    return(0);
1397 	}
1398 	if (xmlDebugCatalogs)
1399 	    xmlCatalogPrintDebug(
1400 		"%s not found in file hash\n", catal->URL);
1401     }
1402 
1403     /*
1404      * Fetch and parse. Note that xmlParseXMLCatalogFile does not
1405      * use the existing catalog, there is no recursion allowed at
1406      * that level.
1407      */
1408     doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
1409     if (doc == NULL) {
1410 	catal->type = XML_CATA_BROKEN_CATALOG;
1411 	xmlRMutexUnlock(&xmlCatalogMutex);
1412 	return(-1);
1413     }
1414 
1415     if (catal->type == XML_CATA_CATALOG)
1416 	catal->children = doc->children;
1417     else
1418 	catal->children = doc;
1419 
1420     doc->dealloc = 1;
1421 
1422     if (xmlCatalogXMLFiles == NULL)
1423 	xmlCatalogXMLFiles = xmlHashCreate(10);
1424     if (xmlCatalogXMLFiles != NULL) {
1425 	if (xmlDebugCatalogs)
1426 	    xmlCatalogPrintDebug(
1427 		"%s added to file hash\n", catal->URL);
1428 	xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
1429     }
1430     xmlRMutexUnlock(&xmlCatalogMutex);
1431     return(0);
1432 }
1433 
1434 /************************************************************************
1435  *									*
1436  *			XML Catalog handling				*
1437  *									*
1438  ************************************************************************/
1439 
1440 /**
1441  * xmlAddXMLCatalog:
1442  * @catal:  top of an XML catalog
1443  * @type:  the type of record to add to the catalog
1444  * @orig:  the system, public or prefix to match (or NULL)
1445  * @replace:  the replacement value for the match
1446  *
1447  * Add an entry in the XML catalog, it may overwrite existing but
1448  * different entries.
1449  *
1450  * Returns 0 if successful, -1 otherwise
1451  */
1452 static int
xmlAddXMLCatalog(xmlCatalogEntryPtr catal,const xmlChar * type,const xmlChar * orig,const xmlChar * replace)1453 xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
1454 	      const xmlChar *orig, const xmlChar *replace) {
1455     xmlCatalogEntryPtr cur;
1456     xmlCatalogEntryType typ;
1457     int doregister = 0;
1458 
1459     if ((catal == NULL) ||
1460 	((catal->type != XML_CATA_CATALOG) &&
1461 	 (catal->type != XML_CATA_BROKEN_CATALOG)))
1462 	return(-1);
1463     if (catal->children == NULL) {
1464 	xmlFetchXMLCatalogFile(catal);
1465     }
1466     if (catal->children == NULL)
1467 	doregister = 1;
1468 
1469     typ = xmlGetXMLCatalogEntryType(type);
1470     if (typ == XML_CATA_NONE) {
1471 	if (xmlDebugCatalogs)
1472 	    xmlCatalogPrintDebug(
1473 		    "Failed to add unknown element %s to catalog\n", type);
1474 	return(-1);
1475     }
1476 
1477     cur = catal->children;
1478     /*
1479      * Might be a simple "update in place"
1480      */
1481     if (cur != NULL) {
1482 	while (cur != NULL) {
1483 	    if ((orig != NULL) && (cur->type == typ) &&
1484 		(xmlStrEqual(orig, cur->name))) {
1485 		if (xmlDebugCatalogs)
1486 		    xmlCatalogPrintDebug(
1487 			    "Updating element %s to catalog\n", type);
1488 		if (cur->value != NULL)
1489 		    xmlFree(cur->value);
1490 		if (cur->URL != NULL)
1491 		    xmlFree(cur->URL);
1492 		cur->value = xmlStrdup(replace);
1493 		cur->URL = xmlStrdup(replace);
1494 		return(0);
1495 	    }
1496 	    if (cur->next == NULL)
1497 		break;
1498 	    cur = cur->next;
1499 	}
1500     }
1501     if (xmlDebugCatalogs)
1502 	xmlCatalogPrintDebug(
1503 		"Adding element %s to catalog\n", type);
1504     if (cur == NULL)
1505 	catal->children = xmlNewCatalogEntry(typ, orig, replace,
1506 		                             NULL, catal->prefer, NULL);
1507     else
1508 	cur->next = xmlNewCatalogEntry(typ, orig, replace,
1509 		                       NULL, catal->prefer, NULL);
1510     if (doregister) {
1511         catal->type = XML_CATA_CATALOG;
1512 	cur = (xmlCatalogEntryPtr)xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1513 	if (cur != NULL)
1514 	    cur->children = catal->children;
1515     }
1516 
1517     return(0);
1518 }
1519 
1520 /**
1521  * xmlDelXMLCatalog:
1522  * @catal:  top of an XML catalog
1523  * @value:  the value to remove from the catalog
1524  *
1525  * Remove entries in the XML catalog where the value or the URI
1526  * is equal to @value
1527  *
1528  * Returns the number of entries removed if successful, -1 otherwise
1529  */
1530 static int
xmlDelXMLCatalog(xmlCatalogEntryPtr catal,const xmlChar * value)1531 xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
1532     xmlCatalogEntryPtr cur;
1533     int ret = 0;
1534 
1535     if ((catal == NULL) ||
1536 	((catal->type != XML_CATA_CATALOG) &&
1537 	 (catal->type != XML_CATA_BROKEN_CATALOG)))
1538 	return(-1);
1539     if (value == NULL)
1540 	return(-1);
1541     if (catal->children == NULL) {
1542 	xmlFetchXMLCatalogFile(catal);
1543     }
1544 
1545     /*
1546      * Scan the children
1547      */
1548     cur = catal->children;
1549     while (cur != NULL) {
1550 	if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
1551 	    (xmlStrEqual(value, cur->value))) {
1552 	    if (xmlDebugCatalogs) {
1553 		if (cur->name != NULL)
1554 		    xmlCatalogPrintDebug(
1555 			    "Removing element %s from catalog\n", cur->name);
1556 		else
1557 		    xmlCatalogPrintDebug(
1558 			    "Removing element %s from catalog\n", cur->value);
1559 	    }
1560 	    cur->type = XML_CATA_REMOVED;
1561 	}
1562 	cur = cur->next;
1563     }
1564     return(ret);
1565 }
1566 
1567 /**
1568  * xmlCatalogXMLResolve:
1569  * @catal:  a catalog list
1570  * @pubID:  the public ID string
1571  * @sysID:  the system ID string
1572  *
1573  * Do a complete resolution lookup of an External Identifier for a
1574  * list of catalog entries.
1575  *
1576  * Implements (or tries to) 7.1. External Identifier Resolution
1577  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1578  *
1579  * Returns the URI of the resource or NULL if not found
1580  */
1581 static xmlChar *
xmlCatalogXMLResolve(xmlCatalogEntryPtr catal,const xmlChar * pubID,const xmlChar * sysID)1582 xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1583 	              const xmlChar *sysID) {
1584     xmlChar *ret = NULL;
1585     xmlCatalogEntryPtr cur;
1586     int haveDelegate = 0;
1587     int haveNext = 0;
1588 
1589     /*
1590      * protection against loops
1591      */
1592     if (catal->depth > MAX_CATAL_DEPTH) {
1593 	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
1594 		      "Detected recursion in catalog %s\n",
1595 		      catal->name, NULL, NULL);
1596 	return(NULL);
1597     }
1598     catal->depth++;
1599 
1600     /*
1601      * First tries steps 2/ 3/ 4/ if a system ID is provided.
1602      */
1603     if (sysID != NULL) {
1604 	xmlCatalogEntryPtr rewrite = NULL;
1605 	int lenrewrite = 0, len;
1606 	cur = catal;
1607 	haveDelegate = 0;
1608 	while (cur != NULL) {
1609 	    switch (cur->type) {
1610 		case XML_CATA_SYSTEM:
1611 		    if (xmlStrEqual(sysID, cur->name)) {
1612 			if (xmlDebugCatalogs)
1613 			    xmlCatalogPrintDebug(
1614 				    "Found system match %s, using %s\n",
1615 				            cur->name, cur->URL);
1616 			catal->depth--;
1617 			return(xmlStrdup(cur->URL));
1618 		    }
1619 		    break;
1620 		case XML_CATA_REWRITE_SYSTEM:
1621 		    len = xmlStrlen(cur->name);
1622 		    if ((len > lenrewrite) &&
1623 			(!xmlStrncmp(sysID, cur->name, len))) {
1624 			lenrewrite = len;
1625 			rewrite = cur;
1626 		    }
1627 		    break;
1628 		case XML_CATA_DELEGATE_SYSTEM:
1629 		    if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
1630 			haveDelegate++;
1631 		    break;
1632 		case XML_CATA_NEXT_CATALOG:
1633 		    haveNext++;
1634 		    break;
1635 		default:
1636 		    break;
1637 	    }
1638 	    cur = cur->next;
1639 	}
1640 	if (rewrite != NULL) {
1641 	    if (xmlDebugCatalogs)
1642 		xmlCatalogPrintDebug(
1643 			"Using rewriting rule %s\n", rewrite->name);
1644 	    ret = xmlStrdup(rewrite->URL);
1645 	    if (ret != NULL)
1646 		ret = xmlStrcat(ret, &sysID[lenrewrite]);
1647 	    catal->depth--;
1648 	    return(ret);
1649 	}
1650 	if (haveDelegate) {
1651 	    const xmlChar *delegates[MAX_DELEGATE];
1652 	    int nbList = 0, i;
1653 
1654 	    /*
1655 	     * Assume the entries have been sorted by decreasing substring
1656 	     * matches when the list was produced.
1657 	     */
1658 	    cur = catal;
1659 	    while (cur != NULL) {
1660 		if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
1661 		    (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
1662 		    for (i = 0;i < nbList;i++)
1663 			if (xmlStrEqual(cur->URL, delegates[i]))
1664 			    break;
1665 		    if (i < nbList) {
1666 			cur = cur->next;
1667 			continue;
1668 		    }
1669 		    if (nbList < MAX_DELEGATE)
1670 			delegates[nbList++] = cur->URL;
1671 
1672 		    if (cur->children == NULL) {
1673 			xmlFetchXMLCatalogFile(cur);
1674 		    }
1675 		    if (cur->children != NULL) {
1676 			if (xmlDebugCatalogs)
1677 			    xmlCatalogPrintDebug(
1678 				    "Trying system delegate %s\n", cur->URL);
1679 			ret = xmlCatalogListXMLResolve(
1680 				cur->children, NULL, sysID);
1681 			if (ret != NULL) {
1682 			    catal->depth--;
1683 			    return(ret);
1684 			}
1685 		    }
1686 		}
1687 		cur = cur->next;
1688 	    }
1689 	    /*
1690 	     * Apply the cut algorithm explained in 4/
1691 	     */
1692 	    catal->depth--;
1693 	    return(XML_CATAL_BREAK);
1694 	}
1695     }
1696     /*
1697      * Then tries 5/ 6/ if a public ID is provided
1698      */
1699     if (pubID != NULL) {
1700 	cur = catal;
1701 	haveDelegate = 0;
1702 	while (cur != NULL) {
1703 	    switch (cur->type) {
1704 		case XML_CATA_PUBLIC:
1705 		    if (xmlStrEqual(pubID, cur->name)) {
1706 			if (xmlDebugCatalogs)
1707 			    xmlCatalogPrintDebug(
1708 				    "Found public match %s\n", cur->name);
1709 			catal->depth--;
1710 			return(xmlStrdup(cur->URL));
1711 		    }
1712 		    break;
1713 		case XML_CATA_DELEGATE_PUBLIC:
1714 		    if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
1715 			(cur->prefer == XML_CATA_PREFER_PUBLIC))
1716 			haveDelegate++;
1717 		    break;
1718 		case XML_CATA_NEXT_CATALOG:
1719 		    if (sysID == NULL)
1720 			haveNext++;
1721 		    break;
1722 		default:
1723 		    break;
1724 	    }
1725 	    cur = cur->next;
1726 	}
1727 	if (haveDelegate) {
1728 	    const xmlChar *delegates[MAX_DELEGATE];
1729 	    int nbList = 0, i;
1730 
1731 	    /*
1732 	     * Assume the entries have been sorted by decreasing substring
1733 	     * matches when the list was produced.
1734 	     */
1735 	    cur = catal;
1736 	    while (cur != NULL) {
1737 		if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
1738 		    (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
1739 		    (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
1740 
1741 		    for (i = 0;i < nbList;i++)
1742 			if (xmlStrEqual(cur->URL, delegates[i]))
1743 			    break;
1744 		    if (i < nbList) {
1745 			cur = cur->next;
1746 			continue;
1747 		    }
1748 		    if (nbList < MAX_DELEGATE)
1749 			delegates[nbList++] = cur->URL;
1750 
1751 		    if (cur->children == NULL) {
1752 			xmlFetchXMLCatalogFile(cur);
1753 		    }
1754 		    if (cur->children != NULL) {
1755 			if (xmlDebugCatalogs)
1756 			    xmlCatalogPrintDebug(
1757 				    "Trying public delegate %s\n", cur->URL);
1758 			ret = xmlCatalogListXMLResolve(
1759 				cur->children, pubID, NULL);
1760 			if (ret != NULL) {
1761 			    catal->depth--;
1762 			    return(ret);
1763 			}
1764 		    }
1765 		}
1766 		cur = cur->next;
1767 	    }
1768 	    /*
1769 	     * Apply the cut algorithm explained in 4/
1770 	     */
1771 	    catal->depth--;
1772 	    return(XML_CATAL_BREAK);
1773 	}
1774     }
1775     if (haveNext) {
1776 	cur = catal;
1777 	while (cur != NULL) {
1778 	    if (cur->type == XML_CATA_NEXT_CATALOG) {
1779 		if (cur->children == NULL) {
1780 		    xmlFetchXMLCatalogFile(cur);
1781 		}
1782 		if (cur->children != NULL) {
1783 		    ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
1784 		    if (ret != NULL) {
1785 			catal->depth--;
1786 			return(ret);
1787 		    } else if (catal->depth > MAX_CATAL_DEPTH) {
1788 		        return(NULL);
1789 		    }
1790 		}
1791 	    }
1792 	    cur = cur->next;
1793 	}
1794     }
1795 
1796     catal->depth--;
1797     return(NULL);
1798 }
1799 
1800 /**
1801  * xmlCatalogXMLResolveURI:
1802  * @catal:  a catalog list
1803  * @URI:  the URI
1804  * @sysID:  the system ID string
1805  *
1806  * Do a complete resolution lookup of an External Identifier for a
1807  * list of catalog entries.
1808  *
1809  * Implements (or tries to) 7.2.2. URI Resolution
1810  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1811  *
1812  * Returns the URI of the resource or NULL if not found
1813  */
1814 static xmlChar *
xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal,const xmlChar * URI)1815 xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
1816     xmlChar *ret = NULL;
1817     xmlCatalogEntryPtr cur;
1818     int haveDelegate = 0;
1819     int haveNext = 0;
1820     xmlCatalogEntryPtr rewrite = NULL;
1821     int lenrewrite = 0, len;
1822 
1823     if (catal == NULL)
1824 	return(NULL);
1825 
1826     if (URI == NULL)
1827 	return(NULL);
1828 
1829     if (catal->depth > MAX_CATAL_DEPTH) {
1830 	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
1831 		      "Detected recursion in catalog %s\n",
1832 		      catal->name, NULL, NULL);
1833 	return(NULL);
1834     }
1835 
1836     /*
1837      * First tries steps 2/ 3/ 4/ if a system ID is provided.
1838      */
1839     cur = catal;
1840     haveDelegate = 0;
1841     while (cur != NULL) {
1842 	switch (cur->type) {
1843 	    case XML_CATA_URI:
1844 		if (xmlStrEqual(URI, cur->name)) {
1845 		    if (xmlDebugCatalogs)
1846 			xmlCatalogPrintDebug(
1847 				"Found URI match %s\n", cur->name);
1848 		    return(xmlStrdup(cur->URL));
1849 		}
1850 		break;
1851 	    case XML_CATA_REWRITE_URI:
1852 		len = xmlStrlen(cur->name);
1853 		if ((len > lenrewrite) &&
1854 		    (!xmlStrncmp(URI, cur->name, len))) {
1855 		    lenrewrite = len;
1856 		    rewrite = cur;
1857 		}
1858 		break;
1859 	    case XML_CATA_DELEGATE_URI:
1860 		if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
1861 		    haveDelegate++;
1862 		break;
1863 	    case XML_CATA_NEXT_CATALOG:
1864 		haveNext++;
1865 		break;
1866 	    default:
1867 		break;
1868 	}
1869 	cur = cur->next;
1870     }
1871     if (rewrite != NULL) {
1872 	if (xmlDebugCatalogs)
1873 	    xmlCatalogPrintDebug(
1874 		    "Using rewriting rule %s\n", rewrite->name);
1875 	ret = xmlStrdup(rewrite->URL);
1876 	if (ret != NULL)
1877 	    ret = xmlStrcat(ret, &URI[lenrewrite]);
1878 	return(ret);
1879     }
1880     if (haveDelegate) {
1881 	const xmlChar *delegates[MAX_DELEGATE];
1882 	int nbList = 0, i;
1883 
1884 	/*
1885 	 * Assume the entries have been sorted by decreasing substring
1886 	 * matches when the list was produced.
1887 	 */
1888 	cur = catal;
1889 	while (cur != NULL) {
1890 	    if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
1891 	         (cur->type == XML_CATA_DELEGATE_URI)) &&
1892 		(!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
1893 		for (i = 0;i < nbList;i++)
1894 		    if (xmlStrEqual(cur->URL, delegates[i]))
1895 			break;
1896 		if (i < nbList) {
1897 		    cur = cur->next;
1898 		    continue;
1899 		}
1900 		if (nbList < MAX_DELEGATE)
1901 		    delegates[nbList++] = cur->URL;
1902 
1903 		if (cur->children == NULL) {
1904 		    xmlFetchXMLCatalogFile(cur);
1905 		}
1906 		if (cur->children != NULL) {
1907 		    if (xmlDebugCatalogs)
1908 			xmlCatalogPrintDebug(
1909 				"Trying URI delegate %s\n", cur->URL);
1910 		    ret = xmlCatalogListXMLResolveURI(
1911 			    cur->children, URI);
1912 		    if (ret != NULL)
1913 			return(ret);
1914 		}
1915 	    }
1916 	    cur = cur->next;
1917 	}
1918 	/*
1919 	 * Apply the cut algorithm explained in 4/
1920 	 */
1921 	return(XML_CATAL_BREAK);
1922     }
1923     if (haveNext) {
1924 	cur = catal;
1925 	while (cur != NULL) {
1926 	    if (cur->type == XML_CATA_NEXT_CATALOG) {
1927 		if (cur->children == NULL) {
1928 		    xmlFetchXMLCatalogFile(cur);
1929 		}
1930 		if (cur->children != NULL) {
1931 		    ret = xmlCatalogListXMLResolveURI(cur->children, URI);
1932 		    if (ret != NULL)
1933 			return(ret);
1934 		}
1935 	    }
1936 	    cur = cur->next;
1937 	}
1938     }
1939 
1940     return(NULL);
1941 }
1942 
1943 /**
1944  * xmlCatalogListXMLResolve:
1945  * @catal:  a catalog list
1946  * @pubID:  the public ID string
1947  * @sysID:  the system ID string
1948  *
1949  * Do a complete resolution lookup of an External Identifier for a
1950  * list of catalogs
1951  *
1952  * Implements (or tries to) 7.1. External Identifier Resolution
1953  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1954  *
1955  * Returns the URI of the resource or NULL if not found
1956  */
1957 static xmlChar *
xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal,const xmlChar * pubID,const xmlChar * sysID)1958 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1959 	              const xmlChar *sysID) {
1960     xmlChar *ret = NULL;
1961     xmlChar *urnID = NULL;
1962     xmlChar *normid;
1963 
1964     if (catal == NULL)
1965         return(NULL);
1966     if ((pubID == NULL) && (sysID == NULL))
1967 	return(NULL);
1968 
1969     normid = xmlCatalogNormalizePublic(pubID);
1970     if (normid != NULL)
1971         pubID = (*normid != 0 ? normid : NULL);
1972 
1973     if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1974 	urnID = xmlCatalogUnWrapURN(pubID);
1975 	if (xmlDebugCatalogs) {
1976 	    if (urnID == NULL)
1977 		xmlCatalogPrintDebug(
1978 			"Public URN ID %s expanded to NULL\n", pubID);
1979 	    else
1980 		xmlCatalogPrintDebug(
1981 			"Public URN ID expanded to %s\n", urnID);
1982 	}
1983 	ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
1984 	if (urnID != NULL)
1985 	    xmlFree(urnID);
1986 	if (normid != NULL)
1987 	    xmlFree(normid);
1988 	return(ret);
1989     }
1990     if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1991 	urnID = xmlCatalogUnWrapURN(sysID);
1992 	if (xmlDebugCatalogs) {
1993 	    if (urnID == NULL)
1994 		xmlCatalogPrintDebug(
1995 			"System URN ID %s expanded to NULL\n", sysID);
1996 	    else
1997 		xmlCatalogPrintDebug(
1998 			"System URN ID expanded to %s\n", urnID);
1999 	}
2000 	if (pubID == NULL)
2001 	    ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2002 	else if (xmlStrEqual(pubID, urnID))
2003 	    ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
2004 	else {
2005 	    ret = xmlCatalogListXMLResolve(catal, pubID, urnID);
2006 	}
2007 	if (urnID != NULL)
2008 	    xmlFree(urnID);
2009 	if (normid != NULL)
2010 	    xmlFree(normid);
2011 	return(ret);
2012     }
2013     while (catal != NULL) {
2014 	if (catal->type == XML_CATA_CATALOG) {
2015 	    if (catal->children == NULL) {
2016 		xmlFetchXMLCatalogFile(catal);
2017 	    }
2018 	    if (catal->children != NULL) {
2019 		ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
2020 		if (ret != NULL) {
2021 		    break;
2022                 } else if (catal->children->depth > MAX_CATAL_DEPTH) {
2023 	            ret = NULL;
2024 		    break;
2025 	        }
2026 	    }
2027 	}
2028 	catal = catal->next;
2029     }
2030     if (normid != NULL)
2031 	xmlFree(normid);
2032     return(ret);
2033 }
2034 
2035 /**
2036  * xmlCatalogListXMLResolveURI:
2037  * @catal:  a catalog list
2038  * @URI:  the URI
2039  *
2040  * Do a complete resolution lookup of an URI for a list of catalogs
2041  *
2042  * Implements (or tries to) 7.2. URI Resolution
2043  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
2044  *
2045  * Returns the URI of the resource or NULL if not found
2046  */
2047 static xmlChar *
xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal,const xmlChar * URI)2048 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
2049     xmlChar *ret = NULL;
2050     xmlChar *urnID = NULL;
2051 
2052     if (catal == NULL)
2053         return(NULL);
2054     if (URI == NULL)
2055 	return(NULL);
2056 
2057     if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2058 	urnID = xmlCatalogUnWrapURN(URI);
2059 	if (xmlDebugCatalogs) {
2060 	    if (urnID == NULL)
2061 		xmlCatalogPrintDebug(
2062 			"URN ID %s expanded to NULL\n", URI);
2063 	    else
2064 		xmlCatalogPrintDebug(
2065 			"URN ID expanded to %s\n", urnID);
2066 	}
2067 	ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2068 	if (urnID != NULL)
2069 	    xmlFree(urnID);
2070 	return(ret);
2071     }
2072     while (catal != NULL) {
2073 	if (catal->type == XML_CATA_CATALOG) {
2074 	    if (catal->children == NULL) {
2075 		xmlFetchXMLCatalogFile(catal);
2076 	    }
2077 	    if (catal->children != NULL) {
2078 		ret = xmlCatalogXMLResolveURI(catal->children, URI);
2079 		if (ret != NULL)
2080 		    return(ret);
2081 	    }
2082 	}
2083 	catal = catal->next;
2084     }
2085     return(ret);
2086 }
2087 
2088 /************************************************************************
2089  *									*
2090  *			The SGML Catalog parser				*
2091  *									*
2092  ************************************************************************/
2093 
2094 
2095 #define RAW *cur
2096 #define NEXT cur++;
2097 #define SKIP(x) cur += x;
2098 
2099 #define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;
2100 
2101 /**
2102  * xmlParseSGMLCatalogComment:
2103  * @cur:  the current character
2104  *
2105  * Skip a comment in an SGML catalog
2106  *
2107  * Returns new current character
2108  */
2109 static const xmlChar *
xmlParseSGMLCatalogComment(const xmlChar * cur)2110 xmlParseSGMLCatalogComment(const xmlChar *cur) {
2111     if ((cur[0] != '-') || (cur[1] != '-'))
2112 	return(cur);
2113     SKIP(2);
2114     while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
2115 	NEXT;
2116     if (cur[0] == 0) {
2117 	return(NULL);
2118     }
2119     return(cur + 2);
2120 }
2121 
2122 /**
2123  * xmlParseSGMLCatalogPubid:
2124  * @cur:  the current character
2125  * @id:  the return location
2126  *
2127  * Parse an SGML catalog ID
2128  *
2129  * Returns new current character and store the value in @id
2130  */
2131 static const xmlChar *
xmlParseSGMLCatalogPubid(const xmlChar * cur,xmlChar ** id)2132 xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
2133     xmlChar *buf = NULL;
2134     int len = 0;
2135     int size = 50;
2136     xmlChar stop;
2137 
2138     *id = NULL;
2139 
2140     if (RAW == '"') {
2141         NEXT;
2142 	stop = '"';
2143     } else if (RAW == '\'') {
2144         NEXT;
2145 	stop = '\'';
2146     } else {
2147 	stop = ' ';
2148     }
2149     buf = xmlMalloc(size);
2150     if (buf == NULL) {
2151         xmlCatalogErrMemory();
2152 	return(NULL);
2153     }
2154     while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {
2155 	if ((*cur == stop) && (stop != ' '))
2156 	    break;
2157 	if ((stop == ' ') && (IS_BLANK_CH(*cur)))
2158 	    break;
2159 	if (len + 1 >= size) {
2160             xmlChar *tmp;
2161             int newSize;
2162 
2163             newSize = xmlGrowCapacity(size, 1, 1, XML_MAX_ITEMS);
2164             if (newSize < 0) {
2165 		xmlCatalogErrMemory();
2166 		xmlFree(buf);
2167 		return(NULL);
2168             }
2169 	    tmp = xmlRealloc(buf, newSize);
2170 	    if (tmp == NULL) {
2171 		xmlCatalogErrMemory();
2172 		xmlFree(buf);
2173 		return(NULL);
2174 	    }
2175 	    buf = tmp;
2176             size = newSize;
2177 	}
2178 	buf[len++] = *cur;
2179 	NEXT;
2180     }
2181     buf[len] = 0;
2182     if (stop == ' ') {
2183 	if (!IS_BLANK_CH(*cur)) {
2184 	    xmlFree(buf);
2185 	    return(NULL);
2186 	}
2187     } else {
2188 	if (*cur != stop) {
2189 	    xmlFree(buf);
2190 	    return(NULL);
2191 	}
2192 	NEXT;
2193     }
2194     *id = buf;
2195     return(cur);
2196 }
2197 
2198 /**
2199  * xmlParseSGMLCatalogName:
2200  * @cur:  the current character
2201  * @name:  the return location
2202  *
2203  * Parse an SGML catalog name
2204  *
2205  * Returns new current character and store the value in @name
2206  */
2207 static const xmlChar *
xmlParseSGMLCatalogName(const xmlChar * cur,xmlChar ** name)2208 xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
2209     xmlChar buf[XML_MAX_NAMELEN + 5];
2210     int len = 0;
2211     int c;
2212 
2213     *name = NULL;
2214 
2215     /*
2216      * Handler for more complex cases
2217      */
2218     c = *cur;
2219     if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
2220 	return(NULL);
2221     }
2222 
2223     while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
2224             (c == '.') || (c == '-') ||
2225 	    (c == '_') || (c == ':'))) {
2226 	buf[len++] = c;
2227 	cur++;
2228 	c = *cur;
2229 	if (len >= XML_MAX_NAMELEN)
2230 	    return(NULL);
2231     }
2232     *name = xmlStrndup(buf, len);
2233     return(cur);
2234 }
2235 
2236 /**
2237  * xmlGetSGMLCatalogEntryType:
2238  * @name:  the entry name
2239  *
2240  * Get the Catalog entry type for a given SGML Catalog name
2241  *
2242  * Returns Catalog entry type
2243  */
2244 static xmlCatalogEntryType
xmlGetSGMLCatalogEntryType(const xmlChar * name)2245 xmlGetSGMLCatalogEntryType(const xmlChar *name) {
2246     xmlCatalogEntryType type = XML_CATA_NONE;
2247     if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2248 	type = SGML_CATA_SYSTEM;
2249     else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2250 	type = SGML_CATA_PUBLIC;
2251     else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2252 	type = SGML_CATA_DELEGATE;
2253     else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2254 	type = SGML_CATA_ENTITY;
2255     else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2256 	type = SGML_CATA_DOCTYPE;
2257     else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2258 	type = SGML_CATA_LINKTYPE;
2259     else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2260 	type = SGML_CATA_NOTATION;
2261     else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2262 	type = SGML_CATA_SGMLDECL;
2263     else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2264 	type = SGML_CATA_DOCUMENT;
2265     else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2266 	type = SGML_CATA_CATALOG;
2267     else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2268 	type = SGML_CATA_BASE;
2269     return(type);
2270 }
2271 
2272 /**
2273  * xmlParseSGMLCatalog:
2274  * @catal:  the SGML Catalog
2275  * @value:  the content of the SGML Catalog serialization
2276  * @file:  the filepath for the catalog
2277  * @super:  should this be handled as a Super Catalog in which case
2278  *          parsing is not recursive
2279  *
2280  * Parse an SGML catalog content and fill up the @catal hash table with
2281  * the new entries found.
2282  *
2283  * Returns 0 in case of success, -1 in case of error.
2284  */
2285 static int
xmlParseSGMLCatalog(xmlCatalogPtr catal,const xmlChar * value,const char * file,int super)2286 xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
2287 	            const char *file, int super) {
2288     const xmlChar *cur = value;
2289     xmlChar *base = NULL;
2290     int res;
2291 
2292     if ((cur == NULL) || (file == NULL))
2293         return(-1);
2294     base = xmlStrdup((const xmlChar *) file);
2295 
2296     while ((cur != NULL) && (cur[0] != 0)) {
2297 	SKIP_BLANKS;
2298 	if (cur[0] == 0)
2299 	    break;
2300 	if ((cur[0] == '-') && (cur[1] == '-')) {
2301 	    cur = xmlParseSGMLCatalogComment(cur);
2302 	    if (cur == NULL) {
2303 		/* error */
2304 		break;
2305 	    }
2306 	} else {
2307 	    xmlChar *sysid = NULL;
2308 	    xmlChar *name = NULL;
2309 	    xmlCatalogEntryType type = XML_CATA_NONE;
2310 
2311 	    cur = xmlParseSGMLCatalogName(cur, &name);
2312 	    if (cur == NULL || name == NULL) {
2313 		/* error */
2314 		break;
2315 	    }
2316 	    if (!IS_BLANK_CH(*cur)) {
2317 		/* error */
2318 		xmlFree(name);
2319 		break;
2320 	    }
2321 	    SKIP_BLANKS;
2322 	    if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2323                 type = SGML_CATA_SYSTEM;
2324 	    else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2325                 type = SGML_CATA_PUBLIC;
2326 	    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2327                 type = SGML_CATA_DELEGATE;
2328 	    else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2329                 type = SGML_CATA_ENTITY;
2330 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2331                 type = SGML_CATA_DOCTYPE;
2332 	    else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2333                 type = SGML_CATA_LINKTYPE;
2334 	    else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2335                 type = SGML_CATA_NOTATION;
2336 	    else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2337                 type = SGML_CATA_SGMLDECL;
2338 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2339                 type = SGML_CATA_DOCUMENT;
2340 	    else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2341                 type = SGML_CATA_CATALOG;
2342 	    else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2343                 type = SGML_CATA_BASE;
2344 	    else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
2345 		xmlFree(name);
2346 		cur = xmlParseSGMLCatalogName(cur, &name);
2347 		if (name == NULL) {
2348 		    /* error */
2349 		    break;
2350 		}
2351 		xmlFree(name);
2352 		continue;
2353 	    }
2354 	    xmlFree(name);
2355 	    name = NULL;
2356 
2357 	    switch(type) {
2358 		case SGML_CATA_ENTITY:
2359 		    if (*cur == '%')
2360 			type = SGML_CATA_PENTITY;
2361                     /* Falls through. */
2362 		case SGML_CATA_PENTITY:
2363 		case SGML_CATA_DOCTYPE:
2364 		case SGML_CATA_LINKTYPE:
2365 		case SGML_CATA_NOTATION:
2366 		    cur = xmlParseSGMLCatalogName(cur, &name);
2367 		    if (cur == NULL) {
2368 			/* error */
2369 			break;
2370 		    }
2371 		    if (!IS_BLANK_CH(*cur)) {
2372 			/* error */
2373 			break;
2374 		    }
2375 		    SKIP_BLANKS;
2376 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2377 		    if (cur == NULL) {
2378 			/* error */
2379 			break;
2380 		    }
2381 		    break;
2382 		case SGML_CATA_PUBLIC:
2383 		case SGML_CATA_SYSTEM:
2384 		case SGML_CATA_DELEGATE:
2385 		    cur = xmlParseSGMLCatalogPubid(cur, &name);
2386 		    if (cur == NULL) {
2387 			/* error */
2388 			break;
2389 		    }
2390 		    if (type != SGML_CATA_SYSTEM) {
2391 		        xmlChar *normid;
2392 
2393 		        normid = xmlCatalogNormalizePublic(name);
2394 		        if (normid != NULL) {
2395 		            if (name != NULL)
2396 		                xmlFree(name);
2397 		            if (*normid != 0)
2398 		                name = normid;
2399 		            else {
2400 		                xmlFree(normid);
2401 		                name = NULL;
2402 		            }
2403 		        }
2404 		    }
2405 		    if (!IS_BLANK_CH(*cur)) {
2406 			/* error */
2407 			break;
2408 		    }
2409 		    SKIP_BLANKS;
2410 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2411 		    if (cur == NULL) {
2412 			/* error */
2413 			break;
2414 		    }
2415 		    break;
2416 		case SGML_CATA_BASE:
2417 		case SGML_CATA_CATALOG:
2418 		case SGML_CATA_DOCUMENT:
2419 		case SGML_CATA_SGMLDECL:
2420 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2421 		    if (cur == NULL) {
2422 			/* error */
2423 			break;
2424 		    }
2425 		    break;
2426 		default:
2427 		    break;
2428 	    }
2429 	    if (cur == NULL) {
2430 		if (name != NULL)
2431 		    xmlFree(name);
2432 		if (sysid != NULL)
2433 		    xmlFree(sysid);
2434 		break;
2435 	    } else if (type == SGML_CATA_BASE) {
2436 		if (base != NULL)
2437 		    xmlFree(base);
2438 		base = xmlStrdup(sysid);
2439 	    } else if ((type == SGML_CATA_PUBLIC) ||
2440 		       (type == SGML_CATA_SYSTEM)) {
2441 		xmlChar *filename;
2442 
2443 		filename = xmlBuildURI(sysid, base);
2444 		if (filename != NULL) {
2445 		    xmlCatalogEntryPtr entry;
2446 
2447 		    entry = xmlNewCatalogEntry(type, name, filename,
2448 			                       NULL, XML_CATA_PREFER_NONE, NULL);
2449 		    res = xmlHashAddEntry(catal->sgml, name, entry);
2450 		    if (res < 0) {
2451 			xmlFreeCatalogEntry(entry, NULL);
2452 		    }
2453 		    xmlFree(filename);
2454 		}
2455 
2456 	    } else if (type == SGML_CATA_CATALOG) {
2457 		if (super) {
2458 		    xmlCatalogEntryPtr entry;
2459 
2460 		    entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
2461 			                       XML_CATA_PREFER_NONE, NULL);
2462 		    res = xmlHashAddEntry(catal->sgml, sysid, entry);
2463 		    if (res < 0) {
2464 			xmlFreeCatalogEntry(entry, NULL);
2465 		    }
2466 		} else {
2467 		    xmlChar *filename;
2468 
2469 		    filename = xmlBuildURI(sysid, base);
2470 		    if (filename != NULL) {
2471 			xmlExpandCatalog(catal, (const char *)filename);
2472 			xmlFree(filename);
2473 		    }
2474 		}
2475 	    }
2476 	    /*
2477 	     * drop anything else we won't handle it
2478 	     */
2479 	    if (name != NULL)
2480 		xmlFree(name);
2481 	    if (sysid != NULL)
2482 		xmlFree(sysid);
2483 	}
2484     }
2485     if (base != NULL)
2486 	xmlFree(base);
2487     if (cur == NULL)
2488 	return(-1);
2489     return(0);
2490 }
2491 
2492 /************************************************************************
2493  *									*
2494  *			SGML Catalog handling				*
2495  *									*
2496  ************************************************************************/
2497 
2498 /**
2499  * xmlCatalogGetSGMLPublic:
2500  * @catal:  an SGML catalog hash
2501  * @pubID:  the public ID string
2502  *
2503  * Try to lookup the catalog local reference associated to a public ID
2504  *
2505  * Returns the local resource if found or NULL otherwise.
2506  */
2507 static const xmlChar *
xmlCatalogGetSGMLPublic(xmlHashTablePtr catal,const xmlChar * pubID)2508 xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
2509     xmlCatalogEntryPtr entry;
2510     xmlChar *normid;
2511 
2512     if (catal == NULL)
2513 	return(NULL);
2514 
2515     normid = xmlCatalogNormalizePublic(pubID);
2516     if (normid != NULL)
2517         pubID = (*normid != 0 ? normid : NULL);
2518 
2519     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
2520     if (entry == NULL) {
2521 	if (normid != NULL)
2522 	    xmlFree(normid);
2523 	return(NULL);
2524     }
2525     if (entry->type == SGML_CATA_PUBLIC) {
2526 	if (normid != NULL)
2527 	    xmlFree(normid);
2528 	return(entry->URL);
2529     }
2530     if (normid != NULL)
2531         xmlFree(normid);
2532     return(NULL);
2533 }
2534 
2535 /**
2536  * xmlCatalogGetSGMLSystem:
2537  * @catal:  an SGML catalog hash
2538  * @sysID:  the system ID string
2539  *
2540  * Try to lookup the catalog local reference for a system ID
2541  *
2542  * Returns the local resource if found or NULL otherwise.
2543  */
2544 static const xmlChar *
xmlCatalogGetSGMLSystem(xmlHashTablePtr catal,const xmlChar * sysID)2545 xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
2546     xmlCatalogEntryPtr entry;
2547 
2548     if (catal == NULL)
2549 	return(NULL);
2550 
2551     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
2552     if (entry == NULL)
2553 	return(NULL);
2554     if (entry->type == SGML_CATA_SYSTEM)
2555 	return(entry->URL);
2556     return(NULL);
2557 }
2558 
2559 /**
2560  * xmlCatalogSGMLResolve:
2561  * @catal:  the SGML catalog
2562  * @pubID:  the public ID string
2563  * @sysID:  the system ID string
2564  *
2565  * Do a complete resolution lookup of an External Identifier
2566  *
2567  * Returns the URI of the resource or NULL if not found
2568  */
2569 static const xmlChar *
xmlCatalogSGMLResolve(xmlCatalogPtr catal,const xmlChar * pubID,const xmlChar * sysID)2570 xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
2571 	              const xmlChar *sysID) {
2572     const xmlChar *ret = NULL;
2573 
2574     if (catal->sgml == NULL)
2575 	return(NULL);
2576 
2577     if (pubID != NULL)
2578 	ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2579     if (ret != NULL)
2580 	return(ret);
2581     if (sysID != NULL)
2582 	ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2583     if (ret != NULL)
2584 	return(ret);
2585     return(NULL);
2586 }
2587 
2588 /************************************************************************
2589  *									*
2590  *			Specific Public interfaces			*
2591  *									*
2592  ************************************************************************/
2593 
2594 /**
2595  * xmlLoadSGMLSuperCatalog:
2596  * @filename:  a file path
2597  *
2598  * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
2599  * references. This is only needed for manipulating SGML Super Catalogs
2600  * like adding and removing CATALOG or DELEGATE entries.
2601  *
2602  * Returns the catalog parsed or NULL in case of error
2603  */
2604 xmlCatalogPtr
xmlLoadSGMLSuperCatalog(const char * filename)2605 xmlLoadSGMLSuperCatalog(const char *filename)
2606 {
2607     xmlChar *content;
2608     xmlCatalogPtr catal;
2609     int ret;
2610 
2611     content = xmlLoadFileContent(filename);
2612     if (content == NULL)
2613         return(NULL);
2614 
2615     catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2616     if (catal == NULL) {
2617 	xmlFree(content);
2618 	return(NULL);
2619     }
2620 
2621     ret = xmlParseSGMLCatalog(catal, content, filename, 1);
2622     xmlFree(content);
2623     if (ret < 0) {
2624 	xmlFreeCatalog(catal);
2625 	return(NULL);
2626     }
2627     return (catal);
2628 }
2629 
2630 /**
2631  * xmlLoadACatalog:
2632  * @filename:  a file path
2633  *
2634  * Load the catalog and build the associated data structures.
2635  * This can be either an XML Catalog or an SGML Catalog
2636  * It will recurse in SGML CATALOG entries. On the other hand XML
2637  * Catalogs are not handled recursively.
2638  *
2639  * Returns the catalog parsed or NULL in case of error
2640  */
2641 xmlCatalogPtr
xmlLoadACatalog(const char * filename)2642 xmlLoadACatalog(const char *filename)
2643 {
2644     xmlChar *content;
2645     xmlChar *first;
2646     xmlCatalogPtr catal;
2647     int ret;
2648 
2649     content = xmlLoadFileContent(filename);
2650     if (content == NULL)
2651         return(NULL);
2652 
2653 
2654     first = content;
2655 
2656     while ((*first != 0) && (*first != '-') && (*first != '<') &&
2657 	   (!(((*first >= 'A') && (*first <= 'Z')) ||
2658 	      ((*first >= 'a') && (*first <= 'z')))))
2659 	first++;
2660 
2661     if (*first != '<') {
2662 	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2663 	if (catal == NULL) {
2664 	    xmlFree(content);
2665 	    return(NULL);
2666 	}
2667         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2668 	if (ret < 0) {
2669 	    xmlFreeCatalog(catal);
2670 	    xmlFree(content);
2671 	    return(NULL);
2672 	}
2673     } else {
2674 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2675 	if (catal == NULL) {
2676 	    xmlFree(content);
2677 	    return(NULL);
2678 	}
2679         catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2680 		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2681     }
2682     xmlFree(content);
2683     return (catal);
2684 }
2685 
2686 /**
2687  * xmlExpandCatalog:
2688  * @catal:  a catalog
2689  * @filename:  a file path
2690  *
2691  * Load the catalog and expand the existing catal structure.
2692  * This can be either an XML Catalog or an SGML Catalog
2693  *
2694  * Returns 0 in case of success, -1 in case of error
2695  */
2696 static int
xmlExpandCatalog(xmlCatalogPtr catal,const char * filename)2697 xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
2698 {
2699     int ret;
2700 
2701     if ((catal == NULL) || (filename == NULL))
2702 	return(-1);
2703 
2704 
2705     if (catal->type == XML_SGML_CATALOG_TYPE) {
2706 	xmlChar *content;
2707 
2708 	content = xmlLoadFileContent(filename);
2709 	if (content == NULL)
2710 	    return(-1);
2711 
2712         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2713 	if (ret < 0) {
2714 	    xmlFree(content);
2715 	    return(-1);
2716 	}
2717 	xmlFree(content);
2718     } else {
2719 	xmlCatalogEntryPtr tmp, cur;
2720 	tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2721 		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2722 
2723 	cur = catal->xml;
2724 	if (cur == NULL) {
2725 	    catal->xml = tmp;
2726 	} else {
2727 	    while (cur->next != NULL) cur = cur->next;
2728 	    cur->next = tmp;
2729 	}
2730     }
2731     return (0);
2732 }
2733 
2734 /**
2735  * xmlACatalogResolveSystem:
2736  * @catal:  a Catalog
2737  * @sysID:  the system ID string
2738  *
2739  * Try to lookup the catalog resource for a system ID
2740  *
2741  * Returns the resource if found or NULL otherwise, the value returned
2742  *      must be freed by the caller.
2743  */
2744 xmlChar *
xmlACatalogResolveSystem(xmlCatalogPtr catal,const xmlChar * sysID)2745 xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
2746     xmlChar *ret = NULL;
2747 
2748     if ((sysID == NULL) || (catal == NULL))
2749 	return(NULL);
2750 
2751     if (xmlDebugCatalogs)
2752 	xmlCatalogPrintDebug(
2753 		"Resolve sysID %s\n", sysID);
2754 
2755     if (catal->type == XML_XML_CATALOG_TYPE) {
2756 	ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
2757 	if (ret == XML_CATAL_BREAK)
2758 	    ret = NULL;
2759     } else {
2760 	const xmlChar *sgml;
2761 
2762 	sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2763 	if (sgml != NULL)
2764 	    ret = xmlStrdup(sgml);
2765     }
2766     return(ret);
2767 }
2768 
2769 /**
2770  * xmlACatalogResolvePublic:
2771  * @catal:  a Catalog
2772  * @pubID:  the public ID string
2773  *
2774  * Try to lookup the catalog local reference associated to a public ID in that catalog
2775  *
2776  * Returns the local resource if found or NULL otherwise, the value returned
2777  *      must be freed by the caller.
2778  */
2779 xmlChar *
xmlACatalogResolvePublic(xmlCatalogPtr catal,const xmlChar * pubID)2780 xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
2781     xmlChar *ret = NULL;
2782 
2783     if ((pubID == NULL) || (catal == NULL))
2784 	return(NULL);
2785 
2786     if (xmlDebugCatalogs)
2787 	xmlCatalogPrintDebug(
2788 		"Resolve pubID %s\n", pubID);
2789 
2790     if (catal->type == XML_XML_CATALOG_TYPE) {
2791 	ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
2792 	if (ret == XML_CATAL_BREAK)
2793 	    ret = NULL;
2794     } else {
2795 	const xmlChar *sgml;
2796 
2797 	sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2798 	if (sgml != NULL)
2799 	    ret = xmlStrdup(sgml);
2800     }
2801     return(ret);
2802 }
2803 
2804 /**
2805  * xmlACatalogResolve:
2806  * @catal:  a Catalog
2807  * @pubID:  the public ID string
2808  * @sysID:  the system ID string
2809  *
2810  * Do a complete resolution lookup of an External Identifier
2811  *
2812  * Returns the URI of the resource or NULL if not found, it must be freed
2813  *      by the caller.
2814  */
2815 xmlChar *
xmlACatalogResolve(xmlCatalogPtr catal,const xmlChar * pubID,const xmlChar * sysID)2816 xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
2817                    const xmlChar * sysID)
2818 {
2819     xmlChar *ret = NULL;
2820 
2821     if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
2822         return (NULL);
2823 
2824     if (xmlDebugCatalogs) {
2825          if ((pubID != NULL) && (sysID != NULL)) {
2826              xmlCatalogPrintDebug(
2827                              "Resolve: pubID %s sysID %s\n", pubID, sysID);
2828          } else if (pubID != NULL) {
2829              xmlCatalogPrintDebug(
2830                              "Resolve: pubID %s\n", pubID);
2831          } else {
2832              xmlCatalogPrintDebug(
2833                              "Resolve: sysID %s\n", sysID);
2834          }
2835     }
2836 
2837     if (catal->type == XML_XML_CATALOG_TYPE) {
2838         ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
2839 	if (ret == XML_CATAL_BREAK)
2840 	    ret = NULL;
2841     } else {
2842         const xmlChar *sgml;
2843 
2844         sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
2845         if (sgml != NULL)
2846             ret = xmlStrdup(sgml);
2847     }
2848     return (ret);
2849 }
2850 
2851 /**
2852  * xmlACatalogResolveURI:
2853  * @catal:  a Catalog
2854  * @URI:  the URI
2855  *
2856  * Do a complete resolution lookup of an URI
2857  *
2858  * Returns the URI of the resource or NULL if not found, it must be freed
2859  *      by the caller.
2860  */
2861 xmlChar *
xmlACatalogResolveURI(xmlCatalogPtr catal,const xmlChar * URI)2862 xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
2863     xmlChar *ret = NULL;
2864 
2865     if ((URI == NULL) || (catal == NULL))
2866 	return(NULL);
2867 
2868     if (xmlDebugCatalogs)
2869 	xmlCatalogPrintDebug(
2870 		"Resolve URI %s\n", URI);
2871 
2872     if (catal->type == XML_XML_CATALOG_TYPE) {
2873 	ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
2874 	if (ret == XML_CATAL_BREAK)
2875 	    ret = NULL;
2876     } else {
2877 	const xmlChar *sgml;
2878 
2879 	sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
2880 	if (sgml != NULL)
2881             ret = xmlStrdup(sgml);
2882     }
2883     return(ret);
2884 }
2885 
2886 #ifdef LIBXML_OUTPUT_ENABLED
2887 /**
2888  * xmlACatalogDump:
2889  * @catal:  a Catalog
2890  * @out:  the file.
2891  *
2892  * Dump the given catalog to the given file.
2893  */
2894 void
xmlACatalogDump(xmlCatalogPtr catal,FILE * out)2895 xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
2896     if ((out == NULL) || (catal == NULL))
2897 	return;
2898 
2899     if (catal->type == XML_XML_CATALOG_TYPE) {
2900 	xmlDumpXMLCatalog(out, catal->xml);
2901     } else {
2902 	xmlHashScan(catal->sgml, xmlCatalogDumpEntry, out);
2903     }
2904 }
2905 #endif /* LIBXML_OUTPUT_ENABLED */
2906 
2907 /**
2908  * xmlACatalogAdd:
2909  * @catal:  a Catalog
2910  * @type:  the type of record to add to the catalog
2911  * @orig:  the system, public or prefix to match
2912  * @replace:  the replacement value for the match
2913  *
2914  * Add an entry in the catalog, it may overwrite existing but
2915  * different entries.
2916  *
2917  * Returns 0 if successful, -1 otherwise
2918  */
2919 int
xmlACatalogAdd(xmlCatalogPtr catal,const xmlChar * type,const xmlChar * orig,const xmlChar * replace)2920 xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
2921               const xmlChar * orig, const xmlChar * replace)
2922 {
2923     int res = -1;
2924 
2925     if (catal == NULL)
2926 	return(-1);
2927 
2928     if (catal->type == XML_XML_CATALOG_TYPE) {
2929         res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
2930     } else {
2931         xmlCatalogEntryType cattype;
2932 
2933         cattype = xmlGetSGMLCatalogEntryType(type);
2934         if (cattype != XML_CATA_NONE) {
2935             xmlCatalogEntryPtr entry;
2936 
2937             entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
2938                                        XML_CATA_PREFER_NONE, NULL);
2939 	    if (catal->sgml == NULL)
2940 		catal->sgml = xmlHashCreate(10);
2941             res = xmlHashAddEntry(catal->sgml, orig, entry);
2942             if (res < 0)
2943                 xmlFreeCatalogEntry(entry, NULL);
2944         }
2945     }
2946     return (res);
2947 }
2948 
2949 /**
2950  * xmlACatalogRemove:
2951  * @catal:  a Catalog
2952  * @value:  the value to remove
2953  *
2954  * Remove an entry from the catalog
2955  *
2956  * Returns the number of entries removed if successful, -1 otherwise
2957  */
2958 int
xmlACatalogRemove(xmlCatalogPtr catal,const xmlChar * value)2959 xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
2960     int res = -1;
2961 
2962     if ((catal == NULL) || (value == NULL))
2963 	return(-1);
2964 
2965     if (catal->type == XML_XML_CATALOG_TYPE) {
2966 	res = xmlDelXMLCatalog(catal->xml, value);
2967     } else {
2968 	res = xmlHashRemoveEntry(catal->sgml, value, xmlFreeCatalogEntry);
2969 	if (res == 0)
2970 	    res = 1;
2971     }
2972     return(res);
2973 }
2974 
2975 /**
2976  * xmlNewCatalog:
2977  * @sgml:  should this create an SGML catalog
2978  *
2979  * create a new Catalog.
2980  *
2981  * Returns the xmlCatalogPtr or NULL in case of error
2982  */
2983 xmlCatalogPtr
xmlNewCatalog(int sgml)2984 xmlNewCatalog(int sgml) {
2985     xmlCatalogPtr catal = NULL;
2986 
2987     if (sgml) {
2988 	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
2989 		                    xmlCatalogDefaultPrefer);
2990         if ((catal != NULL) && (catal->sgml == NULL))
2991 	    catal->sgml = xmlHashCreate(10);
2992     } else
2993 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
2994 		                    xmlCatalogDefaultPrefer);
2995     return(catal);
2996 }
2997 
2998 /**
2999  * xmlCatalogIsEmpty:
3000  * @catal:  should this create an SGML catalog
3001  *
3002  * Check is a catalog is empty
3003  *
3004  * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
3005  */
3006 int
xmlCatalogIsEmpty(xmlCatalogPtr catal)3007 xmlCatalogIsEmpty(xmlCatalogPtr catal) {
3008     if (catal == NULL)
3009 	return(-1);
3010 
3011     if (catal->type == XML_XML_CATALOG_TYPE) {
3012 	if (catal->xml == NULL)
3013 	    return(1);
3014 	if ((catal->xml->type != XML_CATA_CATALOG) &&
3015 	    (catal->xml->type != XML_CATA_BROKEN_CATALOG))
3016 	    return(-1);
3017 	if (catal->xml->children == NULL)
3018 	    return(1);
3019         return(0);
3020     } else {
3021 	int res;
3022 
3023 	if (catal->sgml == NULL)
3024 	    return(1);
3025 	res = xmlHashSize(catal->sgml);
3026 	if (res == 0)
3027 	    return(1);
3028 	if (res < 0)
3029 	    return(-1);
3030     }
3031     return(0);
3032 }
3033 
3034 /************************************************************************
3035  *									*
3036  *   Public interfaces manipulating the global shared default catalog	*
3037  *									*
3038  ************************************************************************/
3039 
3040 /**
3041  * xmlInitCatalogInternal:
3042  *
3043  * Do the catalog initialization only of global data, doesn't try to load
3044  * any catalog actually.
3045  */
3046 void
xmlInitCatalogInternal(void)3047 xmlInitCatalogInternal(void) {
3048     if (getenv("XML_DEBUG_CATALOG"))
3049 	xmlDebugCatalogs = 1;
3050     xmlInitRMutex(&xmlCatalogMutex);
3051 }
3052 
3053 /**
3054  * xmlInitializeCatalog:
3055  *
3056  * Load the default system catalog.
3057  */
3058 void
xmlInitializeCatalog(void)3059 xmlInitializeCatalog(void) {
3060     if (xmlCatalogInitialized != 0)
3061 	return;
3062 
3063     xmlInitParser();
3064 
3065     xmlRMutexLock(&xmlCatalogMutex);
3066 
3067     if (xmlDefaultCatalog == NULL) {
3068 	const char *catalogs;
3069 	char *path;
3070 	const char *cur, *paths;
3071 	xmlCatalogPtr catal;
3072 	xmlCatalogEntryPtr *nextent;
3073 
3074 	catalogs = (const char *) getenv("XML_CATALOG_FILES");
3075 	if (catalogs == NULL)
3076 	    catalogs = XML_XML_DEFAULT_CATALOG;
3077 
3078 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3079 		xmlCatalogDefaultPrefer);
3080 	if (catal != NULL) {
3081 	    /* the XML_CATALOG_FILES envvar is allowed to contain a
3082 	       space-separated list of entries. */
3083 	    cur = catalogs;
3084 	    nextent = &catal->xml;
3085 	    while (*cur != '\0') {
3086 		while (xmlIsBlank_ch(*cur))
3087 		    cur++;
3088 		if (*cur != 0) {
3089 		    paths = cur;
3090 		    while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
3091 			cur++;
3092 		    path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
3093 		    if (path != NULL) {
3094 			*nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3095 				NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
3096 			if (*nextent != NULL)
3097 			    nextent = &((*nextent)->next);
3098 			xmlFree(path);
3099 		    }
3100 		}
3101 	    }
3102 	    xmlDefaultCatalog = catal;
3103 	}
3104     }
3105 
3106     xmlRMutexUnlock(&xmlCatalogMutex);
3107 
3108     xmlCatalogInitialized = 1;
3109 }
3110 
3111 
3112 /**
3113  * xmlLoadCatalog:
3114  * @filename:  a file path
3115  *
3116  * Load the catalog and makes its definitions effective for the default
3117  * external entity loader. It will recurse in SGML CATALOG entries.
3118  * this function is not thread safe, catalog initialization should
3119  * preferably be done once at startup
3120  *
3121  * Returns 0 in case of success -1 in case of error
3122  */
3123 int
xmlLoadCatalog(const char * filename)3124 xmlLoadCatalog(const char *filename)
3125 {
3126     int ret;
3127     xmlCatalogPtr catal;
3128 
3129     xmlInitParser();
3130 
3131     xmlRMutexLock(&xmlCatalogMutex);
3132 
3133     if (xmlDefaultCatalog == NULL) {
3134 	catal = xmlLoadACatalog(filename);
3135 	if (catal == NULL) {
3136 	    xmlRMutexUnlock(&xmlCatalogMutex);
3137 	    return(-1);
3138 	}
3139 
3140 	xmlDefaultCatalog = catal;
3141 	xmlRMutexUnlock(&xmlCatalogMutex);
3142         xmlCatalogInitialized = 1;
3143 	return(0);
3144     }
3145 
3146     ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
3147     xmlRMutexUnlock(&xmlCatalogMutex);
3148     return(ret);
3149 }
3150 
3151 /**
3152  * xmlLoadCatalogs:
3153  * @pathss:  a list of directories separated by a colon or a space.
3154  *
3155  * Load the catalogs and makes their definitions effective for the default
3156  * external entity loader.
3157  * this function is not thread safe, catalog initialization should
3158  * preferably be done once at startup
3159  */
3160 void
xmlLoadCatalogs(const char * pathss)3161 xmlLoadCatalogs(const char *pathss) {
3162     const char *cur;
3163     const char *paths;
3164     xmlChar *path;
3165 #ifdef _WIN32
3166     int i, iLen;
3167 #endif
3168 
3169     if (pathss == NULL)
3170 	return;
3171 
3172     cur = pathss;
3173     while (*cur != 0) {
3174 	while (xmlIsBlank_ch(*cur)) cur++;
3175 	if (*cur != 0) {
3176 	    paths = cur;
3177 	    while ((*cur != 0) && (*cur != PATH_SEPARATOR) && (!xmlIsBlank_ch(*cur)))
3178 		cur++;
3179 	    path = xmlStrndup((const xmlChar *)paths, cur - paths);
3180 	    if (path != NULL) {
3181 #ifdef _WIN32
3182         iLen = strlen((const char*)path);
3183         for(i = 0; i < iLen; i++) {
3184             if(path[i] == '\\') {
3185                 path[i] = '/';
3186             }
3187         }
3188 #endif
3189 		xmlLoadCatalog((const char *) path);
3190 		xmlFree(path);
3191 	    }
3192 	}
3193 	while (*cur == PATH_SEPARATOR)
3194 	    cur++;
3195     }
3196 }
3197 
3198 /**
3199  * xmlCatalogCleanup:
3200  *
3201  * Free up all the memory associated with catalogs
3202  */
3203 void
xmlCatalogCleanup(void)3204 xmlCatalogCleanup(void) {
3205     xmlRMutexLock(&xmlCatalogMutex);
3206     if (xmlDebugCatalogs)
3207 	xmlCatalogPrintDebug(
3208 		"Catalogs cleanup\n");
3209     if (xmlCatalogXMLFiles != NULL)
3210 	xmlHashFree(xmlCatalogXMLFiles, xmlFreeCatalogHashEntryList);
3211     xmlCatalogXMLFiles = NULL;
3212     if (xmlDefaultCatalog != NULL)
3213 	xmlFreeCatalog(xmlDefaultCatalog);
3214     xmlDefaultCatalog = NULL;
3215     xmlDebugCatalogs = 0;
3216     xmlCatalogInitialized = 0;
3217     xmlRMutexUnlock(&xmlCatalogMutex);
3218 }
3219 
3220 /**
3221  * xmlCleanupCatalogInternal:
3222  *
3223  * Free global data.
3224  */
3225 void
xmlCleanupCatalogInternal(void)3226 xmlCleanupCatalogInternal(void) {
3227     xmlCleanupRMutex(&xmlCatalogMutex);
3228 }
3229 
3230 /**
3231  * xmlCatalogResolveSystem:
3232  * @sysID:  the system ID string
3233  *
3234  * Try to lookup the catalog resource for a system ID
3235  *
3236  * Returns the resource if found or NULL otherwise, the value returned
3237  *      must be freed by the caller.
3238  */
3239 xmlChar *
xmlCatalogResolveSystem(const xmlChar * sysID)3240 xmlCatalogResolveSystem(const xmlChar *sysID) {
3241     xmlChar *ret;
3242 
3243     if (!xmlCatalogInitialized)
3244 	xmlInitializeCatalog();
3245 
3246     ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
3247     return(ret);
3248 }
3249 
3250 /**
3251  * xmlCatalogResolvePublic:
3252  * @pubID:  the public ID string
3253  *
3254  * Try to lookup the catalog reference associated to a public ID
3255  *
3256  * Returns the resource if found or NULL otherwise, the value returned
3257  *      must be freed by the caller.
3258  */
3259 xmlChar *
xmlCatalogResolvePublic(const xmlChar * pubID)3260 xmlCatalogResolvePublic(const xmlChar *pubID) {
3261     xmlChar *ret;
3262 
3263     if (!xmlCatalogInitialized)
3264 	xmlInitializeCatalog();
3265 
3266     ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
3267     return(ret);
3268 }
3269 
3270 /**
3271  * xmlCatalogResolve:
3272  * @pubID:  the public ID string
3273  * @sysID:  the system ID string
3274  *
3275  * Do a complete resolution lookup of an External Identifier
3276  *
3277  * Returns the URI of the resource or NULL if not found, it must be freed
3278  *      by the caller.
3279  */
3280 xmlChar *
xmlCatalogResolve(const xmlChar * pubID,const xmlChar * sysID)3281 xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
3282     xmlChar *ret;
3283 
3284     if (!xmlCatalogInitialized)
3285 	xmlInitializeCatalog();
3286 
3287     ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
3288     return(ret);
3289 }
3290 
3291 /**
3292  * xmlCatalogResolveURI:
3293  * @URI:  the URI
3294  *
3295  * Do a complete resolution lookup of an URI
3296  *
3297  * Returns the URI of the resource or NULL if not found, it must be freed
3298  *      by the caller.
3299  */
3300 xmlChar *
xmlCatalogResolveURI(const xmlChar * URI)3301 xmlCatalogResolveURI(const xmlChar *URI) {
3302     xmlChar *ret;
3303 
3304     if (!xmlCatalogInitialized)
3305 	xmlInitializeCatalog();
3306 
3307     ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
3308     return(ret);
3309 }
3310 
3311 #ifdef LIBXML_OUTPUT_ENABLED
3312 /**
3313  * xmlCatalogDump:
3314  * @out:  the file.
3315  *
3316  * Dump all the global catalog content to the given file.
3317  */
3318 void
xmlCatalogDump(FILE * out)3319 xmlCatalogDump(FILE *out) {
3320     if (out == NULL)
3321 	return;
3322 
3323     if (!xmlCatalogInitialized)
3324 	xmlInitializeCatalog();
3325 
3326     xmlACatalogDump(xmlDefaultCatalog, out);
3327 }
3328 #endif /* LIBXML_OUTPUT_ENABLED */
3329 
3330 /**
3331  * xmlCatalogAdd:
3332  * @type:  the type of record to add to the catalog
3333  * @orig:  the system, public or prefix to match
3334  * @replace:  the replacement value for the match
3335  *
3336  * Add an entry in the catalog, it may overwrite existing but
3337  * different entries.
3338  * If called before any other catalog routine, allows to override the
3339  * default shared catalog put in place by xmlInitializeCatalog();
3340  *
3341  * Returns 0 if successful, -1 otherwise
3342  */
3343 int
xmlCatalogAdd(const xmlChar * type,const xmlChar * orig,const xmlChar * replace)3344 xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
3345     int res = -1;
3346 
3347     xmlInitParser();
3348 
3349     xmlRMutexLock(&xmlCatalogMutex);
3350     /*
3351      * Specific case where one want to override the default catalog
3352      * put in place by xmlInitializeCatalog();
3353      */
3354     if ((xmlDefaultCatalog == NULL) &&
3355 	(xmlStrEqual(type, BAD_CAST "catalog"))) {
3356 	xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3357 		                          xmlCatalogDefaultPrefer);
3358 	if (xmlDefaultCatalog != NULL) {
3359 	   xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3360 				    orig, NULL,  xmlCatalogDefaultPrefer, NULL);
3361 	}
3362 	xmlRMutexUnlock(&xmlCatalogMutex);
3363         xmlCatalogInitialized = 1;
3364 	return(0);
3365     }
3366 
3367     res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
3368     xmlRMutexUnlock(&xmlCatalogMutex);
3369     return(res);
3370 }
3371 
3372 /**
3373  * xmlCatalogRemove:
3374  * @value:  the value to remove
3375  *
3376  * Remove an entry from the catalog
3377  *
3378  * Returns the number of entries removed if successful, -1 otherwise
3379  */
3380 int
xmlCatalogRemove(const xmlChar * value)3381 xmlCatalogRemove(const xmlChar *value) {
3382     int res;
3383 
3384     if (!xmlCatalogInitialized)
3385 	xmlInitializeCatalog();
3386 
3387     xmlRMutexLock(&xmlCatalogMutex);
3388     res = xmlACatalogRemove(xmlDefaultCatalog, value);
3389     xmlRMutexUnlock(&xmlCatalogMutex);
3390     return(res);
3391 }
3392 
3393 /**
3394  * xmlCatalogConvert:
3395  *
3396  * Convert all the SGML catalog entries as XML ones
3397  *
3398  * Returns the number of entries converted if successful, -1 otherwise
3399  */
3400 int
xmlCatalogConvert(void)3401 xmlCatalogConvert(void) {
3402     int res = -1;
3403 
3404     if (!xmlCatalogInitialized)
3405 	xmlInitializeCatalog();
3406 
3407     xmlRMutexLock(&xmlCatalogMutex);
3408     res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
3409     xmlRMutexUnlock(&xmlCatalogMutex);
3410     return(res);
3411 }
3412 
3413 /************************************************************************
3414  *									*
3415  *	Public interface manipulating the common preferences		*
3416  *									*
3417  ************************************************************************/
3418 
3419 /**
3420  * xmlCatalogGetDefaults:
3421  *
3422  * DEPRECATED: Use XML_PARSE_NO_SYS_CATALOG and
3423  * XML_PARSE_CATALOG_PI.
3424  *
3425  * Used to get the user preference w.r.t. to what catalogs should
3426  * be accepted
3427  *
3428  * Returns the current xmlCatalogAllow value
3429  */
3430 xmlCatalogAllow
xmlCatalogGetDefaults(void)3431 xmlCatalogGetDefaults(void) {
3432     return(xmlCatalogDefaultAllow);
3433 }
3434 
3435 /**
3436  * xmlCatalogSetDefaults:
3437  * @allow:  what catalogs should be accepted
3438  *
3439  * DEPRECATED: Use XML_PARSE_NO_SYS_CATALOG and
3440  * XML_PARSE_CATALOG_PI.
3441  *
3442  * Used to set the user preference w.r.t. to what catalogs should
3443  * be accepted
3444  */
3445 void
xmlCatalogSetDefaults(xmlCatalogAllow allow)3446 xmlCatalogSetDefaults(xmlCatalogAllow allow) {
3447     if (xmlDebugCatalogs) {
3448 	switch (allow) {
3449 	    case XML_CATA_ALLOW_NONE:
3450 		xmlCatalogPrintDebug(
3451 			"Disabling catalog usage\n");
3452 		break;
3453 	    case XML_CATA_ALLOW_GLOBAL:
3454 		xmlCatalogPrintDebug(
3455 			"Allowing only global catalogs\n");
3456 		break;
3457 	    case XML_CATA_ALLOW_DOCUMENT:
3458 		xmlCatalogPrintDebug(
3459 			"Allowing only catalogs from the document\n");
3460 		break;
3461 	    case XML_CATA_ALLOW_ALL:
3462 		xmlCatalogPrintDebug(
3463 			"Allowing all catalogs\n");
3464 		break;
3465 	}
3466     }
3467     xmlCatalogDefaultAllow = allow;
3468 }
3469 
3470 /**
3471  * xmlCatalogSetDefaultPrefer:
3472  * @prefer:  the default preference for delegation
3473  *
3474  * DEPRECATED: This setting is global and not thread-safe.
3475  *
3476  * Allows to set the preference between public and system for deletion
3477  * in XML Catalog resolution. C.f. section 4.1.1 of the spec
3478  * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
3479  *
3480  * Returns the previous value of the default preference for delegation
3481  */
3482 xmlCatalogPrefer
xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer)3483 xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
3484     xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
3485 
3486     if (prefer == XML_CATA_PREFER_NONE)
3487 	return(ret);
3488 
3489     if (xmlDebugCatalogs) {
3490 	switch (prefer) {
3491 	    case XML_CATA_PREFER_PUBLIC:
3492 		xmlCatalogPrintDebug(
3493 			"Setting catalog preference to PUBLIC\n");
3494 		break;
3495 	    case XML_CATA_PREFER_SYSTEM:
3496 		xmlCatalogPrintDebug(
3497 			"Setting catalog preference to SYSTEM\n");
3498 		break;
3499 	    default:
3500 		return(ret);
3501 	}
3502     }
3503     xmlCatalogDefaultPrefer = prefer;
3504     return(ret);
3505 }
3506 
3507 /**
3508  * xmlCatalogSetDebug:
3509  * @level:  the debug level of catalogs required
3510  *
3511  * Used to set the debug level for catalog operation, 0 disable
3512  * debugging, 1 enable it
3513  *
3514  * Returns the previous value of the catalog debugging level
3515  */
3516 int
xmlCatalogSetDebug(int level)3517 xmlCatalogSetDebug(int level) {
3518     int ret = xmlDebugCatalogs;
3519 
3520     if (level <= 0)
3521         xmlDebugCatalogs = 0;
3522     else
3523 	xmlDebugCatalogs = level;
3524     return(ret);
3525 }
3526 
3527 /************************************************************************
3528  *									*
3529  *   Minimal interfaces used for per-document catalogs by the parser	*
3530  *									*
3531  ************************************************************************/
3532 
3533 /**
3534  * xmlCatalogFreeLocal:
3535  * @catalogs:  a document's list of catalogs
3536  *
3537  * Free up the memory associated to the catalog list
3538  */
3539 void
xmlCatalogFreeLocal(void * catalogs)3540 xmlCatalogFreeLocal(void *catalogs) {
3541     xmlCatalogEntryPtr catal;
3542 
3543     catal = (xmlCatalogEntryPtr) catalogs;
3544     if (catal != NULL)
3545 	xmlFreeCatalogEntryList(catal);
3546 }
3547 
3548 
3549 /**
3550  * xmlCatalogAddLocal:
3551  * @catalogs:  a document's list of catalogs
3552  * @URL:  the URL to a new local catalog
3553  *
3554  * Add the new entry to the catalog list
3555  *
3556  * Returns the updated list
3557  */
3558 void *
xmlCatalogAddLocal(void * catalogs,const xmlChar * URL)3559 xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
3560     xmlCatalogEntryPtr catal, add;
3561 
3562     xmlInitParser();
3563 
3564     if (URL == NULL)
3565 	return(catalogs);
3566 
3567     if (xmlDebugCatalogs)
3568 	xmlCatalogPrintDebug(
3569 		"Adding document catalog %s\n", URL);
3570 
3571     add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
3572 	                     xmlCatalogDefaultPrefer, NULL);
3573     if (add == NULL)
3574 	return(catalogs);
3575 
3576     catal = (xmlCatalogEntryPtr) catalogs;
3577     if (catal == NULL)
3578 	return((void *) add);
3579 
3580     while (catal->next != NULL)
3581 	catal = catal->next;
3582     catal->next = add;
3583     return(catalogs);
3584 }
3585 
3586 /**
3587  * xmlCatalogLocalResolve:
3588  * @catalogs:  a document's list of catalogs
3589  * @pubID:  the public ID string
3590  * @sysID:  the system ID string
3591  *
3592  * Do a complete resolution lookup of an External Identifier using a
3593  * document's private catalog list
3594  *
3595  * Returns the URI of the resource or NULL if not found, it must be freed
3596  *      by the caller.
3597  */
3598 xmlChar *
xmlCatalogLocalResolve(void * catalogs,const xmlChar * pubID,const xmlChar * sysID)3599 xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
3600 	               const xmlChar *sysID) {
3601     xmlCatalogEntryPtr catal;
3602     xmlChar *ret;
3603 
3604     if ((pubID == NULL) && (sysID == NULL))
3605 	return(NULL);
3606 
3607     if (xmlDebugCatalogs) {
3608         if ((pubID != NULL) && (sysID != NULL)) {
3609             xmlCatalogPrintDebug(
3610                             "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
3611         } else if (pubID != NULL) {
3612             xmlCatalogPrintDebug(
3613                             "Local Resolve: pubID %s\n", pubID);
3614         } else {
3615             xmlCatalogPrintDebug(
3616                             "Local Resolve: sysID %s\n", sysID);
3617         }
3618     }
3619 
3620     catal = (xmlCatalogEntryPtr) catalogs;
3621     if (catal == NULL)
3622 	return(NULL);
3623     ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
3624     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3625 	return(ret);
3626     return(NULL);
3627 }
3628 
3629 /**
3630  * xmlCatalogLocalResolveURI:
3631  * @catalogs:  a document's list of catalogs
3632  * @URI:  the URI
3633  *
3634  * Do a complete resolution lookup of an URI using a
3635  * document's private catalog list
3636  *
3637  * Returns the URI of the resource or NULL if not found, it must be freed
3638  *      by the caller.
3639  */
3640 xmlChar *
xmlCatalogLocalResolveURI(void * catalogs,const xmlChar * URI)3641 xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
3642     xmlCatalogEntryPtr catal;
3643     xmlChar *ret;
3644 
3645     if (URI == NULL)
3646 	return(NULL);
3647 
3648     if (xmlDebugCatalogs)
3649 	xmlCatalogPrintDebug(
3650 		"Resolve URI %s\n", URI);
3651 
3652     catal = (xmlCatalogEntryPtr) catalogs;
3653     if (catal == NULL)
3654 	return(NULL);
3655     ret = xmlCatalogListXMLResolveURI(catal, URI);
3656     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3657 	return(ret);
3658     return(NULL);
3659 }
3660 
3661 /************************************************************************
3662  *									*
3663  *			Deprecated interfaces				*
3664  *									*
3665  ************************************************************************/
3666 /**
3667  * xmlCatalogGetSystem:
3668  * @sysID:  the system ID string
3669  *
3670  * Try to lookup the catalog reference associated to a system ID
3671  * DEPRECATED, use xmlCatalogResolveSystem()
3672  *
3673  * Returns the resource if found or NULL otherwise.
3674  */
3675 const xmlChar *
xmlCatalogGetSystem(const xmlChar * sysID)3676 xmlCatalogGetSystem(const xmlChar *sysID) {
3677     xmlChar *ret;
3678     static xmlChar result[1000];
3679     static int msg = 0;
3680 
3681     if (!xmlCatalogInitialized)
3682 	xmlInitializeCatalog();
3683 
3684     if (msg == 0) {
3685 	xmlPrintErrorMessage(
3686 		"Use of deprecated xmlCatalogGetSystem() call\n");
3687 	msg++;
3688     }
3689 
3690     if (sysID == NULL)
3691 	return(NULL);
3692 
3693     /*
3694      * Check first the XML catalogs
3695      */
3696     if (xmlDefaultCatalog != NULL) {
3697 	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
3698 	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3699 	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3700 	    result[sizeof(result) - 1] = 0;
3701 	    return(result);
3702 	}
3703     }
3704 
3705     if (xmlDefaultCatalog != NULL)
3706 	return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
3707     return(NULL);
3708 }
3709 
3710 /**
3711  * xmlCatalogGetPublic:
3712  * @pubID:  the public ID string
3713  *
3714  * Try to lookup the catalog reference associated to a public ID
3715  * DEPRECATED, use xmlCatalogResolvePublic()
3716  *
3717  * Returns the resource if found or NULL otherwise.
3718  */
3719 const xmlChar *
xmlCatalogGetPublic(const xmlChar * pubID)3720 xmlCatalogGetPublic(const xmlChar *pubID) {
3721     xmlChar *ret;
3722     static xmlChar result[1000];
3723     static int msg = 0;
3724 
3725     if (!xmlCatalogInitialized)
3726 	xmlInitializeCatalog();
3727 
3728     if (msg == 0) {
3729 	xmlPrintErrorMessage(
3730 		"Use of deprecated xmlCatalogGetPublic() call\n");
3731 	msg++;
3732     }
3733 
3734     if (pubID == NULL)
3735 	return(NULL);
3736 
3737     /*
3738      * Check first the XML catalogs
3739      */
3740     if (xmlDefaultCatalog != NULL) {
3741 	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
3742 	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3743 	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3744 	    result[sizeof(result) - 1] = 0;
3745 	    return(result);
3746 	}
3747     }
3748 
3749     if (xmlDefaultCatalog != NULL)
3750 	return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
3751     return(NULL);
3752 }
3753 
3754 #endif /* LIBXML_CATALOG_ENABLED */
3755