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
;
39 namespace System
.Security
{
45 public sealed class SecurityElement
47 internal class SecurityAttribute
{
50 private string _value
;
52 public SecurityAttribute (string name
, string value)
54 if (!IsValidAttributeName (name
))
55 throw new ArgumentException (Locale
.GetText ("Invalid XML attribute name") + ": " + name
);
57 if (!IsValidAttributeValue (value))
58 throw new ArgumentException (Locale
.GetText ("Invalid XML attribute value") + ": " + value);
61 _value
= SecurityElement
.Unescape (value);
69 get { return _value; }
78 // these values are determined by a simple test program against the MS.Net implementation:
79 // for (int i = 0; i < 256; i++) {
80 // if (!SecurityElement.IsValidTag ("" + ((char) i))) {
81 // System.Console.WriteLine ("TAG: " + i);
84 // note: this is actually an incorrect implementation of MS, as for example the &
85 // character is not a valid character in tag names.
86 private static readonly char [] invalid_tag_chars
= new char [] { ' ', '<', '>' }
;
87 private static readonly char [] invalid_text_chars
= new char [] { '<', '>' }
;
88 private static readonly char [] invalid_attr_name_chars
= new char [] { ' ', '<', '>' }
;
89 private static readonly char [] invalid_attr_value_chars
= new char [] { '"', '<', '>' }
;
90 private static readonly char [] invalid_chars
= new char [] { '<', '>', '"', '\'', '&' }
;
92 public SecurityElement (string tag
) : this (tag
, null)
96 public SecurityElement (string tag
, string text
)
99 throw new ArgumentNullException ("tag");
100 if (!IsValidTag (tag
))
101 throw new ArgumentException (Locale
.GetText ("Invalid XML string") + ": " + tag
);
107 // not a deep copy (childs are references)
108 internal SecurityElement (SecurityElement se
)
113 if (se
.attributes
!= null) {
114 foreach (SecurityAttribute sa
in se
.attributes
) {
115 this.AddAttribute (sa
.Name
, sa
.Value
);
118 if (se
.children
!= null) {
119 foreach (SecurityElement child
in se
.children
) {
120 this.AddChild (child
);
125 public Hashtable Attributes
{
127 if (attributes
== null)
130 Hashtable result
= new Hashtable (attributes
.Count
);
131 foreach (SecurityAttribute sa
in attributes
) {
132 result
.Add (sa
.Name
, sa
.Value
);
138 if (value == null || value.Count
== 0) {
143 if (attributes
== null)
144 attributes
= new ArrayList ();
147 IDictionaryEnumerator e
= value.GetEnumerator ();
148 while (e
.MoveNext ()) {
149 attributes
.Add (new SecurityAttribute ((string) e
.Key
, (string) e
.Value
));
154 public ArrayList Children
{
161 foreach (object o
in value) {
163 throw new ArgumentNullException ();
164 // shouldn't we also throw an exception
165 // when o isn't an instance of SecurityElement?
178 throw new ArgumentNullException ("Tag");
179 if (!IsValidTag (value))
180 throw new ArgumentException (Locale
.GetText ("Invalid XML string") + ": " + value);
192 if (!IsValidText (value))
193 throw new ArgumentException (
194 Locale
.GetText ("Invalid XML string")
197 text
= Unescape (value);
201 public void AddAttribute (string name
, string value)
204 throw new ArgumentNullException ("name");
206 throw new ArgumentNullException ("value");
207 if (GetAttribute (name
) != null)
208 throw new ArgumentException (Locale
.GetText ("Duplicate attribute : " + name
));
210 if (attributes
== null)
211 attributes
= new ArrayList ();
212 attributes
.Add (new SecurityAttribute (name
, value));
215 public void AddChild (SecurityElement child
)
218 throw new ArgumentNullException ("child");
220 if (children
== null)
221 children
= new ArrayList ();
223 children
.Add (child
);
226 public string Attribute (string name
)
229 throw new ArgumentNullException ("name");
231 SecurityAttribute sa
= GetAttribute (name
);
232 return ((sa
== null) ? null : sa
.Value
);
237 public SecurityElement
Copy ()
239 return new SecurityElement (this);
243 public bool Equal (SecurityElement other
)
251 if (this.text
!= other
.text
)
254 if (this.tag
!= other
.tag
)
257 if (this.attributes
== null && other
.attributes
!= null && other
.attributes
.Count
!= 0)
260 if (other
.attributes
== null && this.attributes
!= null && this.attributes
.Count
!= 0)
263 if (this.attributes
!= null && other
.attributes
!= null) {
264 if (this.attributes
.Count
!= other
.attributes
.Count
)
266 foreach (SecurityAttribute sa1
in attributes
) {
267 SecurityAttribute sa2
= other
.GetAttribute (sa1
.Name
);
268 if ((sa2
== null) || (sa1
.Value
!= sa2
.Value
))
273 if (this.children
== null && other
.children
!= null && other
.children
.Count
!= 0)
276 if (other
.children
== null && this.children
!= null && this.children
.Count
!= 0)
279 if (this.children
!= null && other
.children
!= null) {
280 if (this.children
.Count
!= other
.children
.Count
)
282 for (int i
= 0; i
< this.children
.Count
; i
++)
283 if (!((SecurityElement
) this.children
[i
]).Equal ((SecurityElement
) other
.children
[i
]))
290 public static string Escape (string str
)
297 if (str
.IndexOfAny (invalid_chars
) == -1)
300 sb
= new StringBuilder ();
301 int len
= str
.Length
;
303 for (int i
= 0; i
< len
; i
++) {
307 case '<': sb
.Append ("<"); break;
308 case '>': sb
.Append (">"); break;
309 case '"': sb
.Append ("""); break;
310 case '\'': sb
.Append ("'"); break;
311 case '&': sb
.Append ("&"); break;
312 default: sb
.Append (c
); break;
316 return sb
.ToString ();
319 private static string Unescape (string str
)
326 sb
= new StringBuilder (str
);
327 sb
.Replace ("<", "<");
328 sb
.Replace (">", ">");
329 sb
.Replace ("&", "&");
330 sb
.Replace (""", "\"");
331 sb
.Replace ("'", "'");
332 return sb
.ToString ();
340 static SecurityElement
FromString (string xml
)
343 throw new ArgumentNullException ("xml");
345 throw new XmlSyntaxException (Locale
.GetText ("Empty string."));
348 SecurityParser sp
= new SecurityParser ();
351 } catch (Exception e
) {
352 string msg
= Locale
.GetText ("Invalid XML.");
353 throw new XmlSyntaxException (msg
, e
);
357 public static bool IsValidAttributeName (string name
)
359 return name
!= null && name
.IndexOfAny (invalid_attr_name_chars
) == -1;
362 public static bool IsValidAttributeValue (string value)
364 return value != null && value.IndexOfAny (invalid_attr_value_chars
) == -1;
367 public static bool IsValidTag (string tag
)
369 return tag
!= null && tag
.IndexOfAny (invalid_tag_chars
) == -1;
372 public static bool IsValidText (string text
)
374 return text
!= null && text
.IndexOfAny (invalid_text_chars
) == -1;
377 public SecurityElement
SearchForChildByTag (string tag
)
380 throw new ArgumentNullException ("tag");
382 if (this.children
== null)
385 for (int i
= 0; i
< children
.Count
; i
++) {
386 SecurityElement elem
= (SecurityElement
) children
[i
];
393 public string SearchForTextOfTag (string tag
)
396 throw new ArgumentNullException ("tag");
401 if (this.children
== null)
404 for (int i
= 0; i
< children
.Count
; i
++) {
405 string result
= ((SecurityElement
) children
[i
]).SearchForTextOfTag (tag
);
413 public override string ToString ()
415 StringBuilder s
= new StringBuilder ();
417 return s
.ToString ();
420 private void ToXml (ref StringBuilder s
, int level
)
423 s
.Append (' ', level
* 3);
428 if (attributes
!= null) {
432 for (int i
=0; i
< attributes
.Count
; i
++) {
433 SecurityAttribute sa
= (SecurityAttribute
) attributes
[i
];
436 // all other attributes must align with the first one
438 s
.Append (' ', (level
* 3) + tag
.Length
+ 1);
442 .Append (Escape (sa
.Value
))
444 if (i
!= attributes
.Count
- 1)
445 s
.Append (Environment
.NewLine
);
449 if ((text
== null || text
== String
.Empty
) &&
450 (children
== null || children
.Count
== 0))
451 s
.Append ("/>").Append (Environment
.NewLine
);
453 s
.Append (">").Append (Escape (text
));
454 if (children
!= null) {
455 s
.Append (Environment
.NewLine
);
456 foreach (SecurityElement child
in children
) {
457 child
.ToXml (ref s
, level
+ 1);
460 s
.Append (' ', level
* 3);
466 .Append (Environment
.NewLine
);
470 internal SecurityAttribute
GetAttribute (string name
)
472 if (attributes
!= null) {
473 foreach (SecurityAttribute sa
in attributes
) {