Jump to content

Problem with XSL Transformation (multiple templates)


raz3r

Recommended Posts

First of all, hi everybody :) This is my first post here and I thank you all in advance for answers. Before introducing my problem I want to explain my work so that you can understand what I'm trying to do. I am writing a C# application that transforms visual Firewall rules into Linux IPTables code lines. I choosed to use XML and XSLT to make it easier for me so let's take a look at the XML file (this is not the real XML file ofcourse :)):

<?xml version="1.0" ?><?xml-stylesheet type="text/xsl" href="test.xslt"?> <FIREWALL name="Firewall Test">	<ETH name="0">		<TYPE name="Header">			<RULE name="" override="IPTABLES -F"/>		</TYPE>		<TYPE name="Service">			<RULE name="" service="ICMP" policy="REJECT"/>		</TYPE>	</ETH>	<ETH name="1">		<TYPE name="Test">			<RULE name="" chain="INPUT" policy="DROP"/>		</TYPE>		</ETH></FIREWALL>

This is the structure of my XML file that i build dinamically with the C# application. The XSLT file reads the XML and then transforms it into a list of IPTables commands.The XSLT transforms the rules depending on the attribute "name" of TYPE. For example if name = Header then the XSLT should write on the output file just the "override" attribute of RULE. Instead if the name = Service then it should write on the output IPTABLES bla bla "service" bla bla "policy" bla bla. There are other TYPES of course and I am gonna write the right template for every kind of them (this is not a problem I can do that).Now the problem is that I don't know how to build an XSLT like that. I already did all the transformations in different templates but my program does not want to work them out >.<My XSLT file should look like that (I don't know if this is correct!)

...	<xsl:template match="TYPE">	<xsl:choose>		<xsl:when test="@name = 'Header'">			<xsl:call-template name="Header"></xsl:call-template>		</xsl:when>		<xsl:when test="@name = 'Service'">			<xsl:call-template name="Service"></xsl:call-template>		</xsl:when>	</xsl:choose>	</xsl:template>...	<xsl:template name="Header" match="RULE">		<xsl:value-of select="@override"></xsl:value-of>	</xsl:template>...

The problem is that this structure does not work, I mean it works but it looks like that the XSLT calls those templates (Header, Service etc...) even if they're not called by the XSL choose (I used the Visual Studio XML debug to see what happens). What can I do to solve the problem? It is the first time that I work with XML and XSLT so forgive me if I am a noob :( I've got more skill with C# I promise! :)

Link to comment
Share on other sites

Could you "hardcode" your example into the output you expect from it? As I'm not sure I got it from your explanation.Instead of matching elements and then checking the name attribute, you could do the name checking in the template matching itself. I think this should make things easier... like:

<!-- Do whatever must happen with TYPE elements that have a name attribute with a value of "Header". The following is just what I gathered from your explanation. --><xsl:template match="TYPE[@name='Header']" xml:space="preserve"><xsl:value-of select="RULE/@override" /></xsl:template><!-- Do whatever must happen with TYPE elements that have a name attribute with a value of "Service". The following is just what I gathered from your explanation. --><xsl:template match="TYPE[@name='Service']" xml:space="preserve">IPTABLES <xsl:value-of select="RULE/@name" /> service <xsl:value-of="RULE/@service" /> policy <xsl:value-of select="RULE/@policy" /></xsl:template>

If I've understood correctly, you're trying to generate a list of text commands, one per line. The xml:space addition is just to make sure each template is ended by a new line.If applied only in the context of "ETH[@name=0]", the result from those templates should be

IPTABLES -FIPTABLES service ICMP policy REJECT
Link to comment
Share on other sites

That's exactly what I want to do, a list of text commands, one per line :) Tomorrow I am gonna try it out and then I'll post the complete XSL code, hope it will works! In the meanwhile many thanks ^^

Link to comment
Share on other sites

I'm glad to say that it works! Unfortunately the xml:space does not. I mean if I open the XML with Internet Explorer I get this result:

IPTABLES -t IPTABLES ICMP 80 IPTABLES -F IPTABLES -X

With Visual Studio debugger I get this:

  			IPTABLES -t				IPTABLES ICMP 80				IPTABLES -F	    			IPTABLES -X

With Google Chrome it shows nothing :/In the end my XSLT by C# will create a *.txt file so maybe it is just a browser problem.By the way here are the codes of XML and XSLT file, I changed them a little bit so take a look!

