1 /*
2  * Canonical XML implementation test program
3  * (http://www.w3.org/TR/2001/REC-xml-c14n-20010315)
4  *
5  * See Copyright for the status of this software.
6  *
7  * Author: Aleksey Sanin <aleksey@aleksey.com>
8  */
9 #include "libxml.h"
10 #if defined(LIBXML_C14N_ENABLED) && defined(LIBXML_OUTPUT_ENABLED)
11 
12 #include <stdio.h>
13 #include <string.h>
14 #ifndef STDOUT_FILENO
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #else
18 #define STDOUT_FILENO fileno(stdout)
19 #endif /* HAVE_UNISTD_H */
20 #endif
21 #ifdef _WIN32
22 #include <io.h>
23 #endif
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27 
28 #include <libxml/xmlmemory.h>
29 #include <libxml/parser.h>
30 #include <libxml/xpath.h>
31 #include <libxml/xpathInternals.h>
32 
33 #include <libxml/c14n.h>
34 
35 
usage(const char * name)36 static void usage(const char *name) {
37     fprintf(stderr,
38 	"Usage: %s <mode> <xml-file> [<xpath-expr>] [<inclusive-ns-list>]\n",
39 	    name);
40     fprintf(stderr, "where <mode> is one of following:\n");
41     fprintf(stderr,
42 	"--with-comments       \t XML file canonicalization v1.0 w comments \n");
43     fprintf(stderr,
44 	"--without-comments    \t XML file canonicalization v1.0 w/o comments\n");
45     fprintf(stderr,
46 	"--1-1-with-comments       \t XML file canonicalization v1.1 w comments\n");
47     fprintf(stderr,
48 	"--1-1-without-comments    \t XML file canonicalization v1.1 w/o comments\n");
49     fprintf(stderr,
50     "--exc-with-comments   \t Exclusive XML file canonicalization v1.0 w comments\n");
51     fprintf(stderr,
52     "--exc-without-comments\t Exclusive XML file canonicalization v1.0 w/o comments\n");
53 }
54 
55 static xmlXPathObjectPtr
56 load_xpath_expr (xmlDocPtr parent_doc, const char* filename);
57 
58 static xmlChar **parse_list(xmlChar *str);
59 
60 /* static void print_xpath_nodes(xmlNodeSetPtr nodes); */
61 
62 static int
test_c14n(const char * xml_filename,int with_comments,int mode,const char * xpath_filename,xmlChar ** inclusive_namespaces)63 test_c14n(const char* xml_filename, int with_comments, int mode,
64 	const char* xpath_filename, xmlChar **inclusive_namespaces) {
65     xmlDocPtr doc;
66     xmlXPathObjectPtr xpath = NULL;
67     xmlChar *result = NULL;
68     int ret;
69 
70     /*
71      * build an XML tree from a the file; we need to add default
72      * attributes and resolve all character and entities references
73      */
74     xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
75     xmlSubstituteEntitiesDefault(1);
76 
77     doc = xmlReadFile(xml_filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
78     if (doc == NULL) {
79 	fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename);
80 	return(-1);
81     }
82 
83     /*
84      * Check the document is of the right kind
85      */
86     if(xmlDocGetRootElement(doc) == NULL) {
87         fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename);
88 	xmlFreeDoc(doc);
89 	return(-1);
90     }
91 
92     /*
93      * load xpath file if specified
94      */
95     if(xpath_filename) {
96 	xpath = load_xpath_expr(doc, xpath_filename);
97 	if(xpath == NULL) {
98 	    fprintf(stderr,"Error: unable to evaluate xpath expression\n");
99 	    xmlFreeDoc(doc);
100 	    return(-1);
101 	}
102     }
103 
104     /*
105      * Canonical form
106      */
107     /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */
108     ret = xmlC14NDocDumpMemory(doc,
109 	    (xpath) ? xpath->nodesetval : NULL,
110 	    mode, inclusive_namespaces,
111 	    with_comments, &result);
112     if(ret >= 0) {
113 	if(result != NULL) {
114 	    if (write(STDOUT_FILENO, result, ret) == -1) {
115 		fprintf(stderr, "Can't write data\n");
116 	    }
117 	    xmlFree(result);
118 	}
119     } else {
120 	fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret);
121 	if(result != NULL) xmlFree(result);
122 	xmlFreeDoc(doc);
123 	return(-1);
124     }
125 
126     /*
127      * Cleanup
128      */
129     if(xpath != NULL) xmlXPathFreeObject(xpath);
130     xmlFreeDoc(doc);
131 
132     return(ret);
133 }
134 
main(int argc,char ** argv)135 int main(int argc, char **argv) {
136     int ret = -1;
137 
138     /*
139      * Init libxml
140      */
141     xmlInitParser();
142     LIBXML_TEST_VERSION
143 
144     /*
145      * Parse command line and process file
146      */
147     if( argc < 3 ) {
148 	fprintf(stderr, "Error: wrong number of arguments.\n");
149 	usage(argv[0]);
150     } else if(strcmp(argv[1], "--with-comments") == 0) {
151 	ret = test_c14n(argv[2], 1, XML_C14N_1_0, (argc > 3) ? argv[3] : NULL, NULL);
152     } else if(strcmp(argv[1], "--without-comments") == 0) {
153 	ret = test_c14n(argv[2], 0, XML_C14N_1_0, (argc > 3) ? argv[3] : NULL, NULL);
154     } else if(strcmp(argv[1], "--1-1-with-comments") == 0) {
155 	ret = test_c14n(argv[2], 1, XML_C14N_1_1, (argc > 3) ? argv[3] : NULL, NULL);
156     } else if(strcmp(argv[1], "--1-1-without-comments") == 0) {
157 	ret = test_c14n(argv[2], 0, XML_C14N_1_1, (argc > 3) ? argv[3] : NULL, NULL);
158     } else if(strcmp(argv[1], "--exc-with-comments") == 0) {
159 	xmlChar **list;
160 
161 	/* load exclusive namespace from command line */
162 	list = (argc > 4) ? parse_list((xmlChar *)argv[4]) : NULL;
163 	ret = test_c14n(argv[2], 1, XML_C14N_EXCLUSIVE_1_0, (argc > 3) ? argv[3] : NULL, list);
164 	if(list != NULL) xmlFree(list);
165     } else if(strcmp(argv[1], "--exc-without-comments") == 0) {
166 	xmlChar **list;
167 
168 	/* load exclusive namespace from command line */
169 	list = (argc > 4) ? parse_list((xmlChar *)argv[4]) : NULL;
170 	ret = test_c14n(argv[2], 0, XML_C14N_EXCLUSIVE_1_0, (argc > 3) ? argv[3] : NULL, list);
171 	if(list != NULL) xmlFree(list);
172     } else {
173 	fprintf(stderr, "Error: bad option.\n");
174 	usage(argv[0]);
175     }
176 
177     /*
178      * Shutdown libxml
179      */
180     xmlCleanupParser();
181     xmlMemoryDump();
182 
183     return((ret >= 0) ? 0 : 1);
184 }
185 
186 /*
187  * Macro used to grow the current buffer.
188  */
189 #define growBufferReentrant() {						\
190     buffer_size *= 2;							\
191     buffer = (xmlChar **)						\
192 		xmlRealloc(buffer, buffer_size * sizeof(xmlChar*));	\
193     if (buffer == NULL) {						\
194 	perror("realloc failed");					\
195 	return(NULL);							\
196     }									\
197 }
198 
199 static xmlChar **
parse_list(xmlChar * str)200 parse_list(xmlChar *str) {
201     xmlChar **buffer;
202     xmlChar **out = NULL;
203     int buffer_size = 0;
204     int len;
205 
206     if(str == NULL) {
207 	return(NULL);
208     }
209 
210     len = xmlStrlen(str);
211     if((str[0] == '\'') && (str[len - 1] == '\'')) {
212 	str[len - 1] = '\0';
213 	str++;
214     }
215     /*
216      * allocate an translation buffer.
217      */
218     buffer_size = 1000;
219     buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*));
220     if (buffer == NULL) {
221 	perror("malloc failed");
222 	return(NULL);
223     }
224     out = buffer;
225 
226     while(*str != '\0') {
227 	if (out - buffer > buffer_size - 10) {
228 	    int indx = out - buffer;
229 
230 	    growBufferReentrant();
231 	    out = &buffer[indx];
232 	}
233 	(*out++) = str;
234 	while(*str != ',' && *str != '\0') ++str;
235 	if(*str == ',') *(str++) = '\0';
236     }
237     (*out) = NULL;
238     return buffer;
239 }
240 
241 static xmlXPathObjectPtr
load_xpath_expr(xmlDocPtr parent_doc,const char * filename)242 load_xpath_expr (xmlDocPtr parent_doc, const char* filename) {
243     xmlXPathObjectPtr xpath;
244     xmlDocPtr doc;
245     xmlChar *expr;
246     xmlXPathContextPtr ctx;
247     xmlNodePtr node;
248     xmlNsPtr ns;
249 
250     /*
251      * load XPath expr as a file
252      */
253     xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
254     xmlSubstituteEntitiesDefault(1);
255 
256     doc = xmlReadFile(filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
257     if (doc == NULL) {
258 	fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
259 	return(NULL);
260     }
261 
262     /*
263      * Check the document is of the right kind
264      */
265     if(xmlDocGetRootElement(doc) == NULL) {
266         fprintf(stderr,"Error: empty document for file \"%s\"\n", filename);
267 	xmlFreeDoc(doc);
268 	return(NULL);
269     }
270 
271     node = doc->children;
272     while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) {
273 	node = node->next;
274     }
275 
276     if(node == NULL) {
277         fprintf(stderr,"Error: XPath element expected in the file  \"%s\"\n", filename);
278 	xmlFreeDoc(doc);
279 	return(NULL);
280     }
281 
282     expr = xmlNodeGetContent(node);
283     if(expr == NULL) {
284         fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename);
285 	xmlFreeDoc(doc);
286 	return(NULL);
287     }
288 
289     ctx = xmlXPathNewContext(parent_doc);
290     if(ctx == NULL) {
291         fprintf(stderr,"Error: unable to create new context\n");
292         xmlFree(expr);
293         xmlFreeDoc(doc);
294         return(NULL);
295     }
296 
297     /*
298      * Register namespaces
299      */
300     ns = node->nsDef;
301     while(ns != NULL) {
302 	if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) {
303 	    fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href);
304 	    xmlFree(expr);
305 	    xmlXPathFreeContext(ctx);
306 	    xmlFreeDoc(doc);
307 	    return(NULL);
308 	}
309 	ns = ns->next;
310     }
311 
312     /*
313      * Evaluate xpath
314      */
315     xpath = xmlXPathEvalExpression(expr, ctx);
316     if(xpath == NULL) {
317         fprintf(stderr,"Error: unable to evaluate xpath expression\n");
318 	xmlFree(expr);
319         xmlXPathFreeContext(ctx);
320         xmlFreeDoc(doc);
321         return(NULL);
322     }
323 
324     /* print_xpath_nodes(xpath->nodesetval); */
325 
326     xmlFree(expr);
327     xmlXPathFreeContext(ctx);
328     xmlFreeDoc(doc);
329     return(xpath);
330 }
331 
332 /*
333 static void
334 print_xpath_nodes(xmlNodeSetPtr nodes) {
335     xmlNodePtr cur;
336     int i;
337 
338     if(nodes == NULL ){
339 	fprintf(stderr, "Error: no nodes set defined\n");
340 	return;
341     }
342 
343     fprintf(stderr, "Nodes Set:\n-----\n");
344     for(i = 0; i < nodes->nodeNr; ++i) {
345 	if(nodes->nodeTab[i]->type == XML_NAMESPACE_DECL) {
346 	    xmlNsPtr ns;
347 
348 	    ns = (xmlNsPtr)nodes->nodeTab[i];
349 	    cur = (xmlNodePtr)ns->next;
350 	    fprintf(stderr, "namespace \"%s\"=\"%s\" for node %s:%s\n",
351 		    ns->prefix, ns->href,
352 		    (cur->ns) ? cur->ns->prefix : BAD_CAST "", cur->name);
353 	} else if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE) {
354 	    cur = nodes->nodeTab[i];
355 	    fprintf(stderr, "element node \"%s:%s\"\n",
356 		    (cur->ns) ? cur->ns->prefix : BAD_CAST "", cur->name);
357 	} else {
358 	    cur = nodes->nodeTab[i];
359 	    fprintf(stderr, "node \"%s\": type %d\n", cur->name, cur->type);
360 	}
361     }
362 }
363 */
364 
365 #else
366 #include <stdio.h>
main(int argc ATTRIBUTE_UNUSED,char ** argv ATTRIBUTE_UNUSED)367 int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
368     printf("%s : XPath/Canonicalization and output support not compiled in\n", argv[0]);
369     return(0);
370 }
371 #endif /* LIBXML_C14N_ENABLED */
372 
373 
374