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 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
37 using System.Xml.XPath;
40 namespace System.Security.Cryptography.Xml
43 // www.w3.org/TR/xmldsig-core/
44 // see Section 6.6.3 of the XMLDSIG specification
45 public class XmlDsigXPathTransform : Transform
48 private Type [] input;
49 private Type [] output;
50 private XmlNodeList xpath;
51 private XmlDocument doc;
52 private XsltContext ctx;
54 public XmlDsigXPathTransform ()
56 Algorithm = XmlSignature.AlgorithmNamespaces.XmlDsigXPathTransform;
59 public override Type [] InputTypes {
63 input [0] = typeof (System.IO.Stream);
64 input [1] = typeof (System.Xml.XmlDocument);
65 input [2] = typeof (System.Xml.XmlNodeList);
71 public override Type[] OutputTypes {
74 // this way the result is cached if called multiple time
75 output = new Type [1];
76 output [0] = typeof (System.Xml.XmlNodeList);
82 protected override XmlNodeList GetInnerXml ()
86 XmlDocument xpdoc = new XmlDocument ();
87 xpdoc.LoadXml ("<XPath xmlns=\"" + XmlSignature.NamespaceURI + "\"></XPath>");
88 xpath = xpdoc.ChildNodes;
93 [MonoTODO ("Evaluation of extension function here() results in different from MS.NET (is MS.NET really correct??).")]
94 public override object GetOutput ()
96 if ((xpath == null) || (doc == null))
97 return new XmlDsigNodeList (new ArrayList ());
98 // evaluate every time since input or xpath might have changed.
100 for (int i = 0; i < xpath.Count; i++) {
101 switch (xpath [i].NodeType) {
102 case XmlNodeType.Text:
103 case XmlNodeType.CDATA:
104 case XmlNodeType.Element:
105 x += xpath [i].InnerText;
110 ctx = new XmlDsigXPathContext (doc);
111 foreach (XmlNode n in xpath) {
112 XPathNavigator nav = n.CreateNavigator ();
113 XPathNodeIterator iter = nav.Select ("namespace::*");
114 while (iter.MoveNext ())
115 if (iter.Current.LocalName != "xml")
116 ctx.AddNamespace (iter.Current.LocalName, iter.Current.Value);
118 return EvaluateMatch (doc, x);
121 public override object GetOutput (Type type)
123 if (type != typeof (XmlNodeList))
124 throw new ArgumentException ("type");
128 private XmlDsigNodeList EvaluateMatch (XmlNode n, string xpath)
130 ArrayList al = new ArrayList ();
131 // Strictly to say, document node is explicitly
132 // excluded by W3C spec (context node is initialized
133 // to the document root and XPath expression is
134 // "//. | //@* | //namespace::*)
135 XPathNavigator nav = n.CreateNavigator ();
136 XPathExpression exp = nav.Compile (xpath);
137 exp.SetContext (ctx);
138 EvaluateMatch (n, exp, al);
139 return new XmlDsigNodeList (al);
142 private void EvaluateMatch (XmlNode n, XPathExpression exp, ArrayList al)
144 if (NodeMatches (n, exp))
146 if (n.Attributes != null)
147 for (int i = 0; i < n.Attributes.Count; i++)
148 if (NodeMatches (n.Attributes [i], exp))
149 al.Add (n.Attributes [i]);
150 for (int i = 0; i < n.ChildNodes.Count; i++)
151 EvaluateMatch (n.ChildNodes [i], exp, al);
154 private bool NodeMatches (XmlNode n, XPathExpression exp)
156 // This looks waste of memory since it creates
157 // XPathNavigator every time, but even if we use
158 // XPathNodeIterator.Current, it also clones every time.
159 object ret = n.CreateNavigator ().Evaluate (exp);
163 double d = (double) ret;
164 return !(d == 0.0 || Double.IsNaN (d));
167 return ((string) ret).Length > 0;
168 if (ret is XPathNodeIterator) {
169 XPathNodeIterator retiter = (XPathNodeIterator) ret;
170 return retiter.Count > 0;
175 public override void LoadInnerXml (XmlNodeList nodeList)
177 if (nodeList == null)
178 throw new CryptographicException ("nodeList");
182 public override void LoadInput (object obj)
184 // possible input: Stream, XmlDocument, and XmlNodeList
186 doc = new XmlDocument ();
187 doc.PreserveWhitespace = true;
188 doc.XmlResolver = GetResolver ();
189 doc.Load (new XmlSignatureStreamReader (
190 new StreamReader ((Stream) obj)));
192 else if (obj is XmlDocument) {
193 doc = (obj as XmlDocument);
195 else if (obj is XmlNodeList) {
196 doc = new XmlDocument ();
197 doc.XmlResolver = GetResolver ();
198 foreach (XmlNode xn in (obj as XmlNodeList)) {
199 XmlNode importedNode = doc.ImportNode (xn, true);
200 doc.AppendChild (importedNode);
205 // Internal classes to support XPath extension function here()
207 internal class XmlDsigXPathContext : XsltContext
209 XmlDsigXPathFunctionHere here;
210 public XmlDsigXPathContext (XmlNode node)
212 here = new XmlDsigXPathFunctionHere (node);
215 public override IXsltContextFunction ResolveFunction (
216 string prefix, string name, XPathResultType [] argType)
218 // Here MS.NET incorrectly allows arbitrary
219 // name e.g. "heretic()".
220 if (name == "here" &&
221 prefix == String.Empty &&
228 public override bool Whitespace {
232 public override bool PreserveWhitespace (XPathNavigator node)
237 public override int CompareDocument (string s1, string s2)
239 return String.Compare (s1, s2);
242 public override IXsltContextVariable ResolveVariable (string prefix, string name)
244 throw new InvalidOperationException ();
248 internal class XmlDsigXPathFunctionHere : IXsltContextFunction
252 static XPathResultType [] types;
253 static XmlDsigXPathFunctionHere ()
255 types = new XPathResultType [0];
260 XPathNodeIterator xpathNode;
262 public XmlDsigXPathFunctionHere (XmlNode node)
264 xpathNode = node.CreateNavigator ().Select (".");
267 public XPathResultType [] ArgTypes {
268 get { return types; }
271 public int Maxargs { get { return 0; } }
273 public int Minargs { get { return 0; } }
275 public XPathResultType ReturnType {
276 get { return XPathResultType.NodeSet; }
279 public object Invoke (XsltContext ctx, object [] args, XPathNavigator docContext)
281 if (args.Length != 0)
282 throw new ArgumentException ("Not allowed arguments for function here().", "args");
284 return xpathNode.Clone ();