<?xml version="1.0" ?><?xml-stylesheet type="text/xsl" href="test.xslt"?><FW name="0">  <ETH name="0">	<RULE type="Header" name="" override="IPTABLES -t" service="ERROR" port="ERROR"/>	<RULE type="Service" name="" override="" service="ICMP" port="80"/>	<RULE type="Header" override="IPTABLES -F"/>  </ETH>  <ETH name="1">	<RULE type="Header" override="IPTABLES -X"/>  </ETH></FW>

<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">	<xsl:output method="text"/>	<xsl:template match="RULE[@type='Header']" xml:space="preserve">		<xsl:value-of select="@override" />	</xsl:template>	<xsl:template match="RULE[@type='Service']" xml:space="preserve">		IPTABLES <xsl:value-of select="@service" /> <xsl:value-of select="@port" />	</xsl:template></xsl:stylesheet>

Link to comment
Share on other sites

Despite you having method="text", IE is parsing the resulting document as HTML, and whitespace is therefore "accurately" handled as in HTML.The output from the Visual Studio debugger is the real one. If you look carefully at the whitespace from it (by highlighting it), you'll notice that's because of the whitespace text nodes in the XML. You can take care of those in several ways. Probably the simplest is by explicitly letting any text nodes do nothing (as opposed to being outputted, which is the default XSLT behaviour). You can do this by adding the following template:

<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">    <xsl:output method="text"/>    <xsl:template match="text()" />    <xsl:template match="RULE[@type='Header']" xml:space="preserve"><xsl:value-of select="@override" /></xsl:template>    <xsl:template match="RULE[@type='Service']" xml:space="preserve">IPTABLES <xsl:value-of select="@service" /> <xsl:value-of select="@port" /></xsl:template></xsl:stylesheet>

BTW, instead of transforming the XML within a browser, you should probably transform it within C# itself.

Link to comment
Share on other sites

  • 3 weeks later...

Hi again guys! Your solution works very well, the problem is that transforming RULES on type attribute does not preserve the order of the RULES and you guys know that in a Linux Firewall the order is the most important thing. So I decided to use the for-each element but the result is multiplied by the RULES number. I mean if I transform the XML with this XSLT:

<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  <xsl:output method="text"/>  <xsl:template match="text()" />  <xsl:template match="RULE[@type='Header']" xml:space="preserve"><xsl:value-of select="@override" />	</xsl:template>  <xsl:template match="RULE[@type='Override']" xml:space="preserve"><xsl:value-of select="@override" />	</xsl:template>  <xsl:template match="RULE[@type='Comment']" xml:space="preserve"><xsl:value-of select="@override" />	</xsl:template>  <xsl:template match="RULE[@type='Chain']" xml:space="preserve">$IPTABLES -N <xsl:value-of select="@chain"/>	</xsl:template>  <xsl:template match="RULE[@type='Service']" xml:space="preserve">	<xsl:if test="not(boolean(string (@log)))"> <!--no log-->	  <xsl:if test="not(boolean(string (@destination)))"> <!--tipo source-->		<xsl:if test="not(boolean(string (@denysource)))"><!--source non è negato-->	 $IPTABLES -A <xsl:value-of select="@chain"/> -s <xsl:value-of select="@source"/> -j <xsl:value-of select="@policy"/> 		</xsl:if> 		<xsl:if test="boolean(string (@denysource))"> <!--source è negato-->	 $IPTABLES -A <xsl:value-of select="@chain"/> -s ! <xsl:value-of select="@source"/> -j <xsl:value-of select="@policy"/> 		</xsl:if>	  </xsl:if> 	  <xsl:if test="not(boolean(string (@source)))"> <!--tipo destination-->		<xsl:if test="not(boolean(string (@denydestination)))"> <!--destination non è negato-->	 $IPTABLES -A <xsl:value-of select="@chain"/> -d <xsl:value-of select="@destination"/> -j <xsl:value-of select="@policy"/> 		</xsl:if>		<xsl:if test="boolean(string (@denydestination))"> <!--destination è negato-->	 $IPTABLES -A <xsl:value-of select="@chain"/> -d ! <xsl:value-of select="@destination"/> -j <xsl:value-of select="@policy"/> 		</xsl:if>	  </xsl:if> 	</xsl:if>	<xsl:if test="boolean(string (@log))"> <!--si log-->	 <xsl:if test="not(boolean(string (@destination)))"> <!--tipo source-->		<xsl:if test="not(boolean(string (@denysource)))"><!--source non è negato-->	 $IPTABLES -A <xsl:value-of select="@chain"/> -s <xsl:value-of select="@source"/> -j <xsl:value-of select="@policy"/>	 $IPTABLES -A <xsl:value-of select="@chain"/> -s <xsl:value-of select="@source"/> -J LOG <xsl:value-of select="@logoptions"/> 		</xsl:if> 		<xsl:if test="boolean(string (@denysource))"> <!--source è negato-->	 $IPTABLES -A <xsl:value-of select="@chain"/> -s ! <xsl:value-of select="@source"/> -j <xsl:value-of select="@policy"/>	 $IPTABLES -A <xsl:value-of select="@chain"/> -s ! <xsl:value-of select="@source"/> -J LOG <xsl:value-of select="@logoptions"/> 		</xsl:if>	  </xsl:if> 	  <xsl:if test="not(boolean(string (@source)))"> <!--tipo destination-->		<xsl:if test="not(boolean(string (@denydestination)))"> <!--destination non è negato-->	 $IPTABLES -A <xsl:value-of select="@chain"/> -d <xsl:value-of select="@destination"/> -j <xsl:value-of select="@policy"/>	 $IPTABLES -A <xsl:value-of select="@chain"/> -d <xsl:value-of select="@destination"/> -J LOG <xsl:value-of select="@logoptions"/> 		</xsl:if>		<xsl:if test="boolean(string (@denydestination))"> <!--destination è negato-->	 $IPTABLES -A <xsl:value-of select="@chain"/> -d ! <xsl:value-of select="@destination"/> -j <xsl:value-of select="@policy"/>	 $IPTABLES -A <xsl:value-of select="@chain"/> -d ! <xsl:value-of select="@destination"/> -J LOG <xsl:value-of select="@logoptions"/> 		</xsl:if>	  </xsl:if> 	</xsl:if>	</xsl:template></xsl:stylesheet>

