xref: /aosp_15_r20/external/cronet/third_party/libxml/src/entities.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 /*
2  * entities.c : implementation for the XML entities handling
3  *
4  * See Copyright for the status of this software.
5  *
6  * [email protected]
7  */
8 
9 /* To avoid EBCDIC trouble when parsing on zOS */
10 #if defined(__MVS__)
11 #pragma convert("ISO8859-1")
12 #endif
13 
14 #define IN_LIBXML
15 #include "libxml.h"
16 
17 #include <string.h>
18 #include <stdlib.h>
19 
20 #include <libxml/xmlmemory.h>
21 #include <libxml/hash.h>
22 #include <libxml/entities.h>
23 #include <libxml/parser.h>
24 #include <libxml/parserInternals.h>
25 #include <libxml/xmlerror.h>
26 #include <libxml/dict.h>
27 #include <libxml/xmlsave.h>
28 
29 #include "private/entities.h"
30 #include "private/error.h"
31 
32 /*
33  * The XML predefined entities.
34  */
35 
36 static xmlEntity xmlEntityLt = {
37     NULL, XML_ENTITY_DECL, BAD_CAST "lt",
38     NULL, NULL, NULL, NULL, NULL, NULL,
39     BAD_CAST "<", BAD_CAST "<", 1,
40     XML_INTERNAL_PREDEFINED_ENTITY,
41     NULL, NULL, NULL, NULL, 0, 0, 0
42 };
43 static xmlEntity xmlEntityGt = {
44     NULL, XML_ENTITY_DECL, BAD_CAST "gt",
45     NULL, NULL, NULL, NULL, NULL, NULL,
46     BAD_CAST ">", BAD_CAST ">", 1,
47     XML_INTERNAL_PREDEFINED_ENTITY,
48     NULL, NULL, NULL, NULL, 0, 0, 0
49 };
50 static xmlEntity xmlEntityAmp = {
51     NULL, XML_ENTITY_DECL, BAD_CAST "amp",
52     NULL, NULL, NULL, NULL, NULL, NULL,
53     BAD_CAST "&", BAD_CAST "&", 1,
54     XML_INTERNAL_PREDEFINED_ENTITY,
55     NULL, NULL, NULL, NULL, 0, 0, 0
56 };
57 static xmlEntity xmlEntityQuot = {
58     NULL, XML_ENTITY_DECL, BAD_CAST "quot",
59     NULL, NULL, NULL, NULL, NULL, NULL,
60     BAD_CAST "\"", BAD_CAST "\"", 1,
61     XML_INTERNAL_PREDEFINED_ENTITY,
62     NULL, NULL, NULL, NULL, 0, 0, 0
63 };
64 static xmlEntity xmlEntityApos = {
65     NULL, XML_ENTITY_DECL, BAD_CAST "apos",
66     NULL, NULL, NULL, NULL, NULL, NULL,
67     BAD_CAST "'", BAD_CAST "'", 1,
68     XML_INTERNAL_PREDEFINED_ENTITY,
69     NULL, NULL, NULL, NULL, 0, 0, 0
70 };
71 
72 /*
73  * xmlFreeEntity : clean-up an entity record.
74  */
75 void
xmlFreeEntity(xmlEntityPtr entity)76 xmlFreeEntity(xmlEntityPtr entity)
77 {
78     xmlDictPtr dict = NULL;
79 
80     if (entity == NULL)
81         return;
82 
83     if (entity->doc != NULL)
84         dict = entity->doc->dict;
85 
86 
87     if ((entity->children) &&
88         (entity == (xmlEntityPtr) entity->children->parent))
89         xmlFreeNodeList(entity->children);
90     if ((entity->name != NULL) &&
91         ((dict == NULL) || (!xmlDictOwns(dict, entity->name))))
92         xmlFree((char *) entity->name);
93     if (entity->ExternalID != NULL)
94         xmlFree((char *) entity->ExternalID);
95     if (entity->SystemID != NULL)
96         xmlFree((char *) entity->SystemID);
97     if (entity->URI != NULL)
98         xmlFree((char *) entity->URI);
99     if (entity->content != NULL)
100         xmlFree((char *) entity->content);
101     if (entity->orig != NULL)
102         xmlFree((char *) entity->orig);
103     xmlFree(entity);
104 }
105 
106 /*
107  * xmlCreateEntity:
108  *
109  * internal routine doing the entity node structures allocations
110  */
111 static xmlEntityPtr
xmlCreateEntity(xmlDocPtr doc,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content)112 xmlCreateEntity(xmlDocPtr doc, const xmlChar *name, int type,
113 	        const xmlChar *ExternalID, const xmlChar *SystemID,
114 	        const xmlChar *content) {
115     xmlEntityPtr ret;
116 
117     ret = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
118     if (ret == NULL)
119 	return(NULL);
120     memset(ret, 0, sizeof(xmlEntity));
121     ret->doc = doc;
122     ret->type = XML_ENTITY_DECL;
123 
124     /*
125      * fill the structure.
126      */
127     ret->etype = (xmlEntityType) type;
128     if ((doc == NULL) || (doc->dict == NULL))
129 	ret->name = xmlStrdup(name);
130     else
131         ret->name = xmlDictLookup(doc->dict, name, -1);
132     if (ret->name == NULL)
133         goto error;
134     if (ExternalID != NULL) {
135         ret->ExternalID = xmlStrdup(ExternalID);
136         if (ret->ExternalID == NULL)
137             goto error;
138     }
139     if (SystemID != NULL) {
140         ret->SystemID = xmlStrdup(SystemID);
141         if (ret->SystemID == NULL)
142             goto error;
143     }
144     if (content != NULL) {
145         ret->length = xmlStrlen(content);
146 	ret->content = xmlStrndup(content, ret->length);
147         if (ret->content == NULL)
148             goto error;
149      } else {
150         ret->length = 0;
151         ret->content = NULL;
152     }
153     ret->URI = NULL; /* to be computed by the layer knowing
154 			the defining entity */
155     ret->orig = NULL;
156 
157     return(ret);
158 
159 error:
160     xmlFreeEntity(ret);
161     return(NULL);
162 }
163 
164 /**
165  * xmlAddEntity:
166  * @doc:  the document
167  * @extSubset:  add to the external or internal subset
168  * @name:  the entity name
169  * @type:  the entity type XML_xxx_yyy_ENTITY
170  * @ExternalID:  the entity external ID if available
171  * @SystemID:  the entity system ID if available
172  * @content:  the entity content
173  * @out:  pointer to resulting entity (optional)
174  *
175  * Register a new entity for this document.
176  *
177  * Returns an xmlParserErrors error code.
178  */
179 int
xmlAddEntity(xmlDocPtr doc,int extSubset,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content,xmlEntityPtr * out)180 xmlAddEntity(xmlDocPtr doc, int extSubset, const xmlChar *name, int type,
181 	  const xmlChar *ExternalID, const xmlChar *SystemID,
182 	  const xmlChar *content, xmlEntityPtr *out) {
183     xmlDtdPtr dtd;
184     xmlDictPtr dict = NULL;
185     xmlEntitiesTablePtr table = NULL;
186     xmlEntityPtr ret, predef;
187     int res;
188 
189     if (out != NULL)
190         *out = NULL;
191     if ((doc == NULL) || (name == NULL))
192 	return(XML_ERR_ARGUMENT);
193     dict = doc->dict;
194 
195     if (extSubset)
196         dtd = doc->extSubset;
197     else
198         dtd = doc->intSubset;
199     if (dtd == NULL)
200         return(XML_DTD_NO_DTD);
201 
202     switch (type) {
203         case XML_INTERNAL_GENERAL_ENTITY:
204         case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
205         case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
206             predef = xmlGetPredefinedEntity(name);
207             if (predef != NULL) {
208                 int valid = 0;
209 
210                 /* 4.6 Predefined Entities */
211                 if ((type == XML_INTERNAL_GENERAL_ENTITY) &&
212                     (content != NULL)) {
213                     int c = predef->content[0];
214 
215                     if (((content[0] == c) && (content[1] == 0)) &&
216                         ((c == '>') || (c == '\'') || (c == '"'))) {
217                         valid = 1;
218                     } else if ((content[0] == '&') && (content[1] == '#')) {
219                         if (content[2] == 'x') {
220                             xmlChar *hex = BAD_CAST "0123456789ABCDEF";
221                             xmlChar ref[] = "00;";
222 
223                             ref[0] = hex[c / 16 % 16];
224                             ref[1] = hex[c % 16];
225                             if (xmlStrcasecmp(&content[3], ref) == 0)
226                                 valid = 1;
227                         } else {
228                             xmlChar ref[] = "00;";
229 
230                             ref[0] = '0' + c / 10 % 10;
231                             ref[1] = '0' + c % 10;
232                             if (xmlStrEqual(&content[2], ref))
233                                 valid = 1;
234                         }
235                     }
236                 }
237                 if (!valid)
238                     return(XML_ERR_REDECL_PREDEF_ENTITY);
239             }
240 	    if (dtd->entities == NULL) {
241 		dtd->entities = xmlHashCreateDict(0, dict);
242                 if (dtd->entities == NULL)
243                     return(XML_ERR_NO_MEMORY);
244             }
245 	    table = dtd->entities;
246 	    break;
247         case XML_INTERNAL_PARAMETER_ENTITY:
248         case XML_EXTERNAL_PARAMETER_ENTITY:
249 	    if (dtd->pentities == NULL) {
250 		dtd->pentities = xmlHashCreateDict(0, dict);
251                 if (dtd->pentities == NULL)
252                     return(XML_ERR_NO_MEMORY);
253             }
254 	    table = dtd->pentities;
255 	    break;
256         case XML_INTERNAL_PREDEFINED_ENTITY:
257 	    return(XML_ERR_ARGUMENT);
258     }
259     ret = xmlCreateEntity(dtd->doc, name, type, ExternalID, SystemID, content);
260     if (ret == NULL)
261         return(XML_ERR_NO_MEMORY);
262 
263     res = xmlHashAdd(table, name, ret);
264     if (res < 0) {
265         xmlFreeEntity(ret);
266         return(XML_ERR_NO_MEMORY);
267     } else if (res == 0) {
268 	/*
269 	 * entity was already defined at another level.
270 	 */
271         xmlFreeEntity(ret);
272 	return(XML_WAR_ENTITY_REDEFINED);
273     }
274 
275     /*
276      * Link it to the DTD
277      */
278     ret->parent = dtd;
279     ret->doc = dtd->doc;
280     if (dtd->last == NULL) {
281 	dtd->children = dtd->last = (xmlNodePtr) ret;
282     } else {
283 	dtd->last->next = (xmlNodePtr) ret;
284 	ret->prev = dtd->last;
285 	dtd->last = (xmlNodePtr) ret;
286     }
287 
288     *out = ret;
289     return(0);
290 }
291 
292 /**
293  * xmlGetPredefinedEntity:
294  * @name:  the entity name
295  *
296  * Check whether this name is an predefined entity.
297  *
298  * Returns NULL if not, otherwise the entity
299  */
300 xmlEntityPtr
xmlGetPredefinedEntity(const xmlChar * name)301 xmlGetPredefinedEntity(const xmlChar *name) {
302     if (name == NULL) return(NULL);
303     switch (name[0]) {
304         case 'l':
305 	    if (xmlStrEqual(name, BAD_CAST "lt"))
306 	        return(&xmlEntityLt);
307 	    break;
308         case 'g':
309 	    if (xmlStrEqual(name, BAD_CAST "gt"))
310 	        return(&xmlEntityGt);
311 	    break;
312         case 'a':
313 	    if (xmlStrEqual(name, BAD_CAST "amp"))
314 	        return(&xmlEntityAmp);
315 	    if (xmlStrEqual(name, BAD_CAST "apos"))
316 	        return(&xmlEntityApos);
317 	    break;
318         case 'q':
319 	    if (xmlStrEqual(name, BAD_CAST "quot"))
320 	        return(&xmlEntityQuot);
321 	    break;
322 	default:
323 	    break;
324     }
325     return(NULL);
326 }
327 
328 /**
329  * xmlAddDtdEntity:
330  * @doc:  the document
331  * @name:  the entity name
332  * @type:  the entity type XML_xxx_yyy_ENTITY
333  * @ExternalID:  the entity external ID if available
334  * @SystemID:  the entity system ID if available
335  * @content:  the entity content
336  *
337  * Register a new entity for this document DTD external subset.
338  *
339  * Returns a pointer to the entity or NULL in case of error
340  */
341 xmlEntityPtr
xmlAddDtdEntity(xmlDocPtr doc,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content)342 xmlAddDtdEntity(xmlDocPtr doc, const xmlChar *name, int type,
343 	        const xmlChar *ExternalID, const xmlChar *SystemID,
344 		const xmlChar *content) {
345     xmlEntityPtr ret;
346 
347     xmlAddEntity(doc, 1, name, type, ExternalID, SystemID, content, &ret);
348     return(ret);
349 }
350 
351 /**
352  * xmlAddDocEntity:
353  * @doc:  the document
354  * @name:  the entity name
355  * @type:  the entity type XML_xxx_yyy_ENTITY
356  * @ExternalID:  the entity external ID if available
357  * @SystemID:  the entity system ID if available
358  * @content:  the entity content
359  *
360  * Register a new entity for this document.
361  *
362  * Returns a pointer to the entity or NULL in case of error
363  */
364 xmlEntityPtr
xmlAddDocEntity(xmlDocPtr doc,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content)365 xmlAddDocEntity(xmlDocPtr doc, const xmlChar *name, int type,
366 	        const xmlChar *ExternalID, const xmlChar *SystemID,
367 	        const xmlChar *content) {
368     xmlEntityPtr ret;
369 
370     xmlAddEntity(doc, 0, name, type, ExternalID, SystemID, content, &ret);
371     return(ret);
372 }
373 
374 /**
375  * xmlNewEntity:
376  * @doc:  the document
377  * @name:  the entity name
378  * @type:  the entity type XML_xxx_yyy_ENTITY
379  * @ExternalID:  the entity external ID if available
380  * @SystemID:  the entity system ID if available
381  * @content:  the entity content
382  *
383  * Create a new entity, this differs from xmlAddDocEntity() that if
384  * the document is NULL or has no internal subset defined, then an
385  * unlinked entity structure will be returned, it is then the responsibility
386  * of the caller to link it to the document later or free it when not needed
387  * anymore.
388  *
389  * Returns a pointer to the entity or NULL in case of error
390  */
391 xmlEntityPtr
xmlNewEntity(xmlDocPtr doc,const xmlChar * name,int type,const xmlChar * ExternalID,const xmlChar * SystemID,const xmlChar * content)392 xmlNewEntity(xmlDocPtr doc, const xmlChar *name, int type,
393 	     const xmlChar *ExternalID, const xmlChar *SystemID,
394 	     const xmlChar *content) {
395     if ((doc != NULL) && (doc->intSubset != NULL)) {
396 	return(xmlAddDocEntity(doc, name, type, ExternalID, SystemID, content));
397     }
398     return(xmlCreateEntity(doc, name, type, ExternalID, SystemID, content));
399 }
400 
401 /**
402  * xmlGetEntityFromTable:
403  * @table:  an entity table
404  * @name:  the entity name
405  * @parameter:  look for parameter entities
406  *
407  * Do an entity lookup in the table.
408  * returns the corresponding parameter entity, if found.
409  *
410  * Returns A pointer to the entity structure or NULL if not found.
411  */
412 static xmlEntityPtr
xmlGetEntityFromTable(xmlEntitiesTablePtr table,const xmlChar * name)413 xmlGetEntityFromTable(xmlEntitiesTablePtr table, const xmlChar *name) {
414     return((xmlEntityPtr) xmlHashLookup(table, name));
415 }
416 
417 /**
418  * xmlGetParameterEntity:
419  * @doc:  the document referencing the entity
420  * @name:  the entity name
421  *
422  * Do an entity lookup in the internal and external subsets and
423  * returns the corresponding parameter entity, if found.
424  *
425  * Returns A pointer to the entity structure or NULL if not found.
426  */
427 xmlEntityPtr
xmlGetParameterEntity(xmlDocPtr doc,const xmlChar * name)428 xmlGetParameterEntity(xmlDocPtr doc, const xmlChar *name) {
429     xmlEntitiesTablePtr table;
430     xmlEntityPtr ret;
431 
432     if (doc == NULL)
433 	return(NULL);
434     if ((doc->intSubset != NULL) && (doc->intSubset->pentities != NULL)) {
435 	table = (xmlEntitiesTablePtr) doc->intSubset->pentities;
436 	ret = xmlGetEntityFromTable(table, name);
437 	if (ret != NULL)
438 	    return(ret);
439     }
440     if ((doc->extSubset != NULL) && (doc->extSubset->pentities != NULL)) {
441 	table = (xmlEntitiesTablePtr) doc->extSubset->pentities;
442 	return(xmlGetEntityFromTable(table, name));
443     }
444     return(NULL);
445 }
446 
447 /**
448  * xmlGetDtdEntity:
449  * @doc:  the document referencing the entity
450  * @name:  the entity name
451  *
452  * Do an entity lookup in the DTD entity hash table and
453  * returns the corresponding entity, if found.
454  * Note: the first argument is the document node, not the DTD node.
455  *
456  * Returns A pointer to the entity structure or NULL if not found.
457  */
458 xmlEntityPtr
xmlGetDtdEntity(xmlDocPtr doc,const xmlChar * name)459 xmlGetDtdEntity(xmlDocPtr doc, const xmlChar *name) {
460     xmlEntitiesTablePtr table;
461 
462     if (doc == NULL)
463 	return(NULL);
464     if ((doc->extSubset != NULL) && (doc->extSubset->entities != NULL)) {
465 	table = (xmlEntitiesTablePtr) doc->extSubset->entities;
466 	return(xmlGetEntityFromTable(table, name));
467     }
468     return(NULL);
469 }
470 
471 /**
472  * xmlGetDocEntity:
473  * @doc:  the document referencing the entity
474  * @name:  the entity name
475  *
476  * Do an entity lookup in the document entity hash table and
477  * returns the corresponding entity, otherwise a lookup is done
478  * in the predefined entities too.
479  *
480  * Returns A pointer to the entity structure or NULL if not found.
481  */
482 xmlEntityPtr
xmlGetDocEntity(const xmlDoc * doc,const xmlChar * name)483 xmlGetDocEntity(const xmlDoc *doc, const xmlChar *name) {
484     xmlEntityPtr cur;
485     xmlEntitiesTablePtr table;
486 
487     if (doc != NULL) {
488 	if ((doc->intSubset != NULL) && (doc->intSubset->entities != NULL)) {
489 	    table = (xmlEntitiesTablePtr) doc->intSubset->entities;
490 	    cur = xmlGetEntityFromTable(table, name);
491 	    if (cur != NULL)
492 		return(cur);
493 	}
494 	if (doc->standalone != 1) {
495 	    if ((doc->extSubset != NULL) &&
496 		(doc->extSubset->entities != NULL)) {
497 		table = (xmlEntitiesTablePtr) doc->extSubset->entities;
498 		cur = xmlGetEntityFromTable(table, name);
499 		if (cur != NULL)
500 		    return(cur);
501 	    }
502 	}
503     }
504     return(xmlGetPredefinedEntity(name));
505 }
506 
507 /*
508  * Macro used to grow the current buffer.
509  */
510 #define growBufferReentrant() {						\
511     xmlChar *tmp;                                                       \
512     size_t new_size = buffer_size * 2;                                  \
513     if (new_size < buffer_size) goto mem_error;                         \
514     tmp = (xmlChar *) xmlRealloc(buffer, new_size);	                \
515     if (tmp == NULL) goto mem_error;                                    \
516     buffer = tmp;							\
517     buffer_size = new_size;						\
518 }
519 
520 /**
521  * xmlEncodeEntitiesInternal:
522  * @doc:  the document containing the string
523  * @input:  A string to convert to XML.
524  * @attr: are we handling an attribute value
525  *
526  * Do a global encoding of a string, replacing the predefined entities
527  * and non ASCII values with their entities and CharRef counterparts.
528  * Contrary to xmlEncodeEntities, this routine is reentrant, and result
529  * must be deallocated.
530  *
531  * Returns A newly allocated string with the substitution done.
532  */
533 static xmlChar *
xmlEncodeEntitiesInternal(xmlDocPtr doc,const xmlChar * input,int attr)534 xmlEncodeEntitiesInternal(xmlDocPtr doc, const xmlChar *input, int attr) {
535     const xmlChar *cur = input;
536     xmlChar *buffer = NULL;
537     xmlChar *out = NULL;
538     size_t buffer_size = 0;
539     int html = 0;
540 
541     if (input == NULL) return(NULL);
542     if (doc != NULL)
543         html = (doc->type == XML_HTML_DOCUMENT_NODE);
544 
545     /*
546      * allocate an translation buffer.
547      */
548     buffer_size = 1000;
549     buffer = (xmlChar *) xmlMalloc(buffer_size);
550     if (buffer == NULL)
551 	return(NULL);
552     out = buffer;
553 
554     while (*cur != '\0') {
555         size_t indx = out - buffer;
556         if (indx + 100 > buffer_size) {
557 
558 	    growBufferReentrant();
559 	    out = &buffer[indx];
560 	}
561 
562 	/*
563 	 * By default one have to encode at least '<', '>', '"' and '&' !
564 	 */
565 	if (*cur == '<') {
566 	    const xmlChar *end;
567 
568 	    /*
569 	     * Special handling of server side include in HTML attributes
570 	     */
571 	    if (html && attr &&
572 	        (cur[1] == '!') && (cur[2] == '-') && (cur[3] == '-') &&
573 	        ((end = xmlStrstr(cur, BAD_CAST "-->")) != NULL)) {
574 	        while (cur != end) {
575 		    *out++ = *cur++;
576 		    indx = out - buffer;
577 		    if (indx + 100 > buffer_size) {
578 			growBufferReentrant();
579 			out = &buffer[indx];
580 		    }
581 		}
582 		*out++ = *cur++;
583 		*out++ = *cur++;
584 		*out++ = *cur++;
585 		continue;
586 	    }
587 	    *out++ = '&';
588 	    *out++ = 'l';
589 	    *out++ = 't';
590 	    *out++ = ';';
591 	} else if (*cur == '>') {
592 	    *out++ = '&';
593 	    *out++ = 'g';
594 	    *out++ = 't';
595 	    *out++ = ';';
596 	} else if (*cur == '&') {
597 	    /*
598 	     * Special handling of &{...} construct from HTML 4, see
599 	     * http://www.w3.org/TR/html401/appendix/notes.html#h-B.7.1
600 	     */
601 	    if (html && attr && (cur[1] == '{') &&
602 	        (strchr((const char *) cur, '}'))) {
603 	        while (*cur != '}') {
604 		    *out++ = *cur++;
605 		    indx = out - buffer;
606 		    if (indx + 100 > buffer_size) {
607 			growBufferReentrant();
608 			out = &buffer[indx];
609 		    }
610 		}
611 		*out++ = *cur++;
612 		continue;
613 	    }
614 	    *out++ = '&';
615 	    *out++ = 'a';
616 	    *out++ = 'm';
617 	    *out++ = 'p';
618 	    *out++ = ';';
619 	} else if (((*cur >= 0x20) && (*cur < 0x80)) ||
620 	    (*cur == '\n') || (*cur == '\t') || ((html) && (*cur == '\r'))) {
621 	    /*
622 	     * default case, just copy !
623 	     */
624 	    *out++ = *cur;
625 	} else if (*cur >= 0x80) {
626 	    if (((doc != NULL) && (doc->encoding != NULL)) || (html)) {
627 		/*
628 		 * Bjørn Reese <[email protected]> provided the patch
629 	        xmlChar xc;
630 	        xc = (*cur & 0x3F) << 6;
631 	        if (cur[1] != 0) {
632 		    xc += *(++cur) & 0x3F;
633 		    *out++ = xc;
634 	        } else
635 		 */
636 		*out++ = *cur;
637 	    } else {
638 		/*
639 		 * We assume we have UTF-8 input.
640 		 * It must match either:
641 		 *   110xxxxx 10xxxxxx
642 		 *   1110xxxx 10xxxxxx 10xxxxxx
643 		 *   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
644 		 * That is:
645 		 *   cur[0] is 11xxxxxx
646 		 *   cur[1] is 10xxxxxx
647 		 *   cur[2] is 10xxxxxx if cur[0] is 111xxxxx
648 		 *   cur[3] is 10xxxxxx if cur[0] is 1111xxxx
649 		 *   cur[0] is not 11111xxx
650 		 */
651 		char buf[13], *ptr;
652 		int val, l;
653 
654                 l = 4;
655                 val = xmlGetUTF8Char(cur, &l);
656                 if (val < 0) {
657 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
658                     fprintf(stderr, "xmlEncodeEntitiesInternal: "
659                             "invalid UTF-8\n");
660                     abort();
661 #endif
662                     val = 0xFFFD;
663                     cur++;
664                 } else {
665                     if (!IS_CHAR(val))
666                         val = 0xFFFD;
667                     cur += l;
668 		}
669 		/*
670 		 * We could do multiple things here. Just save as a char ref
671 		 */
672 		snprintf(buf, sizeof(buf), "&#x%X;", val);
673 		buf[sizeof(buf) - 1] = 0;
674 		ptr = buf;
675 		while (*ptr != 0) *out++ = *ptr++;
676 		continue;
677 	    }
678 	} else if (IS_BYTE_CHAR(*cur)) {
679 	    char buf[11], *ptr;
680 
681 	    snprintf(buf, sizeof(buf), "&#%d;", *cur);
682 	    buf[sizeof(buf) - 1] = 0;
683             ptr = buf;
684 	    while (*ptr != 0) *out++ = *ptr++;
685 	}
686 	cur++;
687     }
688     *out = 0;
689     return(buffer);
690 
691 mem_error:
692     xmlFree(buffer);
693     return(NULL);
694 }
695 
696 /**
697  * xmlEncodeAttributeEntities:
698  * @doc:  the document containing the string
699  * @input:  A string to convert to XML.
700  *
701  * Do a global encoding of a string, replacing the predefined entities
702  * and non ASCII values with their entities and CharRef counterparts for
703  * attribute values.
704  *
705  * Returns A newly allocated string with the substitution done.
706  */
707 xmlChar *
xmlEncodeAttributeEntities(xmlDocPtr doc,const xmlChar * input)708 xmlEncodeAttributeEntities(xmlDocPtr doc, const xmlChar *input) {
709     return xmlEncodeEntitiesInternal(doc, input, 1);
710 }
711 
712 /**
713  * xmlEncodeEntitiesReentrant:
714  * @doc:  the document containing the string
715  * @input:  A string to convert to XML.
716  *
717  * Do a global encoding of a string, replacing the predefined entities
718  * and non ASCII values with their entities and CharRef counterparts.
719  * Contrary to xmlEncodeEntities, this routine is reentrant, and result
720  * must be deallocated.
721  *
722  * Returns A newly allocated string with the substitution done.
723  */
724 xmlChar *
xmlEncodeEntitiesReentrant(xmlDocPtr doc,const xmlChar * input)725 xmlEncodeEntitiesReentrant(xmlDocPtr doc, const xmlChar *input) {
726     return xmlEncodeEntitiesInternal(doc, input, 0);
727 }
728 
729 /**
730  * xmlEncodeSpecialChars:
731  * @doc:  the document containing the string
732  * @input:  A string to convert to XML.
733  *
734  * Do a global encoding of a string, replacing the predefined entities
735  * this routine is reentrant, and result must be deallocated.
736  *
737  * Returns A newly allocated string with the substitution done.
738  */
739 xmlChar *
xmlEncodeSpecialChars(const xmlDoc * doc ATTRIBUTE_UNUSED,const xmlChar * input)740 xmlEncodeSpecialChars(const xmlDoc *doc ATTRIBUTE_UNUSED, const xmlChar *input) {
741     const xmlChar *cur = input;
742     xmlChar *buffer = NULL;
743     xmlChar *out = NULL;
744     size_t buffer_size = 0;
745     if (input == NULL) return(NULL);
746 
747     /*
748      * allocate an translation buffer.
749      */
750     buffer_size = 1000;
751     buffer = (xmlChar *) xmlMalloc(buffer_size);
752     if (buffer == NULL)
753 	return(NULL);
754     out = buffer;
755 
756     while (*cur != '\0') {
757         size_t indx = out - buffer;
758         if (indx + 10 > buffer_size) {
759 
760 	    growBufferReentrant();
761 	    out = &buffer[indx];
762 	}
763 
764 	/*
765 	 * By default one have to encode at least '<', '>', '"' and '&' !
766 	 */
767 	if (*cur == '<') {
768 	    *out++ = '&';
769 	    *out++ = 'l';
770 	    *out++ = 't';
771 	    *out++ = ';';
772 	} else if (*cur == '>') {
773 	    *out++ = '&';
774 	    *out++ = 'g';
775 	    *out++ = 't';
776 	    *out++ = ';';
777 	} else if (*cur == '&') {
778 	    *out++ = '&';
779 	    *out++ = 'a';
780 	    *out++ = 'm';
781 	    *out++ = 'p';
782 	    *out++ = ';';
783 	} else if (*cur == '"') {
784 	    *out++ = '&';
785 	    *out++ = 'q';
786 	    *out++ = 'u';
787 	    *out++ = 'o';
788 	    *out++ = 't';
789 	    *out++ = ';';
790 	} else if (*cur == '\r') {
791 	    *out++ = '&';
792 	    *out++ = '#';
793 	    *out++ = '1';
794 	    *out++ = '3';
795 	    *out++ = ';';
796 	} else {
797 	    /*
798 	     * Works because on UTF-8, all extended sequences cannot
799 	     * result in bytes in the ASCII range.
800 	     */
801 	    *out++ = *cur;
802 	}
803 	cur++;
804     }
805     *out = 0;
806     return(buffer);
807 
808 mem_error:
809     xmlFree(buffer);
810     return(NULL);
811 }
812 
813 /**
814  * xmlCreateEntitiesTable:
815  *
816  * create and initialize an empty entities hash table.
817  * This really doesn't make sense and should be deprecated
818  *
819  * Returns the xmlEntitiesTablePtr just created or NULL in case of error.
820  */
821 xmlEntitiesTablePtr
xmlCreateEntitiesTable(void)822 xmlCreateEntitiesTable(void) {
823     return((xmlEntitiesTablePtr) xmlHashCreate(0));
824 }
825 
826 /**
827  * xmlFreeEntityWrapper:
828  * @entity:  An entity
829  * @name:  its name
830  *
831  * Deallocate the memory used by an entities in the hash table.
832  */
833 static void
xmlFreeEntityWrapper(void * entity,const xmlChar * name ATTRIBUTE_UNUSED)834 xmlFreeEntityWrapper(void *entity, const xmlChar *name ATTRIBUTE_UNUSED) {
835     if (entity != NULL)
836 	xmlFreeEntity((xmlEntityPtr) entity);
837 }
838 
839 /**
840  * xmlFreeEntitiesTable:
841  * @table:  An entity table
842  *
843  * Deallocate the memory used by an entities hash table.
844  */
845 void
xmlFreeEntitiesTable(xmlEntitiesTablePtr table)846 xmlFreeEntitiesTable(xmlEntitiesTablePtr table) {
847     xmlHashFree(table, xmlFreeEntityWrapper);
848 }
849 
850 #ifdef LIBXML_TREE_ENABLED
851 /**
852  * xmlCopyEntity:
853  * @ent:  An entity
854  *
855  * Build a copy of an entity
856  *
857  * Returns the new xmlEntitiesPtr or NULL in case of error.
858  */
859 static void *
xmlCopyEntity(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)860 xmlCopyEntity(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
861     xmlEntityPtr ent = (xmlEntityPtr) payload;
862     xmlEntityPtr cur;
863 
864     cur = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
865     if (cur == NULL)
866 	return(NULL);
867     memset(cur, 0, sizeof(xmlEntity));
868     cur->type = XML_ENTITY_DECL;
869 
870     cur->etype = ent->etype;
871     if (ent->name != NULL) {
872 	cur->name = xmlStrdup(ent->name);
873         if (cur->name == NULL)
874             goto error;
875     }
876     if (ent->ExternalID != NULL) {
877 	cur->ExternalID = xmlStrdup(ent->ExternalID);
878         if (cur->ExternalID == NULL)
879             goto error;
880     }
881     if (ent->SystemID != NULL) {
882 	cur->SystemID = xmlStrdup(ent->SystemID);
883         if (cur->SystemID == NULL)
884             goto error;
885     }
886     if (ent->content != NULL) {
887 	cur->content = xmlStrdup(ent->content);
888         if (cur->content == NULL)
889             goto error;
890     }
891     if (ent->orig != NULL) {
892 	cur->orig = xmlStrdup(ent->orig);
893         if (cur->orig == NULL)
894             goto error;
895     }
896     if (ent->URI != NULL) {
897 	cur->URI = xmlStrdup(ent->URI);
898         if (cur->URI == NULL)
899             goto error;
900     }
901     return(cur);
902 
903 error:
904     xmlFreeEntity(cur);
905     return(NULL);
906 }
907 
908 /**
909  * xmlCopyEntitiesTable:
910  * @table:  An entity table
911  *
912  * Build a copy of an entity table.
913  *
914  * Returns the new xmlEntitiesTablePtr or NULL in case of error.
915  */
916 xmlEntitiesTablePtr
xmlCopyEntitiesTable(xmlEntitiesTablePtr table)917 xmlCopyEntitiesTable(xmlEntitiesTablePtr table) {
918     return(xmlHashCopySafe(table, xmlCopyEntity, xmlFreeEntityWrapper));
919 }
920 #endif /* LIBXML_TREE_ENABLED */
921 
922 #ifdef LIBXML_OUTPUT_ENABLED
923 
924 /**
925  * xmlDumpEntityDecl:
926  * @buf:  An XML buffer.
927  * @ent:  An entity table
928  *
929  * This will dump the content of the entity table as an XML DTD definition
930  */
931 void
xmlDumpEntityDecl(xmlBufferPtr buf,xmlEntityPtr ent)932 xmlDumpEntityDecl(xmlBufferPtr buf, xmlEntityPtr ent) {
933     xmlSaveCtxtPtr save;
934 
935     if ((buf == NULL) || (ent == NULL))
936         return;
937 
938     save = xmlSaveToBuffer(buf, NULL, 0);
939     xmlSaveTree(save, (xmlNodePtr) ent);
940     xmlSaveClose(save);
941 }
942 
943 /**
944  * xmlDumpEntityDeclScan:
945  * @ent:  An entity table
946  * @buf:  An XML buffer.
947  *
948  * When using the hash table scan function, arguments need to be reversed
949  */
950 static void
xmlDumpEntityDeclScan(void * ent,void * buf,const xmlChar * name ATTRIBUTE_UNUSED)951 xmlDumpEntityDeclScan(void *ent, void *buf,
952                       const xmlChar *name ATTRIBUTE_UNUSED) {
953     xmlDumpEntityDecl((xmlBufferPtr) buf, (xmlEntityPtr) ent);
954 }
955 
956 /**
957  * xmlDumpEntitiesTable:
958  * @buf:  An XML buffer.
959  * @table:  An entity table
960  *
961  * This will dump the content of the entity table as an XML DTD definition
962  */
963 void
xmlDumpEntitiesTable(xmlBufferPtr buf,xmlEntitiesTablePtr table)964 xmlDumpEntitiesTable(xmlBufferPtr buf, xmlEntitiesTablePtr table) {
965     xmlHashScan(table, xmlDumpEntityDeclScan, buf);
966 }
967 #endif /* LIBXML_OUTPUT_ENABLED */
968