1 #include "relax_ng.h"
2 
3 #include <libxml/parser.h>
4 #include <libxml/relaxng.h>
5 #include <libxml/xmlmemory.h>
6 #include <errno.h>
7 #include <stdlib.h>
8 
9 #include "log.h"
10 
11 #define LOG_MSG_LEN 512
12 
13 static xmlRelaxNGPtr schema;
14 static xmlRelaxNGParserCtxtPtr rngparser;
15 
16 #define VLOG_MSG(level)							\
17 	char log_msg[LOG_MSG_LEN];					\
18 	va_list args;							\
19 	va_start(args, msg);						\
20 	vsnprintf(log_msg, LOG_MSG_LEN, msg, args);			\
21 	va_end(args);							\
22 	pr_val_##level("%s", log_msg);
23 
24 /*
25  * Log callbacks for libxml errors
26  */
27 static void
relax_ng_log_err(void * ctx,const char * msg,...)28 relax_ng_log_err(void *ctx, const char *msg, ...)
29 {
30 	VLOG_MSG(err)
31 }
32 
33 static void
relax_ng_log_warn(void * ctx,const char * msg,...)34 relax_ng_log_warn(void *ctx, const char *msg, ...)
35 {
36 	VLOG_MSG(warn)
37 }
38 
39 static void
relax_ng_log_str_err(void * userData,xmlErrorPtr error)40 relax_ng_log_str_err(void *userData, xmlErrorPtr error)
41 {
42 	char *ptr;
43 
44 	ptr = error->message;
45 	if (ptr[strlen(ptr) - 1] == '\n')
46 		ptr[strlen(ptr) - 1] = '\0';
47 	pr_val_err("%s (at line %d)", ptr, error->line);
48 }
49 
50 /* Initialize global schema to parse RRDP files */
51 int
relax_ng_init(void)52 relax_ng_init(void)
53 {
54 	int error;
55 
56 	xmlInitParser();
57 
58 	rngparser = xmlRelaxNGNewMemParserCtxt(RRDP_V1_RNG, RRDP_V1_RNG_SIZE);
59 	if (rngparser == NULL) {
60 		error = pr_op_err("XML parser init error: xmlRelaxNGNewMemParserCtxt() returned NULL");
61 		goto cleanup_parser;
62 	}
63 
64 	xmlRelaxNGSetParserErrors(rngparser, relax_ng_log_err,
65 	    relax_ng_log_warn, NULL);
66 
67 	schema = xmlRelaxNGParse(rngparser);
68 	if (schema == NULL) {
69 		error = pr_op_err("XML parser init error: xmlRelaxNGParse() returned NULL");
70 		goto free_parser_ctx;
71 	}
72 
73 	return 0;
74 free_parser_ctx:
75 	xmlRelaxNGFreeParserCtxt(rngparser);
76 cleanup_parser:
77 	xmlCleanupParser();
78 	return error;
79 }
80 
81 /*
82  * Validate file at @path against globally loaded schema. The file must be
83  * parsed using @cb (will receive @arg as argument).
84  */
85 int
relax_ng_parse(const char * path,xml_read_cb cb,void * arg)86 relax_ng_parse(const char *path, xml_read_cb cb, void *arg)
87 {
88 	xmlTextReaderPtr reader;
89 	xmlRelaxNGValidCtxtPtr rngvalidctx;
90 	int read;
91 	int error;
92 
93 	reader = xmlNewTextReaderFilename(path);
94 	if (reader == NULL)
95 		return pr_val_err("Couldn't get XML '%s' file.", path);
96 
97 	error = xmlTextReaderRelaxNGSetSchema(reader, schema);
98 	if (error) {
99 		error = pr_val_err("Couldn't set Relax NG schema.");
100 		goto free_reader;
101 	}
102 
103 	rngvalidctx = xmlRelaxNGNewValidCtxt(schema);
104 	if (rngvalidctx == NULL) {
105 		error = pr_val_err("xmlRelaxNGNewValidCtxt() returned NULL");
106 		goto free_reader;
107 	}
108 
109 	xmlRelaxNGSetValidErrors(rngvalidctx, relax_ng_log_err,
110 	    relax_ng_log_warn, NULL);
111 
112 	error = xmlTextReaderRelaxNGValidateCtxt(reader, rngvalidctx, 0);
113 	if (error) {
114 		error = pr_val_err("Invalid XML document");
115 		goto free_valid_ctx;
116 	}
117 
118 	xmlTextReaderSetStructuredErrorHandler(reader, relax_ng_log_str_err,
119 	    NULL);
120 
121 	while ((read = xmlTextReaderRead(reader)) == 1) {
122 		if (xmlTextReaderIsValid(reader) <= 0) {
123 			error = pr_val_err("XML document isn't valid.");
124 			goto free_valid_ctx;
125 		}
126 
127 		error = cb(reader, arg);
128 		if (error)
129 			goto free_valid_ctx;
130 	}
131 
132 	if (read < 0) {
133 		error = pr_val_err("Error parsing XML document.");
134 		goto free_valid_ctx;
135 	}
136 
137 	if (xmlTextReaderIsValid(reader) <= 0) {
138 		error = pr_val_err("XML document isn't valid.");
139 		goto free_valid_ctx;
140 	}
141 
142 	xmlRelaxNGFreeValidCtxt(rngvalidctx);
143 	xmlFreeTextReader(reader);
144 	return 0;
145 free_valid_ctx:
146 	xmlRelaxNGFreeValidCtxt(rngvalidctx);
147 free_reader:
148 	xmlFreeTextReader(reader);
149 	return error;
150 }
151 
152 void
relax_ng_cleanup(void)153 relax_ng_cleanup(void)
154 {
155 	xmlRelaxNGFree(schema);
156 	xmlRelaxNGFreeParserCtxt(rngparser);
157 	xmlCleanupParser();
158 }
159