1 /*
   2  * imports.c: Implementation of the XSLT imports
   3  *
   4  * Reference:
   5  *   http://www.w3.org/TR/1999/REC-xslt-19991116
   6  *
   7  * See Copyright for the status of this software.
   8  *
   9  * daniel@veillard.com
  10  */
  11 
  12 #define IN_LIBXSLT
  13 #include "libxslt.h"
  14 
  15 #include <string.h>
  16 
  17 #ifdef HAVE_SYS_TYPES_H
  18 #include <sys/types.h>
  19 #endif
  20 #ifdef HAVE_MATH_H
  21 #include <math.h>
  22 #endif
  23 #ifdef HAVE_FLOAT_H
  24 #include <float.h>
  25 #endif
  26 #ifdef HAVE_IEEEFP_H
  27 #include <ieeefp.h>
  28 #endif
  29 #ifdef HAVE_NAN_H
  30 #include <nan.h>
  31 #endif
  32 #ifdef HAVE_CTYPE_H
  33 #include <ctype.h>
  34 #endif
  35 
  36 #include <libxml/xmlmemory.h>
  37 #include <libxml/tree.h>
  38 #include <libxml/hash.h>
  39 #include <libxml/xmlerror.h>
  40 #include <libxml/uri.h>
  41 #include "xslt.h"
  42 #include "xsltInternals.h"
  43 #include "xsltutils.h"
  44 #include "preproc.h"
  45 #include "imports.h"
  46 #include "documents.h"
  47 #include "security.h"
  48 #include "pattern.h"
  49 
  50 
  51 /************************************************************************
  52  *                                  *
  53  *          Module interfaces               *
  54  *                                  *
  55  ************************************************************************/
  56 /**
  57  * xsltFixImportedCompSteps:
  58  * @master: the "master" stylesheet
  59  * @style: the stylesheet being imported by the master
  60  *
  61  * normalize the comp steps for the stylesheet being imported
  62  * by the master, together with any imports within that.
  63  *
  64  */
  65 static void xsltFixImportedCompSteps(xsltStylesheetPtr master,
  66             xsltStylesheetPtr style) {
  67     xsltStylesheetPtr res;
  68     xmlHashScan(style->templatesHash, xsltNormalizeCompSteps, master);
  69     master->extrasNr += style->extrasNr;
  70     for (res = style->imports; res != NULL; res = res->next) {
  71         xsltFixImportedCompSteps(master, res);
  72     }
  73 }
  74 
  75 /**
  76  * xsltParseStylesheetImport:
  77  * @style:  the XSLT stylesheet
  78  * @cur:  the import element
  79  *
  80  * parse an XSLT stylesheet import element
  81  *
  82  * Returns 0 in case of success -1 in case of failure.
  83  */
  84 
  85 int
  86 xsltParseStylesheetImport(xsltStylesheetPtr style, xmlNodePtr cur) {
  87     int ret = -1;
  88     xmlDocPtr import = NULL;
  89     xmlChar *base = NULL;
  90     xmlChar *uriRef = NULL;
  91     xmlChar *URI = NULL;
  92     xsltStylesheetPtr res;
  93     xsltSecurityPrefsPtr sec;
  94 
  95     if ((cur == NULL) || (style == NULL))
  96     return (ret);
  97 
  98     uriRef = xmlGetNsProp(cur, (const xmlChar *)"href", NULL);
  99     if (uriRef == NULL) {
 100     xsltTransformError(NULL, style, cur,
 101         "xsl:import : missing href attribute\n");
 102     goto error;
 103     }
 104 
 105     base = xmlNodeGetBase(style->doc, cur);
 106     URI = xmlBuildURI(uriRef, base);
 107     if (URI == NULL) {
 108     xsltTransformError(NULL, style, cur,
 109         "xsl:import : invalid URI reference %s\n", uriRef);
 110     goto error;
 111     }
 112 
 113     res = style;
 114     while (res != NULL) {
 115         if (res->doc == NULL)
 116         break;
 117     if (xmlStrEqual(res->doc->URL, URI)) {
 118         xsltTransformError(NULL, style, cur,
 119            "xsl:import : recursion detected on imported URL %s\n", URI);
 120         goto error;
 121     }
 122     res = res->parent;
 123     }
 124 
 125     /*
 126      * Security framework check
 127      */
 128     sec = xsltGetDefaultSecurityPrefs();
 129     if (sec != NULL) {
 130     int secres;
 131 
 132     secres = xsltCheckRead(sec, NULL, URI);
 133     if (secres == 0) {
 134         xsltTransformError(NULL, NULL, NULL,
 135          "xsl:import: read rights for %s denied\n",
 136                  URI);
 137         goto error;
 138     }
 139     }
 140 
 141     import = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
 142                                   (void *) style, XSLT_LOAD_STYLESHEET);
 143     if (import == NULL) {
 144     xsltTransformError(NULL, style, cur,
 145         "xsl:import : unable to load %s\n", URI);
 146     goto error;
 147     }
 148 
 149     res = xsltParseStylesheetImportedDoc(import, style);
 150     if (res != NULL) {
 151     res->next = style->imports;
 152     style->imports = res;
 153     if (style->parent == NULL) {
 154         xsltFixImportedCompSteps(style, res);
 155     }
 156     ret = 0;
 157     } else {
 158     xmlFreeDoc(import);
 159     }
 160 
 161 error:
 162     if (uriRef != NULL)
 163     xmlFree(uriRef);
 164     if (base != NULL)
 165     xmlFree(base);
 166     if (URI != NULL)
 167     xmlFree(URI);
 168 
 169     return (ret);
 170 }
 171 
 172 /**
 173  * xsltParseStylesheetInclude:
 174  * @style:  the XSLT stylesheet
 175  * @cur:  the include node
 176  *
 177  * parse an XSLT stylesheet include element
 178  *
 179  * Returns 0 in case of success -1 in case of failure
 180  */
 181 
 182 int
 183 xsltParseStylesheetInclude(xsltStylesheetPtr style, xmlNodePtr cur) {
 184     int ret = -1;
 185     xmlDocPtr oldDoc;
 186     xmlChar *base = NULL;
 187     xmlChar *uriRef = NULL;
 188     xmlChar *URI = NULL;
 189     xsltStylesheetPtr result;
 190     xsltDocumentPtr include;
 191     xsltDocumentPtr docptr;
 192     int oldNopreproc;
 193 
 194     if ((cur == NULL) || (style == NULL))
 195     return (ret);
 196 
 197     uriRef = xmlGetNsProp(cur, (const xmlChar *)"href", NULL);
 198     if (uriRef == NULL) {
 199     xsltTransformError(NULL, style, cur,
 200         "xsl:include : missing href attribute\n");
 201     goto error;
 202     }
 203 
 204     base = xmlNodeGetBase(style->doc, cur);
 205     URI = xmlBuildURI(uriRef, base);
 206     if (URI == NULL) {
 207     xsltTransformError(NULL, style, cur,
 208         "xsl:include : invalid URI reference %s\n", uriRef);
 209     goto error;
 210     }
 211 
 212     /*
 213      * in order to detect recursion, we check all previously included
 214      * stylesheets.
 215      */
 216     docptr = style->includes;
 217     while (docptr != NULL) {
 218         if (xmlStrEqual(docptr->doc->URL, URI)) {
 219         xsltTransformError(NULL, style, cur,
 220             "xsl:include : recursion detected on included URL %s\n", URI);
 221         goto error;
 222     }
 223     docptr = docptr->includes;
 224     }
 225 
 226     include = xsltLoadStyleDocument(style, URI);
 227     if (include == NULL) {
 228     xsltTransformError(NULL, style, cur,
 229         "xsl:include : unable to load %s\n", URI);
 230     goto error;
 231     }
 232 #ifdef XSLT_REFACTORED
 233     if (IS_XSLT_ELEM_FAST(cur) && (cur->psvi != NULL)) {
 234     ((xsltStyleItemIncludePtr) cur->psvi)->include = include;
 235     } else {
 236     xsltTransformError(NULL, style, cur,
 237         "Internal error: (xsltParseStylesheetInclude) "
 238         "The xsl:include element was not compiled.\n", URI);
 239     style->errors++;
 240     }
 241 #endif
 242     oldDoc = style->doc;
 243     style->doc = include->doc;
 244     /* chain to stylesheet for recursion checking */
 245     include->includes = style->includes;
 246     style->includes = include;
 247     oldNopreproc = style->nopreproc;
 248     style->nopreproc = include->preproc;
 249     /*
 250     * TODO: This will change some values of the
 251     *  including stylesheet with every included module
 252     *  (e.g. excluded-result-prefixes)
 253     *  We need to strictly seperate such stylesheet-owned values.
 254     */
 255     result = xsltParseStylesheetProcess(style, include->doc);
 256     style->nopreproc = oldNopreproc;
 257     include->preproc = 1;
 258     style->includes = include->includes;
 259     style->doc = oldDoc;
 260     if (result == NULL) {
 261     ret = -1;
 262     goto error;
 263     }
 264     ret = 0;
 265 
 266 error:
 267     if (uriRef != NULL)
 268     xmlFree(uriRef);
 269     if (base != NULL)
 270     xmlFree(base);
 271     if (URI != NULL)
 272     xmlFree(URI);
 273 
 274     return (ret);
 275 }
 276 
 277 /**
 278  * xsltNextImport:
 279  * @cur:  the current XSLT stylesheet
 280  *
 281  * Find the next stylesheet in import precedence.
 282  *
 283  * Returns the next stylesheet or NULL if it was the last one
 284  */
 285 
 286 xsltStylesheetPtr
 287 xsltNextImport(xsltStylesheetPtr cur) {
 288     if (cur == NULL)
 289     return(NULL);
 290     if (cur->imports != NULL)
 291     return(cur->imports);
 292     if (cur->next != NULL)
 293     return(cur->next) ;
 294     do {
 295     cur = cur->parent;
 296     if (cur == NULL) break;
 297     if (cur->next != NULL) return(cur->next);
 298     } while (cur != NULL);
 299     return(cur);
 300 }
 301 
 302 /**
 303  * xsltNeedElemSpaceHandling:
 304  * @ctxt:  an XSLT transformation context
 305  *
 306  * Checks whether that stylesheet requires white-space stripping
 307  *
 308  * Returns 1 if space should be stripped, 0 if not
 309  */
 310 
 311 int
 312 xsltNeedElemSpaceHandling(xsltTransformContextPtr ctxt) {
 313     xsltStylesheetPtr style;
 314 
 315     if (ctxt == NULL)
 316     return(0);
 317     style = ctxt->style;
 318     while (style != NULL) {
 319     if (style->stripSpaces != NULL)
 320         return(1);
 321     style = xsltNextImport(style);
 322     }
 323     return(0);
 324 }
 325 
 326 /**
 327  * xsltFindElemSpaceHandling:
 328  * @ctxt:  an XSLT transformation context
 329  * @node:  an XML node
 330  *
 331  * Find strip-space or preserve-space information for an element
 332  * respect the import precedence or the wildcards
 333  *
 334  * Returns 1 if space should be stripped, 0 if not, and 2 if everything
 335  *         should be CDTATA wrapped.
 336  */
 337 
 338 int
 339 xsltFindElemSpaceHandling(xsltTransformContextPtr ctxt, xmlNodePtr node) {
 340     xsltStylesheetPtr style;
 341     const xmlChar *val;
 342 
 343     if ((ctxt == NULL) || (node == NULL))
 344     return(0);
 345     style = ctxt->style;
 346     while (style != NULL) {
 347     if (node->ns != NULL) {
 348         val = (const xmlChar *)
 349           xmlHashLookup2(style->stripSpaces, node->name, node->ns->href);
 350             if (val == NULL) {
 351                 val = (const xmlChar *)
 352                     xmlHashLookup2(style->stripSpaces, BAD_CAST "*",
 353                                    node->ns->href);
 354             }
 355     } else {
 356         val = (const xmlChar *)
 357           xmlHashLookup2(style->stripSpaces, node->name, NULL);
 358     }
 359     if (val != NULL) {
 360         if (xmlStrEqual(val, (xmlChar *) "strip"))
 361         return(1);
 362         if (xmlStrEqual(val, (xmlChar *) "preserve"))
 363         return(0);
 364     }
 365     if (style->stripAll == 1)
 366         return(1);
 367     if (style->stripAll == -1)
 368         return(0);
 369 
 370     style = xsltNextImport(style);
 371     }
 372     return(0);
 373 }
 374 
 375 /**
 376  * xsltFindTemplate:
 377  * @ctxt:  an XSLT transformation context
 378  * @name: the template name
 379  * @nameURI: the template name URI
 380  *
 381  * Finds the named template, apply import precedence rule.
 382  * REVISIT TODO: We'll change the nameURI fields of
 383  *  templates to be in the string dict, so if the
 384  *  specified @nameURI is in the same dict, then use pointer
 385  *  comparison. Check if this can be done in a sane way.
 386  *  Maybe this function is not needed internally at
 387  *  transformation-time if we hard-wire the called templates
 388  *  to the caller.
 389  *
 390  * Returns the xsltTemplatePtr or NULL if not found
 391  */
 392 xsltTemplatePtr
 393 xsltFindTemplate(xsltTransformContextPtr ctxt, const xmlChar *name,
 394              const xmlChar *nameURI) {
 395     xsltTemplatePtr cur;
 396     xsltStylesheetPtr style;
 397 
 398     if ((ctxt == NULL) || (name == NULL))
 399     return(NULL);
 400     style = ctxt->style;
 401     while (style != NULL) {
 402         if (style->namedTemplates != NULL) {
 403             cur = (xsltTemplatePtr)
 404                 xmlHashLookup2(style->namedTemplates, name, nameURI);
 405             if (cur != NULL)
 406                 return(cur);
 407         }
 408 
 409     style = xsltNextImport(style);
 410     }
 411     return(NULL);
 412 }
 413