185e8b3dcd442fc7045ac16025338b3b953effc0
[mono.git] / mcs / class / System.Security / System.Security.Cryptography.Xml / XmlDsigXPathTransform.cs
1 //
2 // XmlDsigXPathTransform.cs - 
3 //      XmlDsigXPathTransform implementation for XML Signature
4 // http://www.w3.org/TR/1999/REC-xpath-19991116 
5 //
6 // Author:
7 //      Sebastien Pouliot <sebastien@ximian.com>
8 //      Atsushi Enomoto <atsushi@ximian.com>
9 //
10 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
11 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
12 //
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:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
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.
31 //
32
33 using System.Collections;
34 using System.IO;
35 using System.Text;
36 using System.Xml;
37 using System.Xml.XPath;
38 using System.Xml.Xsl;
39
40 namespace System.Security.Cryptography.Xml 
41 {
42
43         // www.w3.org/TR/xmldsig-core/
44         // see Section 6.6.3 of the XMLDSIG specification
45         public class XmlDsigXPathTransform : Transform 
46         {
47
48                 private Type [] input;
49                 private Type [] output;
50                 private XmlNodeList xpath;
51                 private XmlDocument doc;
52                 private XsltContext ctx;
53
54                 public XmlDsigXPathTransform () 
55                 {
56                         Algorithm = XmlSignature.AlgorithmNamespaces.XmlDsigXPathTransform;
57                 }
58
59                 public override Type [] InputTypes {
60                         get {
61                                 if (input == null) {
62                                         input = new Type [3];
63                                         input [0] = typeof (System.IO.Stream);
64                                         input [1] = typeof (System.Xml.XmlDocument);
65                                         input [2] = typeof (System.Xml.XmlNodeList);
66                                 }
67                                 return input;
68                         }
69                 }
70
71                 public override Type[] OutputTypes {
72                         get {
73                                 if (output == null) {
74                                         // this way the result is cached if called multiple time
75                                         output = new Type [1];
76                                         output [0] = typeof (System.Xml.XmlNodeList);
77                                 }
78                                 return output;
79                         }
80                 }
81
82                 protected override XmlNodeList GetInnerXml () 
83                 {
84                         if (xpath == null) {
85                                 // default value
86                                 XmlDocument xpdoc = new XmlDocument ();
87                                 xpdoc.LoadXml ("<XPath xmlns=\"" + XmlSignature.NamespaceURI + "\"></XPath>");
88                                 xpath = xpdoc.ChildNodes;
89                         }
90                         return xpath;
91                 }
92
93                 [MonoTODO ("Evaluation of extension function here() results in different from MS.NET (is MS.NET really correct??).")]
94                 public override object GetOutput () 
95                 {
96 #if NET_2_0
97                         if ((xpath == null) || (doc == null))
98                                 return new XmlDsigNodeList (new ArrayList ());
99 #else
100                         if (xpath == null)
101                                 return new XmlDsigNodeList (new ArrayList ());
102 #endif
103                         // evaluate every time since input or xpath might have changed.
104                         string x = null;
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;
111                                         break;
112                                 }
113                         }
114
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);
122                         }
123                         return EvaluateMatch (doc, x);
124                 }
125
126                 public override object GetOutput (Type type) 
127                 {
128                         if (type != typeof (XmlNodeList))
129                                 throw new ArgumentException ("type");
130                         return GetOutput ();
131                 }
132
133                 private XmlDsigNodeList EvaluateMatch (XmlNode n, string xpath)
134                 {
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);
145                 }
146
147                 private void EvaluateMatch (XmlNode n, XPathExpression exp, ArrayList al)
148                 {
149                         if (NodeMatches (n, exp))
150                                 al.Add (n);
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);
157                 }
158
159                 private bool NodeMatches (XmlNode n, XPathExpression exp)
160                 {
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);
165                         if (ret is bool)
166                                 return (bool) ret;
167                         if (ret is double) {
168                                 double d = (double) ret;
169                                 return !(d == 0.0 || Double.IsNaN (d));
170                         }
171                         if (ret is string)
172                                 return ((string) ret).Length > 0;
173                         if (ret is XPathNodeIterator) {
174                                 XPathNodeIterator retiter = (XPathNodeIterator) ret;
175                                 return retiter.Count > 0;
176                         }
177                         return false;
178                 }
179
180                 public override void LoadInnerXml (XmlNodeList nodeList) 
181                 {
182                         if (nodeList == null)
183                                 throw new CryptographicException ("nodeList");
184                         xpath = nodeList;
185                 }
186
187                 public override void LoadInput (object obj) 
188                 {
189                         // possible input: Stream, XmlDocument, and XmlNodeList
190                         if (obj is Stream) {
191                                 doc = new XmlDocument ();
192                                 doc.PreserveWhitespace = true;
193 #if NET_1_1
194                                 doc.XmlResolver = GetResolver ();
195 #endif
196                                 doc.Load (new XmlSignatureStreamReader (
197                                         new StreamReader ((Stream) obj)));
198                         }
199                         else if (obj is XmlDocument) {
200                                 doc = (obj as XmlDocument);
201                         }
202                         else if (obj is XmlNodeList) {
203                                 doc = new XmlDocument ();
204 #if NET_1_1
205                                 doc.XmlResolver = GetResolver ();
206 #endif
207                                 foreach (XmlNode xn in (obj as XmlNodeList))  {
208                                         XmlNode importedNode = doc.ImportNode (xn, true);
209                                         doc.AppendChild (importedNode);
210                                 }
211                         }
212                 }
213
214                 // Internal classes to support XPath extension function here()
215
216                 internal class XmlDsigXPathContext : XsltContext
217                 {
218                         XmlDsigXPathFunctionHere here;
219                         public XmlDsigXPathContext (XmlNode node)
220                         {
221                                 here = new XmlDsigXPathFunctionHere (node);
222                         }
223
224                         public override IXsltContextFunction ResolveFunction (
225                                 string prefix, string name, XPathResultType [] argType)
226                         {
227                                 // Here MS.NET incorrectly allows arbitrary
228                                 // name e.g. "heretic()".
229                                 if (name == "here" &&
230                                         prefix == String.Empty &&
231                                         argType.Length == 0)
232                                         return here;
233                                 else
234                                         return null; // ????
235                         }
236
237                         public override bool Whitespace {
238                                 get { return true; }
239                         }
240
241                         public override bool PreserveWhitespace (XPathNavigator node)
242                         {
243                                 return true;
244                         }
245
246                         public override int CompareDocument (string s1, string s2)
247                         {
248                                 return String.Compare (s1, s2);
249                         }
250
251                         public override IXsltContextVariable ResolveVariable (string prefix, string name)
252                         {
253                                 throw new InvalidOperationException ();
254                         }
255                 }
256
257                 internal class XmlDsigXPathFunctionHere : IXsltContextFunction
258                 {
259                         // Static
260
261                         static XPathResultType [] types;
262                         static XmlDsigXPathFunctionHere ()
263                         {
264                                 types = new XPathResultType [0];
265                         }
266
267                         // Instance
268
269                         XPathNodeIterator xpathNode;
270
271                         public XmlDsigXPathFunctionHere (XmlNode node)
272                         {
273                                 xpathNode = node.CreateNavigator ().Select (".");
274                         }
275
276                         public XPathResultType [] ArgTypes {
277                                 get { return types; }
278                         }
279                 
280                         public int Maxargs { get { return 0; } }
281                 
282                         public int Minargs { get { return 0; } }
283                 
284                         public XPathResultType ReturnType {
285                                 get { return XPathResultType.NodeSet; }
286                         }
287
288                         public object Invoke (XsltContext ctx, object [] args, XPathNavigator docContext)
289                         {
290                                 if (args.Length != 0)
291                                         throw new ArgumentException ("Not allowed arguments for function here().", "args");
292
293                                 return xpathNode.Clone ();
294                         }
295                 }
296         }
297 }