SecurityElement.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Security / SecurityElement.cs / 1305376 / SecurityElement.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
// [....]
// 
 
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, 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; 
        }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
// [....]
// 
 
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, 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; 
        }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK