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 ()
97 if ((xpath == null) || (doc == null))
98 return new XmlDsigNodeList (new ArrayList ());
101 return new XmlDsigNodeList (new ArrayList ());
103 // evaluate every time since input or xpath might have changed.
105 for (int i = 0; i < xpath.Count; i++) {
106 switch (xpath [i].NodeType) {
107 case XmlNodeType.Text:
108 case XmlNodeType.CDATA:
109 case XmlNodeType.Element:
110 x += xpath [i].InnerText;
115 ctx = new XmlDsigXPathContext (doc);
116 foreach (XmlNode n in xpath) {
117 XPathNavigator nav = n.CreateNavigator ();
118 XPathNodeIterator iter = nav.Select ("namespace::*");
119 while (iter.MoveNext ())
120 if (iter.Current.LocalName != "xml")
121 ctx.AddNamespace (iter.Current.LocalName, iter.Current.Value);
123 return EvaluateMatch (doc, x);
126 public override object GetOutput (Type type)
128 if (type != typeof (XmlNodeList))
129 throw new ArgumentException ("type");
133 private XmlDsigNodeList EvaluateMatch (XmlNode n, string xpath)
135 ArrayList al = new ArrayList ();
136 // Strictly to say, document node is explicitly
137 // excluded by W3C spec (context node is initialized
138 // to the document root and XPath expression is
139 // "//. | //@* | //namespace::*)
140 XPathNavigator nav = n.CreateNavigator ();
141 XPathExpression exp = nav.Compile (xpath);
142 exp.SetContext (ctx);
143 EvaluateMatch (n, exp, al);
144 return new XmlDsigNodeList (al);
147 private void EvaluateMatch (XmlNode n, XPathExpression exp, ArrayList al)
149 if (NodeMatches (n, exp))
151 if (n.Attributes != null)
152 for (int i = 0; i < n.Attributes.Count; i++)
153 if (NodeMatches (n.Attributes [i], exp))
154 al.Add (n.Attributes [i]);
155 for (int i = 0; i < n.ChildNodes.Count; i++)
156 EvaluateMatch (n.ChildNodes [i], exp, al);
159 private bool NodeMatches (XmlNode n, XPathExpression exp)
161 // This looks waste of memory since it creates
162 // XPathNavigator every time, but even if we use
163 // XPathNodeIterator.Current, it also clones every time.
164 object ret = n.CreateNavigator ().Evaluate (exp);
168 double d = (double) ret;
169 return !(d == 0.0 || Double.IsNaN (d));
172 return ((string) ret).Length > 0;
173 if (ret is XPathNodeIterator) {
174 XPathNodeIterator retiter = (XPathNodeIterator) ret;
175 return retiter.Count > 0;
180 public override void LoadInnerXml (XmlNodeList nodeList)
182 if (nodeList == null)
183 throw new CryptographicException ("nodeList");
187 public override void LoadInput (object obj)
189 // possible input: Stream, XmlDocument, and XmlNodeList
191 doc = new XmlDocument ();
192 doc.PreserveWhitespace = true;
194 doc.XmlResolver = GetResolver ();
196 doc.Load (new XmlSignatureStreamReader (
197 new StreamReader ((Stream) obj)));
199 else if (obj is XmlDocument) {
200 doc = (obj as XmlDocument);
202 else if (obj is XmlNodeList) {
203 doc = new XmlDocument ();
205 doc.XmlResolver = GetResolver ();
207 foreach (XmlNode xn in (obj as XmlNodeList)) {
208 XmlNode importedNode = doc.ImportNode (xn, true);
209 doc.AppendChild (importedNode);
214 // Internal classes to support XPath extension function here()
216 internal class XmlDsigXPathContext : XsltContext
218 XmlDsigXPathFunctionHere here;
219 public XmlDsigXPathContext (XmlNode node)
221 here = new XmlDsigXPathFunctionHere (node);
224 public override IXsltContextFunction ResolveFunction (
225 string prefix, string name, XPathResultType [] argType)
227 // Here MS.NET incorrectly allows arbitrary
228 // name e.g. "heretic()".
229 if (name == "here" &&
230 prefix == String.Empty &&
237 public override bool Whitespace {
241 public override bool PreserveWhitespace (XPathNavigator node)
246 public override int CompareDocument (string s1, string s2)
248 return String.Compare (s1, s2);
251 public override IXsltContextVariable ResolveVariable (string prefix, string name)
253 throw new InvalidOperationException ();
257 internal class XmlDsigXPathFunctionHere : IXsltContextFunction
261 static XPathResultType [] types;
262 static XmlDsigXPathFunctionHere ()
264 types = new XPathResultType [0];
269 XPathNodeIterator xpathNode;
271 public XmlDsigXPathFunctionHere (XmlNode node)
273 xpathNode = node.CreateNavigator ().Select (".");
276 public XPathResultType [] ArgTypes {
277 get { return types; }
280 public int Maxargs { get { return 0; } }
282 public int Minargs { get { return 0; } }
284 public XPathResultType ReturnType {
285 get { return XPathResultType.NodeSet; }
288 public object Invoke (XsltContext ctx, object [] args, XPathNavigator docContext)
290 if (args.Length != 0)
291 throw new ArgumentException ("Not allowed arguments for function here().", "args");
293 return xpathNode.Clone ();