| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Text; |
| | | 6 | | using System.Xml; |
| | | 7 | | |
| | | 8 | | namespace CoreWCF.Security |
| | | 9 | | { |
| | | 10 | | internal static class XmlHelper |
| | | 11 | | { |
| | | 12 | | internal static void AddNamespaceDeclaration(XmlDictionaryWriter writer, string prefix, XmlDictionaryString ns) |
| | | 13 | | { |
| | 20 | 14 | | string p = writer.LookupPrefix(ns.Value); |
| | 20 | 15 | | if (p == null || p != prefix) |
| | | 16 | | { |
| | 20 | 17 | | writer.WriteXmlnsAttribute(prefix, ns); |
| | | 18 | | } |
| | 20 | 19 | | } |
| | | 20 | | |
| | | 21 | | internal static string EnsureNamespaceDefined(XmlDictionaryWriter writer, XmlDictionaryString ns, string default |
| | | 22 | | { |
| | 0 | 23 | | string p = writer.LookupPrefix(ns.Value); |
| | 0 | 24 | | if (p == null) |
| | | 25 | | { |
| | 0 | 26 | | writer.WriteXmlnsAttribute(defaultPrefix, ns); |
| | 0 | 27 | | p = defaultPrefix; |
| | | 28 | | } |
| | | 29 | | |
| | 0 | 30 | | return p; |
| | | 31 | | } |
| | | 32 | | |
| | | 33 | | internal static XmlQualifiedName GetAttributeValueAsQName(XmlReader reader, string attributeName) |
| | | 34 | | { |
| | 0 | 35 | | string qname = reader.GetAttribute(attributeName); |
| | 0 | 36 | | if (qname == null) |
| | | 37 | | { |
| | 0 | 38 | | return null; |
| | | 39 | | } |
| | 0 | 40 | | return GetValueAsQName(reader, qname); |
| | | 41 | | } |
| | | 42 | | |
| | | 43 | | /// <summary> |
| | | 44 | | /// Enforces that parent has exactly 1 child of type XML element and nothing else (barring comments and whitespa |
| | | 45 | | /// and returns the child |
| | | 46 | | /// </summary> |
| | | 47 | | internal static XmlElement GetChildElement(XmlElement parent) |
| | | 48 | | { |
| | 10 | 49 | | if (parent == null) |
| | | 50 | | { |
| | 0 | 51 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parent)); |
| | | 52 | | } |
| | | 53 | | |
| | 10 | 54 | | XmlElement result = null; |
| | 40 | 55 | | for (int i = 0; i < parent.ChildNodes.Count; ++i) |
| | | 56 | | { |
| | 10 | 57 | | XmlNode child = parent.ChildNodes[i]; |
| | 10 | 58 | | if (child.NodeType == XmlNodeType.Whitespace || child.NodeType == XmlNodeType.Comment) |
| | | 59 | | { |
| | | 60 | | continue; |
| | | 61 | | } |
| | 10 | 62 | | else if (child.NodeType == XmlNodeType.Element && result == null) |
| | | 63 | | { |
| | 10 | 64 | | result = ((XmlElement)child); |
| | | 65 | | } |
| | | 66 | | else |
| | | 67 | | { |
| | 0 | 68 | | OnUnexpectedChildNodeError(parent, child); |
| | | 69 | | } |
| | | 70 | | } |
| | | 71 | | |
| | 10 | 72 | | if (result == null) |
| | | 73 | | { |
| | 0 | 74 | | OnChildNodeTypeMissing(parent, XmlNodeType.Element); |
| | | 75 | | } |
| | | 76 | | |
| | 10 | 77 | | return result; |
| | | 78 | | } |
| | | 79 | | |
| | | 80 | | internal static XmlElement GetChildElement(XmlElement parent, string childLocalName, string childNamespace) |
| | | 81 | | { |
| | 0 | 82 | | if (parent == null) |
| | | 83 | | { |
| | 0 | 84 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parent)); |
| | | 85 | | } |
| | | 86 | | |
| | 0 | 87 | | for (int i = 0; i < parent.ChildNodes.Count; ++i) |
| | | 88 | | { |
| | 0 | 89 | | XmlNode child = parent.ChildNodes[i]; |
| | | 90 | | |
| | 0 | 91 | | if (child.NodeType == XmlNodeType.Whitespace || child.NodeType == XmlNodeType.Comment) |
| | | 92 | | { |
| | | 93 | | continue; |
| | | 94 | | } |
| | 0 | 95 | | else if (child.NodeType == XmlNodeType.Element) |
| | | 96 | | { |
| | 0 | 97 | | if (child.LocalName == childLocalName && child.NamespaceURI == childNamespace) |
| | | 98 | | { |
| | 0 | 99 | | return ((XmlElement)child); |
| | | 100 | | } |
| | | 101 | | } |
| | | 102 | | else |
| | | 103 | | { |
| | 0 | 104 | | OnUnexpectedChildNodeError(parent, child); |
| | | 105 | | } |
| | | 106 | | } |
| | | 107 | | |
| | 0 | 108 | | return null; |
| | | 109 | | } |
| | | 110 | | |
| | | 111 | | internal static XmlQualifiedName GetValueAsQName(XmlReader reader, string value) |
| | | 112 | | { |
| | 0 | 113 | | SplitIntoPrefixAndName(value, out string prefix, out string name); |
| | 0 | 114 | | string ns = reader.LookupNamespace(prefix); |
| | 0 | 115 | | if (ns == null && prefix.Length > 0) |
| | | 116 | | { |
| | 0 | 117 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.CouldNotFindName |
| | | 118 | | } |
| | 0 | 119 | | return new XmlQualifiedName(name, ns); |
| | | 120 | | } |
| | | 121 | | |
| | | 122 | | internal static string GetWhiteSpace(XmlReader reader) |
| | | 123 | | { |
| | 0 | 124 | | string s = null; |
| | 0 | 125 | | StringBuilder sb = null; |
| | 0 | 126 | | while (reader.NodeType == XmlNodeType.Whitespace || reader.NodeType == XmlNodeType.SignificantWhitespace) |
| | | 127 | | { |
| | 0 | 128 | | if (sb != null) |
| | | 129 | | { |
| | 0 | 130 | | sb.Append(reader.Value); |
| | | 131 | | } |
| | 0 | 132 | | else if (s != null) |
| | | 133 | | { |
| | 0 | 134 | | sb = new StringBuilder(s); |
| | 0 | 135 | | sb.Append(reader.Value); |
| | 0 | 136 | | s = null; |
| | | 137 | | } |
| | | 138 | | else |
| | | 139 | | { |
| | 0 | 140 | | s = reader.Value; |
| | | 141 | | } |
| | 0 | 142 | | if (!reader.Read()) |
| | | 143 | | { |
| | | 144 | | break; |
| | | 145 | | } |
| | | 146 | | } |
| | 0 | 147 | | return sb != null ? sb.ToString() : s; |
| | | 148 | | } |
| | | 149 | | |
| | | 150 | | internal static bool IsWhitespaceOrComment(XmlReader reader) |
| | | 151 | | { |
| | 0 | 152 | | if (reader.NodeType == XmlNodeType.Comment) |
| | | 153 | | { |
| | 0 | 154 | | return true; |
| | | 155 | | } |
| | 0 | 156 | | else if (reader.NodeType == XmlNodeType.Whitespace) |
| | | 157 | | { |
| | 0 | 158 | | return true; |
| | | 159 | | } |
| | | 160 | | else |
| | | 161 | | { |
| | 0 | 162 | | return false; |
| | | 163 | | } |
| | | 164 | | } |
| | | 165 | | |
| | | 166 | | internal static void OnChildNodeTypeMissing(string parentName, XmlNodeType expectedNodeType) |
| | | 167 | | { |
| | 0 | 168 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.ChildNodeTypeMissing |
| | | 169 | | } |
| | | 170 | | |
| | | 171 | | internal static void OnChildNodeTypeMissing(XmlElement parent, XmlNodeType expectedNodeType) |
| | | 172 | | { |
| | 0 | 173 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.ChildNodeTypeMissing |
| | | 174 | | } |
| | | 175 | | |
| | | 176 | | internal static void OnEmptyElementError(XmlReader r) |
| | | 177 | | { |
| | 0 | 178 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.EmptyXmlElementError |
| | | 179 | | } |
| | | 180 | | |
| | | 181 | | internal static void OnEmptyElementError(XmlElement e) |
| | | 182 | | { |
| | 0 | 183 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.EmptyXmlElementError |
| | | 184 | | } |
| | | 185 | | |
| | | 186 | | internal static void OnEOF() |
| | | 187 | | { |
| | 0 | 188 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnexpectedEndOfFile) |
| | | 189 | | } |
| | | 190 | | |
| | | 191 | | internal static void OnNamespaceMissing(string prefix) |
| | | 192 | | { |
| | 0 | 193 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.CouldNotFindNamespac |
| | | 194 | | } |
| | | 195 | | |
| | | 196 | | internal static void OnRequiredAttributeMissing(string attrName, string elementName) |
| | | 197 | | { |
| | 0 | 198 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.RequiredAttributeMis |
| | | 199 | | } |
| | | 200 | | |
| | | 201 | | internal static void OnRequiredElementMissing(string elementName, string elementNamespace) |
| | | 202 | | { |
| | 0 | 203 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.ExpectedElementMissi |
| | | 204 | | } |
| | | 205 | | |
| | | 206 | | internal static void OnUnexpectedChildNodeError(string parentName, XmlReader r) |
| | | 207 | | { |
| | 0 | 208 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnexpectedXmlChildNo |
| | | 209 | | } |
| | | 210 | | |
| | | 211 | | internal static void OnUnexpectedChildNodeError(XmlElement parent, XmlNode n) |
| | | 212 | | { |
| | 0 | 213 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.UnexpectedXmlChildNo |
| | | 214 | | } |
| | | 215 | | |
| | | 216 | | internal static string ReadEmptyElementAndRequiredAttribute(XmlDictionaryReader reader, |
| | | 217 | | XmlDictionaryString name, XmlDictionaryString namespaceUri, XmlDictionaryString attributeName, |
| | | 218 | | out string prefix) |
| | | 219 | | { |
| | 0 | 220 | | reader.MoveToStartElement(name, namespaceUri); |
| | 0 | 221 | | prefix = reader.Prefix; |
| | 0 | 222 | | bool isEmptyElement = reader.IsEmptyElement; |
| | 0 | 223 | | string value = reader.GetAttribute(attributeName, null); |
| | 0 | 224 | | if (value == null) |
| | | 225 | | { |
| | 0 | 226 | | OnRequiredAttributeMissing(attributeName.Value, null); |
| | | 227 | | } |
| | 0 | 228 | | reader.Read(); |
| | | 229 | | |
| | 0 | 230 | | if (!isEmptyElement) |
| | | 231 | | { |
| | 0 | 232 | | reader.ReadEndElement(); |
| | | 233 | | } |
| | 0 | 234 | | return value; |
| | | 235 | | } |
| | | 236 | | |
| | | 237 | | internal static string GetRequiredNonEmptyAttribute(XmlDictionaryReader reader, XmlDictionaryString name, XmlDic |
| | | 238 | | { |
| | 0 | 239 | | string value = reader.GetAttribute(name, ns); |
| | 0 | 240 | | if (value == null || value.Length == 0) |
| | | 241 | | { |
| | 0 | 242 | | OnRequiredAttributeMissing(name.Value, reader?.Name); |
| | | 243 | | } |
| | 0 | 244 | | return value; |
| | | 245 | | } |
| | | 246 | | |
| | | 247 | | internal static byte[] GetRequiredBase64Attribute(XmlDictionaryReader reader, XmlDictionaryString name, XmlDicti |
| | | 248 | | { |
| | 0 | 249 | | if (!reader.MoveToAttribute(name.Value, ns?.Value)) |
| | | 250 | | { |
| | 0 | 251 | | OnRequiredAttributeMissing(name.Value, ns?.Value); |
| | | 252 | | } |
| | 0 | 253 | | byte[] value = reader.ReadContentAsBase64(); |
| | 0 | 254 | | if (value == null || value.Length == 0) |
| | | 255 | | { |
| | 0 | 256 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | 0 | 257 | | new XmlException(SR.Format(SR.EmptyBase64Attribute, name, ns))); |
| | | 258 | | } |
| | | 259 | | |
| | 0 | 260 | | return value; |
| | | 261 | | } |
| | | 262 | | |
| | | 263 | | internal static string ReadTextElementAsTrimmedString(XmlElement element) |
| | | 264 | | { |
| | 40 | 265 | | if (element == null) |
| | | 266 | | { |
| | 0 | 267 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(element)); |
| | | 268 | | } |
| | | 269 | | |
| | 40 | 270 | | using (XmlReader reader = new XmlNodeReader(element)) |
| | | 271 | | { |
| | 40 | 272 | | reader.MoveToContent(); |
| | 40 | 273 | | return XmlUtil.Trim(reader.ReadElementContentAsString()); |
| | | 274 | | } |
| | 40 | 275 | | } |
| | | 276 | | |
| | | 277 | | internal static void SplitIntoPrefixAndName(string qName, out string prefix, out string name) |
| | | 278 | | { |
| | 0 | 279 | | string[] parts = qName.Split(':'); |
| | 0 | 280 | | if (parts.Length > 2) |
| | | 281 | | { |
| | 0 | 282 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.Format(SR.InvalidQName)); |
| | | 283 | | } |
| | | 284 | | |
| | 0 | 285 | | if (parts.Length == 2) |
| | | 286 | | { |
| | 0 | 287 | | prefix = parts[0].Trim(); |
| | 0 | 288 | | name = parts[1].Trim(); |
| | | 289 | | } |
| | | 290 | | else |
| | | 291 | | { |
| | 0 | 292 | | prefix = string.Empty; |
| | 0 | 293 | | name = qName.Trim(); |
| | | 294 | | } |
| | 0 | 295 | | } |
| | | 296 | | |
| | | 297 | | internal static void ValidateIdPrefix(string idPrefix) |
| | | 298 | | { |
| | 0 | 299 | | if (idPrefix == null) |
| | | 300 | | { |
| | 0 | 301 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(idPrefix))); |
| | | 302 | | } |
| | | 303 | | |
| | 0 | 304 | | if (idPrefix.Length == 0) |
| | | 305 | | { |
| | 0 | 306 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(idPrefi |
| | | 307 | | } |
| | | 308 | | |
| | 0 | 309 | | if ((!char.IsLetter(idPrefix[0]) && idPrefix[0] != '_')) |
| | | 310 | | { |
| | 0 | 311 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(idPrefi |
| | | 312 | | } |
| | | 313 | | |
| | 0 | 314 | | for (int i = 1; i < idPrefix.Length; i++) |
| | | 315 | | { |
| | 0 | 316 | | char c = idPrefix[i]; |
| | 0 | 317 | | if (!char.IsLetter(c) && !char.IsNumber(c) && c != '.' && c != '_' && c != '-') |
| | | 318 | | { |
| | 0 | 319 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(idP |
| | | 320 | | } |
| | | 321 | | } |
| | 0 | 322 | | } |
| | | 323 | | |
| | | 324 | | internal static UniqueId GetAttributeAsUniqueId(XmlDictionaryReader reader, XmlDictionaryString localName, XmlDi |
| | | 325 | | { |
| | 20 | 326 | | return GetAttributeAsUniqueId(reader, localName.Value, (ns?.Value)); |
| | | 327 | | } |
| | | 328 | | |
| | | 329 | | private static UniqueId GetAttributeAsUniqueId(XmlDictionaryReader reader, string name, string ns) |
| | | 330 | | { |
| | 20 | 331 | | if (!reader.MoveToAttribute(name, ns)) |
| | | 332 | | { |
| | 10 | 333 | | return null; |
| | | 334 | | } |
| | | 335 | | |
| | 10 | 336 | | UniqueId id = reader.ReadContentAsUniqueId(); |
| | 10 | 337 | | reader.MoveToElement(); |
| | | 338 | | |
| | 10 | 339 | | return id; |
| | | 340 | | } |
| | | 341 | | |
| | | 342 | | public static void WriteAttributeStringAsUniqueId(XmlDictionaryWriter writer, string prefix, XmlDictionaryString |
| | | 343 | | { |
| | 10 | 344 | | writer.WriteStartAttribute(prefix, localName, ns); |
| | 10 | 345 | | writer.WriteValue(id); |
| | 10 | 346 | | writer.WriteEndAttribute(); |
| | 10 | 347 | | } |
| | | 348 | | |
| | | 349 | | public static void WriteElementStringAsUniqueId(XmlWriter writer, string localName, UniqueId id) |
| | | 350 | | { |
| | 0 | 351 | | writer.WriteStartElement(localName); |
| | 0 | 352 | | writer.WriteValue(id); |
| | 0 | 353 | | writer.WriteEndElement(); |
| | 0 | 354 | | } |
| | | 355 | | public static void WriteElementStringAsUniqueId(XmlDictionaryWriter writer, XmlDictionaryString localName, XmlDi |
| | | 356 | | { |
| | 0 | 357 | | writer.WriteStartElement(localName, ns); |
| | 0 | 358 | | writer.WriteValue(id); |
| | 0 | 359 | | writer.WriteEndElement(); |
| | 0 | 360 | | } |
| | | 361 | | |
| | | 362 | | public static void WriteElementContentAsInt64(XmlDictionaryWriter writer, XmlDictionaryString localName, XmlDict |
| | | 363 | | { |
| | 0 | 364 | | writer.WriteStartElement(localName, ns); |
| | 0 | 365 | | writer.WriteValue(value); |
| | 0 | 366 | | writer.WriteEndElement(); |
| | 0 | 367 | | } |
| | | 368 | | |
| | | 369 | | public static long ReadElementContentAsInt64(XmlDictionaryReader reader) |
| | | 370 | | { |
| | 0 | 371 | | reader.ReadFullStartElement(); |
| | 0 | 372 | | long i = reader.ReadContentAsLong(); |
| | 0 | 373 | | reader.ReadEndElement(); |
| | 0 | 374 | | return i; |
| | | 375 | | } |
| | | 376 | | |
| | | 377 | | public static void WriteStringAsUniqueId(XmlDictionaryWriter writer, UniqueId id) |
| | | 378 | | { |
| | 10 | 379 | | writer.WriteValue(id); |
| | 10 | 380 | | } |
| | | 381 | | |
| | | 382 | | public static UniqueId ReadElementStringAsUniqueId(XmlDictionaryReader reader, XmlDictionaryString localName, Xm |
| | | 383 | | { |
| | 20 | 384 | | if (reader.IsStartElement(localName, ns) && reader.IsEmptyElement) |
| | | 385 | | { |
| | 0 | 386 | | reader.Read(); |
| | 0 | 387 | | return new UniqueId(string.Empty); |
| | | 388 | | } |
| | | 389 | | |
| | 20 | 390 | | reader.ReadStartElement(localName, ns); |
| | 20 | 391 | | UniqueId id = reader.ReadContentAsUniqueId(); |
| | 20 | 392 | | reader.ReadEndElement(); |
| | 20 | 393 | | return id; |
| | | 394 | | } |
| | | 395 | | |
| | | 396 | | public static UniqueId ReadElementStringAsUniqueId(XmlDictionaryReader reader) |
| | | 397 | | { |
| | 0 | 398 | | if (reader.IsStartElement() && reader.IsEmptyElement) |
| | | 399 | | { |
| | 0 | 400 | | reader.Read(); |
| | 0 | 401 | | return new UniqueId(string.Empty); |
| | | 402 | | } |
| | | 403 | | |
| | 0 | 404 | | reader.ReadStartElement(); |
| | 0 | 405 | | UniqueId id = reader.ReadContentAsUniqueId(); |
| | 0 | 406 | | reader.ReadEndElement(); |
| | 0 | 407 | | return id; |
| | | 408 | | } |
| | | 409 | | |
| | | 410 | | public static UniqueId ReadTextElementAsUniqueId(XmlElement element) |
| | | 411 | | { |
| | 0 | 412 | | return new UniqueId(ReadTextElementAsTrimmedString(element)); |
| | | 413 | | } |
| | | 414 | | |
| | | 415 | | internal static string[] TokenizeInclusiveNamespacesPrefixList(string inclusiveNamespacesPrefixList) |
| | | 416 | | { |
| | 23 | 417 | | if (inclusiveNamespacesPrefixList == null) |
| | 22 | 418 | | return null; |
| | | 419 | | |
| | 1 | 420 | | string[] prefixes = inclusiveNamespacesPrefixList.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyE |
| | 1 | 421 | | if (prefixes.Length == 0) |
| | 0 | 422 | | return null; |
| | | 423 | | |
| | 6 | 424 | | for (int i = 0; i < prefixes.Length; i++) |
| | | 425 | | { |
| | 2 | 426 | | if (prefixes[i] == "#default") |
| | | 427 | | { |
| | 0 | 428 | | prefixes[i] = string.Empty; |
| | | 429 | | } |
| | | 430 | | } |
| | | 431 | | |
| | 1 | 432 | | return prefixes; |
| | | 433 | | } |
| | | 434 | | } |
| | | 435 | | } |