The result is fine, beside white spaces and the order of the RULES everything is all right.Instead, if I try to transform the XML with this one (wich is not complet but it doesn't matter):

<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  <xsl:output method="text"/>  <xsl:template match="text()" />  <xsl:template match="/">	<xsl:apply-templates/>  </xsl:template>  <xsl:template match="RULE">	<xsl:for-each select="/FIREWALL/ETH/RULE">	  <xsl:if test="@type='Header'">		<!--REGOLA DI TIPO HEADER-->		<xsl:value-of select="@override" /><xsl:text></xsl:text>	  </xsl:if>	  <xsl:if test="@type='Override'">		<!--REGOLA DI TIPO OVERRIDE-->		<xsl:value-of select="@override" /><xsl:text></xsl:text>	  </xsl:if>	  <xsl:if test="@type='Comment'">		<!--REGOLA DI TIPO COMMENT-->		<xsl:value-of select="@override" /><xsl:text></xsl:text>	  </xsl:if>	</xsl:for-each>  </xsl:template></xsl:stylesheet>

The result is fine but if I have 15 RULES then I've got 15 results :/ like:

$IPTABLES --flush$IPTABLES -t nat --flush$IPTABLES -t mangle --flush$IPTABLES -X$IPTABLES -t nat -X$IPTABLES -t mangle -X$IPTABLES -F$IPTABLES -F -nat$IPTABLES -F -t mangle$IPTABLES -X -t nat$IPTABLES -X -t mangle$IPTABLES -F INPUT$IPTABLES -F OUTPUT$IPTABLES -F FORWARD$IPTABLES -P INPUT DROP$IPTABLES -P OUTPUT DROP$IPTABLES -P FORWARD DROP$IPTABLES --flush$IPTABLES -t nat --flush$IPTABLES -t mangle --flush$IPTABLES -X$IPTABLES -t nat -X$IPTABLES -t mangle -X$IPTABLES -F$IPTABLES -F -nat$IPTABLES -F -t mangle$IPTABLES -X -t nat$IPTABLES -X -t mangle$IPTABLES -F INPUT$IPTABLES -F OUTPUT$IPTABLES -F FORWARD$IPTABLES -P INPUT DROP$IPTABLES -P OUTPUT DROP$IPTABLES -P FORWARD DROP... (x N)

What did I do wrong?

Link to comment
Share on other sites

Templates are executed in the order of the matched elements in the source document... what other kind of order do you need? By type? If so, try to create a root template in which you call the rest in the order you desire, like:

<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">    <xsl:output method="text"/>    <xsl:template match="text()" />    <xsl:template match="/">        <xsl:apply-templates match="//RULE[@type='Header']" />        <xsl:apply-templates match="//RULE[@type='Service']" />    </xsl:template>    <xsl:template match="RULE[@type='Header']" xml:space="preserve"><xsl:value-of select="@override" /></xsl:template>    <xsl:template match="RULE[@type='Service']" xml:space="preserve">IPTABLES <xsl:value-of select="@service" /> <xsl:value-of select="@port" /></xsl:template></xsl:stylesheet>

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...