Merge pull request #5714 from alexischr/update_bockbuild
[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-2005 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 using System.Diagnostics.Contracts;
37
38 using Mono.Xml;
39
40 namespace System.Security {
41
42     internal enum SecurityElementType
43     {
44         Regular = 0,
45         Format = 1,
46         Comment = 2
47     }
48
49         [ComVisible (true)]
50         [Serializable]
51         public sealed class SecurityElement 
52         {
53                 internal class SecurityAttribute {
54                         
55                         private string _name;
56                         private string _value;
57
58                         public SecurityAttribute (string name, string value) 
59                         {
60                                 if (!IsValidAttributeName (name))
61                                         throw new ArgumentException (Locale.GetText ("Invalid XML attribute name") + ": " + name);
62
63                                 if (!IsValidAttributeValue (value))
64                                         throw new ArgumentException (Locale.GetText ("Invalid XML attribute value") + ": " + value);
65
66                                 _name = name;
67                                 _value = SecurityElement.Unescape (value);
68                         }
69
70                         public string Name {
71                                 get { return _name; }
72                         }
73
74                         public string Value {
75                                 get { return _value; }
76                         }
77                 }
78
79                 string text;
80                 string tag;
81                 ArrayList attributes;
82                 ArrayList children;
83                 
84                 // these values are determined by a simple test program against the MS.Net implementation:
85                 //      for (int i = 0; i < 256; i++) {
86                 //              if (!SecurityElement.IsValidTag ("" + ((char) i))) {
87                 //                      System.Console.WriteLine ("TAG: " + i);
88                 //              }
89                 //      }               
90                 // note: this is actually an incorrect implementation of MS, as for example the &
91                 // character is not a valid character in tag names.
92                 private static readonly char [] invalid_tag_chars = new char [] { ' ', '<', '>' };
93                 private static readonly char [] invalid_text_chars = new char [] { '<', '>' };
94                 private static readonly char [] invalid_attr_name_chars = new char [] { ' ', '<', '>' };
95                 private static readonly char [] invalid_attr_value_chars = new char [] { '"', '<', '>' };
96                 private static readonly char [] invalid_chars = new char [] { '<', '>', '"', '\'', '&' };
97                 
98                 public SecurityElement (string tag) : this (tag, null)
99                 {
100                 }
101                 
102                 public SecurityElement (string tag, string text)
103                 {
104                         if (tag == null)
105                                 throw new ArgumentNullException ("tag");
106                         if (!IsValidTag (tag))
107                                 throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + tag);
108                         this.tag = tag;
109
110                         Text = text;
111                 }
112
113                 // not a deep copy (childs are references)
114                 internal SecurityElement (SecurityElement se)
115                 {
116                         this.Tag = se.Tag;
117                         this.Text = se.Text;
118
119                         if (se.attributes != null) {
120                                 foreach (SecurityAttribute sa in se.attributes) {
121                                         this.AddAttribute (sa.Name, sa.Value);
122                                 }
123                         }
124                         if (se.children != null) {
125                                 foreach (SecurityElement child in se.children) {
126                                         this.AddChild (child);
127                                 }
128                         }
129                 }
130                 
131                 public Hashtable Attributes {
132                         get {
133                                 if (attributes == null) 
134                                         return null;
135                                         
136                                 Hashtable result = new Hashtable (attributes.Count);
137                                 foreach (SecurityAttribute sa in attributes) {
138                                         result.Add (sa.Name, sa.Value);
139                                 }
140                                 return result;
141                         }
142
143                         set {
144                                 if (value == null || value.Count == 0) {
145                                         attributes.Clear ();
146                                         return;
147                                 }
148                                 
149                                 if (attributes == null)
150                                         attributes = new ArrayList ();
151                                 else
152                                         attributes.Clear ();
153                                 IDictionaryEnumerator e = value.GetEnumerator ();
154                                 while (e.MoveNext ()) {
155                                         attributes.Add (new SecurityAttribute ((string) e.Key, (string) e.Value));
156                                 }
157                         }
158                 }
159
160                 public ArrayList Children {
161                         get {
162                                 return children;
163                         }
164
165                         set {
166                                 if (value != null) {
167                                         foreach (object o in value) {
168                                                 if (o == null)
169                                                         throw new ArgumentNullException ();
170                                                 // shouldn't we also throw an exception 
171                                                 // when o isn't an instance of SecurityElement?
172                                         }
173                                 }
174                                 children = value;
175                         }
176                 }
177
178                 public string Tag {
179                         get {
180                                 return tag;
181                         }
182                         set {
183                                 if (value == null)
184                                         throw new ArgumentNullException ("Tag");
185                                 if (!IsValidTag (value))
186                                         throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + value);
187                                 tag = value;
188                         }
189                 }
190
191                 public string Text {
192                         get {
193                                 return text;
194                         }
195
196                         set {
197                                 if (value != null) {
198                                         if (!IsValidText (value))
199                                                 throw new ArgumentException (
200                                                         Locale.GetText ("Invalid XML string")
201                                                         + ": " + value);
202                                 }
203                                 text = Unescape (value);
204                         }
205                 }
206
207                 public void AddAttribute (string name, string value)
208                 {
209                         if (name == null)
210                                 throw new ArgumentNullException ("name");
211                         if (value == null)
212                                 throw new ArgumentNullException ("value");
213                         if (GetAttribute (name) != null)
214                                 throw new ArgumentException (Locale.GetText ("Duplicate attribute : " + name));
215
216                         if (attributes == null)
217                                 attributes = new ArrayList ();
218                         attributes.Add (new SecurityAttribute (name, value));
219                 }
220
221                 public void AddChild (SecurityElement child)
222                 {
223                         if (child == null)
224                                 throw new ArgumentNullException ("child");
225
226                         if (children == null)
227                                 children = new ArrayList ();
228
229                         children.Add (child);
230                 }
231
232                 public string Attribute (string name)
233                 {
234                         if (name == null)
235                                 throw new ArgumentNullException ("name");
236
237                         SecurityAttribute sa = GetAttribute (name);
238                         return ((sa == null) ? null : sa.Value);
239                 }
240
241                 [ComVisible (false)]
242                 public SecurityElement Copy ()
243                 {
244                         return new SecurityElement (this);
245                 }
246
247                 public bool Equal (SecurityElement other)
248                 {
249                         if (other == null)
250                                 return false;
251                                 
252                         if (this == other)
253                                 return true;
254
255                         if (this.text != other.text)
256                                 return false;
257
258                         if (this.tag != other.tag)
259                                 return false;
260
261                         if (this.attributes == null && other.attributes != null && other.attributes.Count != 0)
262                                 return false;
263                                 
264                         if (other.attributes == null && this.attributes != null && this.attributes.Count != 0)
265                                 return false;
266
267                         if (this.attributes != null && other.attributes != null) {
268                                 if (this.attributes.Count != other.attributes.Count) 
269                                         return false;
270                                 foreach (SecurityAttribute sa1 in attributes) {
271                                         SecurityAttribute sa2 = other.GetAttribute (sa1.Name);
272                                         if ((sa2 == null) || (sa1.Value != sa2.Value))
273                                                 return false;
274                                 }
275                         }
276                         
277                         if (this.children == null && other.children != null && other.children.Count != 0)
278                                 return false;
279                                         
280                         if (other.children == null && this.children != null && this.children.Count != 0)
281                                 return false;
282                                 
283                         if (this.children != null && other.children != null) {
284                                 if (this.children.Count != other.children.Count)
285                                         return false;
286                                 for (int i = 0; i < this.children.Count; i++) 
287                                         if (!((SecurityElement) this.children [i]).Equal ((SecurityElement) other.children [i]))
288                                                 return false;
289                         }
290                         
291                         return true;
292                 }
293
294                 public static string Escape (string str)
295                 {
296                         StringBuilder sb;
297
298                         if (str == null)
299                                 return null;
300
301                         if (str.IndexOfAny (invalid_chars) == -1)
302                                 return str;
303
304                         sb = new StringBuilder ();
305                         int len = str.Length;
306                         
307                         for (int i = 0; i < len; i++) {
308                                 char c = str [i];
309
310                                 switch (c) {
311                                 case '<':  sb.Append ("&lt;"); break;
312                                 case '>':  sb.Append ("&gt;"); break;
313                                 case '"':  sb.Append ("&quot;"); break;
314                                 case '\'': sb.Append ("&apos;"); break;
315                                 case '&':  sb.Append ("&amp;"); break;
316                                 default:   sb.Append (c); break;
317                                 }
318                         }
319
320                         return sb.ToString ();
321                 }
322
323                 private static string Unescape (string str)
324                 {
325                         StringBuilder sb;
326
327                         if (str == null)
328                                 return null;
329
330                         sb = new StringBuilder (str);
331                         sb.Replace ("&lt;", "<");
332                         sb.Replace ("&gt;", ">");
333                         sb.Replace ("&amp;", "&");
334                         sb.Replace ("&quot;", "\"");
335                         sb.Replace ("&apos;", "'");
336                         return sb.ToString ();
337                 }
338
339                 public static SecurityElement FromString (string xml)
340                 {
341                         if (xml == null)
342                                 throw new ArgumentNullException ("xml");
343                         if (xml.Length == 0)
344                                 throw new XmlSyntaxException (Locale.GetText ("Empty string."));
345
346                         try {
347                                 SecurityParser sp = new SecurityParser ();
348                                 sp.LoadXml (xml);
349                                 return sp.ToXml ();
350                         } catch (Exception e) {
351                                 string msg = Locale.GetText ("Invalid XML.");
352                                 throw new XmlSyntaxException (msg, e);
353                         }
354                 }
355
356                 public static bool IsValidAttributeName (string name)
357                 {
358                         return name != null && name.IndexOfAny (invalid_attr_name_chars) == -1;
359                 }
360
361                 public static bool IsValidAttributeValue (string value)
362                 {
363                         return value != null && value.IndexOfAny (invalid_attr_value_chars) == -1;
364                 }
365
366                 public static bool IsValidTag (string tag)
367                 {
368                         return tag != null && tag.IndexOfAny (invalid_tag_chars) == -1;
369                 }
370
371                 public static bool IsValidText (string text)
372                 {
373                         return text != null && text.IndexOfAny (invalid_text_chars) == -1;
374                 }
375
376                 public SecurityElement SearchForChildByTag (string tag) 
377                 {
378                         if (tag == null)
379                                 throw new ArgumentNullException ("tag");
380                                 
381                         if (this.children == null)
382                                 return null;
383                                 
384                         for (int i = 0; i < children.Count; i++) {
385                                 SecurityElement elem = (SecurityElement) children [i];
386                                 if (elem.tag == tag)
387                                         return elem;
388                         }
389                         return null;
390                 }
391
392                 public string SearchForTextOfTag (string tag) 
393                 {
394                         if (tag == null)
395                                 throw new ArgumentNullException ("tag");
396                                 
397                         if (this.tag == tag)
398                                 return this.text;
399                                 
400                         if (this.children == null)
401                                 return null;
402                         
403                         for (int i = 0; i < children.Count; i++) {
404                                 string result = ((SecurityElement) children [i]).SearchForTextOfTag (tag);
405                                 if (result != null) 
406                                         return result;
407                         }
408
409                         return null;
410                 }
411                 
412                 public override string ToString ()
413                 {
414                         StringBuilder s = new StringBuilder ();
415                         ToXml (ref s, 0);
416                         return s.ToString ();
417                 }
418                 
419                 private void ToXml (ref StringBuilder s, int level)
420                 {
421                         s.Append ("<");
422                         s.Append (tag);
423                         
424                         if (attributes != null) {
425                                 s.Append (" ");
426                                 for (int i=0; i < attributes.Count; i++) {
427                                         SecurityAttribute sa = (SecurityAttribute) attributes [i];
428                                         s.Append (sa.Name)
429                                          .Append ("=\"")
430                                          .Append (Escape (sa.Value))
431                                          .Append ("\"");
432                                         if (i != attributes.Count - 1)
433                                                 s.Append (Environment.NewLine);
434                                 }
435                         }
436                         
437                         if ((text == null || text == String.Empty) && 
438                             (children == null || children.Count == 0))
439                                 s.Append ("/>").Append (Environment.NewLine);
440                         else {
441                                 s.Append (">").Append (Escape (text));
442                                 if (children != null) {
443                                         s.Append (Environment.NewLine);
444                                         foreach (SecurityElement child in children) {
445                                                 child.ToXml (ref s, level + 1);
446                                         }
447                                 }
448                                 s.Append ("</")
449                                  .Append (tag)
450                                  .Append (">")
451                                  .Append (Environment.NewLine);
452                         }
453                 }
454
455                 internal SecurityAttribute GetAttribute (string name) 
456                 {
457                         if (attributes != null) {
458                                 foreach (SecurityAttribute sa in attributes) {
459                                         if (sa.Name == name)
460                                                 return sa;
461                                 }
462                         }
463                         return null;
464                 }
465
466                 internal string m_strTag {
467                         get {
468                                 return tag;
469                         }
470                 }
471
472                 internal string m_strText {
473                         get {
474                                 return text;
475                         }
476                         set {
477                                 text = value;
478                         }
479                 }
480
481                 internal ArrayList m_lAttributes {
482                         get {
483                                 return attributes;
484                         }
485                 }
486
487                 internal ArrayList InternalChildren {
488                         get {
489                                 return children;
490                         }
491                 }
492
493         internal String SearchForTextOfLocalName(String strLocalName) 
494         {
495             // Search on each child in order and each
496             // child's child, depth-first
497             
498             if (strLocalName == null)
499                 throw new ArgumentNullException( "strLocalName" );
500             Contract.EndContractBlock();
501                 
502             // Note: we don't check for a valid tag here because
503             // an invalid tag simply won't be found.    
504             
505             // First we check this.
506             
507             if (tag == null) return null;            
508             if (tag.Equals( strLocalName ) || tag.EndsWith( ":" + strLocalName, StringComparison.Ordinal ))
509                 return Unescape( text );               
510             if (children == null)
511                 return null;
512
513             IEnumerator enumerator = children.GetEnumerator();
514
515             while (enumerator.MoveNext())
516             {
517                 String current = ((SecurityElement)enumerator.Current).SearchForTextOfLocalName( strLocalName );
518             
519                 if (current != null)
520                     return current;
521             }
522             return null;            
523         }               
524         }
525 }