2009-06-30 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mcs / class / corlib / System.Security / SecurityElement.cs
old mode 100755 (executable)
new mode 100644 (file)
index 47bda9d..7fd209b
@@ -1,24 +1,78 @@
 //
 // System.Security.SecurityElement.cs
 //
-// Author:
-//   Miguel de Icaza (miguel@ximian.com)
-//   Lawrence Pit (loz@cable.a2000.nl)
+// Authors:
+//     Miguel de Icaza (miguel@ximian.com)
+//     Lawrence Pit (loz@cable.a2000.nl)
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
 // (C) Ximian, Inc. http://www.ximian.com
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
 
 using System.Globalization;
 using System.Collections;
+using System.Runtime.InteropServices;
 using System.Text;
 
-namespace System.Security 
-{
+using Mono.Xml;
+
+namespace System.Security {
+
+#if NET_2_0
+       [ComVisible (true)]
+#endif
        [Serializable]
        public sealed class SecurityElement 
        {
+               internal class SecurityAttribute {
+                       
+                       private string _name;
+                       private string _value;
+
+                       public SecurityAttribute (string name, string value) 
+                       {
+                               if (!IsValidAttributeName (name))
+                                       throw new ArgumentException (Locale.GetText ("Invalid XML attribute name") + ": " + name);
+
+                               if (!IsValidAttributeValue (value))
+                                       throw new ArgumentException (Locale.GetText ("Invalid XML attribute value") + ": " + value);
+
+                               _name = name;
+                               _value = SecurityElement.Unescape (value);
+                       }
+
+                       public string Name {
+                               get { return _name; }
+                       }
+
+                       public string Value {
+                               get { return _value; }
+                       }
+               }
+
                string text;
                string tag;
-               Hashtable attributes;
+               ArrayList attributes;
                ArrayList children;
                
                // these values are determined by a simple test program against the MS.Net implementation:
@@ -29,11 +83,11 @@ namespace System.Security
                //      }               
                // note: this is actually an incorrect implementation of MS, as for example the &
                // character is not a valid character in tag names.
-               private static char [] invalid_tag_chars = new char [] { ' ', '<', '>' };
-               private static char [] invalid_text_chars = new char [] { '<', '>' };
-               private static char [] invalid_attr_name_chars = new char [] { ' ', '<', '>' };
-               private static char [] invalid_attr_value_chars = new char [] { '"', '<', '>' };
-               private static char [] invalid_chars = new char [] { '<', '>', '"', '\'', '&' };
+               private static readonly char [] invalid_tag_chars = new char [] { ' ', '<', '>' };
+               private static readonly char [] invalid_text_chars = new char [] { '<', '>' };
+               private static readonly char [] invalid_attr_name_chars = new char [] { ' ', '<', '>' };
+               private static readonly char [] invalid_attr_value_chars = new char [] { '"', '<', '>' };
+               private static readonly char [] invalid_chars = new char [] { '<', '>', '"', '\'', '&' };
                
                public SecurityElement (string tag) : this (tag, null)
                {
@@ -41,8 +95,31 @@ namespace System.Security
                
                public SecurityElement (string tag, string text)
                {
-                       this.Tag = tag;
-                       this.Text = (text == null) ? String.Empty : text;
+                       if (tag == null)
+                               throw new ArgumentNullException ("tag");
+                       if (!IsValidTag (tag))
+                               throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + tag);
+                       this.tag = tag;
+
+                       Text = text;
+               }
+
+               // not a deep copy (childs are references)
+               internal SecurityElement (SecurityElement se)
+               {
+                       this.Tag = se.Tag;
+                       this.Text = se.Text;
+
+                       if (se.attributes != null) {
+                               foreach (SecurityAttribute sa in se.attributes) {
+                                       this.AddAttribute (sa.Name, sa.Value);
+                               }
+                       }
+                       if (se.children != null) {
+                               foreach (SecurityElement child in se.children) {
+                                       this.AddChild (child);
+                               }
+                       }
                }
                
                public Hashtable Attributes {
@@ -50,31 +127,27 @@ namespace System.Security
                                if (attributes == null) 
                                        return null;
                                        
-                               Hashtable result = new Hashtable ();
-                               IDictionaryEnumerator e = attributes.GetEnumerator ();
-                               while (e.MoveNext ())
-                                       result.Add (e.Key, e.Value);
+                               Hashtable result = new Hashtable (attributes.Count);
+                               foreach (SecurityAttribute sa in attributes) {
+                                       result.Add (sa.Name, sa.Value);
+                               }
                                return result;
                        }
 
-                       set {                           
+                       set {
                                if (value == null || value.Count == 0) {
-                                       attributes = null;
+                                       attributes.Clear ();
                                        return;
                                }
                                
-                               Hashtable result = new Hashtable ();
+                               if (attributes == null)
+                                       attributes = new ArrayList ();
+                               else
+                                       attributes.Clear ();
                                IDictionaryEnumerator e = value.GetEnumerator ();
                                while (e.MoveNext ()) {
-                                       string key = (string) e.Key;
-                                       string val = (string) e.Value;
-                                       if (IsValidAttributeName (key))         
-                                               throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + key);
-                                       if (IsValidAttributeValue (val))                
-                                               throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + key);
-                                       result.Add (key, val);
+                                       attributes.Add (new SecurityAttribute ((string) e.Key, (string) e.Value));
                                }
-                               attributes = result;
                        }
                }
 
@@ -102,7 +175,7 @@ namespace System.Security
                        }
                        set {
                                if (value == null)
-                                       throw new ArgumentNullException ();
+                                       throw new ArgumentNullException ("Tag");
                                if (!IsValidTag (value))
                                        throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + value);
                                tag = value;
