1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 /*============================================================ 7 ** 8 ** CLASS: XMLUtil 9 ** 10 ** <OWNER>Microsoft</OWNER> 11 ** 12 ** PURPOSE: Helpers for XML input & output 13 ** 14 ===========================================================*/ 15 namespace System.Security.Util { 16 17 using System; 18 using System.Security; 19 using System.Security.Permissions; 20 using System.Security.Policy; 21 using System.Runtime.InteropServices; 22 using System.Runtime.Remoting; 23 using System.IO; 24 using System.Text; 25 using System.Runtime.CompilerServices; 26 using PermissionState = System.Security.Permissions.PermissionState; 27 using BindingFlags = System.Reflection.BindingFlags; 28 using Assembly = System.Reflection.Assembly; 29 using System.Threading; 30 using System.Globalization; 31 using System.Reflection; 32 using System.Diagnostics.Contracts; 33 34 internal static class XMLUtil 35 { 36 // 37 // Warning: Element constructors have side-effects on their 38 // third argument. 39 // 40 41 private const String BuiltInPermission = "System.Security.Permissions."; 42 #if FEATURE_CAS_POLICY 43 private const String BuiltInMembershipCondition = "System.Security.Policy."; 44 private const String BuiltInCodeGroup = "System.Security.Policy."; 45 private const String BuiltInApplicationSecurityManager = "System.Security.Policy."; 46 private static readonly char[] sepChar = {',', ' '}; 47 #endif 48 public static SecurityElement NewPermissionElement(IPermission ip)49 NewPermissionElement (IPermission ip) 50 { 51 return NewPermissionElement (ip.GetType ().FullName) ; 52 } 53 54 public static SecurityElement NewPermissionElement(String name)55 NewPermissionElement (String name) 56 { 57 SecurityElement ecr = new SecurityElement( "Permission" ); 58 ecr.AddAttribute( "class", name ); 59 return ecr; 60 } 61 62 public static void AddClassAttribute( SecurityElement element, Type type, String typename )63 AddClassAttribute( SecurityElement element, Type type, String typename ) 64 { 65 // Replace any quotes with apostrophes so that we can include quoted materials 66 // within classnames. Notably the assembly name member 'loc' uses a quoted string. 67 68 // NOTE: this makes assumptions as to what reflection is expecting for a type string 69 // it will need to be updated if reflection changes what it wants. 70 71 if ( typename == null ) 72 typename = type.FullName; 73 Contract.Assert( type.FullName.Equals( typename ), "Incorrect class name passed! Was : " + typename + " Shoule be: " + type.FullName); 74 element.AddAttribute( "class", typename + ", " + type.Module.Assembly.FullName.Replace( '\"', '\'' ) ); 75 } 76 ParseElementForAssemblyIdentification(SecurityElement el, out String className, out String assemblyName, out String assemblyVersion)77 internal static bool ParseElementForAssemblyIdentification(SecurityElement el, 78 out String className, 79 out String assemblyName, // for example "WindowsBase" 80 out String assemblyVersion) 81 { 82 83 className = null; 84 assemblyName = null; 85 assemblyVersion = null; 86 87 String fullClassName = el.Attribute( "class" ); 88 89 if (fullClassName == null) 90 { 91 return false; 92 } 93 if (fullClassName.IndexOf('\'') >= 0) 94 { 95 fullClassName = fullClassName.Replace( '\'', '\"' ); 96 } 97 98 int commaIndex = fullClassName.IndexOf( ',' ); 99 int namespaceClassNameLength; 100 101 // If the classname is tagged with assembly information, find where 102 // the assembly information begins. 103 104 if (commaIndex == -1) 105 { 106 return false; 107 } 108 109 namespaceClassNameLength = commaIndex; 110 className = fullClassName.Substring(0, namespaceClassNameLength); 111 String assemblyFullName = fullClassName.Substring(commaIndex + 1); 112 AssemblyName an = new AssemblyName(assemblyFullName); 113 assemblyName = an.Name; 114 assemblyVersion = an.Version.ToString(); 115 return true; 116 } 117 [System.Security.SecurityCritical] // auto-generated 118 private static bool ParseElementForObjectCreation( SecurityElement el, String requiredNamespace, out String className, out int classNameStart, out int classNameLength )119 ParseElementForObjectCreation( SecurityElement el, 120 String requiredNamespace, 121 out String className, 122 out int classNameStart, 123 out int classNameLength ) 124 { 125 className = null; 126 classNameStart = 0; 127 classNameLength = 0; 128 129 int requiredNamespaceLength = requiredNamespace.Length; 130 131 String fullClassName = el.Attribute( "class" ); 132 133 if (fullClassName == null) 134 { 135 throw new ArgumentException( Environment.GetResourceString( "Argument_NoClass" ) ); 136 } 137 138 if (fullClassName.IndexOf('\'') >= 0) 139 { 140 fullClassName = fullClassName.Replace( '\'', '\"' ); 141 } 142 143 if (!PermissionToken.IsMscorlibClassName( fullClassName )) 144 { 145 return false; 146 } 147 148 int commaIndex = fullClassName.IndexOf( ',' ); 149 int namespaceClassNameLength; 150 151 // If the classname is tagged with assembly information, find where 152 // the assembly information begins. 153 154 if (commaIndex == -1) 155 { 156 namespaceClassNameLength = fullClassName.Length; 157 } 158 else 159 { 160 namespaceClassNameLength = commaIndex; 161 } 162 163 // Only if the length of the class name is greater than the namespace info 164 // on our requiredNamespace do we continue 165 // with our check. 166 167 if (namespaceClassNameLength > requiredNamespaceLength) 168 { 169 // Make sure we are in the required namespace. 170 if (fullClassName.StartsWith(requiredNamespace, StringComparison.Ordinal)) 171 { 172 className = fullClassName; 173 classNameLength = namespaceClassNameLength - requiredNamespaceLength; 174 classNameStart = requiredNamespaceLength; 175 return true; 176 } 177 } 178 179 return false; 180 } 181 182 #if FEATURE_CAS_POLICY SecurityObjectToXmlString(Object ob)183 public static String SecurityObjectToXmlString(Object ob) 184 { 185 if(ob == null) 186 return ""; 187 PermissionSet pset = ob as PermissionSet; 188 if(pset != null) 189 return pset.ToXml().ToString(); 190 return ((IPermission)ob).ToXml().ToString(); 191 } 192 193 [System.Security.SecurityCritical] // auto-generated XmlStringToSecurityObject(String s)194 public static Object XmlStringToSecurityObject(String s) 195 { 196 if(s == null) 197 return null; 198 if(s.Length < 1) 199 return null; 200 return SecurityElement.FromString(s).ToSecurityObject(); 201 } 202 #endif // FEATURE_CAS_POLICY 203 204 [SecuritySafeCritical] 205 public static IPermission CreatePermission(SecurityElement el, PermissionState permState, bool ignoreTypeLoadFailures)206 CreatePermission (SecurityElement el, PermissionState permState, bool ignoreTypeLoadFailures) 207 { 208 if (el == null || !(el.Tag.Equals("Permission") || el.Tag.Equals("IPermission")) ) 209 throw new ArgumentException( String.Format( null, Environment.GetResourceString( "Argument_WrongElementType" ), "<Permission>" ) ) ; 210 Contract.EndContractBlock(); 211 212 String className; 213 int classNameLength; 214 int classNameStart; 215 216 if (!ParseElementForObjectCreation( el, 217 BuiltInPermission, 218 out className, 219 out classNameStart, 220 out classNameLength )) 221 { 222 goto USEREFLECTION; 223 } 224 225 // We have a built in permission, figure out which it is. 226 227 // UIPermission 228 // FileIOPermission 229 // SecurityPermission 230 // PrincipalPermission 231 // ReflectionPermission 232 // FileDialogPermission 233 // EnvironmentPermission 234 // GacIdentityPermission 235 // UrlIdentityPermission 236 // SiteIdentityPermission 237 // ZoneIdentityPermission 238 // KeyContainerPermission 239 // UnsafeForHostPermission 240 // HostProtectionPermission 241 // StrongNameIdentityPermission 242 #if !FEATURE_CORECLR 243 // IsolatedStorageFilePermission 244 #endif 245 #if !FEATURE_PAL 246 // RegistryPermission 247 // PublisherIdentityPermission 248 #endif // !FEATURE_PAL 249 250 switch (classNameLength) 251 { 252 case 12: 253 // UIPermission 254 if (String.Compare(className, classNameStart, "UIPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 255 return new UIPermission( permState ); 256 else 257 goto USEREFLECTION; 258 259 case 16: 260 // FileIOPermission 261 if (String.Compare(className, classNameStart, "FileIOPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 262 return new FileIOPermission( permState ); 263 else 264 goto USEREFLECTION; 265 266 case 18: 267 #if !FEATURE_PAL 268 // RegistryPermission 269 // SecurityPermission 270 if (className[classNameStart] == 'R') 271 { 272 if (String.Compare(className, classNameStart, "RegistryPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 273 return new RegistryPermission( permState ); 274 else 275 goto USEREFLECTION; 276 } 277 else 278 { 279 if (String.Compare(className, classNameStart, "SecurityPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 280 return new SecurityPermission( permState ); 281 else 282 goto USEREFLECTION; 283 } 284 #else // !FEATURE_PAL 285 if (String.Compare(className, classNameStart, "SecurityPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 286 return new SecurityPermission( permState ); 287 else 288 goto USEREFLECTION; 289 #endif // !FEATURE_PAL 290 291 #if !FEATURE_CORECLR 292 case 19: 293 // PrincipalPermission 294 if (String.Compare(className, classNameStart, "PrincipalPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 295 return new PrincipalPermission( permState ); 296 else 297 goto USEREFLECTION; 298 #endif // !FEATURE_CORECLR 299 case 20: 300 // ReflectionPermission 301 // FileDialogPermission 302 if (className[classNameStart] == 'R') 303 { 304 if (String.Compare(className, classNameStart, "ReflectionPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 305 return new ReflectionPermission( permState ); 306 else 307 goto USEREFLECTION; 308 } 309 else 310 { 311 if (String.Compare(className, classNameStart, "FileDialogPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 312 return new FileDialogPermission( permState ); 313 else 314 goto USEREFLECTION; 315 } 316 317 case 21: 318 // EnvironmentPermission 319 // UrlIdentityPermission 320 // GacIdentityPermission 321 if (className[classNameStart] == 'E') 322 { 323 if (String.Compare(className, classNameStart, "EnvironmentPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 324 return new EnvironmentPermission( permState ); 325 else 326 goto USEREFLECTION; 327 } 328 else if (className[classNameStart] == 'U') 329 { 330 if (String.Compare(className, classNameStart, "UrlIdentityPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 331 return new UrlIdentityPermission( permState ); 332 else 333 goto USEREFLECTION; 334 } 335 else 336 { 337 if (String.Compare(className, classNameStart, "GacIdentityPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 338 return new GacIdentityPermission( permState ); 339 else 340 goto USEREFLECTION; 341 } 342 343 344 case 22: 345 // SiteIdentityPermission 346 // ZoneIdentityPermission 347 // KeyContainerPermission 348 if (className[classNameStart] == 'S') 349 { 350 if (String.Compare(className, classNameStart, "SiteIdentityPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 351 return new SiteIdentityPermission( permState ); 352 else 353 goto USEREFLECTION; 354 } 355 else if (className[classNameStart] == 'Z') 356 { 357 if (String.Compare(className, classNameStart, "ZoneIdentityPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 358 return new ZoneIdentityPermission( permState ); 359 else 360 goto USEREFLECTION; 361 } 362 else 363 { 364 #if !FEATURE_PAL 365 if (String.Compare(className, classNameStart, "KeyContainerPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 366 return new KeyContainerPermission( permState ); 367 else 368 #endif // !FEATURE_PAL 369 goto USEREFLECTION; 370 } 371 372 373 case 24: 374 // HostProtectionPermission 375 if (String.Compare(className, classNameStart, "HostProtectionPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 376 return new HostProtectionPermission( permState ); 377 else 378 goto USEREFLECTION; 379 380 #if FEATURE_X509 && FEATURE_CAS_POLICY 381 case 27: 382 // PublisherIdentityPermission 383 if (String.Compare(className, classNameStart, "PublisherIdentityPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 384 return new PublisherIdentityPermission( permState ); 385 else 386 goto USEREFLECTION; 387 #endif // FEATURE_X509 && FEATURE_CAS_POLICY 388 389 case 28: 390 // StrongNameIdentityPermission 391 if (String.Compare(className, classNameStart, "StrongNameIdentityPermission", 0, classNameLength, StringComparison.Ordinal) == 0) 392 return new StrongNameIdentityPermission( permState ); 393 else 394 goto USEREFLECTION; 395 #if !FEATURE_CORECLR 396 case 29: 397 // IsolatedStorageFilePermission 398 if (String.Compare(className, classNameStart, "IsolatedStorageFilePermission", 0, classNameLength, StringComparison.Ordinal) == 0) 399 return new IsolatedStorageFilePermission( permState ); 400 else 401 goto USEREFLECTION; 402 #endif 403 default: 404 goto USEREFLECTION; 405 } 406 407 USEREFLECTION: 408 409 Object[] objs = new Object[1]; 410 objs[0] = permState; 411 412 Type permClass = null; 413 IPermission perm = null; 414 415 new ReflectionPermission(ReflectionPermissionFlag.MemberAccess).Assert(); 416 permClass = GetClassFromElement(el, ignoreTypeLoadFailures); 417 if (permClass == null) 418 return null; 419 if (!(typeof(IPermission).IsAssignableFrom(permClass))) 420 throw new ArgumentException( Environment.GetResourceString("Argument_NotAPermissionType") ); 421 422 perm = (IPermission) Activator.CreateInstance(permClass, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public, null, objs, null ); 423 424 return perm; 425 } 426 427 #if FEATURE_CAS_POLICY 428 #pragma warning disable 618 // CodeGroups are obsolete 429 [System.Security.SecuritySafeCritical] // auto-generated 430 public static CodeGroup CreateCodeGroup(SecurityElement el)431 CreateCodeGroup (SecurityElement el) 432 { 433 if (el == null || !el.Tag.Equals("CodeGroup")) 434 throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_WrongElementType" ), "<CodeGroup>" ) ) ; 435 Contract.EndContractBlock(); 436 437 String className; 438 int classNameLength; 439 int classNameStart; 440 441 if (!ParseElementForObjectCreation( el, 442 BuiltInCodeGroup, 443 out className, 444 out classNameStart, 445 out classNameLength )) 446 { 447 goto USEREFLECTION; 448 } 449 450 switch (classNameLength) 451 { 452 case 12: 453 // NetCodeGroup 454 if (String.Compare(className, classNameStart, "NetCodeGroup", 0, classNameLength, StringComparison.Ordinal) == 0) 455 return new NetCodeGroup(); 456 else 457 goto USEREFLECTION; 458 459 case 13: 460 // FileCodeGroup 461 if (String.Compare(className, classNameStart, "FileCodeGroup", 0, classNameLength, StringComparison.Ordinal) == 0) 462 return new FileCodeGroup(); 463 else 464 goto USEREFLECTION; 465 case 14: 466 // UnionCodeGroup 467 if (String.Compare(className, classNameStart, "UnionCodeGroup", 0, classNameLength, StringComparison.Ordinal) == 0) 468 return new UnionCodeGroup(); 469 else 470 goto USEREFLECTION; 471 472 case 19: 473 // FirstMatchCodeGroup 474 if (String.Compare(className, classNameStart, "FirstMatchCodeGroup", 0, classNameLength, StringComparison.Ordinal) == 0) 475 return new FirstMatchCodeGroup(); 476 else 477 goto USEREFLECTION; 478 479 default: 480 goto USEREFLECTION; 481 } 482 483 USEREFLECTION: 484 Type groupClass = null; 485 CodeGroup group = null; 486 487 new ReflectionPermission(ReflectionPermissionFlag.MemberAccess).Assert(); 488 groupClass = GetClassFromElement(el, true); 489 if (groupClass == null) 490 return null; 491 if (!(typeof(CodeGroup).IsAssignableFrom(groupClass))) 492 throw new ArgumentException( Environment.GetResourceString("Argument_NotACodeGroupType") ); 493 494 group = (CodeGroup) Activator.CreateInstance(groupClass, true); 495 496 Contract.Assert( groupClass.Module.Assembly != Assembly.GetExecutingAssembly(), 497 "This path should not get called for mscorlib based classes" ); 498 499 return group; 500 } 501 #pragma warning restore 618 502 503 [System.Security.SecurityCritical] // auto-generated 504 internal static IMembershipCondition CreateMembershipCondition( SecurityElement el )505 CreateMembershipCondition( SecurityElement el ) 506 { 507 if (el == null || !el.Tag.Equals("IMembershipCondition")) 508 throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_WrongElementType" ), "<IMembershipCondition>" ) ) ; 509 Contract.EndContractBlock(); 510 511 String className; 512 int classNameStart; 513 int classNameLength; 514 515 if (!ParseElementForObjectCreation( el, 516 BuiltInMembershipCondition, 517 out className, 518 out classNameStart, 519 out classNameLength )) 520 { 521 goto USEREFLECTION; 522 } 523 524 // We have a built in membership condition, figure out which it is. 525 526 // Here's the list of built in membership conditions as of 9/17/2002 527 // System.Security.Policy.AllMembershipCondition 528 // System.Security.Policy.URLMembershipCondition 529 // System.Security.Policy.SHA1MembershipCondition 530 // System.Security.Policy.SiteMembershipCondition 531 // System.Security.Policy.ZoneMembershipCondition 532 // System.Security.Policy.PublisherMembershipCondition 533 // System.Security.Policy.StrongNameMembershipCondition 534 // System.Security.Policy.ApplicationMembershipCondition 535 // System.Security.Policy.DomainApplicationMembershipCondition 536 // System.Security.Policy.ApplicationDirectoryMembershipCondition 537 538 switch (classNameLength) 539 { 540 case 22: 541 // AllMembershipCondition 542 // URLMembershipCondition 543 if (className[classNameStart] == 'A') 544 { 545 if (String.Compare(className, classNameStart, "AllMembershipCondition", 0, classNameLength, StringComparison.Ordinal) == 0) 546 return new AllMembershipCondition(); 547 else 548 goto USEREFLECTION; 549 } 550 else 551 { 552 if (String.Compare(className, classNameStart, "UrlMembershipCondition", 0, classNameLength, StringComparison.Ordinal) == 0) 553 return new UrlMembershipCondition(); 554 else 555 goto USEREFLECTION; 556 } 557 558 case 23: 559 #if !FEATURE_PAL 560 // HashMembershipCondition 561 // SiteMembershipCondition 562 // ZoneMembershipCondition 563 if (className[classNameStart] == 'H') 564 { 565 if (String.Compare(className, classNameStart, "HashMembershipCondition", 0, classNameLength, StringComparison.Ordinal) == 0) 566 return new HashMembershipCondition(); 567 else 568 goto USEREFLECTION; 569 } 570 else if (className[classNameStart] == 'S') 571 #else 572 // SiteMembershipCondition 573 // ZoneMembershipCondition 574 if (className[classNameStart] == 'S') 575 #endif // !FEATURE_PAL 576 { 577 if (String.Compare(className, classNameStart, "SiteMembershipCondition", 0, classNameLength, StringComparison.Ordinal) == 0) 578 return new SiteMembershipCondition(); 579 else 580 goto USEREFLECTION; 581 } 582 else 583 { 584 if (String.Compare(className, classNameStart, "ZoneMembershipCondition", 0, classNameLength, StringComparison.Ordinal) == 0) 585 return new ZoneMembershipCondition(); 586 else 587 goto USEREFLECTION; 588 } 589 590 case 28: 591 #if !FEATURE_PAL 592 // PublisherMembershipCondition 593 if (String.Compare(className, classNameStart, "PublisherMembershipCondition", 0, classNameLength, StringComparison.Ordinal) == 0) 594 return new PublisherMembershipCondition(); 595 else 596 #endif // !FEATURE_PAL 597 goto USEREFLECTION; 598 599 case 29: 600 // StrongNameMembershipCondition 601 if (String.Compare(className, classNameStart, "StrongNameMembershipCondition", 0, classNameLength, StringComparison.Ordinal) == 0) 602 return new StrongNameMembershipCondition(); 603 else 604 goto USEREFLECTION; 605 606 case 39: 607 // ApplicationDirectoryMembershipCondition 608 if (String.Compare(className, classNameStart, "ApplicationDirectoryMembershipCondition", 0, classNameLength, StringComparison.Ordinal) == 0) 609 return new ApplicationDirectoryMembershipCondition(); 610 else 611 goto USEREFLECTION; 612 613 default: 614 goto USEREFLECTION; 615 } 616 617 USEREFLECTION: 618 Type condClass = null; 619 IMembershipCondition cond = null; 620 621 new ReflectionPermission(ReflectionPermissionFlag.MemberAccess).Assert(); 622 condClass = GetClassFromElement(el, true); 623 if (condClass == null) 624 return null; 625 if (!(typeof(IMembershipCondition).IsAssignableFrom(condClass))) 626 throw new ArgumentException( Environment.GetResourceString("Argument_NotAMembershipCondition") ); 627 628 cond = (IMembershipCondition) Activator.CreateInstance(condClass, true); 629 630 return cond; 631 } 632 #endif //#if FEATURE_CAS_POLICY 633 internal static Type GetClassFromElement(SecurityElement el, bool ignoreTypeLoadFailures)634 GetClassFromElement (SecurityElement el, bool ignoreTypeLoadFailures) 635 { 636 String className = el.Attribute( "class" ); 637 638 if (className == null) 639 { 640 if (ignoreTypeLoadFailures) 641 return null; 642 else 643 throw new ArgumentException( String.Format( null, Environment.GetResourceString("Argument_InvalidXMLMissingAttr"), "class") ); 644 } 645 646 if (ignoreTypeLoadFailures) 647 { 648 try 649 { 650 return Type.GetType(className, false, false); 651 } 652 catch (SecurityException) 653 { 654 return null; 655 } 656 } 657 else 658 return Type.GetType(className, true, false); 659 } 660 661 public static bool IsPermissionElement(IPermission ip, SecurityElement el)662 IsPermissionElement (IPermission ip, 663 SecurityElement el) 664 { 665 if (!el.Tag.Equals ("Permission") && !el.Tag.Equals ("IPermission")) 666 return false; 667 668 return true; 669 } 670 671 public static bool IsUnrestricted(SecurityElement el)672 IsUnrestricted (SecurityElement el) 673 { 674 String sUnrestricted = el.Attribute( "Unrestricted" ); 675 676 if (sUnrestricted == null) 677 return false; 678 679 return sUnrestricted.Equals( "true" ) || sUnrestricted.Equals( "TRUE" ) || sUnrestricted.Equals( "True" ); 680 } 681 682 BitFieldEnumToString( Type type, Object value )683 public static String BitFieldEnumToString( Type type, Object value ) 684 { 685 int iValue = (int)value; 686 687 if (iValue == 0) 688 return Enum.GetName( type, 0 ); 689 690 StringBuilder result = StringBuilderCache.Acquire(); 691 bool first = true; 692 int flag = 0x1; 693 694 for (int i = 1; i < 32; ++i) 695 { 696 if ((flag & iValue) != 0) 697 { 698 String sFlag = Enum.GetName( type, flag ); 699 700 if (sFlag == null) 701 continue; 702 703 if (!first) 704 { 705 result.Append( ", " ); 706 } 707 708 result.Append( sFlag ); 709 first = false; 710 } 711 712 flag = flag << 1; 713 } 714 715 return StringBuilderCache.GetStringAndRelease(result); 716 } 717 } 718 } 719