1 /*
2  *  Functions converting HTSMSGs to/from XML
3  *  Copyright (C) 2008 Andreas Öman
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * XML parser, written according to this spec:
21  *
22  * http://www.w3.org/TR/2006/REC-xml-20060816/
23  *
24  * Parses of UTF-8 and ISO-8859-1 (Latin 1) encoded XML and output as
25  * htsmsg's with UTF-8 encoded payloads
26  *
27  *  Supports:                             Example:
28  *
29  *  Comments                              <!--  a comment               -->
30  *  Processing Instructions               <?xml                          ?>
31  *  CDATA                                 <![CDATA[  <litteraly copied> ]]>
32  *  Label references                      &amp;
33  *  Character references                  &#65;
34  *  Empty tags                            <tagname/>
35  *
36  *
37  *  Not supported:
38  *
39  *  UTF-16 (mandatory by standard)
40  *  Intelligent parsing of <!DOCTYPE>
41  *  Entity declarations
42  *
43  */
44 
45 
46 #include <assert.h>
47 #include <sys/types.h>
48 #include <stdio.h>
49 #include <unistd.h>
50 #include <stdlib.h>
51 #include <string.h>
52 
53 #include "tvheadend.h"
54 
55 #include "htsmsg_xml.h"
56 #include "htsbuf.h"
57 
58 TAILQ_HEAD(cdata_content_queue, cdata_content);
59 
60 LIST_HEAD(xmlns_list, xmlns);
61 
62 typedef struct xmlns {
63   LIST_ENTRY(xmlns) xmlns_global_link;
64   LIST_ENTRY(xmlns) xmlns_scope_link;
65 
66   char *xmlns_prefix;
67   unsigned int xmlns_prefix_len;
68 
69   char *xmlns_norm;
70   unsigned int xmlns_norm_len;
71 
72 } xmlns_t;
73 
74 typedef struct xmlparser {
75   enum {
76     XML_ENCODING_UTF8,
77     XML_ENCODING_8859_1,
78   } xp_encoding;
79 
80   char xp_errmsg[128];
81 
82   int xp_srcdataused;
83 
84   struct xmlns_list xp_namespaces;
85 
86 } xmlparser_t;
87 
88 #define xmlerr(xp, fmt...) \
89  snprintf((xp)->xp_errmsg, sizeof((xp)->xp_errmsg), fmt)
90 
91 
92 typedef struct cdata_content {
93   TAILQ_ENTRY(cdata_content) cc_link;
94   char *cc_start, *cc_end; /* end points to byte AFTER last char */
95   int cc_encoding;
96   char cc_buf[0];
97 } cdata_content_t;
98 
99 static char *htsmsg_xml_parse_cd(xmlparser_t *xp,
100 				 htsmsg_t *parent, char *src);
101 
102 /**
103  *
104  */
105 static void
add_unicode(struct cdata_content_queue * ccq,int c)106 add_unicode(struct cdata_content_queue *ccq, int c)
107 {
108   cdata_content_t *cc;
109   int r;
110 
111   cc = malloc(sizeof(cdata_content_t) + 6);
112   r = put_utf8(cc->cc_buf, c);
113   if(r == 0) {
114     free(cc);
115     return;
116   }
117 
118   cc->cc_encoding = XML_ENCODING_UTF8;
119   TAILQ_INSERT_TAIL(ccq, cc, cc_link);
120   cc->cc_start = cc->cc_buf;
121   cc->cc_end   = cc->cc_buf + r;
122 }
123 
124 /**
125  *
126  */
127 static int
decode_character_reference(char ** src)128 decode_character_reference(char **src)
129 {
130   int v = 0;
131   char c;
132 
133   if(**src == 'x') {
134     /* hexadecimal */
135     (*src)++;
136 
137     /* decimal */
138     while(1) {
139       c = **src;
140       switch(c) {
141       case '0' ... '9':
142 	v = v * 0x10 + c - '0';
143 	break;
144       case 'a' ... 'f':
145 	v = v * 0x10 + c - 'a' + 10;
146 	break;
147       case 'A' ... 'F':
148 	v = v * 0x10 + c - 'A' + 10;
149 	break;
150       case ';':
151 	(*src)++;
152 	return v;
153       default:
154 	return 0;
155       }
156       (*src)++;
157     }
158 
159   } else {
160 
161     /* decimal */
162     while(1) {
163       c = **src;
164       switch(c) {
165       case '0' ... '9':
166 	v = v * 10 + c - '0';
167 	(*src)++;
168 	break;
169       case ';':
170 	(*src)++;
171 	return v;
172       default:
173 	return 0;
174       }
175     }
176   }
177 }
178 
179 /**
180  *
181  */
182 static inline int
is_xmlws(char c)183 is_xmlws(char c)
184 {
185   return c > 0 && c <= 32;
186   //  return c == 32 || c == 9 || c = 10 || c = 13;
187 }
188 
189 
190 /**
191  *
192  */
193 static void
xmlns_destroy(xmlns_t * ns)194 xmlns_destroy(xmlns_t *ns)
195 {
196   LIST_REMOVE(ns, xmlns_global_link);
197   LIST_REMOVE(ns, xmlns_scope_link);
198   free(ns->xmlns_prefix);
199   free(ns->xmlns_norm);
200   free(ns);
201 }
202 
203 /**
204  *
205  */
206 static char *
htsmsg_xml_parse_attrib(xmlparser_t * xp,htsmsg_t * msg,char * src,struct xmlns_list * xmlns_scope_list)207 htsmsg_xml_parse_attrib(xmlparser_t *xp, htsmsg_t *msg, char *src,
208 			struct xmlns_list *xmlns_scope_list)
209 {
210   char *attribname, *payload;
211   int attriblen, payloadlen;
212   char quote;
213   htsmsg_field_t *f;
214   xmlns_t *ns;
215 
216   attribname = src;
217   /* Parse attribute name */
218   while(1) {
219     if(*src == 0) {
220       xmlerr(xp, "Unexpected end of file during attribute name parsing");
221       return NULL;
222     }
223 
224     if(is_xmlws(*src) || *src == '=')
225       break;
226     src++;
227   }
228 
229   attriblen = src - attribname;
230   if(attriblen < 1 || attriblen > 65535) {
231     xmlerr(xp, "Invalid attribute name");
232     return NULL;
233   }
234 
235   while(is_xmlws(*src))
236     src++;
237 
238   if(*src != '=') {
239     xmlerr(xp, "Expected '=' in attribute parsing");
240     return NULL;
241   }
242   src++;
243 
244   while(is_xmlws(*src))
245     src++;
246 
247 
248   /* Parse attribute payload */
249   quote = *src++;
250   if(quote != '"' && quote != '\'') {
251     xmlerr(xp, "Expected ' or \" before attribute value");
252     return NULL;
253   }
254 
255   payload = src;
256   while(1) {
257     if(*src == 0) {
258       xmlerr(xp, "Unexpected end of file during attribute value parsing");
259       return NULL;
260     }
261     if(*src == quote)
262       break;
263     src++;
264   }
265 
266   payloadlen = src - payload;
267   if(payloadlen < 0 || payloadlen > 65535) {
268     xmlerr(xp, "Invalid attribute value");
269     return NULL;
270   }
271 
272   src++;
273   while(is_xmlws(*src))
274     src++;
275 
276   if(xmlns_scope_list != NULL &&
277      attriblen > 6 && !memcmp(attribname, "xmlns:", 6)) {
278 
279     attribname += 6;
280     attriblen  -= 6;
281 
282     ns = malloc(sizeof(xmlns_t));
283 
284     ns->xmlns_prefix = malloc(attriblen + 1);
285     memcpy(ns->xmlns_prefix, attribname, attriblen);
286     ns->xmlns_prefix[attriblen] = 0;
287     ns->xmlns_prefix_len = attriblen;
288 
289     ns->xmlns_norm = malloc(payloadlen + 1);
290     memcpy(ns->xmlns_norm, payload, payloadlen);
291     ns->xmlns_norm[payloadlen] = 0;
292     ns->xmlns_norm_len = payloadlen;
293 
294     LIST_INSERT_HEAD(&xp->xp_namespaces, ns, xmlns_global_link);
295     LIST_INSERT_HEAD(xmlns_scope_list,   ns, xmlns_scope_link);
296     return src;
297   }
298 
299   xp->xp_srcdataused = 1;
300   attribname[attriblen] = 0;
301   payload[payloadlen] = 0;
302 
303   f = htsmsg_field_add(msg, attribname, HMF_STR, 0, 0);
304   f->hmf_str = payload;
305   return src;
306 }
307 
308 /**
309  *
310  */
311 static char *
htsmsg_xml_parse_tag(xmlparser_t * xp,htsmsg_t * parent,char * src)312 htsmsg_xml_parse_tag(xmlparser_t *xp, htsmsg_t *parent, char *src)
313 {
314   htsmsg_t *m, *attrs;
315   struct xmlns_list nslist;
316   char *tagname;
317   int taglen, empty = 0, i;
318   xmlns_t *ns;
319 
320   tagname = src;
321 
322   LIST_INIT(&nslist);
323 
324   while(1) {
325     if(*src == 0) {
326       xmlerr(xp, "Unexpected end of file during tag name parsing");
327       return NULL;
328     }
329     if(is_xmlws(*src) || *src == '>' || *src == '/')
330       break;
331     src++;
332   }
333 
334   taglen = src - tagname;
335   if(taglen < 1 || taglen > 65535) {
336     xmlerr(xp, "Invalid tag name");
337     return NULL;
338   }
339 
340   attrs = htsmsg_create_map();
341 
342   while(1) {
343 
344     while(is_xmlws(*src))
345       src++;
346 
347     if(*src == 0) {
348       htsmsg_destroy(attrs);
349       xmlerr(xp, "Unexpected end of file in tag");
350       return NULL;
351     }
352 
353     if(src[0] == '/' && src[1] == '>') {
354       empty = 1;
355       src += 2;
356       break;
357     }
358 
359     if(*src == '>') {
360       src++;
361       break;
362     }
363 
364     if((src = htsmsg_xml_parse_attrib(xp, attrs, src, &nslist)) == NULL) {
365       htsmsg_destroy(attrs);
366       return NULL;
367     }
368   }
369 
370   m = htsmsg_create_map();
371 
372   if(TAILQ_FIRST(&attrs->hm_fields) != NULL) {
373     htsmsg_add_msg_extname(m, "attrib", attrs);
374   } else {
375     htsmsg_destroy(attrs);
376   }
377 
378   if(!empty)
379     src = htsmsg_xml_parse_cd(xp, m, src);
380 
381   for(i = 0; i < taglen - 1; i++) {
382     if(tagname[i] == ':') {
383 
384       LIST_FOREACH(ns, &xp->xp_namespaces, xmlns_global_link) {
385 	if(ns->xmlns_prefix_len == i &&
386 	   !memcmp(ns->xmlns_prefix, tagname, ns->xmlns_prefix_len)) {
387 
388 	  int llen = taglen - i - 1;
389 	  char *n = malloc(ns->xmlns_norm_len + llen + 1);
390 
391 	  n[ns->xmlns_norm_len + llen] = 0;
392 	  memcpy(n, ns->xmlns_norm, ns->xmlns_norm_len);
393 	  memcpy(n + ns->xmlns_norm_len, tagname + i + 1, llen);
394 	  htsmsg_add_msg(parent, n, m);
395 	  free(n);
396 	  goto done;
397 	}
398       }
399     }
400   }
401 
402   xp->xp_srcdataused = 1;
403   tagname[taglen] = 0;
404   htsmsg_add_msg_extname(parent, tagname, m);
405 
406  done:
407   while((ns = LIST_FIRST(&nslist)) != NULL)
408     xmlns_destroy(ns);
409   return src;
410 }
411 
412 
413 
414 
415 
416 /**
417  *
418  */
419 static char *
htsmsg_xml_parse_pi(xmlparser_t * xp,htsmsg_t * parent,char * src)420 htsmsg_xml_parse_pi(xmlparser_t *xp, htsmsg_t *parent, char *src)
421 {
422   htsmsg_t *attrs;
423   char *s = src;
424   char *piname;
425   int l;
426 
427   while(1) {
428     if(*src == 0) {
429       xmlerr(xp, "Unexpected end of file during parsing of "
430 	         "Processing instructions");
431       return NULL;
432     }
433 
434     if(is_xmlws(*src) || *src == '?')
435       break;
436     src++;
437   }
438 
439   l = src - s;
440   if(l < 1 || l > 65536) {
441     xmlerr(xp, "Invalid 'Processing instructions' name");
442     return NULL;
443   }
444   piname = alloca(l + 1);
445   memcpy(piname, s, l);
446   piname[l] = 0;
447 
448   attrs = htsmsg_create_map();
449 
450   while(1) {
451 
452     while(is_xmlws(*src))
453       src++;
454 
455     if(*src == 0) {
456       htsmsg_destroy(attrs);
457       xmlerr(xp, "Unexpected end of file during parsing of "
458 	     "Processing instructions");
459       return NULL;
460     }
461 
462     if(src[0] == '?' && src[1] == '>') {
463       src += 2;
464       break;
465     }
466 
467     if((src = htsmsg_xml_parse_attrib(xp, attrs, src, NULL)) == NULL) {
468       htsmsg_destroy(attrs);
469       return NULL;
470     }
471   }
472 
473 
474   if(TAILQ_FIRST(&attrs->hm_fields) != NULL && parent != NULL) {
475     htsmsg_add_msg(parent, piname, attrs);
476   } else {
477     htsmsg_destroy(attrs);
478   }
479   return src;
480 }
481 
482 
483 /**
484  *
485  */
486 static char *
xml_parse_comment(xmlparser_t * xp,char * src)487 xml_parse_comment(xmlparser_t *xp, char *src)
488 {
489   /* comment */
490   while(1) {
491     if(*src == 0) { /* EOF inside comment is invalid */
492       xmlerr(xp, "Unexpected end of file inside a comment");
493       return NULL;
494     }
495 
496     if(src[0] == '-' && src[1] == '-' && src[2] == '>')
497       return src + 3;
498     src++;
499   }
500 }
501 
502 /**
503  *
504  */
505 static char *
decode_label_reference(xmlparser_t * xp,struct cdata_content_queue * ccq,char * src)506 decode_label_reference(xmlparser_t *xp,
507 		       struct cdata_content_queue *ccq, char *src)
508 {
509   char *s = src;
510   int l;
511   char *label;
512 
513   while(*src != 0 && *src != ';')
514     src++;
515   if(*src == 0) {
516     xmlerr(xp, "Unexpected end of file during parsing of label reference");
517     return NULL;
518   }
519 
520   l = src - s;
521   if(l < 1 || l > 65535)
522     return NULL;
523   label = alloca(l + 1);
524   memcpy(label, s, l);
525   label[l] = 0;
526   src++;
527 
528   if(!strcmp(label, "amp"))
529     add_unicode(ccq, '&');
530   else if(!strcmp(label, "gt"))
531     add_unicode(ccq, '>');
532   else if(!strcmp(label, "lt"))
533     add_unicode(ccq, '<');
534   else if(!strcmp(label, "apos"))
535     add_unicode(ccq, '\'');
536   else if(!strcmp(label, "quot"))
537     add_unicode(ccq, '"');
538   else {
539     xmlerr(xp, "Unknown label referense: \"&%s;\"\n", label);
540     return NULL;
541   }
542 
543   return src;
544 }
545 
546 /**
547  *
548  */
549 static char *
htsmsg_xml_parse_cd0(xmlparser_t * xp,struct cdata_content_queue * ccq,htsmsg_t * tags,htsmsg_t * pis,char * src,int raw)550 htsmsg_xml_parse_cd0(xmlparser_t *xp,
551 		     struct cdata_content_queue *ccq, htsmsg_t *tags,
552 		     htsmsg_t *pis, char *src, int raw)
553 {
554   cdata_content_t *cc = NULL;
555   int c;
556 
557   while(src != NULL && *src != 0) {
558 
559     if(raw && src[0] == ']' && src[1] == ']' && src[2] == '>') {
560       if(cc != NULL)
561 	cc->cc_end = src;
562       cc = NULL;
563       src += 3;
564       break;
565     }
566 
567     if(*src == '<' && !raw) {
568 
569       if(cc != NULL)
570 	cc->cc_end = src;
571       cc = NULL;
572 
573       src++;
574       if(*src == '?') {
575 	src++;
576 	src = htsmsg_xml_parse_pi(xp, pis, src);
577 	continue;
578       }
579 
580       if(src[0] == '!') {
581 
582 	src++;
583 
584 	if(src[0] == '-' && src[1] == '-') {
585 	  src = xml_parse_comment(xp, src + 2);
586 	  continue;
587 	}
588 
589 	if(!strncmp(src, "[CDATA[", 7)) {
590 	  src += 7;
591 	  src = htsmsg_xml_parse_cd0(xp, ccq, tags, pis, src, 1);
592 	  continue;
593 	}
594 	xmlerr(xp, "Unknown syntatic element: <!%.10s", src);
595 	return NULL;
596       }
597 
598       if(*src == '/') {
599 	/* End-tag */
600 	src++;
601 	while(*src != '>') {
602 	  if(*src == 0) { /* EOF inside endtag */
603 	    xmlerr(xp, "Unexpected end of file inside close tag");
604 	    return NULL;
605 	  }
606 	  src++;
607 	}
608 	src++;
609 	break;
610       }
611 
612       src = htsmsg_xml_parse_tag(xp, tags, src);
613       continue;
614     }
615 
616     if(*src == '&' && !raw) {
617       if(cc != NULL)
618 	cc->cc_end = src;
619       cc = NULL;
620 
621       src++;
622 
623       if(*src == '#') {
624 	src++;
625 	/* Character reference */
626 	if((c = decode_character_reference(&src)) != 0)
627 	  add_unicode(ccq, c);
628 	else {
629 	  xmlerr(xp, "Invalid character reference");
630 	  return NULL;
631 	}
632       } else {
633 	/* Label references */
634 	src = decode_label_reference(xp, ccq, src);
635       }
636       continue;
637     }
638 
639     if(cc == NULL) {
640       if(*src < 32) {
641 	src++;
642 	continue;
643       }
644       cc = malloc(sizeof(cdata_content_t));
645       cc->cc_encoding = xp->xp_encoding;
646       TAILQ_INSERT_TAIL(ccq, cc, cc_link);
647       cc->cc_start = src;
648     }
649     src++;
650   }
651 
652   if(cc != NULL) {
653     assert(src != NULL);
654     cc->cc_end = src;
655   }
656   return src;
657 }
658 
659 /**
660  *
661  */
662 static char *
htsmsg_xml_parse_cd(xmlparser_t * xp,htsmsg_t * parent,char * src)663 htsmsg_xml_parse_cd(xmlparser_t *xp, htsmsg_t *parent, char *src)
664 {
665   struct cdata_content_queue ccq;
666   htsmsg_field_t *f;
667   cdata_content_t *cc;
668   int c = 0, l, y = 0;
669   char *x, *body;
670   htsmsg_t *tags = htsmsg_create_map();
671 
672   TAILQ_INIT(&ccq);
673   src = htsmsg_xml_parse_cd0(xp, &ccq, tags, NULL, src, 0);
674 
675   /* Assemble body */
676 
677   TAILQ_FOREACH(cc, &ccq, cc_link) {
678 
679     switch(cc->cc_encoding) {
680     case XML_ENCODING_UTF8:
681       c += cc->cc_end - cc->cc_start;
682       y++;
683       break;
684 
685     case XML_ENCODING_8859_1:
686       l = 0;
687       for(x = cc->cc_start; x < cc->cc_end; x++)
688 	l += 1 + (*x >= 0x80);
689 
690       c += l;
691       y += 1 + (l != cc->cc_end - cc->cc_start);
692       break;
693     }
694   }
695 
696   if(y == 1 && c > 0) {
697     /* One segment UTF-8 (or 7bit ASCII),
698        use data directly from source */
699 
700     cc = TAILQ_FIRST(&ccq);
701 
702     assert(cc != NULL);
703     assert(TAILQ_NEXT(cc, cc_link) == NULL);
704 
705     f = htsmsg_field_add(parent, "cdata", HMF_STR, 0, 0);
706     f->hmf_str = cc->cc_start;
707     *cc->cc_end = 0;
708     free(cc);
709 
710   } else if(c > 0) {
711     body = malloc(c + 1);
712     c = 0;
713 
714     while((cc = TAILQ_FIRST(&ccq)) != NULL) {
715 
716       switch(cc->cc_encoding) {
717       case XML_ENCODING_UTF8:
718 	l = cc->cc_end - cc->cc_start;
719 	memcpy(body + c, cc->cc_start, l);
720 	c += l;
721 	break;
722 
723       case XML_ENCODING_8859_1:
724 	for(x = cc->cc_start; x < cc->cc_end; x++)
725 	  c += put_utf8(body + c, *x);
726 	break;
727       }
728 
729       TAILQ_REMOVE(&ccq, cc, cc_link);
730       free(cc);
731     }
732     body[c] = 0;
733 
734     f = htsmsg_field_add(parent, "cdata", HMF_STR, HMF_ALLOCED, 0);
735     f->hmf_str = body;
736 
737   } else {
738 
739     while((cc = TAILQ_FIRST(&ccq)) != NULL) {
740       TAILQ_REMOVE(&ccq, cc, cc_link);
741       free(cc);
742     }
743   }
744 
745 
746   if(src == NULL) {
747     htsmsg_destroy(tags);
748     return NULL;
749   }
750 
751   if(TAILQ_FIRST(&tags->hm_fields) != NULL) {
752     htsmsg_add_msg_extname(parent, "tags", tags);
753   } else {
754     htsmsg_destroy(tags);
755   }
756 
757   return src;
758 }
759 
760 
761 /**
762  *
763  */
764 static char *
htsmsg_parse_prolog(xmlparser_t * xp,char * src)765 htsmsg_parse_prolog(xmlparser_t *xp, char *src)
766 {
767   htsmsg_t *pis = htsmsg_create_map();
768   htsmsg_t *xmlpi;
769   const char *encoding;
770 
771   while(src != NULL && *src != 0) {
772 
773     while(is_xmlws(*src))
774       src++;
775 
776     if(!strncmp(src, "<?", 2)) {
777       src += 2;
778       src = htsmsg_xml_parse_pi(xp, pis, src);
779       continue;
780     }
781 
782     if(!strncmp(src, "<!--", 4)) {
783       src = xml_parse_comment(xp, src + 4);
784       continue;
785     }
786 
787     if(!strncmp(src, "<!DOCTYPE", 9)) {
788       while(*src != 0) {
789 	if(*src == '>') {
790 	  src++;
791 	  break;
792 	}
793 	src++;
794       }
795       continue;
796     }
797     break;
798   }
799 
800   if((xmlpi = htsmsg_get_map(pis, "xml")) != NULL) {
801 
802     if((encoding = htsmsg_get_str(xmlpi, "encoding")) != NULL) {
803       if(!strcasecmp(encoding, "iso-8859-1") ||
804 	 !strcasecmp(encoding, "iso-8859_1") ||
805 	 !strcasecmp(encoding, "iso_8859-1") ||
806 	 !strcasecmp(encoding, "iso_8859_1")) {
807 	xp->xp_encoding = XML_ENCODING_8859_1;
808       }
809     }
810   }
811 
812   htsmsg_destroy(pis);
813 
814   return src;
815 }
816 
817 
818 
819 /**
820  *
821  */
822 htsmsg_t *
htsmsg_xml_deserialize(char * src,char * errbuf,size_t errbufsize)823 htsmsg_xml_deserialize(char *src, char *errbuf, size_t errbufsize)
824 {
825   htsmsg_t *m;
826   xmlparser_t xp;
827   char *src0 = src;
828   int i;
829 
830   memset(&xp, 0, sizeof(xp));
831   xp.xp_encoding = XML_ENCODING_UTF8;
832   LIST_INIT(&xp.xp_namespaces);
833 
834   /* check for UTF-8 BOM */
835   if(src[0] == 0xef && src[1] == 0xbb && src[2] == 0xbf)
836     memmove(src, src + 3, strlen(src) - 2);
837 
838   if((src = htsmsg_parse_prolog(&xp, src)) == NULL)
839     goto err;
840 
841   m = htsmsg_create_map();
842 
843   if(htsmsg_xml_parse_cd(&xp, m, src) == NULL) {
844     htsmsg_destroy(m);
845     goto err;
846   }
847 
848   if(xp.xp_srcdataused) {
849     m->hm_data = src0;
850     m->hm_data_size = strlen(src0) + 1;
851   } else {
852     free(src0);
853   }
854 
855   return m;
856 
857  err:
858   free(src0);
859   snprintf(errbuf, errbufsize, "%s", xp.xp_errmsg);
860 
861   /* Remove any odd chars inside of errmsg */
862   for(i = 0; i < errbufsize; i++) {
863     if(errbuf[i] < 32) {
864       errbuf[i] = 0;
865       break;
866     }
867   }
868 
869   return NULL;
870 }
871 
872 /*
873  * Get cdata string field
874  */
875 const char *
htsmsg_xml_get_cdata_str(htsmsg_t * tags,const char * name)876 htsmsg_xml_get_cdata_str(htsmsg_t *tags, const char *name)
877 {
878   htsmsg_t *sub;
879   if((sub = htsmsg_get_map(tags, name)) == NULL)
880     return NULL;
881   return htsmsg_get_str(sub, "cdata");
882 }
883 
884 /*
885  * Get cdata u32 field
886  */
887 int
htsmsg_xml_get_cdata_u32(htsmsg_t * tags,const char * name,uint32_t * u32)888 htsmsg_xml_get_cdata_u32(htsmsg_t *tags, const char *name, uint32_t *u32)
889 {
890   htsmsg_t *sub;
891   if((sub = htsmsg_get_map(tags, name)) == NULL)
892     return HTSMSG_ERR_FIELD_NOT_FOUND;
893   return htsmsg_get_u32(sub, "cdata", u32);
894 }
895 
896 /*
897  * Get tag attribute
898  */
899 const char *
htsmsg_xml_get_attr_str(htsmsg_t * tag,const char * name)900 htsmsg_xml_get_attr_str ( htsmsg_t *tag, const char *name )
901 {
902   htsmsg_t *attr = htsmsg_get_map(tag, "attrib");
903   if (attr) return htsmsg_get_str(attr, name);
904   return NULL;
905 }
906 
907 int
htsmsg_xml_get_attr_u32(htsmsg_t * tag,const char * name,uint32_t * ret)908 htsmsg_xml_get_attr_u32 ( htsmsg_t *tag, const char *name, uint32_t *ret )
909 {
910   htsmsg_t *attr = htsmsg_get_map(tag, "attrib");
911   if (attr) return htsmsg_get_u32(attr, name, ret);
912   return HTSMSG_ERR_FIELD_NOT_FOUND;
913 }
914