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