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