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