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             if (secres == 0)
 135                 xsltTransformError(NULL, NULL, NULL,
 136                      "xsl:import: read rights for %s denied\n",
 137                                  URI);
 138         goto error;
 139     }
 140     }
 141 
 142     import = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
 143                                   (void *) style, XSLT_LOAD_STYLESHEET);
 144     if (import == NULL) {
 145     xsltTransformError(NULL, style, cur,
 146         "xsl:import : unable to load %s\n", URI);
 147     goto error;
 148     }
 149 
 150     res = xsltParseStylesheetImportedDoc(import, style);
 151     if (res != NULL) {
 152     res->next = style->imports;
 153     style->imports = res;
 154     if (style->parent == NULL) {
 155         xsltFixImportedCompSteps(style, res);
 156     }
 157     ret = 0;
 158     } else {
 159     xmlFreeDoc(import);
 160     }
 161 
 162 error:
 163     if (uriRef != NULL)
 164     xmlFree(uriRef);
 165     if (base != NULL)
 166     xmlFree(base);
 167     if (URI != NULL)
 168     xmlFree(URI);
 169 
 170     return (ret);
 171 }
 172 
 173 /**
 174  * xsltParseStylesheetInclude:
 175  * @style:  the XSLT stylesheet
 176  * @cur:  the include node
 177  *
 178  * parse an XSLT stylesheet include element
 179  *
 180  * Returns 0 in case of success -1 in case of failure
 181  */
 182 
 183 int
 184 xsltParseStylesheetInclude(xsltStylesheetPtr style, xmlNodePtr cur) {
 185     int ret = -1;
 186     xmlDocPtr oldDoc;
 187     xmlChar *base = NULL;
 188     xmlChar *uriRef = NULL;
 189     xmlChar *URI = NULL;
 190     xsltStylesheetPtr result;
 191     xsltDocumentPtr include;
 192     xsltDocumentPtr docptr;
 193     int oldNopreproc;
 194 
 195     if ((cur == NULL) || (style == NULL))
 196     return (ret);
 197 
 198     uriRef = xmlGetNsProp(cur, (const xmlChar *)"href", NULL);
 199     if (uriRef == NULL) {
 200     xsltTransformError(NULL, style, cur,
 201         "xsl:include : missing href attribute\n");
 202     goto error;
 203     }
 204 
 205     base = xmlNodeGetBase(style->doc, cur);
 206     URI = xmlBuildURI(uriRef, base);
 207     if (URI == NULL) {
 208     xsltTransformError(NULL, style, cur,
 209         "xsl:include : invalid URI reference %s\n", uriRef);
 210     goto error;
 211     }
 212 
 213     /*
 214      * in order to detect recursion, we check all previously included
 215      * stylesheets.
 216      */
 217     docptr = style->includes;
 218     while (docptr != NULL) {
 219         if (xmlStrEqual(docptr->doc->URL, URI)) {
 220         xsltTransformError(NULL, style, cur,
 221             "xsl:include : recursion detected on included URL %s\n", URI);
 222         goto error;
 223     }
 224     docptr = docptr->includes;
 225     }
 226 
 227     include = xsltLoadStyleDocument(style, URI);
 228     if (include == NULL) {
 229     xsltTransformError(NULL, style, cur,
 230         "xsl:include : unable to load %s\n", URI);
 231     goto error;
 232     }
 233 #ifdef XSLT_REFACTORED
 234     if (IS_XSLT_ELEM_FAST(cur) && (cur->psvi != NULL)) {
 235     ((xsltStyleItemIncludePtr) cur->psvi)->include = include;
 236     } else {
 237     xsltTransformError(NULL, style, cur,
 238         "Internal error: (xsltParseStylesheetInclude) "
 239         "The xsl:include element was not compiled.\n", URI);
 240     style->errors++;
 241     }
 242 #endif
 243     oldDoc = style->doc;
 244     style->doc = include->doc;
 245     /* chain to stylesheet for recursion checking */
 246     include->includes = style->includes;
 247     style->includes = include;
 248     oldNopreproc = style->nopreproc;
 249     style->nopreproc = include->preproc;
 250     /*
 251     * TODO: This will change some values of the
 252     *  including stylesheet with every included module
 253     *  (e.g. excluded-result-prefixes)
 254     *  We need to strictly seperate such stylesheet-owned values.
 255     */
 256     result = xsltParseStylesheetProcess(style, include->doc);
 257     style->nopreproc = oldNopreproc;
 258     include->preproc = 1;
 259     style->includes = include->includes;
 260     style->doc = oldDoc;
 261     if (result == NULL) {
 262     ret = -1;
 263     goto error;
 264     }
 265     ret = 0;
 266 
 267 error:
 268     if (uriRef != NULL)
 269     xmlFree(uriRef);
 270     if (base != NULL)
 271     xmlFree(base);
 272     if (URI != NULL)
 273     xmlFree(URI);
 274 
 275     return (ret);
 276 }
 277 
 278 /**
 279  * xsltNextImport:
 280  * @cur:  the current XSLT stylesheet
 281  *
 282  * Find the next stylesheet in import precedence.
 283  *
 284  * Returns the next stylesheet or NULL if it was the last one
 285  */
 286 
 287 xsltStylesheetPtr
 288 xsltNextImport(xsltStylesheetPtr cur) {
 289     if (cur == NULL)
 290     return(NULL);
 291     if (cur->imports != NULL)
 292     return(cur->imports);
 293     if (cur->next != NULL)
 294     return(cur->next) ;
 295     do {
 296     cur = cur->parent;
 297     if (cur == NULL) break;
 298     if (cur->next != NULL) return(cur->next);
 299     } while (cur != NULL);
 300     return(cur);
 301 }
 302 
 303 /**
 304  * xsltNeedElemSpaceHandling:
 305  * @ctxt:  an XSLT transformation context
 306  *
 307  * Checks whether that stylesheet requires white-space stripping
 308  *
 309  * Returns 1 if space should be stripped, 0 if not
 310  */
 311 
 312 int
 313 xsltNeedElemSpaceHandling(xsltTransformContextPtr ctxt) {
 314     xsltStylesheetPtr style;
 315 
 316     if (ctxt == NULL)
 317     return(0);
 318     style = ctxt->style;
 319     while (style != NULL) {
 320     if (style->stripSpaces != NULL)
 321         return(1);
 322     style = xsltNextImport(style);
 323     }
 324     return(0);
 325 }
 326 
 327 /**
 328  * xsltFindElemSpaceHandling:
 329  * @ctxt:  an XSLT transformation context
 330  * @node:  an XML node
 331  *
 332  * Find strip-space or preserve-space information for an element
 333  * respect the import precedence or the wildcards
 334  *
 335  * Returns 1 if space should be stripped, 0 if not, and 2 if everything
 336  *         should be CDTATA wrapped.
 337  */
 338 
 339 int
 340 xsltFindElemSpaceHandling(xsltTransformContextPtr ctxt, xmlNodePtr node) {
 341     xsltStylesheetPtr style;
 342     const xmlChar *val;
 343 
 344     if ((ctxt == NULL) || (node == NULL))
 345     return(0);
 346     style = ctxt->style;
 347     while (style != NULL) {
 348     if (node->ns != NULL) {
 349         val = (const xmlChar *)
 350           xmlHashLookup2(style->stripSpaces, node->name, node->ns->href);
 351             if (val == NULL) {
 352                 val = (const xmlChar *)
 353                     xmlHashLookup2(style->stripSpaces, BAD_CAST "*",
 354                                    node->ns->href);
 355             }
 356     } else {
 357         val = (const xmlChar *)
 358           xmlHashLookup2(style->stripSpaces, node->name, NULL);
 359     }
 360     if (val != NULL) {
 361         if (xmlStrEqual(val, (xmlChar *) "strip"))
 362         return(1);
 363         if (xmlStrEqual(val, (xmlChar *) "preserve"))
 364         return(0);
 365     }
 366     if (style->stripAll == 1)
 367         return(1);
 368     if (style->stripAll == -1)
 369         return(0);
 370 
 371     style = xsltNextImport(style);
 372     }
 373     return(0);
 374 }
 375 
 376 /**
 377  * xsltFindTemplate:
 378  * @ctxt:  an XSLT transformation context
 379  * @name: the template name
 380  * @nameURI: the template name URI
 381  *
 382  * Finds the named template, apply import precedence rule.
 383  * REVISIT TODO: We'll change the nameURI fields of
 384  *  templates to be in the string dict, so if the
 385  *  specified @nameURI is in the same dict, then use pointer
 386  *  comparison. Check if this can be done in a sane way.
 387  *  Maybe this function is not needed internally at
 388  *  transformation-time if we hard-wire the called templates
 389  *  to the caller.
 390  *
 391  * Returns the xsltTemplatePtr or NULL if not found
 392  */
 393 xsltTemplatePtr
 394 xsltFindTemplate(xsltTransformContextPtr ctxt, const xmlChar *name,
 395              const xmlChar *nameURI) {
 396     xsltTemplatePtr cur;
 397     xsltStylesheetPtr style;
 398 
 399     if ((ctxt == NULL) || (name == NULL))
 400     return(NULL);
 401     style = ctxt->style;
 402     while (style != NULL) {
 403         if (style->namedTemplates != NULL) {
 404             cur = (xsltTemplatePtr)
 405                 xmlHashLookup2(style->namedTemplates, name, nameURI);
 406             if (cur != NULL)
 407                 return(cur);
 408         }
 409 
 410     style = xsltNextImport(style);
 411     }
 412     return(NULL);
 413 }
 414