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             if (res == 0)
 301                 xsltTransformError(ctxt, NULL, NULL,
 302                      "xsltLoadDocument: read rights for %s denied\n",
 303                                  URI);
 304         return(NULL);
 305     }
 306     }
 307 
 308     /*
 309      * Walk the context list to find the document if preparsed
 310      */
 311     ret = ctxt->docList;
 312     while (ret != NULL) {
 313     if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
 314         (xmlStrEqual(ret->doc->URL, URI)))
 315         return(ret);
 316     ret = ret->next;
 317     }
 318 
 319     doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions,
 320                                (void *) ctxt, XSLT_LOAD_DOCUMENT);
 321 
 322     if (doc == NULL)
 323     return(NULL);
 324 
 325     if (ctxt->xinclude != 0) {
 326 #ifdef LIBXML_XINCLUDE_ENABLED
 327 #if LIBXML_VERSION >= 20603
 328     xmlXIncludeProcessFlags(doc, ctxt->parserOptions);
 329 #else
 330     xmlXIncludeProcess(doc);
 331 #endif
 332 #else
 333     xsltTransformError(ctxt, NULL, NULL,
 334         "xsltLoadDocument(%s) : XInclude processing not compiled in\n",
 335                      URI);
 336 #endif
 337     }
 338     /*
 339      * Apply white-space stripping if asked for
 340      */
 341     if (xsltNeedElemSpaceHandling(ctxt))
 342     xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
 343     if (ctxt->debugStatus == XSLT_DEBUG_NONE)
 344     xmlXPathOrderDocElems(doc);
 345 
 346     ret = xsltNewDocument(ctxt, doc);
 347     return(ret);
 348 }
 349 
 350 /**
 351  * xsltLoadStyleDocument:
 352  * @style: an XSLT style sheet
 353  * @URI:  the computed URI of the document
 354  *
 355  * Try to load a stylesheet document within the XSLT transformation context
 356  *
 357  * Returns the new xsltDocumentPtr or NULL in case of error
 358  */
 359 xsltDocumentPtr
 360 xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) {
 361     xsltDocumentPtr ret;
 362     xmlDocPtr doc;
 363     xsltSecurityPrefsPtr sec;
 364 
 365     if ((style == NULL) || (URI == NULL))
 366     return(NULL);
 367 
 368     /*
 369      * Security framework check
 370      */
 371     sec = xsltGetDefaultSecurityPrefs();
 372     if (sec != NULL) {
 373     int res;
 374 
 375     res = xsltCheckRead(sec, NULL, URI);
 376     if (res <= 0) {
 377             if (res == 0)
 378                 xsltTransformError(NULL, NULL, NULL,
 379                      "xsltLoadStyleDocument: read rights for %s denied\n",
 380                                  URI);
 381         return(NULL);
 382     }
 383     }
 384 
 385     /*
 386      * Walk the context list to find the document if preparsed
 387      */
 388     ret = style->docList;
 389     while (ret != NULL) {
 390     if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
 391         (xmlStrEqual(ret->doc->URL, URI)))
 392         return(ret);
 393     ret = ret->next;
 394     }
 395 
 396     doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
 397                                (void *) style, XSLT_LOAD_STYLESHEET);
 398     if (doc == NULL)
 399     return(NULL);
 400 
 401     ret = xsltNewStyleDocument(style, doc);
 402     return(ret);
 403 }
 404 
 405 /**
 406  * xsltFindDocument:
 407  * @ctxt: an XSLT transformation context
 408  * @doc: a parsed XML document
 409  *
 410  * Try to find a document within the XSLT transformation context.
 411  * This will not find document infos for temporary
 412  * Result Tree Fragments.
 413  *
 414  * Returns the desired xsltDocumentPtr or NULL in case of error
 415  */
 416 xsltDocumentPtr
 417 xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) {
 418     xsltDocumentPtr ret;
 419 
 420     if ((ctxt == NULL) || (doc == NULL))
 421     return(NULL);
 422 
 423     /*
 424      * Walk the context list to find the document
 425      */
 426     ret = ctxt->docList;
 427     while (ret != NULL) {
 428     if (ret->doc == doc)
 429         return(ret);
 430     ret = ret->next;
 431     }
 432     if (doc == ctxt->style->doc)
 433     return(ctxt->document);
 434     return(NULL);
 435 }
 436