@@ -115,37 +188,34 @@ namespace System.Security
                        }
 
                        set {
-                               if (!IsValidText (value))
-                                       throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + text);                              
-                               text = value;
+                               if (value != null) {
+                                       if (!IsValidText (value))
+                                               throw new ArgumentException (
+                                                       Locale.GetText ("Invalid XML string")
+                                                       + ": " + value);
+                               }
+                               text = Unescape (value);
                        }
                }
 
                public void AddAttribute (string name, string value)
                {
-                       if (name == null || value == null)
-                               throw new ArgumentNullException ();
+                       if (name == null)
+                               throw new ArgumentNullException ("name");
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
+                       if (GetAttribute (name) != null)
+                               throw new ArgumentException (Locale.GetText ("Duplicate attribute : " + name));
 
                        if (attributes == null)
-                               attributes = new Hashtable ();
-
-                       //
-                       // The hashtable will throw ArgumentException if name is already there
-                       //
-
-                       if (!IsValidAttributeName (name))
-                               throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + name);
-
-                       if (!IsValidAttributeValue (value))
-                               throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + value);
-                       
-                       attributes.Add (name, value);
+                               attributes = new ArrayList ();
+                       attributes.Add (new SecurityAttribute (name, value));
                }
 
                public void AddChild (SecurityElement child)
                {
                        if (child == null)
-                               throw new ArgumentNullException ();
+                               throw new ArgumentNullException ("child");
 
                        if (children == null)
                                children = new ArrayList ();
@@ -156,13 +226,19 @@ namespace System.Security
                public string Attribute (string name)
                {
                        if (name == null)
-                               throw new ArgumentNullException ();
+                               throw new ArgumentNullException ("name");
 
-                       if (attributes != null)
-                               return (string) attributes [name];
-                       else
-                               return null;
+                       SecurityAttribute sa = GetAttribute (name);
+                       return ((sa == null) ? null : sa.Value);
+               }
+
+#if NET_2_0
+               [ComVisible (false)]
+               public SecurityElement Copy ()
+               {
+                       return new SecurityElement (this);
                }
+#endif
 
                public bool Equal (SecurityElement other)
                {
@@ -187,16 +263,17 @@ namespace System.Security
                        if (this.attributes != null && other.attributes != null) {
                                if (this.attributes.Count != other.attributes.Count) 
                                        return false;
-                               IDictionaryEnumerator e = attributes.GetEnumerator ();
-                               while (e.MoveNext ()) 
-                                       if (other.attributes [e.Key] != e.Value)
+                               foreach (SecurityAttribute sa1 in attributes) {
+                                       SecurityAttribute sa2 = other.GetAttribute (sa1.Name);
+                                       if ((sa2 == null) || (sa1.Value != sa2.Value))
                                                return false;
+                               }
                        }
                        
-                       if (this.children == null && other.children != null & other.children.Count != 0)
+                       if (this.children == null && other.children != null && other.children.Count != 0)
                                return false;
                                        
-                       if (other.children == null && this.children != null & this.children.Count != 0)
+                       if (other.children == null && this.children != null && this.children.Count != 0)
                                return false;
                                
                        if (this.children != null && other.children != null) {
@@ -213,7 +290,10 @@ namespace System.Security
                public static string Escape (string str)
                {
                        StringBuilder sb;
-                       
+
+                       if (str == null)
+                               return null;
+
                        if (str.IndexOfAny (invalid_chars) == -1)
                                return str;
 
@@ -236,6 +316,44 @@ namespace System.Security
                        return sb.ToString ();
                }
 
+               private static string Unescape (string str)
+               {
+                       StringBuilder sb;
+
+                       if (str == null)
+                               return null;
+
+                       sb = new StringBuilder (str);
+                       sb.Replace ("&lt;", "<");
+                       sb.Replace ("&gt;", ">");
+                       sb.Replace ("&amp;", "&");
+                       sb.Replace ("&quot;", "\"");
+                       sb.Replace ("&apos;", "'");
+                       return sb.ToString ();
+               }
+
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               static SecurityElement FromString (string xml)
+               {
+                       if (xml == null)
+                               throw new ArgumentNullException ("xml");
+                       if (xml.Length == 0)
+                               throw new XmlSyntaxException (Locale.GetText ("Empty string."));
+
+                       try {
+                               SecurityParser sp = new SecurityParser ();
+                               sp.LoadXml (xml);
+                               return sp.ToXml ();
+                       } catch (Exception e) {
+                               string msg = Locale.GetText ("Invalid XML.");
+                               throw new XmlSyntaxException (msg, e);
+                       }
+               }
+
                public static bool IsValidAttributeName (string name)
                {
                        return name != null && name.IndexOfAny (invalid_attr_name_chars) == -1;
@@ -246,14 +364,14 @@ namespace System.Security
                        return value != null && value.IndexOfAny (invalid_attr_value_chars) == -1;
                }
 
-               public static bool IsValidTag (string value)
+               public static bool IsValidTag (string tag)
                {
-                       return value != null && value.IndexOfAny (invalid_tag_chars) == -1;
+                       return tag != null && tag.IndexOfAny (invalid_tag_chars) == -1;
                }
 
-               public static bool IsValidText (string value)
+               public static bool IsValidText (string text)
                {
-                       return value != null && value.IndexOfAny (invalid_text_chars) == -1;
+                       return text != null && text.IndexOfAny (invalid_text_chars) == -1;
                }
 
                public SecurityElement SearchForChildByTag (string tag) 
@@ -270,7 +388,7 @@ namespace System.Security
                                        return elem;
                        }
                        return null;
-               }                       
+               }
 
                public string SearchForTextOfTag (string tag) 
                {
@@ -289,7 +407,7 @@ namespace System.Security
                                        return result;
                        }
 
