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