diff options
Diffstat (limited to 'src/mscorlib/src/System/Security/SecurityElement.cs')
-rw-r--r-- | src/mscorlib/src/System/Security/SecurityElement.cs | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Security/SecurityElement.cs b/src/mscorlib/src/System/Security/SecurityElement.cs new file mode 100644 index 0000000000..aa63029422 --- /dev/null +++ b/src/mscorlib/src/System/Security/SecurityElement.cs @@ -0,0 +1,929 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +namespace System.Security +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Security.Util; + using System.Text; + using System.Globalization; + using System.IO; + using System.Security.Permissions; + using System.Diagnostics.Contracts; + + internal enum SecurityElementType + { + Regular = 0, + Format = 1, + Comment = 2 + } + + + internal interface ISecurityElementFactory + { + SecurityElement CreateSecurityElement(); + + Object Copy(); + + String GetTag(); + + String Attribute( String attributeName ); + } + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + sealed public class SecurityElement : ISecurityElementFactory + { + internal String m_strTag; + internal String m_strText; + private ArrayList m_lChildren; + internal ArrayList m_lAttributes; + internal SecurityElementType m_type = SecurityElementType.Regular; + + private static readonly char[] s_tagIllegalCharacters = new char[] { ' ', '<', '>' }; + private static readonly char[] s_textIllegalCharacters = new char[] { '<', '>' }; + private static readonly char[] s_valueIllegalCharacters = new char[] { '<', '>', '\"' }; + private const String s_strIndent = " "; + + private const int c_AttributesTypical = 4 * 2; // 4 attributes, times 2 strings per attribute + private const int c_ChildrenTypical = 1; + + private static readonly String[] s_escapeStringPairs = new String[] + { + // these must be all once character escape sequences or a new escaping algorithm is needed + "<", "<", + ">", ">", + "\"", """, + "\'", "'", + "&", "&" + }; + + private static readonly char[] s_escapeChars = new char[] { '<', '>', '\"', '\'', '&' }; + + //-------------------------- Constructors --------------------------- + + internal SecurityElement() + { + } + +////// ISecurityElementFactory implementation + + SecurityElement ISecurityElementFactory.CreateSecurityElement() + { + return this; + } + + String ISecurityElementFactory.GetTag() + { + return ((SecurityElement)this).Tag; + } + + Object ISecurityElementFactory.Copy() + { + return ((SecurityElement)this).Copy(); + } + + String ISecurityElementFactory.Attribute( String attributeName ) + { + return ((SecurityElement)this).Attribute( attributeName ); + } + +////////////// + +#if FEATURE_CAS_POLICY + public static SecurityElement FromString( String xml ) + { + if (xml == null) + throw new ArgumentNullException( "xml" ); + Contract.EndContractBlock(); + + return new Parser( xml ).GetTopElement(); + } +#endif // FEATURE_CAS_POLICY + + public SecurityElement( String tag ) + { + if (tag == null) + throw new ArgumentNullException( "tag" ); + + if (!IsValidTag( tag )) + throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidElementTag" ), tag ) ); + Contract.EndContractBlock(); + + m_strTag = tag; + m_strText = null; + } + + public SecurityElement( String tag, String text ) + { + if (tag == null) + throw new ArgumentNullException( "tag" ); + + if (!IsValidTag( tag )) + throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidElementTag" ), tag ) ); + + if (text != null && !IsValidText( text )) + throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidElementText" ), text ) ); + Contract.EndContractBlock(); + + m_strTag = tag; + m_strText = text; + } + + //-------------------------- Properties ----------------------------- + + public String Tag + { + [Pure] + get + { + return m_strTag; + } + + set + { + if (value == null) + throw new ArgumentNullException( "Tag" ); + + if (!IsValidTag( value )) + throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidElementTag" ), value ) ); + Contract.EndContractBlock(); + + m_strTag = value; + } + } + + public Hashtable Attributes + { + get + { + if (m_lAttributes == null || m_lAttributes.Count == 0) + { + return null; + } + else + { + Hashtable hashtable = new Hashtable( m_lAttributes.Count/2 ); + + int iMax = m_lAttributes.Count; + Contract.Assert( iMax % 2 == 0, "Odd number of strings means the attr/value pairs were not added correctly" ); + + for (int i = 0; i < iMax; i += 2) + { + hashtable.Add( m_lAttributes[i], m_lAttributes[i+1]); + } + + return hashtable; + } + } + + set + { + if (value == null || value.Count == 0) + { + m_lAttributes = null; + } + else + { + ArrayList list = new ArrayList(value.Count); + + System.Collections.IDictionaryEnumerator enumerator = (System.Collections.IDictionaryEnumerator)value.GetEnumerator(); + + while (enumerator.MoveNext()) + { + String attrName = (String)enumerator.Key; + String attrValue = (String)enumerator.Value; + + if (!IsValidAttributeName( attrName )) + throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidElementName" ), (String)enumerator.Current ) ); + + if (!IsValidAttributeValue( attrValue )) + throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidElementValue" ), (String)enumerator.Value ) ); + + list.Add(attrName); + list.Add(attrValue); + } + + m_lAttributes = list; + } + } + } + + public String Text + { + get + { + return Unescape( m_strText ); + } + + set + { + if (value == null) + { + m_strText = null; + } + else + { + if (!IsValidText( value )) + throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidElementTag" ), value ) ); + + m_strText = value; + } + } + } + + public ArrayList Children + { + get + { + ConvertSecurityElementFactories(); + return m_lChildren; + } + + set + { + if (value != null) + { + IEnumerator enumerator = value.GetEnumerator(); + + while (enumerator.MoveNext()) + { + if (enumerator.Current == null) + throw new ArgumentException( Environment.GetResourceString( "ArgumentNull_Child" ) ); + } + } + + m_lChildren = value; + } + } + + internal void ConvertSecurityElementFactories() + { + if (m_lChildren == null) + return; + + for (int i = 0; i < m_lChildren.Count; ++i) + { + ISecurityElementFactory iseFactory = m_lChildren[i] as ISecurityElementFactory; + if (iseFactory != null && !(m_lChildren[i] is SecurityElement)) + m_lChildren[i] = iseFactory.CreateSecurityElement(); + } + } + + internal ArrayList InternalChildren + { + get + { + // Beware! This array list can contain SecurityElements and other ISecurityElementFactories. + // If you want to get a consistent SecurityElement view, call get_Children. + return m_lChildren; + } + } + + //-------------------------- Public Methods ----------------------------- + + internal void AddAttributeSafe( String name, String value ) + { + if (m_lAttributes == null) + { + m_lAttributes = new ArrayList( c_AttributesTypical ); + } + else + { + int iMax = m_lAttributes.Count; + Contract.Assert( iMax % 2 == 0, "Odd number of strings means the attr/value pairs were not added correctly" ); + + for (int i = 0; i < iMax; i += 2) + { + String strAttrName = (String)m_lAttributes[i]; + + if (String.Equals(strAttrName, name)) + throw new ArgumentException( Environment.GetResourceString( "Argument_AttributeNamesMustBeUnique" ) ); + } + } + + m_lAttributes.Add(name); + m_lAttributes.Add(value); + } + + public void AddAttribute( String name, String value ) + { + if (name == null) + throw new ArgumentNullException( "name" ); + + if (value == null) + throw new ArgumentNullException( "value" ); + + if (!IsValidAttributeName( name )) + throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidElementName" ), name ) ); + + if (!IsValidAttributeValue( value )) + throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidElementValue" ), value ) ); + Contract.EndContractBlock(); + + AddAttributeSafe( name, value ); + } + + public void AddChild( SecurityElement child ) + { + if (child == null) + throw new ArgumentNullException( "child" ); + Contract.EndContractBlock(); + + if (m_lChildren == null) + m_lChildren = new ArrayList( c_ChildrenTypical ); + + m_lChildren.Add( child ); + } + + internal void AddChild( ISecurityElementFactory child ) + { + if (child == null) + throw new ArgumentNullException( "child" ); + Contract.EndContractBlock(); + + if (m_lChildren == null) + m_lChildren = new ArrayList( c_ChildrenTypical ); + + m_lChildren.Add( child ); + } + + internal void AddChildNoDuplicates( ISecurityElementFactory child ) + { + if (child == null) + throw new ArgumentNullException( "child" ); + Contract.EndContractBlock(); + + if (m_lChildren == null) + { + m_lChildren = new ArrayList( c_ChildrenTypical ); + m_lChildren.Add( child ); + } + else + { + for (int i = 0; i < m_lChildren.Count; ++i) + { + if (m_lChildren[i] == child) + return; + } + m_lChildren.Add( child ); + } + } + + public bool Equal( SecurityElement other ) + { + if (other == null) + return false; + + // Check if the tags are the same + if (!String.Equals(m_strTag, other.m_strTag)) + return false; + + // Check if the text is the same + if (!String.Equals(m_strText, other.m_strText)) + return false; + + // Check if the attributes are the same and appear in the same + // order. + + // Maybe we can get away by only checking the number of attributes + if (m_lAttributes == null || other.m_lAttributes == null) + { + if (m_lAttributes != other.m_lAttributes) + return false; + } + else + { + int iMax = m_lAttributes.Count; + Contract.Assert( iMax % 2 == 0, "Odd number of strings means the attr/value pairs were not added correctly" ); + + if (iMax != other.m_lAttributes.Count) + return false; + + for (int i = 0; i < iMax; i++) + { + String lhs = (String)m_lAttributes[i]; + String rhs = (String)other.m_lAttributes[i]; + + if (!String.Equals(lhs, rhs)) + return false; + } + } + + // Finally we must check the child and make sure they are + // equal and in the same order + + // Maybe we can get away by only checking the number of children + if (m_lChildren == null || other.m_lChildren == null) + { + if (m_lChildren != other.m_lChildren) + return false; + } + else + { + if (m_lChildren.Count != other.m_lChildren.Count) + return false; + + this.ConvertSecurityElementFactories(); + other.ConvertSecurityElementFactories(); + + // Okay, we'll need to go through each one of them + IEnumerator lhs = m_lChildren.GetEnumerator(); + IEnumerator rhs = other.m_lChildren.GetEnumerator(); + + SecurityElement e1, e2; + while (lhs.MoveNext()) + { + rhs.MoveNext(); + e1 = (SecurityElement)lhs.Current; + e2 = (SecurityElement)rhs.Current; + if (e1 == null || !e1.Equal(e2)) + return false; + } + } + return true; + } + + [System.Runtime.InteropServices.ComVisible(false)] + public SecurityElement Copy() + { + SecurityElement element = new SecurityElement( this.m_strTag, this.m_strText ); + element.m_lChildren = this.m_lChildren == null ? null : new ArrayList( this.m_lChildren ); + element.m_lAttributes = this.m_lAttributes == null ? null : new ArrayList(this.m_lAttributes); + + return element; + } + + [Pure] + public static bool IsValidTag( String tag ) + { + if (tag == null) + return false; + + return tag.IndexOfAny( s_tagIllegalCharacters ) == -1; + } + + [Pure] + public static bool IsValidText( String text ) + { + if (text == null) + return false; + + return text.IndexOfAny( s_textIllegalCharacters ) == -1; + } + + [Pure] + public static bool IsValidAttributeName( String name ) + { + return IsValidTag( name ); + } + + [Pure] + public static bool IsValidAttributeValue( String value ) + { + if (value == null) + return false; + + return value.IndexOfAny( s_valueIllegalCharacters ) == -1; + } + + private static String GetEscapeSequence( char c ) + { + int iMax = s_escapeStringPairs.Length; + Contract.Assert( iMax % 2 == 0, "Odd number of strings means the attr/value pairs were not added correctly" ); + + for (int i = 0; i < iMax; i += 2) + { + String strEscSeq = s_escapeStringPairs[i]; + String strEscValue = s_escapeStringPairs[i+1]; + + if (strEscSeq[0] == c) + return strEscValue; + } + + Contract.Assert( false, "Unable to find escape sequence for this character" ); + return c.ToString(); + } + + public static String Escape( String str ) + { + if (str == null) + return null; + + StringBuilder sb = null; + + int strLen = str.Length; + int index; // Pointer into the string that indicates the location of the current '&' character + int newIndex = 0; // Pointer into the string that indicates the start index of the "remaining" string (that still needs to be processed). + + + do + { + index = str.IndexOfAny( s_escapeChars, newIndex ); + + if (index == -1) + { + if (sb == null) + return str; + else + { + sb.Append( str, newIndex, strLen - newIndex ); + return sb.ToString(); + } + } + else + { + if (sb == null) + sb = new StringBuilder(); + + sb.Append( str, newIndex, index - newIndex ); + sb.Append( GetEscapeSequence( str[index] ) ); + + newIndex = ( index + 1 ); + } + } + while (true); + + // no normal exit is possible + } + + private static String GetUnescapeSequence( String str, int index, out int newIndex ) + { + int maxCompareLength = str.Length - index; + + int iMax = s_escapeStringPairs.Length; + Contract.Assert( iMax % 2 == 0, "Odd number of strings means the attr/value pairs were not added correctly" ); + + for (int i = 0; i < iMax; i += 2) + { + String strEscSeq = s_escapeStringPairs[i]; + String strEscValue = s_escapeStringPairs[i+1]; + + int length = strEscValue.Length; + + if (length <= maxCompareLength && String.Compare( strEscValue, 0, str, index, length, StringComparison.Ordinal) == 0) + { + newIndex = index + strEscValue.Length; + return strEscSeq; + } + } + + newIndex = index + 1; + return str[index].ToString(); + } + + + private static String Unescape( String str ) + { + if (str == null) + return null; + + StringBuilder sb = null; + + int strLen = str.Length; + int index; // Pointer into the string that indicates the location of the current '&' character + int newIndex = 0; // Pointer into the string that indicates the start index of the "remainging" string (that still needs to be processed). + + do + { + index = str.IndexOf( '&', newIndex ); + + if (index == -1) + { + if (sb == null) + return str; + else + { + sb.Append( str, newIndex, strLen - newIndex ); + return sb.ToString(); + } + } + else + { + if (sb == null) + sb = new StringBuilder(); + + sb.Append(str, newIndex, index - newIndex); + sb.Append( GetUnescapeSequence( str, index, out newIndex ) ); // updates the newIndex too + + } + } + while (true); + + // C# reports a warning if I leave this in, but I still kinda want to just in case. + // Contract.Assert( false, "If you got here, the execution engine or compiler is really confused" ); + // return str; + } + + private delegate void ToStringHelperFunc( Object obj, String str ); + + private static void ToStringHelperStringBuilder( Object obj, String str ) + { + ((StringBuilder)obj).Append( str ); + } + + private static void ToStringHelperStreamWriter( Object obj, String str ) + { + ((StreamWriter)obj).Write( str ); + } + + public override String ToString () + { + StringBuilder sb = new StringBuilder(); + + ToString( "", sb, new ToStringHelperFunc( ToStringHelperStringBuilder ) ); + + return sb.ToString(); + } + + internal void ToWriter( StreamWriter writer ) + { + ToString( "", writer, new ToStringHelperFunc( ToStringHelperStreamWriter ) ); + } + + private void ToString( String indent, Object obj, ToStringHelperFunc func ) + { + // First add the indent + + // func( obj, indent ); + + // Add in the opening bracket and the tag. + + func( obj, "<" ); + + switch (m_type) + { + case SecurityElementType.Format: + func( obj, "?" ); + break; + + case SecurityElementType.Comment: + func( obj, "!" ); + break; + + default: + break; + } + + func( obj, m_strTag ); + + // If there are any attributes, plop those in. + + if (m_lAttributes != null && m_lAttributes.Count > 0) + { + func( obj, " " ); + + int iMax = m_lAttributes.Count; + Contract.Assert( iMax % 2 == 0, "Odd number of strings means the attr/value pairs were not added correctly" ); + + for (int i = 0; i < iMax; i += 2) + { + String strAttrName = (String)m_lAttributes[i]; + String strAttrValue = (String)m_lAttributes[i+1]; + + func( obj, strAttrName ); + func( obj, "=\"" ); + func( obj, strAttrValue ); + func( obj, "\"" ); + + if (i != m_lAttributes.Count - 2) + { + if (m_type == SecurityElementType.Regular) + { + func( obj, Environment.NewLine ); + } + else + { + func( obj, " " ); + } + } + } + } + + if (m_strText == null && (m_lChildren == null || m_lChildren.Count == 0)) + { + // If we are a single tag with no children, just add the end of tag text. + + switch (m_type) + { + case SecurityElementType.Comment: + func( obj, ">" ); + break; + + case SecurityElementType.Format: + func( obj, " ?>" ); + break; + + default: + func( obj, "/>" ); + break; + } + func( obj, Environment.NewLine ); + } + else + { + // Close the current tag. + + func( obj, ">" ); + + // Output the text + + func( obj, m_strText ); + + // Output any children. + + if (m_lChildren != null) + { + this.ConvertSecurityElementFactories(); + + func( obj, Environment.NewLine ); + + // String childIndent = indent + s_strIndent; + + for (int i = 0; i < m_lChildren.Count; ++i) + { + ((SecurityElement)m_lChildren[i]).ToString( "", obj, func ); + } + + // In the case where we have children, the close tag will not be on the same line as the + // opening tag, so we need to indent. + + // func( obj, indent ); + } + + // Output the closing tag + + func( obj, "</" ); + func( obj, m_strTag ); + func( obj, ">" ); + func( obj, Environment.NewLine ); + } + } + + + + public String Attribute( String name ) + { + if (name == null) + throw new ArgumentNullException( "name" ); + Contract.EndContractBlock(); + + // Note: we don't check for validity here because an + // if an invalid name is passed we simply won't find it. + + if (m_lAttributes == null) + return null; + + // Go through all the attribute and see if we know about + // the one we are asked for + + int iMax = m_lAttributes.Count; + Contract.Assert( iMax % 2 == 0, "Odd number of strings means the attr/value pairs were not added correctly" ); + + for (int i = 0; i < iMax; i += 2) + { + String strAttrName = (String)m_lAttributes[i]; + + if (String.Equals(strAttrName, name)) + { + String strAttrValue = (String)m_lAttributes[i+1]; + + return Unescape(strAttrValue); + } + } + + // In the case where we didn't find it, we are expected to + // return null + return null; + } + + public SecurityElement SearchForChildByTag( String tag ) + { + // Go through all the children and see if we can + // find the one are are asked for (matching tags) + + if (tag == null) + throw new ArgumentNullException( "tag" ); + Contract.EndContractBlock(); + + // Note: we don't check for a valid tag here because + // an invalid tag simply won't be found. + + if (m_lChildren == null) + return null; + + IEnumerator enumerator = m_lChildren.GetEnumerator(); + + while (enumerator.MoveNext()) + { + SecurityElement current = (SecurityElement)enumerator.Current; + + if (current != null && String.Equals(current.Tag, tag)) + return current; + } + return null; + } + +#if FEATURE_CAS_POLICY + internal IPermission ToPermission(bool ignoreTypeLoadFailures) + { + IPermission ip = XMLUtil.CreatePermission( this, PermissionState.None, ignoreTypeLoadFailures ); + if (ip == null) + return null; + ip.FromXml(this); + + // Get the permission token here to ensure that the token + // type is updated appropriately now that we've loaded the type. + PermissionToken token = PermissionToken.GetToken( ip ); + Contract.Assert((token.m_type & PermissionTokenType.DontKnow) == 0, "Token type not properly assigned"); + + return ip; + } + + [System.Security.SecurityCritical] // auto-generated + internal Object ToSecurityObject() + { + switch (m_strTag) + { + case "PermissionSet": + PermissionSet pset = new PermissionSet(PermissionState.None); + pset.FromXml(this); + return pset; + + default: + return ToPermission(false); + } + } +#endif // FEATURE_CAS_POLICY + + 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 (m_strTag == null) return null; + if (m_strTag.Equals( strLocalName ) || m_strTag.EndsWith( ":" + strLocalName, StringComparison.Ordinal )) + return Unescape( m_strText ); + if (m_lChildren == null) + return null; + + IEnumerator enumerator = m_lChildren.GetEnumerator(); + + while (enumerator.MoveNext()) + { + String current = ((SecurityElement)enumerator.Current).SearchForTextOfLocalName( strLocalName ); + + if (current != null) + return current; + } + return null; + } + + public String SearchForTextOfTag( String tag ) + { + // Search on each child in order and each + // child's child, depth-first + + if (tag == null) + throw new ArgumentNullException( "tag" ); + 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 (String.Equals(m_strTag, tag)) + return Unescape( m_strText ); + if (m_lChildren == null) + return null; + + IEnumerator enumerator = m_lChildren.GetEnumerator(); + + this.ConvertSecurityElementFactories(); + + while (enumerator.MoveNext()) + { + String current = ((SecurityElement)enumerator.Current).SearchForTextOfTag( tag ); + + if (current != null) + return current; + } + return null; + } + } +} |