2010-04-06 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / System.Net / System.Net.Policy / ClientAccessPolicyParser.cs
1 //
2 // ClientAccessPolicyParser.cs
3 //
4 // Authors:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //      Moonlight List (moonlight-list@lists.ximian.com)
7 //
8 // Copyright (C) 2009-2010 Novell, Inc.  http://www.novell.com
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 #if NET_2_1
31
32 using System;
33 using System.Collections.Generic;
34 using System.IO;
35 using System.Linq;
36 using System.Xml;
37
38 /*
39 default namespace = ""
40
41 grammar {
42
43 start = access-policy
44
45 access-policy = element access-policy {
46   element cross-domain-access {
47     element policy { allow-from, grant-to }
48   }
49 }
50
51 allow-from = element allow-from {
52   attribute http-request-headers { text },
53   element domain {
54     attribute uri { text }
55   }
56 }
57
58 grant-to = element grant-to {
59   (resource | socket-resource)+
60 }
61
62 resource = element resource {
63   attribute path { text },
64   attribute include-subpaths { "true" | "false" }
65 }
66
67 socket-resource = element socket-resource {
68   attribute port { text },
69   attribute protocol { text }
70 }
71
72 }
73 */
74
75 namespace System.Net.Policy {
76
77         partial class ClientAccessPolicy {
78
79                 static public ICrossDomainPolicy FromStream (Stream stream)
80                 {
81                         ClientAccessPolicy cap = new ClientAccessPolicy ();
82
83                         // Silverlight accepts whitespaces before the XML - which is invalid XML
84                         StreamReader sr = new StreamReader (stream);
85                         while (Char.IsWhiteSpace ((char) sr.Peek ()))
86                                 sr.Read ();
87
88                         XmlReaderSettings policy_settings = new XmlReaderSettings ();
89                         policy_settings.DtdProcessing = DtdProcessing.Ignore;
90                         using (XmlReader reader = XmlReader.Create (sr, policy_settings)) {
91                                 reader.MoveToContent ();
92                                 if (reader.IsEmptyElement) {
93                                         reader.Skip ();
94                                         return null;
95                                 }
96                                 reader.ReadStartElement ("access-policy", String.Empty);
97                                 for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
98                                         if (reader.NodeType != XmlNodeType.Element)
99                                                 throw new XmlException (String.Format ("Unexpected access-policy content: {0}", reader.NodeType));
100
101                                         if ((reader.LocalName != "cross-domain-access") || reader.IsEmptyElement) {
102                                                 reader.Skip ();
103                                                 continue;
104                                         }
105
106                                         reader.ReadStartElement ("cross-domain-access", String.Empty);
107                                         for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
108                                                 if (reader.NodeType != XmlNodeType.Element)
109                                                         throw new XmlException (String.Format ("Unexpected access-policy content: {0}", reader.NodeType));
110
111                                                 if ((reader.Name != "policy") || reader.IsEmptyElement) {
112                                                         reader.Skip ();
113                                                         continue;
114                                                 }
115
116                                                 ReadPolicyElement (reader, cap);
117                                         }
118                                         reader.ReadEndElement ();
119                                 }
120                                 reader.ReadEndElement ();
121                         }
122                         return cap;
123                 }
124
125                 static void ReadPolicyElement (XmlReader reader, ClientAccessPolicy cap)
126                 {
127                         if (reader.HasAttributes || reader.IsEmptyElement) {
128                                 reader.Skip ();
129                                 return;
130                         }
131
132                         var policy = new AccessPolicy ();
133                         bool valid = true;
134
135                         reader.ReadStartElement ("policy", String.Empty);
136                         for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
137                                 if (reader.NodeType != XmlNodeType.Element)
138                                         throw new XmlException (String.Format ("Unexpected policy content: {0}", reader.NodeType));
139
140                                 switch (reader.LocalName) {
141                                 case "allow-from":
142                                         ReadAllowFromElement (reader, policy);
143                                         break;
144                                 case "grant-to":
145                                         ReadGrantToElement (reader, policy);
146                                         break;
147                                 default:
148                                         valid = false;
149                                         reader.Skip ();
150                                         break;
151                                 }
152                         }
153
154                         if (valid)
155                                 cap.AccessPolicyList.Add (policy);
156                         reader.ReadEndElement ();
157                 }
158
159                 static void ReadAllowFromElement (XmlReader reader, AccessPolicy policy)
160                 {
161                         var v = new AllowFrom ();
162                         bool valid = true;
163
164                         if (reader.HasAttributes || reader.IsEmptyElement) {
165                                 reader.Skip ();
166                                 return;
167                         }
168
169                         v.HttpRequestHeaders.SetHeaders (reader.GetAttribute ("http-request-headers"));
170                         reader.ReadStartElement ("allow-from", String.Empty);
171                         for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
172                                 if (reader.NodeType != XmlNodeType.Element)
173                                         throw new XmlException (String.Format ("Unexpected allow-from content: {0}", reader.NodeType));
174                                 if (!String.IsNullOrEmpty (reader.NamespaceURI)) {
175                                         reader.Skip ();
176                                         continue;
177                                 }
178                                 switch (reader.LocalName) {
179                                 case "domain":
180                                         var d = reader.GetAttribute ("uri");
181                                         switch (d) {
182                                         case "*":
183                                                 v.AllowAnyDomain = true;
184                                                 break;
185                                         case "http://*":
186                                                 v.Scheme = "http";
187                                                 break;
188                                         case "https://*":
189                                                 v.Scheme = "https";
190                                                 break;
191                                         case "file:///":
192                                                 v.Scheme = "file";
193                                                 break;
194                                         default:
195                                                 v.Domains.Add (d);
196                                                 break;
197                                         }
198                                         reader.Skip ();
199                                         break;
200                                 default:
201                                         valid = false;
202                                         reader.Skip ();
203                                         continue;
204                                 }
205                         }
206                         if (valid)
207                                 policy.AllowedServices.Add (v);
208                         reader.ReadEndElement ();
209                 }
210
211                 // only "path" and "include-subpaths" attributes are allowed - anything else is not considered
212                 static Resource CreateResource (XmlReader reader)
213                 {
214                         int n = reader.AttributeCount;
215                         string path = reader.GetAttribute ("path");
216                         if (path != null)
217                                 n--;
218                         string subpaths = reader.GetAttribute ("include-subpaths");
219                         if (subpaths != null)
220                                 n--;
221                         if ((n != 0) || !reader.IsEmptyElement)
222                                 return null;
223
224                         return new Resource () { 
225                                 Path = path,
226                                 IncludeSubpaths = subpaths == null ? false : XmlConvert.ToBoolean (subpaths)
227                         };
228                 }
229
230                 static void ReadGrantToElement (XmlReader reader, AccessPolicy policy)
231                 {
232                         var v = new GrantTo ();
233                         bool valid = true;
234
235                         if (reader.HasAttributes || reader.IsEmptyElement) {
236                                 reader.Skip ();
237                                 return;
238                         }
239
240                         reader.ReadStartElement ("grant-to", String.Empty);
241                         for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
242                                 if (reader.NodeType != XmlNodeType.Element)
243                                         throw new XmlException (String.Format ("Unexpected grant-to content: {0}", reader.NodeType));
244                                 if (!String.IsNullOrEmpty (reader.NamespaceURI)) {
245                                         reader.Skip ();
246                                         continue;
247                                 }
248
249                                 switch (reader.LocalName) {
250                                 case "resource":
251                                         var r = CreateResource (reader);
252                                         if (r == null)
253                                                 valid = false;
254                                         else
255                                                 v.Resources.Add (r);
256                                         break;
257                                 case "socket-resource":
258                                         // ignore everything that is not TCP
259                                         if (reader.GetAttribute ("protocol") != "tcp")
260                                                 break;
261                                         // we can merge them all together inside a policy
262                                         policy.PortMask |= ParsePorts (reader.GetAttribute ("port"));
263                                         break;
264                                 default:
265                                         valid = false;
266                                         break;
267                                 }
268                                 reader.Skip ();
269                         }
270                         if (valid)
271                                 policy.GrantedResources.Add (v);
272                         reader.ReadEndElement ();
273                 }
274
275                 // e.g. reserved ? 4534-4502
276                 static long ParsePorts (string ports)
277                 {
278                         long mask = 0;
279                         int sep = ports.IndexOf ('-');
280                         if (sep >= 0) {
281                                 // range
282                                 ushort from = ParsePort (ports.Substring (0, sep));
283                                 ushort to = ParsePort (ports.Substring (sep + 1));
284                                 for (int port = from; port <= to; port++)
285                                         mask |= (long) (1ul << (port - AccessPolicy.MinPort));
286                         } else {
287                                 // single
288                                 ushort port = ParsePort (ports);
289                                 mask |= (long) (1ul << (port - AccessPolicy.MinPort));
290                         }
291                         return mask;
292                 }
293
294                 static ushort ParsePort (string s)
295                 {
296                         ushort port;
297                         if (!UInt16.TryParse (s, out port) || (port < AccessPolicy.MinPort) || (port > AccessPolicy.MaxPort))
298                                 throw new XmlException ("Invalid port");
299                         return port;
300                 }
301         }
302 }
303
304 #endif
305