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