Paul Cercueil / libxml2

Dependents:   libiio

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers c14n.c Source File

c14n.c

00001 /*
00002  * "Canonical XML" implementation
00003  * http://www.w3.org/TR/xml-c14n
00004  *
00005  * "Exclusive XML Canonicalization" implementation
00006  * http://www.w3.org/TR/xml-exc-c14n
00007  *
00008  * See Copyright for the status of this software.
00009  *
00010  * Author: Aleksey Sanin <aleksey@aleksey.com>
00011  */
00012 #define IN_LIBXML
00013 #include "libxml.h"
00014 #ifdef LIBXML_C14N_ENABLED
00015 #ifdef LIBXML_OUTPUT_ENABLED
00016 
00017 #ifdef HAVE_STDLIB_H
00018 #include <stdlib.h>
00019 #endif
00020 #include <string.h>
00021 
00022 #include <libxml/tree.h>
00023 #include <libxml/parser.h>
00024 #include <libxml/uri.h>
00025 #include <libxml/xmlerror.h>
00026 #include <libxml/globals.h>
00027 #include <libxml/xpathInternals.h>
00028 #include <libxml/c14n.h>
00029 
00030 #include "buf.h"
00031 
00032 /************************************************************************
00033  *                                  *
00034  *      Some declaration better left private ATM        *
00035  *                                  *
00036  ************************************************************************/
00037 
00038 typedef enum {
00039     XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
00040     XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
00041     XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
00042 } xmlC14NPosition;
00043 
00044 typedef struct _xmlC14NVisibleNsStack {
00045     int nsCurEnd;           /* number of nodes in the set */
00046     int nsPrevStart;        /* the begginning of the stack for previous visible node */
00047     int nsPrevEnd;          /* the end of the stack for previous visible node */
00048     int nsMax;              /* size of the array as allocated */
00049     xmlNsPtr    *nsTab;     /* array of ns in no particular order */
00050     xmlNodePtr  *nodeTab;   /* array of nodes in no particular order */
00051 } xmlC14NVisibleNsStack, *xmlC14NVisibleNsStackPtr;
00052 
00053 typedef struct _xmlC14NCtx {
00054     /* input parameters */
00055     xmlDocPtr doc;
00056     xmlC14NIsVisibleCallback is_visible_callback;
00057     void* user_data;
00058     int with_comments;
00059     xmlOutputBufferPtr buf;
00060 
00061     /* position in the XML document */
00062     xmlC14NPosition pos;
00063     int parent_is_doc;
00064     xmlC14NVisibleNsStackPtr ns_rendered;
00065 
00066     /* C14N mode */
00067     xmlC14NMode mode;
00068 
00069     /* exclusive canonicalization */
00070     xmlChar **inclusive_ns_prefixes;
00071 
00072     /* error number */
00073     int error;
00074 } xmlC14NCtx, *xmlC14NCtxPtr;
00075 
00076 static xmlC14NVisibleNsStackPtr xmlC14NVisibleNsStackCreate (void);
00077 static void     xmlC14NVisibleNsStackDestroy    (xmlC14NVisibleNsStackPtr cur);
00078 static void     xmlC14NVisibleNsStackAdd        (xmlC14NVisibleNsStackPtr cur,
00079                                                  xmlNsPtr ns,
00080                                                  xmlNodePtr node);
00081 static void         xmlC14NVisibleNsStackSave   (xmlC14NVisibleNsStackPtr cur,
00082                                  xmlC14NVisibleNsStackPtr state);
00083 static void         xmlC14NVisibleNsStackRestore    (xmlC14NVisibleNsStackPtr cur,
00084                                  xmlC14NVisibleNsStackPtr state);
00085 static void         xmlC14NVisibleNsStackShift  (xmlC14NVisibleNsStackPtr cur);
00086 static int          xmlC14NVisibleNsStackFind   (xmlC14NVisibleNsStackPtr cur,
00087                                  xmlNsPtr ns);
00088 static int          xmlExcC14NVisibleNsStackFind    (xmlC14NVisibleNsStackPtr cur,
00089                                  xmlNsPtr ns,
00090                                  xmlC14NCtxPtr ctx);
00091 
00092 static int          xmlC14NIsNodeInNodeset      (xmlNodeSetPtr nodes,
00093                                  xmlNodePtr node,
00094                                  xmlNodePtr parent);
00095 
00096 
00097 
00098 static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
00099 static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
00100 typedef enum {
00101     XMLC14N_NORMALIZE_ATTR = 0,
00102     XMLC14N_NORMALIZE_COMMENT = 1,
00103     XMLC14N_NORMALIZE_PI = 2,
00104     XMLC14N_NORMALIZE_TEXT = 3
00105 } xmlC14NNormalizationMode;
00106 
00107 static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
00108                                        xmlC14NNormalizationMode mode);
00109 
00110 #define xmlC11NNormalizeAttr( a ) \
00111     xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
00112 #define xmlC11NNormalizeComment( a ) \
00113     xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
00114 #define xmlC11NNormalizePI( a ) \
00115     xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
00116 #define xmlC11NNormalizeText( a ) \
00117     xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
00118 
00119 #define xmlC14NIsVisible( ctx, node, parent ) \
00120      (((ctx)->is_visible_callback != NULL) ? \
00121     (ctx)->is_visible_callback((ctx)->user_data, \
00122         (xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
00123 
00124 #define xmlC14NIsExclusive( ctx ) \
00125     ( (ctx)->mode == XML_C14N_EXCLUSIVE_1_0 )
00126 
00127 /************************************************************************
00128  *                                  *
00129  *      Some factorized error routines              *
00130  *                                  *
00131  ************************************************************************/
00132 
00133 /**
00134  * xmlC14NErrMemory:
00135  * @extra:  extra informations
00136  *
00137  * Handle a redefinition of memory error
00138  */
00139 static void
00140 xmlC14NErrMemory(const char *extra)
00141 {
00142     __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
00143             XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, extra,
00144             NULL, NULL, 0, 0,
00145             "Memory allocation failed : %s\n", extra);
00146 }
00147 
00148 /**
00149  * xmlC14NErrParam:
00150  * @extra:  extra informations
00151  *
00152  * Handle a redefinition of param error
00153  */
00154 static void
00155 xmlC14NErrParam(const char *extra)
00156 {
00157     __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
00158             XML_ERR_INTERNAL_ERROR, XML_ERR_ERROR, NULL, 0, extra,
00159             NULL, NULL, 0, 0,
00160             "Invalid parameter : %s\n", extra);
00161 }
00162 
00163 /**
00164  * xmlC14NErrInternal:
00165  * @extra:  extra informations
00166  *
00167  * Handle a redefinition of internal error
00168  */
00169 static void
00170 xmlC14NErrInternal(const char *extra)
00171 {
00172     __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
00173             XML_ERR_INTERNAL_ERROR, XML_ERR_ERROR, NULL, 0, extra,
00174             NULL, NULL, 0, 0,
00175             "Internal error : %s\n", extra);
00176 }
00177 
00178 /**
00179  * xmlC14NErrInvalidNode:
00180  * @extra:  extra informations
00181  *
00182  * Handle a redefinition of invalid node error
00183  */
00184 static void
00185 xmlC14NErrInvalidNode(const char *node_type, const char *extra)
00186 {
00187     __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
00188             XML_C14N_INVALID_NODE, XML_ERR_ERROR, NULL, 0, extra,
00189             NULL, NULL, 0, 0,
00190             "Node %s is invalid here : %s\n", node_type, extra);
00191 }
00192 
00193 /**
00194  * xmlC14NErrUnknownNode:
00195  * @extra:  extra informations
00196  *
00197  * Handle a redefinition of unknown node error
00198  */
00199 static void
00200 xmlC14NErrUnknownNode(int node_type, const char *extra)
00201 {
00202     __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
00203             XML_C14N_UNKNOW_NODE, XML_ERR_ERROR, NULL, 0, extra,
00204             NULL, NULL, 0, 0,
00205             "Unknown node type %d found : %s\n", node_type, extra);
00206 }
00207 
00208 /**
00209  * xmlC14NErrRelativeNamespace:
00210  * @extra:  extra informations
00211  *
00212  * Handle a redefinition of relative namespace error
00213  */
00214 static void
00215 xmlC14NErrRelativeNamespace(const char *ns_uri)
00216 {
00217     __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
00218             XML_C14N_RELATIVE_NAMESPACE, XML_ERR_ERROR, NULL, 0, NULL,
00219             NULL, NULL, 0, 0,
00220             "Relative namespace UR is invalid here : %s\n", ns_uri);
00221 }
00222 
00223 
00224 
00225 /**
00226  * xmlC14NErr:
00227  * @ctxt:  a C14N evaluation context
00228  * @node:  the context node
00229  * @error:  the erorr code
00230  * @msg:  the message
00231  * @extra:  extra informations
00232  *
00233  * Handle a redefinition of attribute error
00234  */
00235 static void
00236 xmlC14NErr(xmlC14NCtxPtr ctxt, xmlNodePtr node, int error,
00237            const char * msg)
00238 {
00239     if (ctxt != NULL)
00240         ctxt->error = error;
00241     __xmlRaiseError(NULL, NULL, NULL,
00242             ctxt, node, XML_FROM_C14N, error,
00243             XML_ERR_ERROR, NULL, 0,
00244             NULL, NULL, NULL, 0, 0, "%s", msg);
00245 }
00246 
00247 /************************************************************************
00248  *                                  *
00249  *      The implementation internals                *
00250  *                                  *
00251  ************************************************************************/
00252 #define XML_NAMESPACES_DEFAULT      16
00253 
00254 static int
00255 xmlC14NIsNodeInNodeset(xmlNodeSetPtr nodes, xmlNodePtr node, xmlNodePtr parent) {
00256     if((nodes != NULL) && (node != NULL)) {
00257     if(node->type != XML_NAMESPACE_DECL) {
00258         return(xmlXPathNodeSetContains(nodes, node));
00259     } else {
00260         xmlNs ns;
00261 
00262         memcpy(&ns, node, sizeof(ns));
00263 
00264         /* this is a libxml hack! check xpath.c for details */
00265         if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
00266         ns.next = (xmlNsPtr)parent->parent;
00267         } else {
00268         ns.next = (xmlNsPtr)parent;
00269         }
00270 
00271         /*
00272          * If the input is an XPath node-set, then the node-set must explicitly
00273          * contain every node to be rendered to the canonical form.
00274          */
00275         return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns));
00276     }
00277     }
00278     return(1);
00279 }
00280 
00281 static xmlC14NVisibleNsStackPtr
00282 xmlC14NVisibleNsStackCreate(void) {
00283     xmlC14NVisibleNsStackPtr ret;
00284 
00285     ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
00286     if (ret == NULL) {
00287         xmlC14NErrMemory("creating namespaces stack");
00288     return(NULL);
00289     }
00290     memset(ret, 0 , (size_t) sizeof(xmlC14NVisibleNsStack));
00291     return(ret);
00292 }
00293 
00294 static void
00295 xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
00296     if(cur == NULL) {
00297         xmlC14NErrParam("destroying namespaces stack");
00298         return;
00299     }
00300     if(cur->nsTab != NULL) {
00301     memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
00302     xmlFree(cur->nsTab);
00303     }
00304     if(cur->nodeTab != NULL) {
00305     memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
00306     xmlFree(cur->nodeTab);
00307     }
00308     memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
00309     xmlFree(cur);
00310 
00311 }
00312 
00313 static void
00314 xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
00315     if((cur == NULL) ||
00316        ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
00317        ((cur->nsTab != NULL) && (cur->nodeTab == NULL))) {
00318         xmlC14NErrParam("adding namespace to stack");
00319     return;
00320     }
00321 
00322     if ((cur->nsTab == NULL) && (cur->nodeTab == NULL)) {
00323         cur->nsTab = (xmlNsPtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
00324         cur->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
00325     if ((cur->nsTab == NULL) || (cur->nodeTab == NULL)) {
00326         xmlC14NErrMemory("adding node to stack");
00327         return;
00328     }
00329     memset(cur->nsTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
00330     memset(cur->nodeTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
00331         cur->nsMax = XML_NAMESPACES_DEFAULT;
00332     } else if(cur->nsMax == cur->nsCurEnd) {
00333     void *tmp;
00334     int tmpSize;
00335 
00336     tmpSize = 2 * cur->nsMax;
00337     tmp = xmlRealloc(cur->nsTab, tmpSize * sizeof(xmlNsPtr));
00338     if (tmp == NULL) {
00339         xmlC14NErrMemory("adding node to stack");
00340         return;
00341     }
00342     cur->nsTab = (xmlNsPtr*)tmp;
00343 
00344     tmp = xmlRealloc(cur->nodeTab, tmpSize * sizeof(xmlNodePtr));
00345     if (tmp == NULL) {
00346         xmlC14NErrMemory("adding node to stack");
00347         return;
00348     }
00349     cur->nodeTab = (xmlNodePtr*)tmp;
00350 
00351     cur->nsMax = tmpSize;
00352     }
00353     cur->nsTab[cur->nsCurEnd] = ns;
00354     cur->nodeTab[cur->nsCurEnd] = node;
00355 
00356     ++cur->nsCurEnd;
00357 }
00358 
00359 static void
00360 xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
00361     if((cur == NULL) || (state == NULL)) {
00362         xmlC14NErrParam("saving namespaces stack");
00363     return;
00364     }
00365 
00366     state->nsCurEnd = cur->nsCurEnd;
00367     state->nsPrevStart = cur->nsPrevStart;
00368     state->nsPrevEnd = cur->nsPrevEnd;
00369 }
00370 
00371 static void
00372 xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
00373     if((cur == NULL) || (state == NULL)) {
00374         xmlC14NErrParam("restoring namespaces stack");
00375     return;
00376     }
00377     cur->nsCurEnd = state->nsCurEnd;
00378     cur->nsPrevStart = state->nsPrevStart;
00379     cur->nsPrevEnd = state->nsPrevEnd;
00380 }
00381 
00382 static void
00383 xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
00384     if(cur == NULL) {
00385         xmlC14NErrParam("shifting namespaces stack");
00386     return;
00387     }
00388     cur->nsPrevStart = cur->nsPrevEnd;
00389     cur->nsPrevEnd = cur->nsCurEnd;
00390 }
00391 
00392 static int
00393 xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
00394     if (str1 == str2) return(1);
00395     if (str1 == NULL) return((*str2) == '\0');
00396     if (str2 == NULL) return((*str1) == '\0');
00397     do {
00398     if (*str1++ != *str2) return(0);
00399     } while (*str2++);
00400     return(1);
00401 }
00402 
00403 /**
00404  * xmlC14NVisibleNsStackFind:
00405  * @ctx:        the C14N context
00406  * @ns:         the namespace to check
00407  *
00408  * Checks whether the given namespace was already rendered or not
00409  *
00410  * Returns 1 if we already wrote this namespace or 0 otherwise
00411  */
00412 static int
00413 xmlC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns)
00414 {
00415     int i;
00416     const xmlChar *prefix;
00417     const xmlChar *href;
00418     int has_empty_ns;
00419 
00420     if(cur == NULL) {
00421         xmlC14NErrParam("searching namespaces stack (c14n)");
00422         return (0);
00423     }
00424 
00425     /*
00426      * if the default namespace xmlns="" is not defined yet then
00427      * we do not want to print it out
00428      */
00429     prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
00430     href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
00431     has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
00432 
00433     if (cur->nsTab != NULL) {
00434     int start = (has_empty_ns) ? 0 : cur->nsPrevStart;
00435         for (i = cur->nsCurEnd - 1; i >= start; --i) {
00436             xmlNsPtr ns1 = cur->nsTab[i];
00437 
00438         if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
00439         return(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL));
00440         }
00441         }
00442     }
00443     return(has_empty_ns);
00444 }
00445 
00446 static int
00447 xmlExcC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlC14NCtxPtr ctx) {
00448     int i;
00449     const xmlChar *prefix;
00450     const xmlChar *href;
00451     int has_empty_ns;
00452 
00453     if(cur == NULL) {
00454         xmlC14NErrParam("searching namespaces stack (exc c14n)");
00455         return (0);
00456     }
00457 
00458     /*
00459      * if the default namespace xmlns="" is not defined yet then
00460      * we do not want to print it out
00461      */
00462     prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
00463     href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
00464     has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
00465 
00466     if (cur->nsTab != NULL) {
00467     int start = 0;
00468         for (i = cur->nsCurEnd - 1; i >= start; --i) {
00469             xmlNsPtr ns1 = cur->nsTab[i];
00470 
00471         if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
00472         if(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL)) {
00473             return(xmlC14NIsVisible(ctx, ns1, cur->nodeTab[i]));
00474         } else {
00475             return(0);
00476         }
00477         }
00478         }
00479     }
00480     return(has_empty_ns);
00481 }
00482 
00483 
00484 
00485 
00486 /**
00487  * xmlC14NIsXmlNs:
00488  * @ns:     the namespace to check
00489  *
00490  * Checks whether the given namespace is a default "xml:" namespace
00491  * with href="http://www.w3.org/XML/1998/namespace"
00492  *
00493  * Returns 1 if the node is default or 0 otherwise
00494  */
00495 
00496 /* todo: make it a define? */
00497 static int
00498 xmlC14NIsXmlNs(xmlNsPtr ns)
00499 {
00500     return ((ns != NULL) &&
00501             (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
00502             (xmlStrEqual(ns->href, XML_XML_NAMESPACE)));
00503 }
00504 
00505 
00506 /**
00507  * xmlC14NNsCompare:
00508  * @ns1:        the pointer to first namespace
00509  * @ns2:        the pointer to second namespace
00510  *
00511  * Compares the namespaces by names (prefixes).
00512  *
00513  * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
00514  */
00515 static int
00516 xmlC14NNsCompare(xmlNsPtr ns1, xmlNsPtr ns2)
00517 {
00518     if (ns1 == ns2)
00519         return (0);
00520     if (ns1 == NULL)
00521         return (-1);
00522     if (ns2 == NULL)
00523         return (1);
00524 
00525     return (xmlStrcmp(ns1->prefix, ns2->prefix));
00526 }
00527 
00528 
00529 /**
00530  * xmlC14NPrintNamespaces:
00531  * @ns:         the pointer to namespace
00532  * @ctx:        the C14N context
00533  *
00534  * Prints the given namespace to the output buffer from C14N context.
00535  *
00536  * Returns 1 on success or 0 on fail.
00537  */
00538 static int
00539 xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
00540 {
00541 
00542     if ((ns == NULL) || (ctx == NULL)) {
00543         xmlC14NErrParam("writing namespaces");
00544         return 0;
00545     }
00546 
00547     if (ns->prefix != NULL) {
00548         xmlOutputBufferWriteString(ctx->buf, " xmlns:");
00549         xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
00550         xmlOutputBufferWriteString(ctx->buf, "=");
00551     } else {
00552         xmlOutputBufferWriteString(ctx->buf, " xmlns=");
00553     }
00554     if(ns->href != NULL) {
00555     xmlBufWriteQuotedString(ctx->buf->buffer, ns->href);
00556     } else {
00557         xmlOutputBufferWriteString(ctx->buf, "\"\"");
00558     }
00559     return (1);
00560 }
00561 
00562 /**
00563  * xmlC14NProcessNamespacesAxis:
00564  * @ctx:        the C14N context
00565  * @node:       the current node
00566  *
00567  * Prints out canonical namespace axis of the current node to the
00568  * buffer from C14N context as follows
00569  *
00570  * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
00571  *
00572  * Namespace Axis
00573  * Consider a list L containing only namespace nodes in the
00574  * axis and in the node-set in lexicographic order (ascending). To begin
00575  * processing L, if the first node is not the default namespace node (a node
00576  * with no namespace URI and no local name), then generate a space followed
00577  * by xmlns="" if and only if the following conditions are met:
00578  *    - the element E that owns the axis is in the node-set
00579  *    - The nearest ancestor element of E in the node-set has a default
00580  *      namespace node in the node-set (default namespace nodes always
00581  *      have non-empty values in XPath)
00582  * The latter condition eliminates unnecessary occurrences of xmlns="" in
00583  * the canonical form since an element only receives an xmlns="" if its
00584  * default namespace is empty and if it has an immediate parent in the
00585  * canonical form that has a non-empty default namespace. To finish
00586  * processing  L, simply process every namespace node in L, except omit
00587  * namespace node with local name xml, which defines the xml prefix,
00588  * if its string value is http://www.w3.org/XML/1998/namespace.
00589  *
00590  * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
00591  * Canonical XML applied to a document subset requires the search of the
00592  * ancestor nodes of each orphan element node for attributes in the xml
00593  * namespace, such as xml:lang and xml:space. These are copied into the
00594  * element node except if a declaration of the same attribute is already
00595  * in the attribute axis of the element (whether or not it is included in
00596  * the document subset). This search and copying are omitted from the
00597  * Exclusive XML Canonicalization method.
00598  *
00599  * Returns 0 on success or -1 on fail.
00600  */
00601 static int
00602 xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
00603 {
00604     xmlNodePtr n;
00605     xmlNsPtr ns, tmp;
00606     xmlListPtr list;
00607     int already_rendered;
00608     int has_empty_ns = 0;
00609 
00610     if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
00611         xmlC14NErrParam("processing namespaces axis (c14n)");
00612         return (-1);
00613     }
00614 
00615     /*
00616      * Create a sorted list to store element namespaces
00617      */
00618     list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
00619     if (list == NULL) {
00620         xmlC14NErrInternal("creating namespaces list (c14n)");
00621         return (-1);
00622     }
00623 
00624     /* check all namespaces */
00625     for(n = cur; n != NULL; n = n->parent) {
00626     for(ns = n->nsDef; ns != NULL; ns = ns->next) {
00627         tmp = xmlSearchNs(cur->doc, cur, ns->prefix);
00628 
00629         if((tmp == ns) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
00630         already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
00631         if(visible) {
00632         xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
00633         }
00634         if(!already_rendered) {
00635             xmlListInsert(list, ns);
00636         }
00637         if(xmlStrlen(ns->prefix) == 0) {
00638             has_empty_ns = 1;
00639         }
00640         }
00641     }
00642     }
00643 
00644     /**
00645      * if the first node is not the default namespace node (a node with no
00646      * namespace URI and no local name), then generate a space followed by
00647      * xmlns="" if and only if the following conditions are met:
00648      *  - the element E that owns the axis is in the node-set
00649      *  - the nearest ancestor element of E in the node-set has a default
00650      *     namespace node in the node-set (default namespace nodes always
00651      *     have non-empty values in XPath)
00652      */
00653     if(visible && !has_empty_ns) {
00654         static xmlNs ns_default;
00655 
00656         memset(&ns_default, 0, sizeof(ns_default));
00657         if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
00658         xmlC14NPrintNamespaces(&ns_default, ctx);
00659     }
00660     }
00661 
00662 
00663     /*
00664      * print out all elements from list
00665      */
00666     xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
00667 
00668     /*
00669      * Cleanup
00670      */
00671     xmlListDelete(list);
00672     return (0);
00673 }
00674 
00675 
00676 /**
00677  * xmlExcC14NProcessNamespacesAxis:
00678  * @ctx:        the C14N context
00679  * @node:       the current node
00680  *
00681  * Prints out exclusive canonical namespace axis of the current node to the
00682  * buffer from C14N context as follows
00683  *
00684  * Exclusive XML Canonicalization
00685  * http://www.w3.org/TR/xml-exc-c14n
00686  *
00687  * If the element node is in the XPath subset then output the node in
00688  * accordance with Canonical XML except for namespace nodes which are
00689  * rendered as follows:
00690  *
00691  * 1. Render each namespace node iff:
00692  *    * it is visibly utilized by the immediate parent element or one of
00693  *      its attributes, or is present in InclusiveNamespaces PrefixList, and
00694  *    * its prefix and value do not appear in ns_rendered. ns_rendered is
00695  *      obtained by popping the state stack in order to obtain a list of
00696  *      prefixes and their values which have already been rendered by
00697  *      an output ancestor of the namespace node's parent element.
00698  * 2. Append the rendered namespace node to the list ns_rendered of namespace
00699  * nodes rendered by output ancestors. Push ns_rendered on state stack and
00700  * recurse.
00701  * 3. After the recursion returns, pop thestate stack.
00702  *
00703  *
00704  * Returns 0 on success or -1 on fail.
00705  */
00706 static int
00707 xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
00708 {
00709     xmlNsPtr ns;
00710     xmlListPtr list;
00711     xmlAttrPtr attr;
00712     int already_rendered;
00713     int has_empty_ns = 0;
00714     int has_visibly_utilized_empty_ns = 0;
00715     int has_empty_ns_in_inclusive_list = 0;
00716 
00717     if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
00718         xmlC14NErrParam("processing namespaces axis (exc c14n)");
00719         return (-1);
00720     }
00721 
00722     if(!xmlC14NIsExclusive(ctx)) {
00723         xmlC14NErrParam("processing namespaces axis (exc c14n)");
00724         return (-1);
00725 
00726     }
00727 
00728     /*
00729      * Create a sorted list to store element namespaces
00730      */
00731     list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
00732     if (list == NULL) {
00733         xmlC14NErrInternal("creating namespaces list (exc c14n)");
00734         return (-1);
00735     }
00736 
00737     /*
00738      * process inclusive namespaces:
00739      * All namespace nodes appearing on inclusive ns list are
00740      * handled as provided in Canonical XML
00741      */
00742     if(ctx->inclusive_ns_prefixes != NULL) {
00743     xmlChar *prefix;
00744     int i;
00745 
00746     for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
00747         prefix = ctx->inclusive_ns_prefixes[i];
00748         /*
00749          * Special values for namespace with empty prefix
00750          */
00751             if (xmlStrEqual(prefix, BAD_CAST "#default")
00752                 || xmlStrEqual(prefix, BAD_CAST "")) {
00753                 prefix = NULL;
00754         has_empty_ns_in_inclusive_list = 1;
00755             }
00756 
00757         ns = xmlSearchNs(cur->doc, cur, prefix);
00758         if((ns != NULL) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
00759         already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
00760         if(visible) {
00761             xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
00762         }
00763         if(!already_rendered) {
00764             xmlListInsert(list, ns);
00765         }
00766         if(xmlStrlen(ns->prefix) == 0) {
00767             has_empty_ns = 1;
00768         }
00769         }
00770     }
00771     }
00772 
00773     /* add node namespace */
00774     if(cur->ns != NULL) {
00775     ns = cur->ns;
00776     } else {
00777         ns = xmlSearchNs(cur->doc, cur, NULL);
00778     has_visibly_utilized_empty_ns = 1;
00779     }
00780     if((ns != NULL) && !xmlC14NIsXmlNs(ns)) {
00781     if(visible && xmlC14NIsVisible(ctx, ns, cur)) {
00782         if(!xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, ns, ctx)) {
00783         xmlListInsert(list, ns);
00784         }
00785     }
00786     if(visible) {
00787         xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
00788     }
00789     if(xmlStrlen(ns->prefix) == 0) {
00790         has_empty_ns = 1;
00791     }
00792     }
00793 
00794 
00795     /* add attributes */
00796     for(attr = cur->properties; attr != NULL; attr = attr->next) {
00797         /*
00798          * we need to check that attribute is visible and has non
00799          * default namespace (XML Namespaces: "default namespaces
00800      * do not apply directly to attributes")
00801          */
00802     if((attr->ns != NULL) && !xmlC14NIsXmlNs(attr->ns) && xmlC14NIsVisible(ctx, attr, cur)) {
00803         already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, attr->ns, ctx);
00804         xmlC14NVisibleNsStackAdd(ctx->ns_rendered, attr->ns, cur);
00805         if(!already_rendered && visible) {
00806         xmlListInsert(list, attr->ns);
00807         }
00808         if(xmlStrlen(attr->ns->prefix) == 0) {
00809         has_empty_ns = 1;
00810         }
00811     } else if((attr->ns != NULL) && (xmlStrlen(attr->ns->prefix) == 0) && (xmlStrlen(attr->ns->href) == 0)) {
00812         has_visibly_utilized_empty_ns = 1;
00813     }
00814     }
00815 
00816     /*
00817      * Process xmlns=""
00818      */
00819     if(visible && has_visibly_utilized_empty_ns &&
00820         !has_empty_ns && !has_empty_ns_in_inclusive_list) {
00821         static xmlNs ns_default;
00822 
00823         memset(&ns_default, 0, sizeof(ns_default));
00824 
00825         already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default, ctx);
00826     if(!already_rendered) {
00827         xmlC14NPrintNamespaces(&ns_default, ctx);
00828     }
00829     } else if(visible && !has_empty_ns && has_empty_ns_in_inclusive_list) {
00830         static xmlNs ns_default;
00831 
00832         memset(&ns_default, 0, sizeof(ns_default));
00833         if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
00834         xmlC14NPrintNamespaces(&ns_default, ctx);
00835     }
00836     }
00837 
00838 
00839 
00840     /*
00841      * print out all elements from list
00842      */
00843     xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
00844 
00845     /*
00846      * Cleanup
00847      */
00848     xmlListDelete(list);
00849     return (0);
00850 }
00851 
00852 
00853 /**
00854  * xmlC14NIsXmlAttr:
00855  * @attr:       the attr to check
00856  *
00857  * Checks whether the given attribute is a default "xml:" namespace
00858  * with href="http://www.w3.org/XML/1998/namespace"
00859  *
00860  * Returns 1 if the node is default or 0 otherwise
00861  */
00862 
00863 /* todo: make it a define? */
00864 static int
00865 xmlC14NIsXmlAttr(xmlAttrPtr attr)
00866 {
00867     return ((attr->ns != NULL) &&
00868            (xmlC14NIsXmlNs(attr->ns) != 0));
00869 }
00870 
00871 
00872 /**
00873  * xmlC14NAttrsCompare:
00874  * @attr1:      the pointer tls o first attr
00875  * @attr2:      the pointer to second attr
00876  *
00877  * Prints the given attribute to the output buffer from C14N context.
00878  *
00879  * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
00880  */
00881 static int
00882 xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
00883 {
00884     int ret = 0;
00885 
00886     /*
00887      * Simple cases
00888      */
00889     if (attr1 == attr2)
00890         return (0);
00891     if (attr1 == NULL)
00892         return (-1);
00893     if (attr2 == NULL)
00894         return (1);
00895     if (attr1->ns == attr2->ns) {
00896         return (xmlStrcmp(attr1->name, attr2->name));
00897     }
00898 
00899     /*
00900      * Attributes in the default namespace are first
00901      * because the default namespace is not applied to
00902      * unqualified attributes
00903      */
00904     if (attr1->ns == NULL)
00905         return (-1);
00906     if (attr2->ns == NULL)
00907         return (1);
00908     if (attr1->ns->prefix == NULL)
00909         return (-1);
00910     if (attr2->ns->prefix == NULL)
00911         return (1);
00912 
00913     ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
00914     if (ret == 0) {
00915         ret = xmlStrcmp(attr1->name, attr2->name);
00916     }
00917     return (ret);
00918 }
00919 
00920 
00921 /**
00922  * xmlC14NPrintAttrs:
00923  * @attr:       the pointer to attr
00924  * @ctx:        the C14N context
00925  *
00926  * Prints out canonical attribute urrent node to the
00927  * buffer from C14N context as follows
00928  *
00929  * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
00930  *
00931  * Returns 1 on success or 0 on fail.
00932  */
00933 static int
00934 xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
00935 {
00936     xmlChar *value;
00937     xmlChar *buffer;
00938 
00939     if ((attr == NULL) || (ctx == NULL)) {
00940         xmlC14NErrParam("writing attributes");
00941         return (0);
00942     }
00943 
00944     xmlOutputBufferWriteString(ctx->buf, " ");
00945     if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
00946         xmlOutputBufferWriteString(ctx->buf,
00947                                    (const char *) attr->ns->prefix);
00948         xmlOutputBufferWriteString(ctx->buf, ":");
00949     }
00950     xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
00951     xmlOutputBufferWriteString(ctx->buf, "=\"");
00952 
00953     value = xmlNodeListGetString(ctx->doc, attr->children, 1);
00954     /* todo: should we log an error if value==NULL ? */
00955     if (value != NULL) {
00956         buffer = xmlC11NNormalizeAttr(value);
00957         xmlFree(value);
00958         if (buffer != NULL) {
00959             xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
00960             xmlFree(buffer);
00961         } else {
00962             xmlC14NErrInternal("normalizing attributes axis");
00963             return (0);
00964         }
00965     }
00966     xmlOutputBufferWriteString(ctx->buf, "\"");
00967     return (1);
00968 }
00969 
00970 /**
00971  * xmlC14NFindHiddenParentAttr:
00972  *
00973  * Finds an attribute in a hidden parent node.
00974  *
00975  * Returns a pointer to the attribute node (if found) or NULL otherwise.
00976  */
00977 static xmlAttrPtr
00978 xmlC14NFindHiddenParentAttr(xmlC14NCtxPtr ctx, xmlNodePtr cur, const xmlChar * name, const xmlChar * ns)
00979 {
00980     xmlAttrPtr res;
00981     while((cur != NULL) && (!xmlC14NIsVisible(ctx, cur, cur->parent))) {
00982         res = xmlHasNsProp(cur, name, ns);
00983         if(res != NULL) {
00984             return res;
00985         }
00986 
00987         cur = cur->parent;
00988     }
00989 
00990     return NULL;
00991 }
00992 
00993 /**
00994  * xmlC14NFixupBaseAttr:
00995  *
00996  * Fixes up the xml:base attribute
00997  *
00998  * Returns the newly created attribute or NULL
00999  */
01000 static xmlAttrPtr
01001 xmlC14NFixupBaseAttr(xmlC14NCtxPtr ctx, xmlAttrPtr xml_base_attr)
01002 {
01003     xmlChar * res = NULL;
01004     xmlNodePtr cur;
01005     xmlAttrPtr attr;
01006     xmlChar * tmp_str;
01007     xmlChar * tmp_str2;
01008     int tmp_str_len;
01009 
01010     if ((ctx == NULL) || (xml_base_attr == NULL) || (xml_base_attr->parent == NULL)) {
01011         xmlC14NErrParam("processing xml:base attribute");
01012         return (NULL);
01013     }
01014 
01015     /* start from current value */
01016     res = xmlNodeListGetString(ctx->doc, xml_base_attr->children, 1);
01017     if(res == NULL) {
01018         xmlC14NErrInternal("processing xml:base attribute - can't get attr value");
01019         return (NULL);
01020     }
01021 
01022     /* go up the stack until we find a node that we rendered already */
01023     cur = xml_base_attr->parent->parent;
01024     while((cur != NULL) && (!xmlC14NIsVisible(ctx, cur, cur->parent))) {
01025         attr = xmlHasNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
01026         if(attr != NULL) {
01027             /* get attr value */
01028             tmp_str = xmlNodeListGetString(ctx->doc, attr->children, 1);
01029             if(tmp_str == NULL) {
01030                 xmlFree(res);
01031 
01032                 xmlC14NErrInternal("processing xml:base attribute - can't get attr value");
01033                 return (NULL);
01034             }
01035 
01036             /* we need to add '/' if our current base uri ends with '..' or '.'
01037             to ensure that we are forced to go "up" all the time */
01038             tmp_str_len = xmlStrlen(tmp_str);
01039             if(tmp_str_len > 1 && tmp_str[tmp_str_len - 2] == '.') {
01040                 tmp_str2 = xmlStrcat(tmp_str, BAD_CAST "/");
01041                 if(tmp_str2 == NULL) {
01042                     xmlFree(tmp_str);
01043                     xmlFree(res);
01044 
01045                     xmlC14NErrInternal("processing xml:base attribute - can't modify uri");
01046                     return (NULL);
01047                 }
01048 
01049                 tmp_str = tmp_str2;
01050             }
01051 
01052             /* build uri */
01053             tmp_str2 = xmlBuildURI(res, tmp_str);
01054             if(tmp_str2 == NULL) {
01055                 xmlFree(tmp_str);
01056                 xmlFree(res);
01057 
01058                 xmlC14NErrInternal("processing xml:base attribute - can't construct uri");
01059                 return (NULL);
01060             }
01061 
01062             /* cleanup and set the new res */
01063             xmlFree(tmp_str);
01064             xmlFree(res);
01065             res = tmp_str2;
01066         }
01067 
01068         /* next */
01069         cur = cur->parent;
01070     }
01071 
01072     /* check if result uri is empty or not */
01073     if((res == NULL) || xmlStrEqual(res, BAD_CAST "")) {
01074         xmlFree(res);
01075         return (NULL);
01076     }
01077 
01078     /* create and return the new attribute node */
01079     attr = xmlNewNsProp(NULL, xml_base_attr->ns, BAD_CAST "base", res);
01080     if(attr == NULL) {
01081         xmlFree(res);
01082 
01083         xmlC14NErrInternal("processing xml:base attribute - can't construct attribute");
01084         return (NULL);
01085     }
01086 
01087     /* done */
01088     xmlFree(res);
01089     return (attr);
01090 }
01091 
01092 /**
01093  * xmlC14NProcessAttrsAxis:
01094  * @ctx:        the C14N context
01095  * @cur:        the current node
01096  * @parent_visible: the visibility of parent node
01097  * @all_parents_visible: the visibility of all parent nodes
01098  *
01099  * Prints out canonical attribute axis of the current node to the
01100  * buffer from C14N context as follows
01101  *
01102  * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
01103  *
01104  * Attribute Axis
01105  * In lexicographic order (ascending), process each node that
01106  * is in the element's attribute axis and in the node-set.
01107  *
01108  * The processing of an element node E MUST be modified slightly
01109  * when an XPath node-set is given as input and the element's
01110  * parent is omitted from the node-set.
01111  *
01112  *
01113  * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
01114  *
01115  * Canonical XML applied to a document subset requires the search of the
01116  * ancestor nodes of each orphan element node for attributes in the xml
01117  * namespace, such as xml:lang and xml:space. These are copied into the
01118  * element node except if a declaration of the same attribute is already
01119  * in the attribute axis of the element (whether or not it is included in
01120  * the document subset). This search and copying are omitted from the
01121  * Exclusive XML Canonicalization method.
01122  *
01123  * Returns 0 on success or -1 on fail.
01124  */
01125 static int
01126 xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int parent_visible)
01127 {
01128     xmlAttrPtr attr;
01129     xmlListPtr list;
01130     xmlAttrPtr attrs_to_delete = NULL;
01131 
01132     /* special processing for 1.1 spec */
01133     xmlAttrPtr xml_base_attr = NULL;
01134     xmlAttrPtr xml_lang_attr = NULL;
01135     xmlAttrPtr xml_space_attr = NULL;
01136 
01137     if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
01138         xmlC14NErrParam("processing attributes axis");
01139         return (-1);
01140     }
01141 
01142     /*
01143      * Create a sorted list to store element attributes
01144      */
01145     list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
01146     if (list == NULL) {
01147         xmlC14NErrInternal("creating attributes list");
01148         return (-1);
01149     }
01150 
01151     switch(ctx->mode) {
01152     case XML_C14N_1_0:
01153         /* The processing of an element node E MUST be modified slightly when an XPath node-set is
01154          * given as input and the element's parent is omitted from the node-set. The method for processing
01155          * the attribute axis of an element E in the node-set is enhanced. All element nodes along E's
01156          * ancestor axis are examined for nearest occurrences of attributes in the xml namespace, such
01157          * as xml:lang and xml:space (whether or not they are in the node-set). From this list of attributes,
01158          * remove any that are in E's attribute axis (whether or not they are in the node-set). Then,
01159          * lexicographically merge this attribute list with the nodes of E's attribute axis that are in
01160          * the node-set. The result of visiting the attribute axis is computed by processing the attribute
01161          * nodes in this merged attribute list.
01162          */
01163 
01164         /*
01165          * Add all visible attributes from current node.
01166          */
01167         attr = cur->properties;
01168         while (attr != NULL) {
01169             /* check that attribute is visible */
01170             if (xmlC14NIsVisible(ctx, attr, cur)) {
01171                 xmlListInsert(list, attr);
01172             }
01173             attr = attr->next;
01174         }
01175 
01176         /*
01177          * Handle xml attributes
01178          */
01179         if (parent_visible && (cur->parent != NULL) &&
01180             (!xmlC14NIsVisible(ctx, cur->parent, cur->parent->parent)))
01181         {
01182             xmlNodePtr tmp;
01183 
01184             /*
01185              * If XPath node-set is not specified then the parent is always
01186              * visible!
01187              */
01188             tmp = cur->parent;
01189             while (tmp != NULL) {
01190                 attr = tmp->properties;
01191                 while (attr != NULL) {
01192                     if (xmlC14NIsXmlAttr(attr) != 0) {
01193                         if (xmlListSearch(list, attr) == NULL) {
01194                             xmlListInsert(list, attr);
01195                         }
01196                     }
01197                     attr = attr->next;
01198                 }
01199                 tmp = tmp->parent;
01200             }
01201         }
01202 
01203         /* done */
01204         break;
01205     case XML_C14N_EXCLUSIVE_1_0:
01206         /* attributes in the XML namespace, such as xml:lang and xml:space
01207          * are not imported into orphan nodes of the document subset
01208          */
01209 
01210         /*
01211          * Add all visible attributes from current node.
01212          */
01213         attr = cur->properties;
01214         while (attr != NULL) {
01215             /* check that attribute is visible */
01216             if (xmlC14NIsVisible(ctx, attr, cur)) {
01217                 xmlListInsert(list, attr);
01218             }
01219             attr = attr->next;
01220         }
01221 
01222         /* do nothing special for xml attributes */
01223         break;
01224     case XML_C14N_1_1:
01225         /* The processing of an element node E MUST be modified slightly when an XPath node-set is
01226          * given as input and some of the element's ancestors are omitted from the node-set.
01227          *
01228          * Simple inheritable attributes are attributes that have a value that requires at most a simple
01229          * redeclaration. This redeclaration is done by supplying a new value in the child axis. The
01230          * redeclaration of a simple inheritable attribute A contained in one of E's ancestors is done
01231          * by supplying a value to an attribute Ae inside E with the same name. Simple inheritable attributes
01232          * are xml:lang and xml:space.
01233          *
01234          * The method for processing the attribute axis of an element E in the node-set is hence enhanced.
01235          * All element nodes along E's ancestor axis are examined for the nearest occurrences of simple
01236          * inheritable attributes in the xml namespace, such as xml:lang and xml:space (whether or not they
01237          * are in the node-set). From this list of attributes, any simple inheritable attributes that are
01238          * already in E's attribute axis (whether or not they are in the node-set) are removed. Then,
01239          * lexicographically merge this attribute list with the nodes of E's attribute axis that are in
01240          * the node-set. The result of visiting the attribute axis is computed by processing the attribute
01241          * nodes in this merged attribute list.
01242          *
01243          * The xml:id attribute is not a simple inheritable attribute and no processing of these attributes is
01244          * performed.
01245          *
01246          * The xml:base attribute is not a simple inheritable attribute and requires special processing beyond
01247          * a simple redeclaration.
01248          *
01249          * Attributes in the XML namespace other than xml:base, xml:id, xml:lang, and xml:space MUST be processed
01250          * as ordinary attributes.
01251          */
01252 
01253         /*
01254          * Add all visible attributes from current node.
01255          */
01256         attr = cur->properties;
01257         while (attr != NULL) {
01258             /* special processing for XML attribute kiks in only when we have invisible parents */
01259             if ((!parent_visible) || (xmlC14NIsXmlAttr(attr) == 0)) {
01260                 /* check that attribute is visible */
01261                 if (xmlC14NIsVisible(ctx, attr, cur)) {
01262                     xmlListInsert(list, attr);
01263                 }
01264             } else {
01265                 int matched = 0;
01266 
01267                 /* check for simple inheritance attributes */
01268                 if((!matched) && (xml_lang_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "lang")) {
01269                     xml_lang_attr = attr;
01270                     matched = 1;
01271                 }
01272                 if((!matched) && (xml_space_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "space")) {
01273                     xml_space_attr = attr;
01274                     matched = 1;
01275                 }
01276 
01277                 /* check for base attr */
01278                 if((!matched) && (xml_base_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "base")) {
01279                     xml_base_attr = attr;
01280                     matched = 1;
01281                 }
01282 
01283                 /* otherwise, it is a normal attribute, so just check if it is visible */
01284                 if((!matched) && xmlC14NIsVisible(ctx, attr, cur)) {
01285                     xmlListInsert(list, attr);
01286                 }
01287             }
01288 
01289             /* move to the next one */
01290             attr = attr->next;
01291         }
01292 
01293         /* special processing for XML attribute kiks in only when we have invisible parents */
01294         if ((parent_visible)) {
01295 
01296             /* simple inheritance attributes - copy */
01297             if(xml_lang_attr == NULL) {
01298                 xml_lang_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "lang", XML_XML_NAMESPACE);
01299             }
01300             if(xml_lang_attr != NULL) {
01301                 xmlListInsert(list, xml_lang_attr);
01302             }
01303             if(xml_space_attr == NULL) {
01304                 xml_space_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "space", XML_XML_NAMESPACE);
01305             }
01306             if(xml_space_attr != NULL) {
01307                 xmlListInsert(list, xml_space_attr);
01308             }
01309 
01310             /* base uri attribute - fix up */
01311             if(xml_base_attr == NULL) {
01312                 /* if we don't have base uri attribute, check if we have a "hidden" one above */
01313                 xml_base_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "base", XML_XML_NAMESPACE);
01314             }
01315             if(xml_base_attr != NULL) {
01316                 xml_base_attr = xmlC14NFixupBaseAttr(ctx, xml_base_attr);
01317                 if(xml_base_attr != NULL) {
01318                     xmlListInsert(list, xml_base_attr);
01319 
01320                     /* note that we MUST delete returned attr node ourselves! */
01321                     xml_base_attr->next = attrs_to_delete;
01322                     attrs_to_delete = xml_base_attr;
01323                 }
01324             }
01325         }
01326 
01327         /* done */
01328         break;
01329     }
01330 
01331     /*
01332      * print out all elements from list
01333      */
01334     xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs, (const void *) ctx);
01335 
01336     /*
01337      * Cleanup
01338      */
01339     xmlFreePropList(attrs_to_delete);
01340     xmlListDelete(list);
01341     return (0);
01342 }
01343 
01344 /**
01345  * xmlC14NCheckForRelativeNamespaces:
01346  * @ctx:        the C14N context
01347  * @cur:        the current element node
01348  *
01349  * Checks that current element node has no relative namespaces defined
01350  *
01351  * Returns 0 if the node has no relative namespaces or -1 otherwise.
01352  */
01353 static int
01354 xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
01355 {
01356     xmlNsPtr ns;
01357 
01358     if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
01359         xmlC14NErrParam("checking for relative namespaces");
01360         return (-1);
01361     }
01362 
01363     ns = cur->nsDef;
01364     while (ns != NULL) {
01365         if (xmlStrlen(ns->href) > 0) {
01366             xmlURIPtr uri;
01367 
01368             uri = xmlParseURI((const char *) ns->href);
01369             if (uri == NULL) {
01370                 xmlC14NErrInternal("parsing namespace uri");
01371                 return (-1);
01372             }
01373             if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
01374                 xmlC14NErrRelativeNamespace(uri->scheme);
01375                 xmlFreeURI(uri);
01376                 return (-1);
01377             }
01378             if ((xmlStrcasecmp((const xmlChar *) uri->scheme, BAD_CAST "urn") != 0)
01379                 && (xmlStrcasecmp((const xmlChar *) uri->scheme, BAD_CAST "dav") !=0)
01380                 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
01381                 xmlC14NErrRelativeNamespace(uri->scheme);
01382                 xmlFreeURI(uri);
01383                 return (-1);
01384             }
01385             xmlFreeURI(uri);
01386         }
01387         ns = ns->next;
01388     }
01389     return (0);
01390 }
01391 
01392 /**
01393  * xmlC14NProcessElementNode:
01394  * @ctx:        the pointer to C14N context object
01395  * @cur:        the node to process
01396  * @visible:    this node is visible
01397  * @all_parents_visible: whether all the parents of this node are visible
01398  *
01399  * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
01400  *
01401  * Element Nodes
01402  * If the element is not in the node-set, then the result is obtained
01403  * by processing the namespace axis, then the attribute axis, then
01404  * processing the child nodes of the element that are in the node-set
01405  * (in document order). If the element is in the node-set, then the result
01406  * is an open angle bracket (<), the element QName, the result of
01407  * processing the namespace axis, the result of processing the attribute
01408  * axis, a close angle bracket (>), the result of processing the child
01409  * nodes of the element that are in the node-set (in document order), an
01410  * open angle bracket, a forward slash (/), the element QName, and a close
01411  * angle bracket.
01412  *
01413  * Returns non-negative value on success or negative value on fail
01414  */
01415 static int
01416 xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
01417 {
01418     int ret;
01419     xmlC14NVisibleNsStack state;
01420     int parent_is_doc = 0;
01421 
01422     if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
01423         xmlC14NErrParam("processing element node");
01424         return (-1);
01425     }
01426 
01427     /*
01428      * Check relative relative namespaces:
01429      * implementations of XML canonicalization MUST report an operation
01430      * failure on documents containing relative namespace URIs.
01431      */
01432     if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
01433         xmlC14NErrInternal("checking for relative namespaces");
01434         return (-1);
01435     }
01436 
01437 
01438     /*
01439      * Save ns_rendered stack position
01440      */
01441     memset(&state, 0, sizeof(state));
01442     xmlC14NVisibleNsStackSave(ctx->ns_rendered, &state);
01443 
01444     if (visible) {
01445         if (ctx->parent_is_doc) {
01446         /* save this flag into the stack */
01447         parent_is_doc = ctx->parent_is_doc;
01448         ctx->parent_is_doc = 0;
01449             ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
01450         }
01451         xmlOutputBufferWriteString(ctx->buf, "<");
01452 
01453         if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
01454             xmlOutputBufferWriteString(ctx->buf,
01455                                        (const char *) cur->ns->prefix);
01456             xmlOutputBufferWriteString(ctx->buf, ":");
01457         }
01458         xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
01459     }
01460 
01461     if (!xmlC14NIsExclusive(ctx)) {
01462         ret = xmlC14NProcessNamespacesAxis(ctx, cur, visible);
01463     } else {
01464         ret = xmlExcC14NProcessNamespacesAxis(ctx, cur, visible);
01465     }
01466     if (ret < 0) {
01467         xmlC14NErrInternal("processing namespaces axis");
01468         return (-1);
01469     }
01470     /* todo: shouldn't this go to "visible only"? */
01471     if(visible) {
01472     xmlC14NVisibleNsStackShift(ctx->ns_rendered);
01473     }
01474 
01475     ret = xmlC14NProcessAttrsAxis(ctx, cur, visible);
01476     if (ret < 0) {
01477     xmlC14NErrInternal("processing attributes axis");
01478     return (-1);
01479     }
01480 
01481     if (visible) {
01482         xmlOutputBufferWriteString(ctx->buf, ">");
01483     }
01484     if (cur->children != NULL) {
01485         ret = xmlC14NProcessNodeList(ctx, cur->children);
01486         if (ret < 0) {
01487             xmlC14NErrInternal("processing childrens list");
01488             return (-1);
01489         }
01490     }
01491     if (visible) {
01492         xmlOutputBufferWriteString(ctx->buf, "</");
01493         if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
01494             xmlOutputBufferWriteString(ctx->buf,
01495                                        (const char *) cur->ns->prefix);
01496             xmlOutputBufferWriteString(ctx->buf, ":");
01497         }
01498         xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
01499         xmlOutputBufferWriteString(ctx->buf, ">");
01500         if (parent_is_doc) {
01501         /* restore this flag from the stack for next node */
01502             ctx->parent_is_doc = parent_is_doc;
01503         ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
01504         }
01505     }
01506 
01507     /*
01508      * Restore ns_rendered stack position
01509      */
01510     xmlC14NVisibleNsStackRestore(ctx->ns_rendered, &state);
01511     return (0);
01512 }
01513 
01514 /**
01515  * xmlC14NProcessNode:
01516  * @ctx:        the pointer to C14N context object
01517  * @cur:        the node to process
01518  *
01519  * Processes the given node
01520  *
01521  * Returns non-negative value on success or negative value on fail
01522  */
01523 static int
01524 xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
01525 {
01526     int ret = 0;
01527     int visible;
01528 
01529     if ((ctx == NULL) || (cur == NULL)) {
01530         xmlC14NErrParam("processing node");
01531         return (-1);
01532     }
01533 
01534     visible = xmlC14NIsVisible(ctx, cur, cur->parent);
01535     switch (cur->type) {
01536         case XML_ELEMENT_NODE:
01537             ret = xmlC14NProcessElementNode(ctx, cur, visible);
01538             break;
01539         case XML_CDATA_SECTION_NODE:
01540         case XML_TEXT_NODE:
01541             /*
01542              * Text Nodes
01543              * the string value, except all ampersands are replaced
01544              * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
01545              * angle brackets (>) are replaced by &gt;, and all #xD characters are
01546              * replaced by &#xD;.
01547              */
01548             /* cdata sections are processed as text nodes */
01549             /* todo: verify that cdata sections are included in XPath nodes set */
01550             if ((visible) && (cur->content != NULL)) {
01551                 xmlChar *buffer;
01552 
01553                 buffer = xmlC11NNormalizeText(cur->content);
01554                 if (buffer != NULL) {
01555                     xmlOutputBufferWriteString(ctx->buf,
01556                                                (const char *) buffer);
01557                     xmlFree(buffer);
01558                 } else {
01559                     xmlC14NErrInternal("normalizing text node");
01560                     return (-1);
01561                 }
01562             }
01563             break;
01564         case XML_PI_NODE:
01565             /*
01566              * Processing Instruction (PI) Nodes-
01567              * The opening PI symbol (<?), the PI target name of the node,
01568              * a leading space and the string value if it is not empty, and
01569              * the closing PI symbol (?>). If the string value is empty,
01570              * then the leading space is not added. Also, a trailing #xA is
01571              * rendered after the closing PI symbol for PI children of the
01572              * root node with a lesser document order than the document
01573              * element, and a leading #xA is rendered before the opening PI
01574              * symbol of PI children of the root node with a greater document
01575              * order than the document element.
01576              */
01577             if (visible) {
01578                 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
01579                     xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
01580                 } else {
01581                     xmlOutputBufferWriteString(ctx->buf, "<?");
01582                 }
01583 
01584                 xmlOutputBufferWriteString(ctx->buf,
01585                                            (const char *) cur->name);
01586                 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
01587                     xmlChar *buffer;
01588 
01589                     xmlOutputBufferWriteString(ctx->buf, " ");
01590 
01591                     /* todo: do we need to normalize pi? */
01592                     buffer = xmlC11NNormalizePI(cur->content);
01593                     if (buffer != NULL) {
01594                         xmlOutputBufferWriteString(ctx->buf,
01595                                                    (const char *) buffer);
01596                         xmlFree(buffer);
01597                     } else {
01598                         xmlC14NErrInternal("normalizing pi node");
01599                         return (-1);
01600                     }
01601                 }
01602 
01603                 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
01604                     xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
01605                 } else {
01606                     xmlOutputBufferWriteString(ctx->buf, "?>");
01607                 }
01608             }
01609             break;
01610         case XML_COMMENT_NODE:
01611             /*
01612              * Comment Nodes
01613              * Nothing if generating canonical XML without  comments. For
01614              * canonical XML with comments, generate the opening comment
01615              * symbol (<!--), the string value of the node, and the
01616              * closing comment symbol (-->). Also, a trailing #xA is rendered
01617              * after the closing comment symbol for comment children of the
01618              * root node with a lesser document order than the document
01619              * element, and a leading #xA is rendered before the opening
01620              * comment symbol of comment children of the root node with a
01621              * greater document order than the document element. (Comment
01622              * children of the root node represent comments outside of the
01623              * top-level document element and outside of the document type
01624              * declaration).
01625              */
01626             if (visible && ctx->with_comments) {
01627                 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
01628                     xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
01629                 } else {
01630                     xmlOutputBufferWriteString(ctx->buf, "<!--");
01631                 }
01632 
01633                 if (cur->content != NULL) {
01634                     xmlChar *buffer;
01635 
01636                     /* todo: do we need to normalize comment? */
01637                     buffer = xmlC11NNormalizeComment(cur->content);
01638                     if (buffer != NULL) {
01639                         xmlOutputBufferWriteString(ctx->buf,
01640                                                    (const char *) buffer);
01641                         xmlFree(buffer);
01642                     } else {
01643                         xmlC14NErrInternal("normalizing comment node");
01644                         return (-1);
01645                     }
01646                 }
01647 
01648                 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
01649                     xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
01650                 } else {
01651                     xmlOutputBufferWriteString(ctx->buf, "-->");
01652                 }
01653             }
01654             break;
01655         case XML_DOCUMENT_NODE:
01656         case XML_DOCUMENT_FRAG_NODE:   /* should be processed as document? */
01657 #ifdef LIBXML_DOCB_ENABLED
01658         case XML_DOCB_DOCUMENT_NODE:   /* should be processed as document? */
01659 #endif
01660 #ifdef LIBXML_HTML_ENABLED
01661         case XML_HTML_DOCUMENT_NODE:   /* should be processed as document? */
01662 #endif
01663             if (cur->children != NULL) {
01664                 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
01665                 ctx->parent_is_doc = 1;
01666                 ret = xmlC14NProcessNodeList(ctx, cur->children);
01667             }
01668             break;
01669 
01670         case XML_ATTRIBUTE_NODE:
01671             xmlC14NErrInvalidNode("XML_ATTRIBUTE_NODE", "processing node");
01672             return (-1);
01673         case XML_NAMESPACE_DECL:
01674             xmlC14NErrInvalidNode("XML_NAMESPACE_DECL", "processing node");
01675             return (-1);
01676         case XML_ENTITY_REF_NODE:
01677             xmlC14NErrInvalidNode("XML_ENTITY_REF_NODE", "processing node");
01678             return (-1);
01679         case XML_ENTITY_NODE:
01680             xmlC14NErrInvalidNode("XML_ENTITY_NODE", "processing node");
01681             return (-1);
01682 
01683         case XML_DOCUMENT_TYPE_NODE:
01684         case XML_NOTATION_NODE:
01685         case XML_DTD_NODE:
01686         case XML_ELEMENT_DECL:
01687         case XML_ATTRIBUTE_DECL:
01688         case XML_ENTITY_DECL:
01689 #ifdef LIBXML_XINCLUDE_ENABLED
01690         case XML_XINCLUDE_START:
01691         case XML_XINCLUDE_END:
01692 #endif
01693             /*
01694              * should be ignored according to "W3C Canonical XML"
01695              */
01696             break;
01697         default:
01698             xmlC14NErrUnknownNode(cur->type, "processing node");
01699             return (-1);
01700     }
01701 
01702     return (ret);
01703 }
01704 
01705 /**
01706  * xmlC14NProcessNodeList:
01707  * @ctx:        the pointer to C14N context object
01708  * @cur:        the node to start from
01709  *
01710  * Processes all nodes in the row starting from cur.
01711  *
01712  * Returns non-negative value on success or negative value on fail
01713  */
01714 static int
01715 xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
01716 {
01717     int ret;
01718 
01719     if (ctx == NULL) {
01720         xmlC14NErrParam("processing node list");
01721         return (-1);
01722     }
01723 
01724     for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
01725         ret = xmlC14NProcessNode(ctx, cur);
01726     }
01727     return (ret);
01728 }
01729 
01730 
01731 /**
01732  * xmlC14NFreeCtx:
01733  * @ctx: the pointer to C14N context object
01734  *
01735  * Cleanups the C14N context object.
01736  */
01737 
01738 static void
01739 xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
01740 {
01741     if (ctx == NULL) {
01742         xmlC14NErrParam("freeing context");
01743         return;
01744     }
01745 
01746     if (ctx->ns_rendered != NULL) {
01747         xmlC14NVisibleNsStackDestroy(ctx->ns_rendered);
01748     }
01749     xmlFree(ctx);
01750 }
01751 
01752 /**
01753  * xmlC14NNewCtx:
01754  * @doc:        the XML document for canonization
01755  * @is_visible_callback:the function to use to determine is node visible
01756  *          or not
01757  * @user_data:      the first parameter for @is_visible_callback function
01758  *          (in most cases, it is nodes set)
01759  * @mode:   the c14n mode (see @xmlC14NMode)
01760  * @inclusive_ns_prefixe the list of inclusive namespace prefixes
01761  *          ended with a NULL or NULL if there is no
01762  *          inclusive namespaces (only for `
01763  *          canonicalization)
01764  * @with_comments:  include comments in the result (!=0) or not (==0)
01765  * @buf:        the output buffer to store canonical XML; this
01766  *          buffer MUST have encoder==NULL because C14N requires
01767  *          UTF-8 output
01768  *
01769  * Creates new C14N context object to store C14N parameters.
01770  *
01771  * Returns pointer to newly created object (success) or NULL (fail)
01772  */
01773 static xmlC14NCtxPtr
01774 xmlC14NNewCtx(xmlDocPtr doc,
01775           xmlC14NIsVisibleCallback is_visible_callback, void* user_data,
01776               xmlC14NMode mode, xmlChar ** inclusive_ns_prefixes,
01777               int with_comments, xmlOutputBufferPtr buf)
01778 {
01779     xmlC14NCtxPtr ctx = NULL;
01780 
01781     if ((doc == NULL) || (buf == NULL)) {
01782         xmlC14NErrParam("creating new context");
01783         return (NULL);
01784     }
01785 
01786     /*
01787      *  Validate the encoding output buffer encoding
01788      */
01789     if (buf->encoder != NULL) {
01790         xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
01791 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
01792         return (NULL);
01793     }
01794 
01795     /*
01796      *  Validate the XML document encoding value, if provided.
01797      */
01798     if (doc->charset != XML_CHAR_ENCODING_UTF8) {
01799         xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
01800            "xmlC14NNewCtx: source document not in UTF8\n");
01801         return (NULL);
01802     }
01803 
01804     /*
01805      * Allocate a new xmlC14NCtxPtr and fill the fields.
01806      */
01807     ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
01808     if (ctx == NULL) {
01809     xmlC14NErrMemory("creating context");
01810         return (NULL);
01811     }
01812     memset(ctx, 0, sizeof(xmlC14NCtx));
01813 
01814     /*
01815      * initialize C14N context
01816      */
01817     ctx->doc = doc;
01818     ctx->with_comments = with_comments;
01819     ctx->is_visible_callback = is_visible_callback;
01820     ctx->user_data = user_data;
01821     ctx->buf = buf;
01822     ctx->parent_is_doc = 1;
01823     ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
01824     ctx->ns_rendered = xmlC14NVisibleNsStackCreate();
01825 
01826     if(ctx->ns_rendered == NULL) {
01827         xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_CREATE_STACK,
01828            "xmlC14NNewCtx: xmlC14NVisibleNsStackCreate failed\n");
01829     xmlC14NFreeCtx(ctx);
01830         return (NULL);
01831     }
01832 
01833     /*
01834      * Set "mode" flag and remember list of incluseve prefixes
01835      * for exclusive c14n
01836      */
01837     ctx->mode = mode;
01838     if(xmlC14NIsExclusive(ctx)) {
01839         ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
01840     }
01841     return (ctx);
01842 }
01843 
01844 /**
01845  * xmlC14NExecute:
01846  * @doc:        the XML document for canonization
01847  * @is_visible_callback:the function to use to determine is node visible
01848  *          or not
01849  * @user_data:      the first parameter for @is_visible_callback function
01850  *          (in most cases, it is nodes set)
01851  * @mode:   the c14n mode (see @xmlC14NMode)
01852  * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
01853  *          ended with a NULL or NULL if there is no
01854  *          inclusive namespaces (only for exclusive
01855  *          canonicalization, ignored otherwise)
01856  * @with_comments:  include comments in the result (!=0) or not (==0)
01857  * @buf:        the output buffer to store canonical XML; this
01858  *          buffer MUST have encoder==NULL because C14N requires
01859  *          UTF-8 output
01860  *
01861  * Dumps the canonized image of given XML document into the provided buffer.
01862  * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
01863  * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
01864  *
01865  * Returns non-negative value on success or a negative value on fail
01866  */
01867 int
01868 xmlC14NExecute(xmlDocPtr doc, xmlC14NIsVisibleCallback is_visible_callback,
01869      void* user_data, int mode, xmlChar **inclusive_ns_prefixes,
01870      int with_comments, xmlOutputBufferPtr buf) {
01871 
01872     xmlC14NCtxPtr ctx;
01873     xmlC14NMode c14n_mode = XML_C14N_1_0;
01874     int ret;
01875 
01876     if ((buf == NULL) || (doc == NULL)) {
01877         xmlC14NErrParam("executing c14n");
01878         return (-1);
01879     }
01880 
01881     /* for backward compatibility, we have to have "mode" as "int"
01882        and here we check that user gives valid value */
01883     switch(mode) {
01884     case XML_C14N_1_0:
01885     case XML_C14N_EXCLUSIVE_1_0:
01886     case XML_C14N_1_1:
01887          c14n_mode = (xmlC14NMode)mode;
01888          break;
01889     default:
01890         xmlC14NErrParam("invalid mode for executing c14n");
01891         return (-1);
01892     }
01893 
01894     /*
01895      *  Validate the encoding output buffer encoding
01896      */
01897     if (buf->encoder != NULL) {
01898         xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
01899 "xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n");
01900         return (-1);
01901     }
01902 
01903     ctx = xmlC14NNewCtx(doc, is_visible_callback, user_data,
01904                 c14n_mode, inclusive_ns_prefixes,
01905                     with_comments, buf);
01906     if (ctx == NULL) {
01907         xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_CREATE_CTXT,
01908            "xmlC14NExecute: unable to create C14N context\n");
01909         return (-1);
01910     }
01911 
01912 
01913 
01914     /*
01915      * Root Node
01916      * The root node is the parent of the top-level document element. The
01917      * result of processing each of its child nodes that is in the node-set
01918      * in document order. The root node does not generate a byte order mark,
01919      * XML declaration, nor anything from within the document type
01920      * declaration.
01921      */
01922     if (doc->children != NULL) {
01923         ret = xmlC14NProcessNodeList(ctx, doc->children);
01924         if (ret < 0) {
01925             xmlC14NErrInternal("processing docs children list");
01926             xmlC14NFreeCtx(ctx);
01927             return (-1);
01928         }
01929     }
01930 
01931     /*
01932      * Flush buffer to get number of bytes written
01933      */
01934     ret = xmlOutputBufferFlush(buf);
01935     if (ret < 0) {
01936         xmlC14NErrInternal("flushing output buffer");
01937         xmlC14NFreeCtx(ctx);
01938         return (-1);
01939     }
01940 
01941     /*
01942      * Cleanup
01943      */
01944     xmlC14NFreeCtx(ctx);
01945     return (ret);
01946 }
01947 
01948 /**
01949  * xmlC14NDocSaveTo:
01950  * @doc:        the XML document for canonization
01951  * @nodes:      the nodes set to be included in the canonized image
01952  *      or NULL if all document nodes should be included
01953  * @mode:       the c14n mode (see @xmlC14NMode)
01954  * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
01955  *          ended with a NULL or NULL if there is no
01956  *          inclusive namespaces (only for exclusive
01957  *          canonicalization, ignored otherwise)
01958  * @with_comments:  include comments in the result (!=0) or not (==0)
01959  * @buf:        the output buffer to store canonical XML; this
01960  *          buffer MUST have encoder==NULL because C14N requires
01961  *          UTF-8 output
01962  *
01963  * Dumps the canonized image of given XML document into the provided buffer.
01964  * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
01965  * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
01966  *
01967  * Returns non-negative value on success or a negative value on fail
01968  */
01969 int
01970 xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
01971                  int mode, xmlChar ** inclusive_ns_prefixes,
01972                  int with_comments, xmlOutputBufferPtr buf) {
01973     return(xmlC14NExecute(doc,
01974             (xmlC14NIsVisibleCallback)xmlC14NIsNodeInNodeset,
01975             nodes,
01976             mode,
01977             inclusive_ns_prefixes,
01978             with_comments,
01979             buf));
01980 }
01981 
01982 
01983 /**
01984  * xmlC14NDocDumpMemory:
01985  * @doc:        the XML document for canonization
01986  * @nodes:      the nodes set to be included in the canonized image
01987  *      or NULL if all document nodes should be included
01988  * @mode:       the c14n mode (see @xmlC14NMode)
01989  * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
01990  *          ended with a NULL or NULL if there is no
01991  *          inclusive namespaces (only for exclusive
01992  *          canonicalization, ignored otherwise)
01993  * @with_comments:  include comments in the result (!=0) or not (==0)
01994  * @doc_txt_ptr:    the memory pointer for allocated canonical XML text;
01995  *          the caller of this functions is responsible for calling
01996  *          xmlFree() to free allocated memory
01997  *
01998  * Dumps the canonized image of given XML document into memory.
01999  * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
02000  * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
02001  *
02002  * Returns the number of bytes written on success or a negative value on fail
02003  */
02004 int
02005 xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
02006                      int mode, xmlChar ** inclusive_ns_prefixes,
02007                      int with_comments, xmlChar ** doc_txt_ptr)
02008 {
02009     int ret;
02010     xmlOutputBufferPtr buf;
02011 
02012     if (doc_txt_ptr == NULL) {
02013         xmlC14NErrParam("dumping doc to memory");
02014         return (-1);
02015     }
02016 
02017     *doc_txt_ptr = NULL;
02018 
02019     /*
02020      * create memory buffer with UTF8 (default) encoding
02021      */
02022     buf = xmlAllocOutputBuffer(NULL);
02023     if (buf == NULL) {
02024         xmlC14NErrMemory("creating output buffer");
02025         return (-1);
02026     }
02027 
02028     /*
02029      * canonize document and write to buffer
02030      */
02031     ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
02032                            with_comments, buf);
02033     if (ret < 0) {
02034         xmlC14NErrInternal("saving doc to output buffer");
02035         (void) xmlOutputBufferClose(buf);
02036         return (-1);
02037     }
02038 
02039     ret = xmlBufUse(buf->buffer);
02040     if (ret > 0) {
02041         *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), ret);
02042     }
02043     (void) xmlOutputBufferClose(buf);
02044 
02045     if ((*doc_txt_ptr == NULL) && (ret > 0)) {
02046         xmlC14NErrMemory("coping canonicanized document");
02047         return (-1);
02048     }
02049     return (ret);
02050 }
02051 
02052 /**
02053  * xmlC14NDocSave:
02054  * @doc:        the XML document for canonization
02055  * @nodes:      the nodes set to be included in the canonized image
02056  *      or NULL if all document nodes should be included
02057  * @mode:       the c14n mode (see @xmlC14NMode)
02058  * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
02059  *          ended with a NULL or NULL if there is no
02060  *          inclusive namespaces (only for exclusive
02061  *          canonicalization, ignored otherwise)
02062  * @with_comments:  include comments in the result (!=0) or not (==0)
02063  * @filename:       the filename to store canonical XML image
02064  * @compression:    the compression level (zlib requred):
02065  *              -1 - libxml default,
02066  *               0 - uncompressed,
02067  *              >0 - compression level
02068  *
02069  * Dumps the canonized image of given XML document into the file.
02070  * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
02071  * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
02072  *
02073  * Returns the number of bytes written success or a negative value on fail
02074  */
02075 int
02076 xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
02077                int mode, xmlChar ** inclusive_ns_prefixes,
02078                int with_comments, const char *filename, int compression)
02079 {
02080     xmlOutputBufferPtr buf;
02081     int ret;
02082 
02083     if (filename == NULL) {
02084         xmlC14NErrParam("saving doc");
02085         return (-1);
02086     }
02087 #ifdef HAVE_ZLIB_H
02088     if (compression < 0)
02089         compression = xmlGetCompressMode();
02090 #endif
02091 
02092     /*
02093      * save the content to a temp buffer, use default UTF8 encoding.
02094      */
02095     buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
02096     if (buf == NULL) {
02097         xmlC14NErrInternal("creating temporary filename");
02098         return (-1);
02099     }
02100 
02101     /*
02102      * canonize document and write to buffer
02103      */
02104     ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
02105                            with_comments, buf);
02106     if (ret < 0) {
02107         xmlC14NErrInternal("cannicanize document to buffer");
02108         (void) xmlOutputBufferClose(buf);
02109         return (-1);
02110     }
02111 
02112     /*
02113      * get the numbers of bytes written
02114      */
02115     ret = xmlOutputBufferClose(buf);
02116     return (ret);
02117 }
02118 
02119 
02120 
02121 /*
02122  * Macro used to grow the current buffer.
02123  */
02124 #define growBufferReentrant() {                     \
02125     buffer_size *= 2;                           \
02126     buffer = (xmlChar *)                        \
02127         xmlRealloc(buffer, buffer_size * sizeof(xmlChar));  \
02128     if (buffer == NULL) {                       \
02129     xmlC14NErrMemory("growing buffer");             \
02130     return(NULL);                           \
02131     }                                   \
02132 }
02133 
02134 /**
02135  * xmlC11NNormalizeString:
02136  * @input:      the input string
02137  * @mode:       the normalization mode (attribute, comment, PI or text)
02138  *
02139  * Converts a string to a canonical (normalized) format. The code is stolen
02140  * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
02141  * and the @mode parameter
02142  *
02143  * Returns a normalized string (caller is responsible for calling xmlFree())
02144  * or NULL if an error occurs
02145  */
02146 static xmlChar *
02147 xmlC11NNormalizeString(const xmlChar * input,
02148                        xmlC14NNormalizationMode mode)
02149 {
02150     const xmlChar *cur = input;
02151     xmlChar *buffer = NULL;
02152     xmlChar *out = NULL;
02153     int buffer_size = 0;
02154 
02155     if (input == NULL)
02156         return (NULL);
02157 
02158     /*
02159      * allocate an translation buffer.
02160      */
02161     buffer_size = 1000;
02162     buffer = (xmlChar *) xmlMallocAtomic(buffer_size * sizeof(xmlChar));
02163     if (buffer == NULL) {
02164     xmlC14NErrMemory("allocating buffer");
02165         return (NULL);
02166     }
02167     out = buffer;
02168 
02169     while (*cur != '\0') {
02170         if ((out - buffer) > (buffer_size - 10)) {
02171             int indx = out - buffer;
02172 
02173             growBufferReentrant();
02174             out = &buffer[indx];
02175         }
02176 
02177         if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
02178                               (mode == XMLC14N_NORMALIZE_TEXT))) {
02179             *out++ = '&';
02180             *out++ = 'l';
02181             *out++ = 't';
02182             *out++ = ';';
02183         } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
02184             *out++ = '&';
02185             *out++ = 'g';
02186             *out++ = 't';
02187             *out++ = ';';
02188         } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
02189                                      (mode == XMLC14N_NORMALIZE_TEXT))) {
02190             *out++ = '&';
02191             *out++ = 'a';
02192             *out++ = 'm';
02193             *out++ = 'p';
02194             *out++ = ';';
02195         } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
02196             *out++ = '&';
02197             *out++ = 'q';
02198             *out++ = 'u';
02199             *out++ = 'o';
02200             *out++ = 't';
02201             *out++ = ';';
02202         } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
02203             *out++ = '&';
02204             *out++ = '#';
02205             *out++ = 'x';
02206             *out++ = '9';
02207             *out++ = ';';
02208         } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
02209             *out++ = '&';
02210             *out++ = '#';
02211             *out++ = 'x';
02212             *out++ = 'A';
02213             *out++ = ';';
02214         } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
02215                                         (mode == XMLC14N_NORMALIZE_TEXT) ||
02216                                         (mode == XMLC14N_NORMALIZE_COMMENT) ||
02217                     (mode == XMLC14N_NORMALIZE_PI))) {
02218             *out++ = '&';
02219             *out++ = '#';
02220             *out++ = 'x';
02221             *out++ = 'D';
02222             *out++ = ';';
02223         } else {
02224             /*
02225              * Works because on UTF-8, all extended sequences cannot
02226              * result in bytes in the ASCII range.
02227              */
02228             *out++ = *cur;
02229         }
02230         cur++;
02231     }
02232     *out = 0;
02233     return (buffer);
02234 }
02235 #endif /* LIBXML_OUTPUT_ENABLED */
02236 #define bottom_c14n
02237 #include "elfgcchack.h"
02238 #endif /* LIBXML_C14N_ENABLED */
02239