6992685b86
Former-commit-id: 0a113cb3a6feb7873f632839b1307cc6033cd595
526 lines
13 KiB
C#
526 lines
13 KiB
C#
//
|
|
// System.Security.SecurityElement.cs
|
|
//
|
|
// 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;
|
|
using System.Diagnostics.Contracts;
|
|
|
|
using Mono.Xml;
|
|
|
|
namespace System.Security {
|
|
|
|
internal enum SecurityElementType
|
|
{
|
|
Regular = 0,
|
|
Format = 1,
|
|
Comment = 2
|
|
}
|
|
|
|
[ComVisible (true)]
|
|
[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;
|
|
ArrayList attributes;
|
|
ArrayList children;
|
|
|
|
// these values are determined by a simple test program against the MS.Net implementation:
|
|
// for (int i = 0; i < 256; i++) {
|
|
// if (!SecurityElement.IsValidTag ("" + ((char) i))) {
|
|
// System.Console.WriteLine ("TAG: " + i);
|
|
// }
|
|
// }
|
|
// note: this is actually an incorrect implementation of MS, as for example the &
|
|
// character is not a valid character in tag names.
|
|
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)
|
|
{
|
|
}
|
|
|
|
public SecurityElement (string tag, string 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 {
|
|
get {
|
|
if (attributes == null)
|
|
return null;
|
|
|
|
Hashtable result = new Hashtable (attributes.Count);
|
|
foreach (SecurityAttribute sa in attributes) {
|
|
result.Add (sa.Name, sa.Value);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
set {
|
|
if (value == null || value.Count == 0) {
|
|
attributes.Clear ();
|
|
return;
|
|
}
|
|
|
|
if (attributes == null)
|
|
attributes = new ArrayList ();
|
|
else
|
|
attributes.Clear ();
|
|
IDictionaryEnumerator e = value.GetEnumerator ();
|
|
while (e.MoveNext ()) {
|
|
attributes.Add (new SecurityAttribute ((string) e.Key, (string) e.Value));
|
|
}
|
|
}
|
|
}
|
|
|
|
public ArrayList Children {
|
|
get {
|
|
return children;
|
|
}
|
|
|
|
set {
|
|
if (value != null) {
|
|
foreach (object o in value) {
|
|
if (o == null)
|
|
throw new ArgumentNullException ();
|
|
// shouldn't we also throw an exception
|
|
// when o isn't an instance of SecurityElement?
|
|
}
|
|
}
|
|
children = value;
|
|
}
|
|
}
|
|
|
|
public string Tag {
|
|
get {
|
|
return tag;
|
|
}
|
|
set {
|
|
if (value == null)
|
|
throw new ArgumentNullException ("Tag");
|
|
if (!IsValidTag (value))
|
|
throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + value);
|
|
tag = value;
|
|
}
|
|
}
|
|
|
|
public string Text {
|
|
get {
|
|
return text;
|
|
}
|
|
|
|
set {
|
|
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)
|
|
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 ArrayList ();
|
|
attributes.Add (new SecurityAttribute (name, value));
|
|
}
|
|
|
|
public void AddChild (SecurityElement child)
|
|
{
|
|
if (child == null)
|
|
throw new ArgumentNullException ("child");
|
|
|
|
if (children == null)
|
|
children = new ArrayList ();
|
|
|
|
children.Add (child);
|
|
}
|
|
|
|
public string Attribute (string name)
|
|
{
|
|
if (name == null)
|
|
throw new ArgumentNullException ("name");
|
|
|
|
SecurityAttribute sa = GetAttribute (name);
|
|
return ((sa == null) ? null : sa.Value);
|
|
}
|
|
|
|
[ComVisible (false)]
|
|
public SecurityElement Copy ()
|
|
{
|
|
return new SecurityElement (this);
|
|
}
|
|
|
|
public bool Equal (SecurityElement other)
|
|
{
|
|
if (other == null)
|
|
return false;
|
|
|
|
if (this == other)
|
|
return true;
|
|
|
|
if (this.text != other.text)
|
|
return false;
|
|
|
|
if (this.tag != other.tag)
|
|
return false;
|
|
|
|
if (this.attributes == null && other.attributes != null && other.attributes.Count != 0)
|
|
return false;
|
|
|
|
if (other.attributes == null && this.attributes != null && this.attributes.Count != 0)
|
|
return false;
|
|
|
|
if (this.attributes != null && other.attributes != null) {
|
|
if (this.attributes.Count != other.attributes.Count)
|
|
return false;
|
|
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)
|
|
return false;
|
|
|
|
if (other.children == null && this.children != null && this.children.Count != 0)
|
|
return false;
|
|
|
|
if (this.children != null && other.children != null) {
|
|
if (this.children.Count != other.children.Count)
|
|
return false;
|
|
for (int i = 0; i < this.children.Count; i++)
|
|
if (!((SecurityElement) this.children [i]).Equal ((SecurityElement) other.children [i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static string Escape (string str)
|
|
{
|
|
StringBuilder sb;
|
|
|
|
if (str == null)
|
|
return null;
|
|
|
|
if (str.IndexOfAny (invalid_chars) == -1)
|
|
return str;
|
|
|
|
sb = new StringBuilder ();
|
|
int len = str.Length;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
char c = str [i];
|
|
|
|
switch (c) {
|
|
case '<': sb.Append ("<"); break;
|
|
case '>': sb.Append (">"); break;
|
|
case '"': sb.Append ("""); break;
|
|
case '\'': sb.Append ("'"); break;
|
|
case '&': sb.Append ("&"); break;
|
|
default: sb.Append (c); break;
|
|
}
|
|
}
|
|
|
|
return sb.ToString ();
|
|
}
|
|
|
|
private static string Unescape (string str)
|
|
{
|
|
StringBuilder sb;
|
|
|
|
if (str == null)
|
|
return null;
|
|
|
|
sb = new StringBuilder (str);
|
|
sb.Replace ("<", "<");
|
|
sb.Replace (">", ">");
|
|
sb.Replace ("&", "&");
|
|
sb.Replace (""", "\"");
|
|
sb.Replace ("'", "'");
|
|
return sb.ToString ();
|
|
}
|
|
|
|
public 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;
|
|
}
|
|
|
|
public static bool IsValidAttributeValue (string value)
|
|
{
|
|
return value != null && value.IndexOfAny (invalid_attr_value_chars) == -1;
|
|
}
|
|
|
|
public static bool IsValidTag (string tag)
|
|
{
|
|
return tag != null && tag.IndexOfAny (invalid_tag_chars) == -1;
|
|
}
|
|
|
|
public static bool IsValidText (string text)
|
|
{
|
|
return text != null && text.IndexOfAny (invalid_text_chars) == -1;
|
|
}
|
|
|
|
public SecurityElement SearchForChildByTag (string tag)
|
|
{
|
|
if (tag == null)
|
|
throw new ArgumentNullException ("tag");
|
|
|
|
if (this.children == null)
|
|
return null;
|
|
|
|
for (int i = 0; i < children.Count; i++) {
|
|
SecurityElement elem = (SecurityElement) children [i];
|
|
if (elem.tag == tag)
|
|
return elem;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public string SearchForTextOfTag (string tag)
|
|
{
|
|
if (tag == null)
|
|
throw new ArgumentNullException ("tag");
|
|
|
|
if (this.tag == tag)
|
|
return this.text;
|
|
|
|
if (this.children == null)
|
|
return null;
|
|
|
|
for (int i = 0; i < children.Count; i++) {
|
|
string result = ((SecurityElement) children [i]).SearchForTextOfTag (tag);
|
|
if (result != null)
|
|
return result;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override string ToString ()
|
|
{
|
|
StringBuilder s = new StringBuilder ();
|
|
ToXml (ref s, 0);
|
|
return s.ToString ();
|
|
}
|
|
|
|
private void ToXml (ref StringBuilder s, int level)
|
|
{
|
|
s.Append ("<");
|
|
s.Append (tag);
|
|
|
|
if (attributes != null) {
|
|
s.Append (" ");
|
|
for (int i=0; i < attributes.Count; i++) {
|
|
SecurityAttribute sa = (SecurityAttribute) attributes [i];
|
|
s.Append (sa.Name)
|
|
.Append ("=\"")
|
|
.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 ("/>").Append (Environment.NewLine);
|
|
else {
|
|
s.Append (">").Append (Escape (text));
|
|
if (children != null) {
|
|
s.Append (Environment.NewLine);
|
|
foreach (SecurityElement child in children) {
|
|
child.ToXml (ref s, level + 1);
|
|
}
|
|
}
|
|
s.Append ("</")
|
|
.Append (tag)
|
|
.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;
|
|
}
|
|
|
|
internal string m_strTag {
|
|
get {
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
internal string m_strText {
|
|
get {
|
|
return text;
|
|
}
|
|
set {
|
|
text = value;
|
|
}
|
|
}
|
|
|
|
internal ArrayList m_lAttributes {
|
|
get {
|
|
return attributes;
|
|
}
|
|
}
|
|
|
|
internal ArrayList InternalChildren {
|
|
get {
|
|
return children;
|
|
}
|
|
}
|
|
|
|
internal String SearchForTextOfLocalName(String strLocalName)
|
|
{
|
|
// Search on each child in order and each
|
|
// child's child, depth-first
|
|
|
|
if (strLocalName == null)
|
|
throw new ArgumentNullException( "strLocalName" );
|
|
Contract.EndContractBlock();
|
|
|
|
// Note: we don't check for a valid tag here because
|
|
// an invalid tag simply won't be found.
|
|
|
|
// First we check this.
|
|
|
|
if (tag == null) return null;
|
|
if (tag.Equals( strLocalName ) || tag.EndsWith( ":" + strLocalName, StringComparison.Ordinal ))
|
|
return Unescape( text );
|
|
if (children == null)
|
|
return null;
|
|
|
|
IEnumerator enumerator = children.GetEnumerator();
|
|
|
|
while (enumerator.MoveNext())
|
|
{
|
|
String current = ((SecurityElement)enumerator.Current).SearchForTextOfLocalName( strLocalName );
|
|
|
|
if (current != null)
|
|
return current;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
}
|