-                       return null;                    
+                       return null;
                }
                
                public override string ToString ()
@@ -299,40 +417,65 @@ namespace System.Security
                        return s.ToString ();
                }
                
-               private void ToXml(ref StringBuilder s, int level)
+               private void ToXml (ref StringBuilder s, int level)
                {
-                       s.Append (' ', level << 2);
+#if ! NET_2_0
+                       s.Append (' ', level * 3);
+#endif
                        s.Append ("<");
                        s.Append (tag);
                        
                        if (attributes != null) {
-                               IDictionaryEnumerator e = attributes.GetEnumerator ();                          
-                               while (e.MoveNext ()) {
-                                       s.Append (" ")
-                                        .Append (e.Key)
+#if NET_2_0
+                               s.Append (" ");
+#endif
+                               for (int i=0; i < attributes.Count; i++) {
+                                       SecurityAttribute sa = (SecurityAttribute) attributes [i];
+#if ! NET_2_0
+                                       s.Append (" ");
+                                       // all other attributes must align with the first one
+                                       if (i != 0)
+                                               s.Append (' ', (level * 3) + tag.Length + 1);
+#endif
+                                       s.Append (sa.Name)
                                         .Append ("=\"")
-                                        .Append (e.Value)
+                                        .Append (Escape (sa.Value))
                                         .Append ("\"");
+                                       if (i != attributes.Count - 1)
+                                               s.Append (Environment.NewLine);
                                }
                        }
                        
                        if ((text == null || text == String.Empty) && 
                            (children == null || children.Count == 0))
-                               s.Append ("/>");
+                               s.Append ("/>").Append (Environment.NewLine);
                        else {
-                               s.Append (">").Append (text);
+                               s.Append (">").Append (Escape (text));
                                if (children != null) {
+                                       s.Append (Environment.NewLine);
                                        foreach (SecurityElement child in children) {
-                                               s.Append (Environment.NewLine);
                                                child.ToXml (ref s, level + 1);
                                        }
+#if ! NET_2_0
+                                       s.Append (' ', level * 3);
+#endif
                                }
-                               s.Append (Environment.NewLine)
-                                .Append (' ', level << 2)
-                                .Append ("</")
+                               s.Append ("</")
                                 .Append (tag)
-                                .Append (">");
+                                .Append (">")
+                                .Append (Environment.NewLine);
+                       }
+               }
+
+               internal SecurityAttribute GetAttribute (string name) 
+               {
+                       if (attributes != null) {
+                               foreach (SecurityAttribute sa in attributes) {
+                                       if (sa.Name == name)
+                                               return sa;
+                               }
                        }
+                       return null;
                }
        }
 }