2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System.Security / SecurityElement.cs
1 //
2 // System.Security.SecurityElement.cs
3 //
4 // Authors:
5 //      Miguel de Icaza (miguel@ximian.com)
6 //      Lawrence Pit (loz@cable.a2000.nl)
7 //      Sebastien Pouliot  <sebastien@ximian.com>
8 //
9 // (C) Ximian, Inc. http://www.ximian.com
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Globalization;
33 using System.Collections;
34 using System.Runtime.InteropServices;
35 using System.Text;
36
37 using Mono.Xml;
38
39 namespace System.Security 
40 {
41         [Serializable]
42         public sealed class SecurityElement 
43         {
44                 internal class SecurityAttribute {
45                         
46                         private string _name;
47                         private string _value;
48
49                         public SecurityAttribute (string name, string value) 
50                         {
51                                 if (!IsValidAttributeName (name))
52                                         throw new ArgumentException (Locale.GetText ("Invalid XML attribute name") + ": " + name);
53
54                                 if (!IsValidAttributeValue (value))
55                                         throw new ArgumentException (Locale.GetText ("Invalid XML attribute value") + ": " + value);
56
57                                 _name = name;
58                                 _value = value;
59                         }
60
61                         public string Name {
62                                 get { return _name; }
63                         }
64
65                         public string Value {
66                                 get { return _value; }
67                         }
68                 }
69
70                 string text;
71                 string tag;
72                 ArrayList attributes;
73                 ArrayList children;
74                 
75                 // these values are determined by a simple test program against the MS.Net implementation:
76                 //      for (int i = 0; i < 256; i++) {
77                 //              if (!SecurityElement.IsValidTag ("" + ((char) i))) {
78                 //                      System.Console.WriteLine ("TAG: " + i);
79                 //              }
80                 //      }               
81                 // note: this is actually an incorrect implementation of MS, as for example the &
82                 // character is not a valid character in tag names.
83                 private static readonly char [] invalid_tag_chars = new char [] { ' ', '<', '>' };
84                 private static readonly char [] invalid_text_chars = new char [] { '<', '>' };
85                 private static readonly char [] invalid_attr_name_chars = new char [] { ' ', '<', '>' };
86                 private static readonly char [] invalid_attr_value_chars = new char [] { '"', '<', '>' };
87                 private static readonly char [] invalid_chars = new char [] { '<', '>', '"', '\'', '&' };
88                 
89                 public SecurityElement (string tag) : this (tag, null)
90                 {
91                 }
92                 
93                 public SecurityElement (string tag, string text)
94                 {
95                         this.Tag = tag;
96                         this.Text = text;
97                 }
98
99                 // not a deep copy (childs are references)
100                 internal SecurityElement (SecurityElement se)
101                 {
102                         this.Tag = se.Tag;
103                         this.Text = se.Text;
104
105                         if (se.attributes != null) {
106                                 foreach (SecurityAttribute sa in se.attributes) {
107                                         this.AddAttribute (sa.Name, sa.Value);
108                                 }
109                         }
110                         if (se.children != null) {
111                                 foreach (SecurityElement child in se.children) {
112                                         this.AddChild (child);
113                                 }
114                         }
115                 }
116                 
117                 public Hashtable Attributes {
118                         get {
119                                 if (attributes == null) 
120                                         return null;
121                                         
122                                 Hashtable result = new Hashtable (attributes.Count);
123                                 foreach (SecurityAttribute sa in attributes) {
124                                         result.Add (sa.Name, sa.Value);
125                                 }
126                                 return result;
127                         }
128
129                         set {                           
130                                 if (value == null || value.Count == 0) {
131                                         attributes.Clear ();
132                                         return;
133                                 }
134                                 
135                                 if (attributes == null)
136                                         attributes = new ArrayList ();
137                                 else
138                                         attributes.Clear ();
139                                 IDictionaryEnumerator e = value.GetEnumerator ();
140                                 while (e.MoveNext ()) {
141                                         attributes.Add (new SecurityAttribute ((string) e.Key, (string) e.Value));
142                                 }
143                         }
144                 }
145
146                 public ArrayList Children {
147                         get {
148                                 return children;
149                         }
150
151                         set {
152                                 if (value != null) {
153                                         foreach (object o in value) {
154                                                 if (o == null)
155                                                         throw new ArgumentNullException ();
156                                                 // shouldn't we also throw an exception 
157                                                 // when o isn't an instance of SecurityElement?
158                                         }
159                                 }
160                                 children = value;
161                         }
162                 }
163
164                 public string Tag {
165                         get {
166                                 return tag;
167                         }
168                         set {
169                                 if (value == null)
170                                         throw new ArgumentNullException ();
171                                 if (!IsValidTag (value))
172                                         throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + value);
173                                 int colon = value.IndexOf (':');
174                                 tag = colon < 0 ? value : value.Substring (colon + 1);
175                         }
176                 }
177
178                 public string Text {
179                         get {
180                                 return text;
181                         }
182
183                         set {
184                                 if (!IsValidText (value))
185                                         throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + text);                              
186                                 text = value;
187                         }
188                 }
189
190                 public void AddAttribute (string name, string value)
191                 {
192                         if (name == null)
193                                 throw new ArgumentNullException ("name");
194                         if (value == null)
195                                 throw new ArgumentNullException ("value");
196                         if (GetAttribute (name) != null)
197                                 throw new ArgumentException (Locale.GetText ("Duplicate attribute : " + name));
198
199                         if (attributes == null)
200                                 attributes = new ArrayList ();
201                         attributes.Add (new SecurityAttribute (name, value));
202                 }
203
204                 public void AddChild (SecurityElement child)
205                 {
206                         if (child == null)
207                                 throw new ArgumentNullException ("child");
208
209                         if (children == null)
210                                 children = new ArrayList ();
211
212                         children.Add (child);
213                 }
214
215                 public string Attribute (string name)
216                 {
217                         if (name == null)
218                                 throw new ArgumentNullException ("name");
219
220                         SecurityAttribute sa = GetAttribute (name);
221                         return ((sa == null) ? null : sa.Value);
222                 }
223
224 #if NET_2_0
225                 [ComVisible (false)]
226                 public SecurityElement Copy ()
227                 {
228                         return new SecurityElement (this);
229                 }
230 #endif
231
232                 public bool Equal (SecurityElement other)
233                 {
234                         if (other == null)
235                                 return false;
236                                 
237                         if (this == other)
238                                 return true;
239
240                         if (this.text != other.text)
241                                 return false;
242
243                         if (this.tag != other.tag)
244                                 return false;
245
246                         if (this.attributes == null && other.attributes != null && other.attributes.Count != 0)
247                                 return false;
248                                 
249                         if (other.attributes == null && this.attributes != null && this.attributes.Count != 0)
250                                 return false;
251
252                         if (this.attributes != null && other.attributes != null) {
253                                 if (this.attributes.Count != other.attributes.Count) 
254                                         return false;
255                                 foreach (SecurityAttribute sa1 in attributes) {
256                                         SecurityAttribute sa2 = other.GetAttribute (sa1.Name);
257                                         if ((sa2 == null) || (sa1.Value != sa2.Value))
258                                                 return false;
259                                 }
260                         }
261                         
262                         if (this.children == null && other.children != null && other.children.Count != 0)
263                                 return false;
264                                         
265                         if (other.children == null && this.children != null && this.children.Count != 0)
266                                 return false;
267                                 
268                         if (this.children != null && other.children != null) {
269                                 if (this.children.Count != other.children.Count)
270                                         return false;
271                                 for (int i = 0; i < this.children.Count; i++) 
272                                         if (!((SecurityElement) this.children [i]).Equal ((SecurityElement) other.children [i]))
273                                                 return false;
274                         }
275                         
276                         return true;
277                 }
278
279                 public static string Escape (string str)
280                 {
281                         StringBuilder sb;
282                         
283                         if (str.IndexOfAny (invalid_chars) == -1)
284                                 return str;
285
286                         sb = new StringBuilder ();
287                         int len = str.Length;
288                         
289                         for (int i = 0; i < len; i++) {
290                                 char c = str [i];
291
292                                 switch (c) {
293                                 case '<':  sb.Append ("&lt;"); break;
294                                 case '>':  sb.Append ("&gt;"); break;
295                                 case '"':  sb.Append ("&quot;"); break;
296                                 case '\'': sb.Append ("&apos;"); break;
297                                 case '&':  sb.Append ("&amp;"); break;
298                                 default:   sb.Append (c); break;
299                                 }
300                         }
301
302                         return sb.ToString ();
303                 }
304
305 #if NET_2_0
306                 public 
307 #else
308                 internal
309 #endif
310                 static SecurityElement FromString (string xml)
311                 {
312                         if (xml == null)
313                                 throw new ArgumentNullException ("xml");
314                         if (xml.Length == 0)
315                                 throw new XmlSyntaxException (Locale.GetText ("Empty string."));
316
317                         try {
318                                 SecurityParser sp = new SecurityParser ();
319                                 sp.LoadXml (xml);
320                                 return sp.ToXml ();
321                         }
322                         catch (Exception e) {
323                                 string msg = Locale.GetText ("Invalid XML.");
324                                 throw new XmlSyntaxException (msg, e);
325                         }
326                 }
327
328                 public static bool IsValidAttributeName (string name)
329                 {
330                         return name != null && name.IndexOfAny (invalid_attr_name_chars) == -1;
331                 }
332
333                 public static bool IsValidAttributeValue (string value)
334                 {
335                         return value != null && value.IndexOfAny (invalid_attr_value_chars) == -1;
336                 }
337
338                 public static bool IsValidTag (string value)
339                 {
340                         return value != null && value.IndexOfAny (invalid_tag_chars) == -1;
341                 }
342
343                 public static bool IsValidText (string value)
344                 {
345                         if (value == null)
346                                 return true;
347                         return value.IndexOfAny (invalid_text_chars) == -1;
348                 }
349
350                 public SecurityElement SearchForChildByTag (string tag) 
351                 {
352                         if (tag == null)
353                                 throw new ArgumentNullException ("tag");
354                                 
355                         if (this.children == null)
356                                 return null;
357                                 
358                         for (int i = 0; i < children.Count; i++) {
359                                 SecurityElement elem = (SecurityElement) children [i];
360                                 if (elem.tag == tag)
361                                         return elem;
362                         }
363                         return null;
364                 }                       
365
366                 public string SearchForTextOfTag (string tag) 
367                 {
368                         if (tag == null)
369                                 throw new ArgumentNullException ("tag");
370                                 
371                         if (this.tag == tag)
372                                 return this.text;
373                                 
374                         if (this.children == null)
375                                 return null;
376                         
377                         for (int i = 0; i < children.Count; i++) {
378                                 string result = ((SecurityElement) children [i]).SearchForTextOfTag (tag);
379                                 if (result != null) 
380                                         return result;
381                         }
382
383                         return null;                    
384                 }
385                 
386                 public override string ToString ()
387                 {
388                         StringBuilder s = new StringBuilder ();
389                         ToXml (ref s, 0);
390                         return s.ToString ();
391                 }
392                 
393                 private void ToXml (ref StringBuilder s, int level)
394                 {
395 #if ! NET_2_0
396                         s.Append (' ', level * 3);
397 #endif
398                         s.Append ("<");
399                         s.Append (tag);
400                         
401                         if (attributes != null) {
402 #if NET_2_0
403                                 s.Append (" ");
404 #endif
405                                 for (int i=0; i < attributes.Count; i++) {
406                                         SecurityAttribute sa = (SecurityAttribute) attributes [i];
407 #if ! NET_2_0
408                                         s.Append (" ");
409                                         // all other attributes must align with the first one
410                                         if (i != 0)
411                                                 s.Append (' ', (level * 3) + tag.Length + 1);
412 #endif
413                                         s.Append (sa.Name)
414                                          .Append ("=\"")
415                                          .Append (sa.Value)
416                                          .Append ("\"");
417                                         if (i != attributes.Count - 1)
418                                                 s.Append (Environment.NewLine);
419                                 }
420                         }
421                         
422                         if ((text == null || text == String.Empty) && 
423                             (children == null || children.Count == 0))
424                                 s.Append ("/>").Append (Environment.NewLine);
425                         else {
426                                 s.Append (">").Append (text);
427                                 if (children != null) {
428                                         s.Append (Environment.NewLine);
429                                         foreach (SecurityElement child in children) {
430                                                 child.ToXml (ref s, level + 1);
431                                         }
432 #if ! NET_2_0
433                                         s.Append (' ', level * 3);
434 #endif
435                                 }
436                                 s.Append ("</")
437                                  .Append (tag)
438                                  .Append (">")
439                                  .Append (Environment.NewLine);
440                         }
441                 }
442
443                 internal SecurityAttribute GetAttribute (string name) 
444                 {
445                         if (attributes != null) {
446                                 foreach (SecurityAttribute sa in attributes) {
447                                         if (sa.Name == name)
448                                                 return sa;
449                                 }
450                         }
451                         return null;
452                 }
453         }
454 }