2 // XmlDsigXPathTransform.cs -
3 // XmlDsigXPathTransform implementation for XML Signature
4 // http://www.w3.org/TR/1999/REC-xpath-19991116
7 // Sebastien Pouliot <sebastien@ximian.com>
8 // Atsushi Enomoto <atsushi@ximian.com>
10 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
11 // (C) 2004 Novell (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System
.Collections
;
39 using System
.Xml
.XPath
;
42 namespace System
.Security
.Cryptography
.Xml
45 // www.w3.org/TR/xmldsig-core/
46 // see Section 6.6.3 of the XMLDSIG specification
47 public class XmlDsigXPathTransform
: Transform
50 private Type
[] input
;
51 private Type
[] output
;
52 private XmlNodeList xpath
;
53 private XmlDocument doc
;
54 private XsltContext ctx
;
56 public XmlDsigXPathTransform ()
58 Algorithm
= "http://www.w3.org/TR/1999/REC-xpath-19991116";
61 public override Type
[] InputTypes
{
65 // this way the result is cached if called multiple time
67 input
[0] = typeof (System
.IO
.Stream
);
68 input
[1] = typeof (System
.Xml
.XmlDocument
);
69 input
[2] = typeof (System
.Xml
.XmlNodeList
);
76 public override Type
[] OutputTypes
{
80 // this way the result is cached if called multiple time
81 output
= new Type
[1];
82 output
[0] = typeof (System
.Xml
.XmlNodeList
);
89 protected override XmlNodeList
GetInnerXml ()
93 XmlDocument xpdoc
= new XmlDocument ();
94 xpdoc
.LoadXml ("<XPath xmlns=\"" + XmlSignature
.NamespaceURI
+ "\"></XPath>");
95 xpath
= xpdoc
.ChildNodes
;
100 [MonoTODO ("Evaluation of extension function here() results in different from MS.NET (is MS.NET really correct??).")]
101 public override object GetOutput ()
104 return new XmlDsigNodeList (new ArrayList ());
106 // evaluate every time since input or xpath might have changed.
108 for (int i
= 0; i
< xpath
.Count
; i
++) {
109 switch (xpath
[i
].NodeType
) {
110 case XmlNodeType
.Text
:
111 case XmlNodeType
.CDATA
:
112 case XmlNodeType
.Element
:
113 x
+= xpath
[i
].InnerText
;
118 ctx
= new XmlDsigXPathContext (doc
);
119 foreach (XmlNode n
in xpath
) {
120 XPathNavigator nav
= n
.CreateNavigator ();
121 XPathNodeIterator iter
= nav
.Select ("namespace::*");
122 while (iter
.MoveNext ())
123 if (iter
.Current
.LocalName
!= "xml")
124 ctx
.AddNamespace (iter
.Current
.LocalName
, iter
.Current
.Value
);
126 return EvaluateMatch (doc
, x
);
129 public override object GetOutput (Type type
)
131 if (type
!= typeof (XmlNodeList
))
132 throw new ArgumentException ("type");
136 private XmlDsigNodeList
EvaluateMatch (XmlNode n
, string xpath
)
138 ArrayList al
= new ArrayList ();
139 // Strictly to say, document node is explicitly
140 // excluded by W3C spec (context node is initialized
141 // to the document root and XPath expression is
142 // "//. | //@* | //namespace::*)
143 XPathNavigator nav
= n
.CreateNavigator ();
144 XPathExpression exp
= nav
.Compile (xpath
);
145 exp
.SetContext (ctx
);
146 EvaluateMatch (n
, exp
, al
);
147 return new XmlDsigNodeList (al
);
150 private void EvaluateMatch (XmlNode n
, XPathExpression exp
, ArrayList al
)
152 if (NodeMatches (n
, exp
))
154 if (n
.Attributes
!= null)
155 for (int i
= 0; i
< n
.Attributes
.Count
; i
++)
156 if (NodeMatches (n
.Attributes
[i
], exp
))
157 al
.Add (n
.Attributes
[i
]);
158 for (int i
= 0; i
< n
.ChildNodes
.Count
; i
++)
159 EvaluateMatch (n
.ChildNodes
[i
], exp
, al
);
162 private bool NodeMatches (XmlNode n
, XPathExpression exp
)
164 // This looks waste of memory since it creates
165 // XPathNavigator every time, but even if we use
166 // XPathNodeIterator.Current, it also clones every time.
167 object ret
= n
.CreateNavigator ().Evaluate (exp
);
171 double d
= (double) ret
;
172 return !(d
== 0.0 || d
== double.NaN
);
175 return ((string) ret
).Length
> 0;
176 if (ret
is XPathNodeIterator
) {
177 XPathNodeIterator retiter
= (XPathNodeIterator
) ret
;
178 return retiter
.Count
> 0;
183 public override void LoadInnerXml (XmlNodeList nodeList
)
185 if (nodeList
== null)
186 throw new CryptographicException ("nodeList");
190 public override void LoadInput (object obj
)
192 // possible input: Stream, XmlDocument, and XmlNodeList
194 doc
= new XmlDocument ();
196 doc
.XmlResolver
= GetResolver ();
198 doc
.Load (obj
as Stream
);
200 else if (obj
is XmlDocument
) {
201 doc
= (obj
as XmlDocument
);
203 else if (obj
is XmlNodeList
) {
204 doc
= new XmlDocument ();
206 doc
.XmlResolver
= GetResolver ();
208 foreach (XmlNode xn
in (obj
as XmlNodeList
)) {
209 XmlNode importedNode
= doc
.ImportNode (xn
, true);
210 doc
.AppendChild (importedNode
);
215 // Internal classes to support XPath extension function here()
217 internal class XmlDsigXPathContext
: XsltContext
219 XmlDsigXPathFunctionHere here
;
220 public XmlDsigXPathContext (XmlNode node
)
222 here
= new XmlDsigXPathFunctionHere (node
);
225 public override IXsltContextFunction
ResolveFunction (
226 string prefix
, string name
, XPathResultType
[] argType
)
228 // Here MS.NET incorrectly allows arbitrary
229 // name e.g. "heretic()".
230 if (name
== "here" &&
231 prefix
== String
.Empty
&&
238 public override bool Whitespace
{
242 public override bool PreserveWhitespace (XPathNavigator node
)
247 public override int CompareDocument (string s1
, string s2
)
249 return String
.Compare (s1
, s2
);
252 public override IXsltContextVariable
ResolveVariable (string prefix
, string name
)
254 throw new InvalidOperationException ();
258 internal class XmlDsigXPathFunctionHere
: IXsltContextFunction
262 static XPathResultType
[] types
;
263 static XmlDsigXPathFunctionHere ()
265 types
= new XPathResultType
[0];
270 XPathNodeIterator xpathNode
;
272 public XmlDsigXPathFunctionHere (XmlNode node
)
274 xpathNode
= node
.CreateNavigator ().Select (".");
277 public XPathResultType
[] ArgTypes
{
278 get { return types; }
281 public int Maxargs { get { return 0; }
}
283 public int Minargs { get { return 0; }
}
285 public XPathResultType ReturnType
{
286 get { return XPathResultType.NodeSet; }
289 public object Invoke (XsltContext ctx
, object [] args
, XPathNavigator docContext
)
291 if (args
.Length
!= 0)
292 throw new ArgumentException ("Not allowed arguments for function here().", "args");
294 return xpathNode
.Clone ();