2 // System.Security.SecurityElement.cs
5 // Miguel de Icaza (miguel@ximian.com)
6 // Lawrence Pit (loz@cable.a2000.nl)
7 // Sebastien Pouliot <sebastien@ximian.com>
9 // (C) Ximian, Inc. http://www.ximian.com
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
32 using System.Globalization;
33 using System.Collections;
34 using System.Runtime.InteropServices;
36 using System.Diagnostics.Contracts;
40 namespace System.Security {
42 internal enum SecurityElementType
51 public sealed class SecurityElement
53 internal class SecurityAttribute {
56 private string _value;
58 public SecurityAttribute (string name, string value)
60 if (!IsValidAttributeName (name))
61 throw new ArgumentException (Locale.GetText ("Invalid XML attribute name") + ": " + name);
63 if (!IsValidAttributeValue (value))
64 throw new ArgumentException (Locale.GetText ("Invalid XML attribute value") + ": " + value);
67 _value = SecurityElement.Unescape (value);
75 get { return _value; }
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);
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 [] { '<', '>', '"', '\'', '&' };
98 public SecurityElement (string tag) : this (tag, null)
102 public SecurityElement (string tag, string text)
105 throw new ArgumentNullException ("tag");
106 if (!IsValidTag (tag))
107 throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + tag);
113 // not a deep copy (childs are references)
114 internal SecurityElement (SecurityElement se)
119 if (se.attributes != null) {
120 foreach (SecurityAttribute sa in se.attributes) {
121 this.AddAttribute (sa.Name, sa.Value);
124 if (se.children != null) {
125 foreach (SecurityElement child in se.children) {
126 this.AddChild (child);
131 public Hashtable Attributes {
133 if (attributes == null)
136 Hashtable result = new Hashtable (attributes.Count);
137 foreach (SecurityAttribute sa in attributes) {
138 result.Add (sa.Name, sa.Value);
144 if (value == null || value.Count == 0) {
149 if (attributes == null)
150 attributes = new ArrayList ();
153 IDictionaryEnumerator e = value.GetEnumerator ();
154 while (e.MoveNext ()) {
155 attributes.Add (new SecurityAttribute ((string) e.Key, (string) e.Value));
160 public ArrayList Children {
167 foreach (object o in value) {
169 throw new ArgumentNullException ();
170 // shouldn't we also throw an exception
171 // when o isn't an instance of SecurityElement?
184 throw new ArgumentNullException ("Tag");
185 if (!IsValidTag (value))
186 throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + value);
198 if (!IsValidText (value))
199 throw new ArgumentException (
200 Locale.GetText ("Invalid XML string")
203 text = Unescape (value);
207 public void AddAttribute (string name, string value)
210 throw new ArgumentNullException ("name");
212 throw new ArgumentNullException ("value");
213 if (GetAttribute (name) != null)
214 throw new ArgumentException (Locale.GetText ("Duplicate attribute : " + name));
216 if (attributes == null)
217 attributes = new ArrayList ();
218 attributes.Add (new SecurityAttribute (name, value));
221 public void AddChild (SecurityElement child)
224 throw new ArgumentNullException ("child");
226 if (children == null)
227 children = new ArrayList ();
229 children.Add (child);
232 public string Attribute (string name)
235 throw new ArgumentNullException ("name");
237 SecurityAttribute sa = GetAttribute (name);
238 return ((sa == null) ? null : sa.Value);
242 public SecurityElement Copy ()
244 return new SecurityElement (this);
247 public bool Equal (SecurityElement other)
255 if (this.text != other.text)
258 if (this.tag != other.tag)
261 if (this.attributes == null && other.attributes != null && other.attributes.Count != 0)
264 if (other.attributes == null && this.attributes != null && this.attributes.Count != 0)
267 if (this.attributes != null && other.attributes != null) {
268 if (this.attributes.Count != other.attributes.Count)
270 foreach (SecurityAttribute sa1 in attributes) {
271 SecurityAttribute sa2 = other.GetAttribute (sa1.Name);
272 if ((sa2 == null) || (sa1.Value != sa2.Value))
277 if (this.children == null && other.children != null && other.children.Count != 0)
280 if (other.children == null && this.children != null && this.children.Count != 0)
283 if (this.children != null && other.children != null) {
284 if (this.children.Count != other.children.Count)
286 for (int i = 0; i < this.children.Count; i++)
287 if (!((SecurityElement) this.children [i]).Equal ((SecurityElement) other.children [i]))
294 public static string Escape (string str)
301 if (str.IndexOfAny (invalid_chars) == -1)
304 sb = new StringBuilder ();
305 int len = str.Length;
307 for (int i = 0; i < len; i++) {
311 case '<': sb.Append ("<"); break;
312 case '>': sb.Append (">"); break;
313 case '"': sb.Append ("""); break;
314 case '\'': sb.Append ("'"); break;
315 case '&': sb.Append ("&"); break;
316 default: sb.Append (c); break;
320 return sb.ToString ();
323 private static string Unescape (string str)
330 sb = new StringBuilder (str);
331 sb.Replace ("<", "<");
332 sb.Replace (">", ">");
333 sb.Replace ("&", "&");
334 sb.Replace (""", "\"");
335 sb.Replace ("'", "'");
336 return sb.ToString ();
339 public static SecurityElement FromString (string xml)
342 throw new ArgumentNullException ("xml");
344 throw new XmlSyntaxException (Locale.GetText ("Empty string."));
347 SecurityParser sp = new SecurityParser ();
350 } catch (Exception e) {
351 string msg = Locale.GetText ("Invalid XML.");
352 throw new XmlSyntaxException (msg, e);
356 public static bool IsValidAttributeName (string name)
358 return name != null && name.IndexOfAny (invalid_attr_name_chars) == -1;
361 public static bool IsValidAttributeValue (string value)
363 return value != null && value.IndexOfAny (invalid_attr_value_chars) == -1;
366 public static bool IsValidTag (string tag)
368 return tag != null && tag.IndexOfAny (invalid_tag_chars) == -1;
371 public static bool IsValidText (string text)
373 return text != null && text.IndexOfAny (invalid_text_chars) == -1;
376 public SecurityElement SearchForChildByTag (string tag)
379 throw new ArgumentNullException ("tag");
381 if (this.children == null)
384 for (int i = 0; i < children.Count; i++) {
385 SecurityElement elem = (SecurityElement) children [i];
392 public string SearchForTextOfTag (string tag)
395 throw new ArgumentNullException ("tag");
400 if (this.children == null)
403 for (int i = 0; i < children.Count; i++) {
404 string result = ((SecurityElement) children [i]).SearchForTextOfTag (tag);
412 public override string ToString ()
414 StringBuilder s = new StringBuilder ();
416 return s.ToString ();
419 private void ToXml (ref StringBuilder s, int level)
424 if (attributes != null) {
426 for (int i=0; i < attributes.Count; i++) {
427 SecurityAttribute sa = (SecurityAttribute) attributes [i];
430 .Append (Escape (sa.Value))
432 if (i != attributes.Count - 1)
433 s.Append (Environment.NewLine);
437 if ((text == null || text == String.Empty) &&
438 (children == null || children.Count == 0))
439 s.Append ("/>").Append (Environment.NewLine);
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);
451 .Append (Environment.NewLine);
455 internal SecurityAttribute GetAttribute (string name)
457 if (attributes != null) {
458 foreach (SecurityAttribute sa in attributes) {
466 internal string m_strTag {
472 internal string m_strText {
481 internal ArrayList m_lAttributes {
487 internal ArrayList InternalChildren {
493 internal String SearchForTextOfLocalName(String strLocalName)
495 // Search on each child in order and each
496 // child's child, depth-first
498 if (strLocalName == null)
499 throw new ArgumentNullException( "strLocalName" );
500 Contract.EndContractBlock();
502 // Note: we don't check for a valid tag here because
503 // an invalid tag simply won't be found.
505 // First we check this.
507 if (tag == null) return null;
508 if (tag.Equals( strLocalName ) || tag.EndsWith( ":" + strLocalName, StringComparison.Ordinal ))
509 return Unescape( text );
510 if (children == null)
513 IEnumerator enumerator = children.GetEnumerator();
515 while (enumerator.MoveNext())
517 String current = ((SecurityElement)enumerator.Current).SearchForTextOfLocalName( strLocalName );