Bump bockbuild.
[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 ((xpath == null) || (doc == null))
97                                 return new XmlDsigNodeList (new ArrayList ());
98                         // evaluate every time since input or xpath might have changed.
99                         string x = null;
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;
106                                         break;
107                                 }
108                         }
109
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);
117                         }
118                         return EvaluateMatch (doc, x);
119                 }
120
121                 public override object GetOutput (Type type) 
122                 {
123                         if (type != typeof (XmlNodeList))
124                                 throw new ArgumentException ("type");
125                         return GetOutput ();
126                 }
127
128                 private XmlDsigNodeList EvaluateMatch (XmlNode n, string xpath)
129                 {
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);
140                 }
141
142                 private void EvaluateMatch (XmlNode n, XPathExpression exp, ArrayList al)
143                 {
144                         if (NodeMatches (n, exp))
145                                 al.Add (n);
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);
152                 }
153
154                 private bool NodeMatches (XmlNode n, XPathExpression exp)
155                 {
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);
160                         if (ret is bool)
161                                 return (bool) ret;
162                         if (ret is double) {
163                                 double d = (double) ret;
164                                 return !(d == 0.0 || Double.IsNaN (d));
165                         }
166                         if (ret is string)
167                                 return ((string) ret).Length > 0;
168                         if (ret is XPathNodeIterator) {
169                                 XPathNodeIterator retiter = (XPathNodeIterator) ret;
170                                 return retiter.Count > 0;
171                         }
172                         return false;
173                 }
174
175                 public override void LoadInnerXml (XmlNodeList nodeList) 
176                 {
177                         if (nodeList == null)
178                                 throw new CryptographicException ("nodeList");
179                         xpath = nodeList;
180                 }
181
182                 public override void LoadInput (object obj) 
183                 {
184                         // possible input: Stream, XmlDocument, and XmlNodeList
185                         if (obj is Stream) {
186                                 doc = new XmlDocument ();
187                                 doc.PreserveWhitespace = true;
188                                 doc.XmlResolver = GetResolver ();
189                                 doc.Load (new XmlSignatureStreamReader (
190                                         new StreamReader ((Stream) obj)));
191                         }
192                         else if (obj is XmlDocument) {
193                                 doc = (obj as XmlDocument);
194                         }
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);
201                                 }
202                         }
203                 }
204
205                 // Internal classes to support XPath extension function here()
206
207                 internal class XmlDsigXPathContext : XsltContext
208                 {
209                         XmlDsigXPathFunctionHere here;
210                         public XmlDsigXPathContext (XmlNode node)
211                         {
212                                 here = new XmlDsigXPathFunctionHere (node);
213                         }
214
215                         public override IXsltContextFunction ResolveFunction (
216                                 string prefix, string name, XPathResultType [] argType)
217                         {
218                                 // Here MS.NET incorrectly allows arbitrary
219                                 // name e.g. "heretic()".
220                                 if (name == "here" &&
221                                         prefix == String.Empty &&
222                                         argType.Length == 0)
223                                         return here;
224                                 else
225                                         return null; // ????
226                         }
227
228                         public override bool Whitespace {
229                                 get { return true; }
230                         }
231
232                         public override bool PreserveWhitespace (XPathNavigator node)
233                         {
234                                 return true;
235                         }
236
237                         public override int CompareDocument (string s1, string s2)
238                         {
239                                 return String.Compare (s1, s2);
240                         }
241
242                         public override IXsltContextVariable ResolveVariable (string prefix, string name)
243                         {
244                                 throw new InvalidOperationException ();
245                         }
246                 }
247
248                 internal class XmlDsigXPathFunctionHere : IXsltContextFunction
249                 {
250                         // Static
251
252                         static XPathResultType [] types;
253                         static XmlDsigXPathFunctionHere ()
254                         {
255                                 types = new XPathResultType [0];
256                         }
257
258                         // Instance
259
260                         XPathNodeIterator xpathNode;
261
262                         public XmlDsigXPathFunctionHere (XmlNode node)
263                         {
264                                 xpathNode = node.CreateNavigator ().Select (".");
265                         }
266
267                         public XPathResultType [] ArgTypes {
268                                 get { return types; }
269                         }
270                 
271                         public int Maxargs { get { return 0; } }
272                 
273                         public int Minargs { get { return 0; } }
274                 
275                         public XPathResultType ReturnType {
276                                 get { return XPathResultType.NodeSet; }
277                         }
278
279                         public object Invoke (XsltContext ctx, object [] args, XPathNavigator docContext)
280                         {
281                                 if (args.Length != 0)
282                                         throw new ArgumentException ("Not allowed arguments for function here().", "args");
283
284                                 return xpathNode.Clone ();
285                         }
286                 }
287         }
288 }