| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 |
- // 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.s
- using System.Collections;
- using System.Diagnostics;
- using System.Globalization;
- using System.IO;
- using System.Text;
- namespace System.Security
- {
- internal interface ISecurityElementFactory
- {
- SecurityElement CreateSecurityElement();
- object Copy();
- string GetTag();
- string Attribute(string attributeName);
- }
- public sealed class SecurityElement : ISecurityElementFactory
- {
- internal string _tag;
- internal string _text;
- private ArrayList _children;
- internal ArrayList _attributes;
- private const int AttributesTypical = 4 * 2; // 4 attributes, times 2 strings per attribute
- private const int ChildrenTypical = 1;
- private const string Indent = " ";
- 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 static readonly char[] s_escapeChars = new char[] { '<', '>', '\"', '\'', '&' };
- private static readonly string[] s_escapeStringPairs = new string[]
- {
- // these must be all once character escape sequences or a new escaping algorithm is needed
- "<", "<",
- ">", ">",
- "\"", """,
- "\'", "'",
- "&", "&"
- };
- //-------------------------- Constructors ---------------------------
- internal SecurityElement()
- {
- }
- public SecurityElement(string tag)
- {
- if (tag == null)
- throw new ArgumentNullException(nameof(tag));
- if (!IsValidTag(tag))
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_InvalidElementTag, tag));
- _tag = tag;
- _text = null;
- }
- public SecurityElement(string tag, string text)
- {
- if (tag == null)
- throw new ArgumentNullException(nameof(tag));
- if (!IsValidTag(tag))
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_InvalidElementTag, tag));
- if (text != null && !IsValidText(text))
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_InvalidElementText, text));
- _tag = tag;
- _text = text;
- }
- //-------------------------- Properties -----------------------------
- public string Tag
- {
- get
- {
- return _tag;
- }
- set
- {
- if (value == null)
- throw new ArgumentNullException(nameof(Tag));
- if (!IsValidTag(value))
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_InvalidElementTag, value));
- _tag = value;
- }
- }
- public Hashtable Attributes
- {
- get
- {
- if (_attributes == null || _attributes.Count == 0)
- {
- return null;
- }
- else
- {
- Hashtable hashtable = new Hashtable(_attributes.Count / 2);
- int iMax = _attributes.Count;
- Debug.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(_attributes[i], _attributes[i + 1]);
- }
- return hashtable;
- }
- }
- set
- {
- if (value == null || value.Count == 0)
- {
- _attributes = null;
- }
- else
- {
- ArrayList list = new ArrayList(value.Count);
- IDictionaryEnumerator enumerator = 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, SR.Argument_InvalidElementName, attrName));
- if (!IsValidAttributeValue(attrValue))
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_InvalidElementValue, attrValue));
- list.Add(attrName);
- list.Add(attrValue);
- }
- _attributes = list;
- }
- }
- }
- public string Text
- {
- get
- {
- return Unescape(_text);
- }
- set
- {
- if (value == null)
- {
- _text = null;
- }
- else
- {
- if (!IsValidText(value))
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_InvalidElementTag, value));
- _text = value;
- }
- }
- }
- public ArrayList Children
- {
- get
- {
- ConvertSecurityElementFactories();
- return _children;
- }
- set
- {
- if (value != null && value.Contains(null))
- {
- throw new ArgumentException(SR.ArgumentNull_Child);
- }
- _children = value;
- }
- }
- internal void ConvertSecurityElementFactories()
- {
- if (_children == null)
- return;
- for (int i = 0; i < _children.Count; ++i)
- {
- ISecurityElementFactory iseFactory = _children[i] as ISecurityElementFactory;
- if (iseFactory != null && !(_children[i] is SecurityElement))
- _children[i] = iseFactory.CreateSecurityElement();
- }
- }
- //-------------------------- Public Methods -----------------------------
- internal void AddAttributeSafe(string name, string value)
- {
- if (_attributes == null)
- {
- _attributes = new ArrayList(AttributesTypical);
- }
- else
- {
- int iMax = _attributes.Count;
- Debug.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)_attributes[i];
- if (string.Equals(strAttrName, name))
- throw new ArgumentException(SR.Argument_AttributeNamesMustBeUnique);
- }
- }
- _attributes.Add(name);
- _attributes.Add(value);
- }
- public void AddAttribute(string name, string value)
- {
- if (name == null)
- throw new ArgumentNullException(nameof(name));
- if (value == null)
- throw new ArgumentNullException(nameof(value));
- if (!IsValidAttributeName(name))
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_InvalidElementName, name));
- if (!IsValidAttributeValue(value))
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Argument_InvalidElementValue, value));
- AddAttributeSafe(name, value);
- }
- public void AddChild(SecurityElement child)
- {
- if (child == null)
- throw new ArgumentNullException(nameof(child));
- if (_children == null)
- _children = new ArrayList(ChildrenTypical);
- _children.Add(child);
- }
- public bool Equal(SecurityElement other)
- {
- if (other == null)
- return false;
- // Check if the tags are the same
- if (!string.Equals(_tag, other._tag))
- return false;
- // Check if the text is the same
- if (!string.Equals(_text, other._text))
- return false;
- // Check if the attributes are the same and appear in the same
- // order.
- if (_attributes == null || other._attributes == null)
- {
- if (_attributes != other._attributes)
- return false;
- }
- else
- {
- int iMax = _attributes.Count;
- Debug.Assert(iMax % 2 == 0, "Odd number of strings means the attr/value pairs were not added correctly");
- // Maybe we can get away by only checking the number of attributes
- if (iMax != other._attributes.Count)
- return false;
- for (int i = 0; i < iMax; i++)
- {
- string lhs = (string)_attributes[i];
- string rhs = (string)other._attributes[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
- if (_children == null || other._children == null)
- {
- if (_children != other._children)
- return false;
- }
- else
- {
- // Maybe we can get away by only checking the number of children
- if (_children.Count != other._children.Count)
- return false;
- ConvertSecurityElementFactories();
- other.ConvertSecurityElementFactories();
- IEnumerator lhs = _children.GetEnumerator();
- IEnumerator rhs = other._children.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;
- }
- public SecurityElement Copy()
- {
- SecurityElement element = new SecurityElement(_tag, _text);
- element._children = _children == null ? null : new ArrayList(_children);
- element._attributes = _attributes == null ? null : new ArrayList(_attributes);
- return element;
- }
- public static bool IsValidTag(string tag)
- {
- if (tag == null)
- return false;
- return tag.IndexOfAny(s_tagIllegalCharacters) == -1;
- }
- public static bool IsValidText(string text)
- {
- if (text == null)
- return false;
- return text.IndexOfAny(s_textIllegalCharacters) == -1;
- }
- public static bool IsValidAttributeName(string name)
- {
- return IsValidTag(name);
- }
- 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;
- Debug.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;
- }
- Debug.Fail("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).
- while (true)
- {
- 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);
- }
- }
- // 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;
- Debug.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);
- }
- public override string ToString()
- {
- StringBuilder sb = new StringBuilder();
- ToString("", sb, (obj, str) => ((StringBuilder)obj).Append(str));
- return sb.ToString();
- }
- private void ToString(string indent, object obj, Action<object, string> write)
- {
- write(obj, "<");
- write(obj, _tag);
- // If there are any attributes, plop those in.
- if (_attributes != null && _attributes.Count > 0)
- {
- write(obj, " ");
- int iMax = _attributes.Count;
- Debug.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)_attributes[i];
- string strAttrValue = (string)_attributes[i + 1];
- write(obj, strAttrName);
- write(obj, "=\"");
- write(obj, strAttrValue);
- write(obj, "\"");
- if (i != _attributes.Count - 2)
- {
- write(obj, Environment.NewLine);
- }
- }
- }
- if (_text == null && (_children == null || _children.Count == 0))
- {
- // If we are a single tag with no children, just add the end of tag text.
- write(obj, "/>");
- write(obj, Environment.NewLine);
- }
- else
- {
- // Close the current tag.
- write(obj, ">");
- // Output the text
- write(obj, _text);
- // Output any children.
- if (_children != null)
- {
- ConvertSecurityElementFactories();
- write(obj, Environment.NewLine);
- for (int i = 0; i < _children.Count; ++i)
- {
- ((SecurityElement)_children[i]).ToString(string.Empty, obj, write);
- }
- }
- // Output the closing tag
- write(obj, "</");
- write(obj, _tag);
- write(obj, ">");
- write(obj, Environment.NewLine);
- }
- }
- public string Attribute(string name)
- {
- if (name == null)
- throw new ArgumentNullException(nameof(name));
- // Note: we don't check for validity here because an
- // if an invalid name is passed we simply won't find it.
- if (_attributes == null)
- return null;
- // Go through all the attribute and see if we know about
- // the one we are asked for
- int iMax = _attributes.Count;
- Debug.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)_attributes[i];
- if (string.Equals(strAttrName, name))
- {
- string strAttrValue = (string)_attributes[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(nameof(tag));
- // Note: we don't check for a valid tag here because
- // an invalid tag simply won't be found.
- if (_children == null)
- return null;
- foreach (SecurityElement current in _children)
- {
- if (current != null && string.Equals(current.Tag, tag))
- 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(nameof(tag));
- // Note: we don't check for a valid tag here because
- // an invalid tag simply won't be found.
- if (string.Equals(_tag, tag))
- return Unescape(_text);
- if (_children == null)
- return null;
- foreach (SecurityElement child in Children)
- {
- string text = child.SearchForTextOfTag(tag);
- if (text != null)
- return text;
- }
- return null;
- }
- public static SecurityElement FromString(string xml)
- {
- if (xml == null)
- throw new ArgumentNullException(nameof(xml));
- return default(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);
- }
- }
- }
|