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