2 * XML Security Library (http://www.aleksey.com/xmlsec).
4 * XML Parser transform and utility functions.
6 * This is free software; see Copyright file in the source
7 * distribution for preciese wording.
9 * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
16 #include <libxml/tree.h>
17 #include <libxml/parser.h>
18 #include <libxml/parserInternals.h>
20 #include <xmlsec/xmlsec.h>
21 #include <xmlsec/xmltree.h>
22 #include <xmlsec/keys.h>
23 #include <xmlsec/transforms.h>
24 #include <xmlsec/parser.h>
25 #include <xmlsec/errors.h>
27 /**************************************************************************
31 *****************************************************************************/
32 typedef struct _xmlSecParserCtx xmlSecParserCtx,
34 struct _xmlSecParserCtx {
35 xmlParserCtxtPtr parserCtx;
38 /**************************************************************************
40 * XML Parser transform
42 * xmlSecParserCtx is located after xmlSecTransform
44 ***************************************************************************/
45 #define xmlSecParserSize \
46 (sizeof(xmlSecTransform) + sizeof(xmlSecParserCtx))
47 #define xmlSecParserGetCtx(transform) \
48 ((xmlSecTransformCheckSize((transform), xmlSecParserSize)) ? \
49 ((xmlSecParserCtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform))) : \
50 (xmlSecParserCtxPtr)NULL)
52 static int xmlSecParserInitialize (xmlSecTransformPtr transform);
53 static void xmlSecParserFinalize (xmlSecTransformPtr transform);
54 static int xmlSecParserPushBin (xmlSecTransformPtr transform,
55 const xmlSecByte* data,
58 xmlSecTransformCtxPtr transformCtx);
59 static int xmlSecParserPopXml (xmlSecTransformPtr transform,
60 xmlSecNodeSetPtr* nodes,
61 xmlSecTransformCtxPtr transformCtx);
63 static xmlSecTransformKlass xmlSecParserKlass = {
64 /* klass/object sizes */
65 sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
66 xmlSecParserSize, /* xmlSecSize objSize */
68 BAD_CAST "xml-parser", /* const xmlChar* name; */
69 NULL, /* const xmlChar* href; */
70 xmlSecTransformUsageDSigTransform, /* xmlSecTransformUsage usage; */
72 xmlSecParserInitialize, /* xmlSecTransformInitializeMethod initialize; */
73 xmlSecParserFinalize, /* xmlSecTransformFinalizeMethod finalize; */
74 NULL, /* xmlSecTransformNodeReadMethod readNode; */
75 NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
76 NULL, /* xmlSecTransformSetKeyReqMethod setKeyReq; */
77 NULL, /* xmlSecTransformSetKeyMethod setKey; */
78 NULL, /* xmlSecTransformValidateMethod validate; */
79 xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
80 xmlSecParserPushBin, /* xmlSecTransformPushBinMethod pushBin; */
81 NULL, /* xmlSecTransformPopBinMethod popBin; */
82 NULL, /* xmlSecTransformPushXmlMethod pushXml; */
83 xmlSecParserPopXml, /* xmlSecTransformPopXmlMethod popXml; */
84 NULL, /* xmlSecTransformExecuteMethod execute; */
86 NULL, /* void* reserved0; */
87 NULL, /* void* reserved1; */
91 * xmlSecTransformXmlParserGetKlass:
93 * The XML parser transform.
95 * Returns: XML parser transform klass.
98 xmlSecTransformXmlParserGetKlass(void) {
99 return(&xmlSecParserKlass);
103 xmlSecParserInitialize(xmlSecTransformPtr transform) {
104 xmlSecParserCtxPtr ctx;
106 xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId), -1);
107 xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecParserSize), -1);
109 ctx = xmlSecParserGetCtx(transform);
110 xmlSecAssert2(ctx != NULL, -1);
112 /* initialize context */
113 memset(ctx, 0, sizeof(xmlSecParserCtx));
118 xmlSecParserFinalize(xmlSecTransformPtr transform) {
119 xmlSecParserCtxPtr ctx;
121 xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId));
122 xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecParserSize));
124 ctx = xmlSecParserGetCtx(transform);
125 xmlSecAssert(ctx != NULL);
127 if(ctx->parserCtx != NULL) {
128 xmlFreeParserCtxt(ctx->parserCtx);
130 memset(ctx, 0, sizeof(xmlSecParserCtx));
134 xmlSecParserPushBin(xmlSecTransformPtr transform, const xmlSecByte* data,
135 xmlSecSize dataSize, int final, xmlSecTransformCtxPtr transformCtx) {
136 xmlSecParserCtxPtr ctx;
139 xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId), -1);
140 xmlSecAssert2(transformCtx != NULL, -1);
142 ctx = xmlSecParserGetCtx(transform);
143 xmlSecAssert2(ctx != NULL, -1);
145 /* check/update current transform status */
146 if(transform->status == xmlSecTransformStatusNone) {
147 xmlSecAssert2(ctx->parserCtx == NULL, -1);
149 ctx->parserCtx = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
150 if(ctx->parserCtx == NULL) {
151 xmlSecError(XMLSEC_ERRORS_HERE,
152 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
153 "xmlCreatePushParserCtxt",
154 XMLSEC_ERRORS_R_XML_FAILED,
155 XMLSEC_ERRORS_NO_MESSAGE);
159 /* required for c14n! */
160 ctx->parserCtx->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
161 ctx->parserCtx->replaceEntities = 1;
163 transform->status = xmlSecTransformStatusWorking;
164 } else if(transform->status == xmlSecTransformStatusFinished) {
166 } else if(transform->status != xmlSecTransformStatusWorking) {
167 xmlSecError(XMLSEC_ERRORS_HERE,
168 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
170 XMLSEC_ERRORS_R_INVALID_STATUS,
171 "status=%d", transform->status);
174 xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1);
175 xmlSecAssert2(ctx->parserCtx != NULL, -1);
177 /* push data to the input buffer */
178 if((data != NULL) && (dataSize > 0)) {
179 ret = xmlParseChunk(ctx->parserCtx, (const char*)data, dataSize, 0);
181 xmlSecError(XMLSEC_ERRORS_HERE,
182 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
184 XMLSEC_ERRORS_R_XML_FAILED,
185 "size=%d", dataSize);
190 /* finish parsing and push to next in the chain */
192 ret = xmlParseChunk(ctx->parserCtx, NULL, 0, 1);
193 if((ret != 0) || (ctx->parserCtx->myDoc == NULL)) {
194 xmlSecError(XMLSEC_ERRORS_HERE,
195 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
197 XMLSEC_ERRORS_R_XML_FAILED,
198 XMLSEC_ERRORS_NO_MESSAGE);
202 /* todo: check that document is well formed? */
203 transform->outNodes = xmlSecNodeSetCreate(ctx->parserCtx->myDoc,
204 NULL, xmlSecNodeSetTree);
205 if(transform->outNodes == NULL) {
206 xmlSecError(XMLSEC_ERRORS_HERE,
207 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
208 "xmlSecNodeSetCreate",
209 XMLSEC_ERRORS_R_XMLSEC_FAILED,
210 XMLSEC_ERRORS_NO_MESSAGE);
211 xmlFreeDoc(ctx->parserCtx->myDoc);
212 ctx->parserCtx->myDoc = NULL;
215 xmlSecNodeSetDocDestroy(transform->outNodes); /* this node set "owns" the doc pointer */
216 ctx->parserCtx->myDoc = NULL;
218 /* push result to the next transform (if exist) */
219 if(transform->next != NULL) {
220 ret = xmlSecTransformPushXml(transform->next, transform->outNodes, transformCtx);
222 xmlSecError(XMLSEC_ERRORS_HERE,
223 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
224 "xmlSecTransformPushXml",
225 XMLSEC_ERRORS_R_XMLSEC_FAILED,
226 XMLSEC_ERRORS_NO_MESSAGE);
231 transform->status = xmlSecTransformStatusFinished;
238 xmlSecParserPopXml(xmlSecTransformPtr transform, xmlSecNodeSetPtr* nodes,
239 xmlSecTransformCtxPtr transformCtx) {
240 xmlSecParserCtxPtr ctx;
241 xmlParserInputBufferPtr buf;
242 xmlParserInputPtr input;
243 xmlParserCtxtPtr ctxt;
247 xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXmlParserId), -1);
248 xmlSecAssert2(nodes != NULL, -1);
249 xmlSecAssert2(transformCtx != NULL, -1);
251 ctx = xmlSecParserGetCtx(transform);
252 xmlSecAssert2(ctx != NULL, -1);
254 /* check/update current transform status */
255 switch(transform->status) {
256 case xmlSecTransformStatusNone:
257 transform->status = xmlSecTransformStatusWorking;
259 case xmlSecTransformStatusWorking:
260 /* just do nothing */
262 case xmlSecTransformStatusFinished:
266 xmlSecError(XMLSEC_ERRORS_HERE,
267 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
269 XMLSEC_ERRORS_R_INVALID_STATUS,
270 "status=%d", transform->status);
273 xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1);
275 /* prepare parser context */
276 if(transform->prev == NULL) {
277 xmlSecError(XMLSEC_ERRORS_HERE,
278 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
280 XMLSEC_ERRORS_R_INVALID_TRANSFORM,
281 "prev transform is null");
285 buf = xmlSecTransformCreateInputBuffer(transform->prev, transformCtx);
287 xmlSecError(XMLSEC_ERRORS_HERE,
288 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
289 "xmlSecTransformCreateInputBuffer",
290 XMLSEC_ERRORS_R_XMLSEC_FAILED,
291 XMLSEC_ERRORS_NO_MESSAGE);
295 ctxt = xmlNewParserCtxt();
297 xmlSecError(XMLSEC_ERRORS_HERE,
298 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
300 XMLSEC_ERRORS_R_XML_FAILED,
301 XMLSEC_ERRORS_NO_MESSAGE);
302 xmlFreeParserInputBuffer(buf);
306 input = xmlNewIOInputStream(ctxt, buf, XML_CHAR_ENCODING_NONE);
308 xmlSecError(XMLSEC_ERRORS_HERE,
309 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
311 XMLSEC_ERRORS_R_XML_FAILED,
312 XMLSEC_ERRORS_NO_MESSAGE);
313 xmlFreeParserCtxt(ctxt);
314 xmlFreeParserInputBuffer(buf);
318 ret = inputPush(ctxt, input);
320 xmlSecError(XMLSEC_ERRORS_HERE,
321 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
323 XMLSEC_ERRORS_R_XML_FAILED,
324 XMLSEC_ERRORS_NO_MESSAGE);
325 xmlFreeInputStream(input);
326 xmlFreeParserCtxt(ctxt);
330 /* required for c14n! */
331 ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
332 ctxt->replaceEntities = 1;
334 /* finaly do the parsing */
335 ret = xmlParseDocument(ctxt);
337 xmlSecError(XMLSEC_ERRORS_HERE,
338 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
340 XMLSEC_ERRORS_R_XML_FAILED,
341 XMLSEC_ERRORS_NO_MESSAGE);
342 if(ctxt->myDoc != NULL) {
343 xmlFreeDoc(ctxt->myDoc);
346 xmlFreeParserCtxt(ctxt);
350 /* remember the result and free parsing context */
353 xmlFreeParserCtxt(ctxt);
355 /* return result to the caller */
356 (*nodes) = xmlSecNodeSetCreate(doc, NULL, xmlSecNodeSetTree);
357 if((*nodes) == NULL) {
358 xmlSecError(XMLSEC_ERRORS_HERE,
359 xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
360 "xmlSecNodeSetCreate",
361 XMLSEC_ERRORS_R_XMLSEC_FAILED,
362 XMLSEC_ERRORS_NO_MESSAGE);
366 xmlSecNodeSetDocDestroy((*nodes)); /* this node set "owns" the doc pointer */
367 transform->status = xmlSecTransformStatusFinished;
371 /**************************************************************************
373 * XML Parser functions
375 *************************************************************************/
376 typedef struct _xmlSecExtMemoryParserCtx {
377 const xmlSecByte *prefix;
378 xmlSecSize prefixSize;
379 const xmlSecByte *buffer;
380 xmlSecSize bufferSize;
381 const xmlSecByte *postfix;
382 xmlSecSize postfixSize;
383 } xmlSecExtMemoryParserCtx, *xmlSecExtMemoryParserCtxPtr;
387 * @filename: the filename.
389 * Loads XML Doc from file @filename. We need a special version because of
390 * c14n issue. The code is copied from xmlSAXParseFileWithData() function.
392 * Returns: pointer to the loaded XML document or NULL if an error occurs.
395 xmlSecParseFile(const char *filename) {
397 xmlParserCtxtPtr ctxt;
398 char *directory = NULL;
400 xmlSecAssert2(filename != NULL, NULL);
403 ctxt = xmlCreateFileParserCtxt(filename);
408 /* todo: set directories from current doc? */
409 if ((ctxt->directory == NULL) && (directory == NULL))
410 directory = xmlParserGetDirectory(filename);
411 if ((ctxt->directory == NULL) && (directory != NULL))
412 ctxt->directory = (char *) xmlStrdup((xmlChar *) directory);
414 /* required for c14n! */
415 ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
416 ctxt->replaceEntities = 1;
418 xmlParseDocument(ctxt);
420 if(ctxt->wellFormed) {
424 xmlFreeDoc(ctxt->myDoc);
427 xmlFreeParserCtxt(ctxt);
433 * xmlSecParseMemoryExt:
434 * @prefix: the first part of the input.
435 * @prefixSize: the size of the first part of the input.
436 * @buffer: the second part of the input.
437 * @bufferSize: the size of the second part of the input.
438 * @postfix: the third part of the input.
439 * @postfixSize: the size of the third part of the input.
441 * Loads XML Doc from 3 chunks of memory: @prefix, @buffer and @postfix.
443 * Returns: pointer to the loaded XML document or NULL if an error occurs.
446 xmlSecParseMemoryExt(const xmlSecByte *prefix, xmlSecSize prefixSize,
447 const xmlSecByte *buffer, xmlSecSize bufferSize,
448 const xmlSecByte *postfix, xmlSecSize postfixSize) {
449 xmlParserCtxtPtr ctxt = NULL;
450 xmlDocPtr doc = NULL;
454 ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
456 xmlSecError(XMLSEC_ERRORS_HERE,
458 "xmlCreatePushParserCtxt",
459 XMLSEC_ERRORS_R_XML_FAILED,
460 XMLSEC_ERRORS_NO_MESSAGE);
464 /* required for c14n! */
465 ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
466 ctxt->replaceEntities = 1;
469 if((prefix != NULL) && (prefixSize > 0)) {
470 ret = xmlParseChunk(ctxt, (const char*)prefix, prefixSize, 0);
472 xmlSecError(XMLSEC_ERRORS_HERE,
475 XMLSEC_ERRORS_R_XML_FAILED,
476 "prefixSize=%d", prefixSize);
482 if((buffer != NULL) && (bufferSize > 0)) {
483 ret = xmlParseChunk(ctxt, (const char*)buffer, bufferSize, 0);
485 xmlSecError(XMLSEC_ERRORS_HERE,
488 XMLSEC_ERRORS_R_XML_FAILED,
489 "bufferSize=%d", bufferSize);
495 if((postfix != NULL) && (postfixSize > 0)) {
496 ret = xmlParseChunk(ctxt, (const char*)postfix, postfixSize, 0);
498 xmlSecError(XMLSEC_ERRORS_HERE,
501 XMLSEC_ERRORS_R_XML_FAILED,
502 "postfixSize=%d", postfixSize);
508 ret = xmlParseChunk(ctxt, NULL, 0, 1);
509 if((ret != 0) || (ctxt->myDoc == NULL)) {
510 xmlSecError(XMLSEC_ERRORS_HERE,
513 XMLSEC_ERRORS_R_XML_FAILED,
514 XMLSEC_ERRORS_NO_MESSAGE);
521 xmlFreeParserCtxt(ctxt);
529 * @buffer: the input buffer.
530 * @size: the input buffer size.
531 * @recovery: the flag.
533 * Loads XML Doc from memory. We need a special version because of
534 * c14n issue. The code is copied from xmlSAXParseMemory() function.
536 * Returns: pointer to the loaded XML document or NULL if an error occurs.
539 xmlSecParseMemory(const xmlSecByte *buffer, xmlSecSize size, int recovery) {
541 xmlParserCtxtPtr ctxt;
543 xmlSecAssert2(buffer != NULL, NULL);
545 ctxt = xmlCreateMemoryParserCtxt((char*)buffer, size);
547 xmlSecError(XMLSEC_ERRORS_HERE,
549 "xmlCreateMemoryParserCtxt",
550 XMLSEC_ERRORS_R_XML_FAILED,
551 XMLSEC_ERRORS_NO_MESSAGE);
555 /* required for c14n! */
556 ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
557 ctxt->replaceEntities = 1;
559 xmlParseDocument(ctxt);
561 if((ctxt->wellFormed) || recovery) {
565 xmlFreeDoc(ctxt->myDoc);
568 xmlFreeParserCtxt(ctxt);