1 /*
   2  * documents.c: Implementation of the documents handling
   3  *
   4  * See Copyright for the status of this software.
   5  *
   6  * daniel@veillard.com
   7  */
   8 
   9 #define IN_LIBXSLT
  10 #include "libxslt.h"
  11 
  12 #include <string.h>
  13 
  14 #include <libxml/xmlmemory.h>
  15 #include <libxml/tree.h>
  16 #include <libxml/hash.h>
  17 #include <libxml/parser.h>
  18 #include <libxml/parserInternals.h>
  19 #include "xslt.h"
  20 #include "xsltInternals.h"
  21 #include "xsltutils.h"
  22 #include "documents.h"
  23 #include "transform.h"
  24 #include "imports.h"
  25 #include "keys.h"
  26 #include "security.h"
  27 
  28 #ifdef LIBXML_XINCLUDE_ENABLED
  29 #include <libxml/xinclude.h>
  30 #endif
  31 
  32 #define WITH_XSLT_DEBUG_DOCUMENTS
  33 
  34 #ifdef WITH_XSLT_DEBUG
  35 #define WITH_XSLT_DEBUG_DOCUMENTS
  36 #endif
  37 
  38 /************************************************************************
  39  *                                  *
  40  *      Hooks for the document loader               *
  41  *                                  *
  42  ************************************************************************/
  43 
  44 /**
  45  * xsltDocDefaultLoaderFunc:
  46  * @URI: the URI of the document to load
  47  * @dict: the dictionary to use when parsing that document
  48  * @options: parsing options, a set of xmlParserOption
  49  * @ctxt: the context, either a stylesheet or a transformation context
  50  * @type: the xsltLoadType indicating the kind of loading required
  51  *
  52  * Default function to load document not provided by the compilation or
  53  * transformation API themselve, for example when an xsl:import,
  54  * xsl:include is found at compilation time or when a document()
  55  * call is made at runtime.
  56  *
  57  * Returns the pointer to the document (which will be modified and
  58  * freed by the engine later), or NULL in case of error.
  59  */
  60 static xmlDocPtr
  61 xsltDocDefaultLoaderFunc(const xmlChar * URI, xmlDictPtr dict, int options,
  62                          void *ctxt ATTRIBUTE_UNUSED,
  63              xsltLoadType type ATTRIBUTE_UNUSED)
  64 {
  65     xmlParserCtxtPtr pctxt;
  66     xmlParserInputPtr inputStream;
  67     xmlDocPtr doc;
  68 
  69     pctxt = xmlNewParserCtxt();
  70     if (pctxt == NULL)
  71         return(NULL);
  72     if ((dict != NULL) && (pctxt->dict != NULL)) {
  73         xmlDictFree(pctxt->dict);
  74     pctxt->dict = NULL;
  75     }
  76     if (dict != NULL) {
  77     pctxt->dict = dict;
  78     xmlDictReference(pctxt->dict);
  79 #ifdef WITH_XSLT_DEBUG
  80     xsltGenericDebug(xsltGenericDebugContext,
  81                      "Reusing dictionary for document\n");
  82 #endif
  83     }
  84     xmlCtxtUseOptions(pctxt, options);
  85     inputStream = xmlLoadExternalEntity((const char *) URI, NULL, pctxt);
  86     if (inputStream == NULL) {
  87         xmlFreeParserCtxt(pctxt);
  88     return(NULL);
  89     }
  90     inputPush(pctxt, inputStream);
  91     if (pctxt->directory == NULL)
  92         pctxt->directory = xmlParserGetDirectory((const char *) URI);
  93 
  94     xmlParseDocument(pctxt);
  95 
  96     if (pctxt->wellFormed) {
  97         doc = pctxt->myDoc;
  98     }
  99     else {
 100         doc = NULL;
 101         xmlFreeDoc(pctxt->myDoc);
 102         pctxt->myDoc = NULL;
 103     }
 104     xmlFreeParserCtxt(pctxt);
 105 
 106     return(doc);
 107 }
 108 
 109 
 110 xsltDocLoaderFunc xsltDocDefaultLoader = xsltDocDefaultLoaderFunc;
 111 
 112 /**
 113  * xsltSetLoaderFunc:
 114  * @f: the new function to handle document loading.
 115  *
 116  * Set the new function to load document, if NULL it resets it to the
 117  * default function.
 118  */
 119 
 120 void
 121 xsltSetLoaderFunc(xsltDocLoaderFunc f) {
 122     if (f == NULL)
 123         xsltDocDefaultLoader = xsltDocDefaultLoaderFunc;
 124     else
 125         xsltDocDefaultLoader = f;
 126 }
 127 
 128 /************************************************************************
 129  *                                  *
 130  *          Module interfaces               *
 131  *                                  *
 132  ************************************************************************/
 133 
 134 /**
 135  * xsltNewDocument:
 136  * @ctxt: an XSLT transformation context (or NULL)
 137  * @doc:  a parsed XML document
 138  *
 139  * Register a new document, apply key computations
 140  *
 141  * Returns a handler to the document
 142  */
 143 xsltDocumentPtr
 144 xsltNewDocument(xsltTransformContextPtr ctxt, xmlDocPtr doc) {
 145     xsltDocumentPtr cur;
 146 
 147     cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument));
 148     if (cur == NULL) {
 149     xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
 150         "xsltNewDocument : malloc failed\n");
 151     return(NULL);
 152     }
 153     memset(cur, 0, sizeof(xsltDocument));
 154     cur->doc = doc;
 155     if (ctxt != NULL) {
 156         if (! XSLT_IS_RES_TREE_FRAG(doc)) {
 157         cur->next = ctxt->docList;
 158         ctxt->docList = cur;
 159     }
 160     /*
 161     * A key with a specific name for a specific document
 162     * will only be computed if there's a call to the key()
 163     * function using that specific name for that specific
 164     * document. I.e. computation of keys will be done in
 165     * xsltGetKey() (keys.c) on an on-demand basis.
 166     *
 167     * xsltInitCtxtKeys(ctxt, cur); not called here anymore
 168     */
 169     }
 170     return(cur);
 171 }
 172 
 173 /**
 174  * xsltNewStyleDocument:
 175  * @style: an XSLT style sheet
 176  * @doc:  a parsed XML document
 177  *
 178  * Register a new document, apply key computations
 179  *
 180  * Returns a handler to the document
 181  */
 182 xsltDocumentPtr
 183 xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) {
 184     xsltDocumentPtr cur;
 185 
 186     cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument));
 187     if (cur == NULL) {
 188     xsltTransformError(NULL, style, (xmlNodePtr) doc,
 189         "xsltNewStyleDocument : malloc failed\n");
 190     return(NULL);
 191     }
 192     memset(cur, 0, sizeof(xsltDocument));
 193     cur->doc = doc;
 194     if (style != NULL) {
 195     cur->next = style->docList;
 196     style->docList = cur;
 197     }
 198     return(cur);
 199 }
 200 
 201 /**
 202  * xsltFreeStyleDocuments:
 203  * @style: an XSLT stylesheet (representing a stylesheet-level)
 204  *
 205  * Frees the node-trees (and xsltDocument structures) of all
 206  * stylesheet-modules of the stylesheet-level represented by
 207  * the given @style.
 208  */
 209 void
 210 xsltFreeStyleDocuments(xsltStylesheetPtr style) {
 211     xsltDocumentPtr doc, cur;
 212 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
 213     xsltNsMapPtr nsMap;
 214 #endif
 215 
 216     if (style == NULL)
 217     return;
 218 
 219 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
 220     if (XSLT_HAS_INTERNAL_NSMAP(style))
 221     nsMap = XSLT_GET_INTERNAL_NSMAP(style);
 222     else
 223     nsMap = NULL;
 224 #endif
 225 
 226     cur = style->docList;
 227     while (cur != NULL) {
 228     doc = cur;
 229     cur = cur->next;
 230 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
 231     /*
 232     * Restore all changed namespace URIs of ns-decls.
 233     */
 234     if (nsMap)
 235         xsltRestoreDocumentNamespaces(nsMap, doc->doc);
 236 #endif
 237     xsltFreeDocumentKeys(doc);
 238     if (!doc->main)
 239         xmlFreeDoc(doc->doc);
 240         xmlFree(doc);
 241     }
 242 }
 243 
 244 /**
 245  * xsltFreeDocuments:
 246  * @ctxt: an XSLT transformation context
 247  *
 248  * Free up all the space used by the loaded documents
 249  */
 250 void
 251 xsltFreeDocuments(xsltTransformContextPtr ctxt) {
 252     xsltDocumentPtr doc, cur;
 253 
 254     cur = ctxt->docList;
 255     while (cur != NULL) {
 256     doc = cur;
 257     cur = cur->next;
 258     xsltFreeDocumentKeys(doc);
 259     if (!doc->main)
 260         xmlFreeDoc(doc->doc);
 261         xmlFree(doc);
 262     }
 263     cur = ctxt->styleList;
 264     while (cur != NULL) {
 265     doc = cur;
 266     cur = cur->next;
 267     xsltFreeDocumentKeys(doc);
 268     if (!doc->main)
 269         xmlFreeDoc(doc->doc);
 270         xmlFree(doc);
 271     }
 272 }
 273 
 274 /**
 275  * xsltLoadDocument:
 276  * @ctxt: an XSLT transformation context
 277  * @URI:  the computed URI of the document
 278  *
 279  * Try to load a document (not a stylesheet)
 280  * within the XSLT transformation context
 281  *
 282  * Returns the new xsltDocumentPtr or NULL in case of error
 283  */
 284 xsltDocumentPtr
 285 xsltLoadDocument(xsltTransformContextPtr ctxt, const xmlChar *URI) {
 286     xsltDocumentPtr ret;
 287     xmlDocPtr doc;
 288 
 289     if ((ctxt == NULL) || (URI == NULL))
 290     return(NULL);
 291 
 292     /*
 293      * Security framework check
 294      */
 295     if (ctxt->sec != NULL) {
 296     int res;
 297 
 298     res = xsltCheckRead(ctxt->sec, ctxt, URI);
 299     if (res == 0) {
 300         xsltTransformError(ctxt, NULL, NULL,
 301          "xsltLoadDocument: read rights for %s denied\n",
 302                  URI);
 303         return(NULL);
 304     }
 305     }
 306 
 307     /*
 308      * Walk the context list to find the document if preparsed
 309      */
 310     ret = ctxt->docList;
 311     while (ret != NULL) {
 312     if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
 313         (xmlStrEqual(ret->doc->URL, URI)))
 314         return(ret);
 315     ret = ret->next;
 316     }
 317 
 318     doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions,
 319                                (void *) ctxt, XSLT_LOAD_DOCUMENT);
 320 
 321     if (doc == NULL)
 322     return(NULL);
 323 
 324     if (ctxt->xinclude != 0) {
 325 #ifdef LIBXML_XINCLUDE_ENABLED
 326 #if LIBXML_VERSION >= 20603
 327     xmlXIncludeProcessFlags(doc, ctxt->parserOptions);
 328 #else
 329     xmlXIncludeProcess(doc);
 330 #endif
 331 #else
 332     xsltTransformError(ctxt, NULL, NULL,
 333         "xsltLoadDocument(%s) : XInclude processing not compiled in\n",
 334                      URI);
 335 #endif
 336     }
 337     /*
 338      * Apply white-space stripping if asked for
 339      */
 340     if (xsltNeedElemSpaceHandling(ctxt))
 341     xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
 342     if (ctxt->debugStatus == XSLT_DEBUG_NONE)
 343     xmlXPathOrderDocElems(doc);
 344 
 345     ret = xsltNewDocument(ctxt, doc);
 346     return(ret);
 347 }
 348 
 349 /**
 350  * xsltLoadStyleDocument:
 351  * @style: an XSLT style sheet
 352  * @URI:  the computed URI of the document
 353  *
 354  * Try to load a stylesheet document within the XSLT transformation context
 355  *
 356  * Returns the new xsltDocumentPtr or NULL in case of error
 357  */
 358 xsltDocumentPtr
 359 xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) {
 360     xsltDocumentPtr ret;
 361     xmlDocPtr doc;
 362     xsltSecurityPrefsPtr sec;
 363 
 364     if ((style == NULL) || (URI == NULL))
 365     return(NULL);
 366 
 367     /*
 368      * Security framework check
 369      */
 370     sec = xsltGetDefaultSecurityPrefs();
 371     if (sec != NULL) {
 372     int res;
 373 
 374     res = xsltCheckRead(sec, NULL, URI);
 375     if (res == 0) {
 376         xsltTransformError(NULL, NULL, NULL,
 377          "xsltLoadStyleDocument: read rights for %s denied\n",
 378                  URI);
 379         return(NULL);
 380     }
 381     }
 382 
 383     /*
 384      * Walk the context list to find the document if preparsed
 385      */
 386     ret = style->docList;
 387     while (ret != NULL) {
 388     if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
 389         (xmlStrEqual(ret->doc->URL, URI)))
 390         return(ret);
 391     ret = ret->next;
 392     }
 393 
 394     doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
 395                                (void *) style, XSLT_LOAD_STYLESHEET);
 396     if (doc == NULL)
 397     return(NULL);
 398 
 399     ret = xsltNewStyleDocument(style, doc);
 400     return(ret);
 401 }
 402 
 403 /**
 404  * xsltFindDocument:
 405  * @ctxt: an XSLT transformation context
 406  * @doc: a parsed XML document
 407  *
 408  * Try to find a document within the XSLT transformation context.
 409  * This will not find document infos for temporary
 410  * Result Tree Fragments.
 411  *
 412  * Returns the desired xsltDocumentPtr or NULL in case of error
 413  */
 414 xsltDocumentPtr
 415 xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) {
 416     xsltDocumentPtr ret;
 417 
 418     if ((ctxt == NULL) || (doc == NULL))
 419     return(NULL);
 420 
 421     /*
 422      * Walk the context list to find the document
 423      */
 424     ret = ctxt->docList;
 425     while (ret != NULL) {
 426     if (ret->doc == doc)
 427         return(ret);
 428     ret = ret->next;
 429     }
 430     if (doc == ctxt->style->doc)
 431     return(ctxt->document);
 432     return(NULL);
 433 }
 434