1 //------------------------------------------------------------------------------ 2 // <copyright file="VirtualPath.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 //------------------------------------------------------------------------------ 6 7 namespace System.Web { 8 9 using System.Globalization; 10 using System.Collections; 11 using System.IO; 12 using System.Web.Util; 13 using System.Web.Hosting; 14 using System.Web.Caching; 15 using System.Security.Permissions; 16 using Microsoft.Win32; 17 18 [Serializable] 19 internal sealed class VirtualPath : IComparable { 20 private string _appRelativeVirtualPath; 21 private string _virtualPath; 22 23 // const masks into the BitVector32 24 private const int isWithinAppRootComputed = 0x00000001; 25 private const int isWithinAppRoot = 0x00000002; 26 private const int appRelativeAttempted = 0x00000004; 27 28 #pragma warning disable 0649 29 private SimpleBitVector32 flags; 30 #pragma warning restore 0649 31 32 #if DBG 33 private static char[] s_illegalVirtualPathChars = new char[] { '\0' }; 34 35 // Debug only method to check that the object is in a consistent state ValidateState()36 private void ValidateState() { 37 38 Debug.Assert(_virtualPath != null || _appRelativeVirtualPath != null); 39 40 if (_virtualPath != null) { 41 CheckValidVirtualPath(_virtualPath); 42 } 43 44 if (_appRelativeVirtualPath != null) { 45 Debug.Assert(UrlPath.IsAppRelativePath(_appRelativeVirtualPath)); 46 CheckValidVirtualPath(_appRelativeVirtualPath); 47 } 48 } 49 CheckValidVirtualPath(string virtualPath)50 private static void CheckValidVirtualPath(string virtualPath) { 51 Debug.Assert(virtualPath.IndexOfAny(s_illegalVirtualPathChars) < 0); 52 Debug.Assert(virtualPath.IndexOf('\\') < 0); 53 } 54 #endif 55 56 internal static VirtualPath RootVirtualPath = VirtualPath.Create("/"); 57 VirtualPath()58 private VirtualPath() { } 59 60 // This is called to set the appropriate virtual path field when we already know 61 // that the path is generally well formed. VirtualPath(string virtualPath)62 private VirtualPath(string virtualPath) { 63 if (UrlPath.IsAppRelativePath(virtualPath)) { 64 _appRelativeVirtualPath = virtualPath; 65 } 66 else { 67 _virtualPath = virtualPath; 68 } 69 } 70 IComparable.CompareTo(object obj)71 int IComparable.CompareTo(object obj) { 72 73 VirtualPath virtualPath = obj as VirtualPath; 74 75 // Make sure we're compared to another VirtualPath 76 if (virtualPath == null) 77 throw new ArgumentException(); 78 79 // Check if it's the same object 80 if (virtualPath == this) 81 return 0; 82 83 return StringComparer.InvariantCultureIgnoreCase.Compare( 84 this.VirtualPathString, virtualPath.VirtualPathString); 85 } 86 87 public string VirtualPathString { 88 get { 89 if (_virtualPath == null) { 90 Debug.Assert(_appRelativeVirtualPath != null); 91 92 // This is not valid if we don't know the app path 93 if (HttpRuntime.AppDomainAppVirtualPathObject == null) { 94 throw new HttpException(SR.GetString(SR.VirtualPath_CantMakeAppAbsolute, 95 _appRelativeVirtualPath)); 96 } 97 98 if (_appRelativeVirtualPath.Length == 1) { 99 _virtualPath = HttpRuntime.AppDomainAppVirtualPath; 100 } 101 else { 102 _virtualPath = HttpRuntime.AppDomainAppVirtualPathString + 103 _appRelativeVirtualPath.Substring(2); 104 } 105 } 106 107 return _virtualPath; 108 } 109 } 110 111 internal string VirtualPathStringNoTrailingSlash { 112 get { 113 return UrlPath.RemoveSlashFromPathIfNeeded(VirtualPathString); 114 } 115 } 116 117 // Return the virtual path string if we have it, otherwise null 118 internal string VirtualPathStringIfAvailable { 119 get { 120 return _virtualPath; 121 } 122 } 123 124 internal string AppRelativeVirtualPathStringOrNull { 125 get { 126 if (_appRelativeVirtualPath == null) { 127 Debug.Assert(_virtualPath != null); 128 129 // If we already tried to get it and couldn't, return null 130 if (flags[appRelativeAttempted]) 131 return null; 132 133 // This is not valid if we don't know the app path 134 if (HttpRuntime.AppDomainAppVirtualPathObject == null) { 135 throw new HttpException(SR.GetString(SR.VirtualPath_CantMakeAppRelative, _virtualPath)); 136 } 137 138 _appRelativeVirtualPath = UrlPath.MakeVirtualPathAppRelativeOrNull(_virtualPath); 139 140 // Remember that we've attempted it 141 flags[appRelativeAttempted] = true; 142 143 // It could be null if it's not under the app root 144 if (_appRelativeVirtualPath == null) 145 return null; 146 #if DBG 147 ValidateState(); 148 #endif 149 } 150 151 return _appRelativeVirtualPath; 152 } 153 } 154 155 // Return the app relative path if possible. Otherwise, settle for the absolute. 156 public string AppRelativeVirtualPathString { 157 get { 158 string appRelativeVirtualPath = AppRelativeVirtualPathStringOrNull; 159 return (appRelativeVirtualPath != null) ? appRelativeVirtualPath : _virtualPath; 160 } 161 } 162 163 // Return the app relative virtual path string if we have it, otherwise null 164 internal string AppRelativeVirtualPathStringIfAvailable { 165 get { 166 return _appRelativeVirtualPath; 167 } 168 } 169 // Return the virtual string that's either app relative or not, depending on which 170 // one we already have internally. If we have both, we return absolute 171 internal string VirtualPathStringWhicheverAvailable { 172 get { 173 return _virtualPath != null ? _virtualPath : _appRelativeVirtualPath; 174 } 175 } 176 177 public string Extension { 178 get { 179 return UrlPath.GetExtension(VirtualPathString); 180 } 181 } 182 183 public string FileName { 184 get { 185 return UrlPath.GetFileName(VirtualPathStringNoTrailingSlash); 186 } 187 } 188 189 // If it's relative, combine it with the app root CombineWithAppRoot()190 public VirtualPath CombineWithAppRoot() { 191 return HttpRuntime.AppDomainAppVirtualPathObject.Combine(this); 192 } 193 Combine(VirtualPath relativePath)194 public VirtualPath Combine(VirtualPath relativePath) { 195 if (relativePath == null) 196 throw new ArgumentNullException("relativePath"); 197 198 // If it's not relative, return it unchanged 199 if (!relativePath.IsRelative) 200 return relativePath; 201 202 // The base of the combine should never be relative 203 FailIfRelativePath(); 204 205 // Get either _appRelativeVirtualPath or _virtualPath 206 string virtualPath = VirtualPathStringWhicheverAvailable; 207 208 // Combine it with the relative 209 virtualPath = UrlPath.Combine(virtualPath, relativePath.VirtualPathString); 210 211 // Set the appropriate virtual path in the new object 212 return new VirtualPath(virtualPath); 213 } 214 215 // This simple version of combine should only be used when the relative 216 // path is known to be relative. It's more efficient, but doesn't do any 217 // sanity checks. SimpleCombine(string relativePath)218 internal VirtualPath SimpleCombine(string relativePath) { 219 return SimpleCombine(relativePath, false /*addTrailingSlash*/); 220 } 221 SimpleCombineWithDir(string directoryName)222 internal VirtualPath SimpleCombineWithDir(string directoryName) { 223 return SimpleCombine(directoryName, true /*addTrailingSlash*/); 224 } 225 SimpleCombine(string filename, bool addTrailingSlash)226 private VirtualPath SimpleCombine(string filename, bool addTrailingSlash) { 227 228 // The left part should always be a directory 229 Debug.Assert(HasTrailingSlash); 230 231 // The right part should not start or end with a slash 232 Debug.Assert(filename[0] != '/' && !UrlPath.HasTrailingSlash(filename)); 233 234 // Use either _appRelativeVirtualPath or _virtualPath 235 string virtualPath = VirtualPathStringWhicheverAvailable + filename; 236 if (addTrailingSlash) 237 virtualPath += "/"; 238 239 // Set the appropriate virtual path in the new object 240 VirtualPath combinedVirtualPath = new VirtualPath(virtualPath); 241 242 // Copy some flags over to avoid having to recalculate them 243 combinedVirtualPath.CopyFlagsFrom(this, isWithinAppRootComputed | isWithinAppRoot | appRelativeAttempted); 244 #if DBG 245 combinedVirtualPath.ValidateState(); 246 #endif 247 return combinedVirtualPath; 248 } 249 MakeRelative(VirtualPath toVirtualPath)250 public VirtualPath MakeRelative(VirtualPath toVirtualPath) { 251 VirtualPath resultVirtualPath = new VirtualPath(); 252 253 // Neither path can be relative 254 FailIfRelativePath(); 255 toVirtualPath.FailIfRelativePath(); 256 257 // Set it directly since we know the slashes are already ok 258 resultVirtualPath._virtualPath = UrlPath.MakeRelative(this.VirtualPathString, toVirtualPath.VirtualPathString); 259 #if DBG 260 resultVirtualPath.ValidateState(); 261 #endif 262 return resultVirtualPath; 263 } 264 MapPath()265 public string MapPath() { 266 return HostingEnvironment.MapPath(this); 267 } 268 MapPathInternal()269 internal string MapPathInternal() { 270 return HostingEnvironment.MapPathInternal(this); 271 } 272 MapPathInternal(bool permitNull)273 internal string MapPathInternal(bool permitNull) { 274 return HostingEnvironment.MapPathInternal(this, permitNull); 275 } 276 MapPathInternal(VirtualPath baseVirtualDir, bool allowCrossAppMapping)277 internal string MapPathInternal(VirtualPath baseVirtualDir, bool allowCrossAppMapping) { 278 return HostingEnvironment.MapPathInternal(this, baseVirtualDir, allowCrossAppMapping); 279 } 280 281 ///////////// VirtualPathProvider wrapper methods ///////////// 282 GetFileHash(IEnumerable virtualPathDependencies)283 public string GetFileHash(IEnumerable virtualPathDependencies) { 284 return HostingEnvironment.VirtualPathProvider.GetFileHash(this, virtualPathDependencies); 285 } 286 GetCacheDependency(IEnumerable virtualPathDependencies, DateTime utcStart)287 public CacheDependency GetCacheDependency(IEnumerable virtualPathDependencies, DateTime utcStart) { 288 return HostingEnvironment.VirtualPathProvider.GetCacheDependency( 289 this, virtualPathDependencies, utcStart); 290 } 291 FileExists()292 public bool FileExists() { 293 return HostingEnvironment.VirtualPathProvider.FileExists(this); 294 } 295 DirectoryExists()296 public bool DirectoryExists() { 297 return HostingEnvironment.VirtualPathProvider.DirectoryExists(this); 298 } 299 GetFile()300 public VirtualFile GetFile() { 301 return HostingEnvironment.VirtualPathProvider.GetFile(this); 302 } 303 GetDirectory()304 public VirtualDirectory GetDirectory() { 305 Debug.Assert(this.HasTrailingSlash); 306 return HostingEnvironment.VirtualPathProvider.GetDirectory(this); 307 } 308 GetCacheKey()309 public string GetCacheKey() { 310 return HostingEnvironment.VirtualPathProvider.GetCacheKey(this); 311 } 312 OpenFile()313 public Stream OpenFile() { 314 return VirtualPathProvider.OpenFile(this); 315 } 316 317 ///////////// end of VirtualPathProvider methods ///////////// 318 319 320 internal bool HasTrailingSlash { 321 get { 322 if (_virtualPath != null) { 323 return UrlPath.HasTrailingSlash(_virtualPath); 324 } 325 else { 326 return UrlPath.HasTrailingSlash(_appRelativeVirtualPath); 327 } 328 } 329 } 330 331 public bool IsWithinAppRoot { 332 get { 333 // If we don't already know it, compute it and cache it 334 if (!flags[isWithinAppRootComputed]) { 335 if (HttpRuntime.AppDomainIdInternal == null) { 336 Debug.Assert(false); 337 return true; // app domain not initialized 338 } 339 340 if (flags[appRelativeAttempted]) { 341 // If we already tried to get the app relative path, we can tell whether 342 // it's in the app root by checking whether it's not null 343 flags[isWithinAppRoot] = (_appRelativeVirtualPath != null); 344 } 345 else { 346 flags[isWithinAppRoot] = UrlPath.IsEqualOrSubpath(HttpRuntime.AppDomainAppVirtualPathString, 347 VirtualPathString); 348 } 349 350 flags[isWithinAppRootComputed] = true; 351 } 352 353 return flags[isWithinAppRoot]; 354 } 355 } 356 FailIfNotWithinAppRoot()357 internal void FailIfNotWithinAppRoot() { 358 if (!this.IsWithinAppRoot) { 359 throw new ArgumentException(SR.GetString(SR.Cross_app_not_allowed, this.VirtualPathString)); 360 } 361 } 362 FailIfRelativePath()363 internal void FailIfRelativePath() { 364 if (this.IsRelative) { 365 throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowRelativePath, _virtualPath)); 366 } 367 } 368 369 public bool IsRelative { 370 get { 371 // Note that we don't need to check for "~/", since _virtualPath never contains 372 // app relative paths (_appRelativeVirtualPath does) 373 return _virtualPath != null && _virtualPath[0] != '/'; 374 } 375 } 376 377 public bool IsRoot { 378 get { 379 return _virtualPath == "/"; 380 } 381 } 382 383 public VirtualPath Parent { 384 get { 385 // Getting the parent doesn't make much sense on relative paths 386 FailIfRelativePath(); 387 388 // "/" doesn't have a parent, so return null 389 if (IsRoot) 390 return null; 391 392 // Get either _appRelativeVirtualPath or _virtualPath 393 string virtualPath = VirtualPathStringWhicheverAvailable; 394 395 // Get rid of the ending slash, otherwise we end up with Parent("/app/sub/") == "/app/sub/" 396 virtualPath = UrlPath.RemoveSlashFromPathIfNeeded(virtualPath); 397 398 // But if it's just "~", use the absolute path instead to get the parent 399 if (virtualPath == "~") 400 virtualPath = VirtualPathStringNoTrailingSlash; 401 402 int index = virtualPath.LastIndexOf('/'); 403 Debug.Assert(index >= 0); 404 405 // e.g. the parent of "/blah" is "/" 406 if (index == 0) 407 return RootVirtualPath; 408 409 // 410 411 // Get the parent 412 virtualPath = virtualPath.Substring(0, index + 1); 413 414 // Set the appropriate virtual path in the new object 415 return new VirtualPath(virtualPath); 416 } 417 } 418 Combine(VirtualPath v1, VirtualPath v2)419 internal static VirtualPath Combine(VirtualPath v1, VirtualPath v2) { 420 421 // If the first is null, use the app root instead 422 if (v1 == null) { 423 v1 = HttpRuntime.AppDomainAppVirtualPathObject; 424 } 425 426 // If the first is still null, return the second, unless it's relative 427 if (v1 == null) { 428 v2.FailIfRelativePath(); 429 return v2; 430 } 431 432 return v1.Combine(v2); 433 } 434 operator ==(VirtualPath v1, VirtualPath v2)435 public static bool operator == (VirtualPath v1, VirtualPath v2) { 436 return VirtualPath.Equals(v1, v2); 437 } 438 operator !=(VirtualPath v1, VirtualPath v2)439 public static bool operator != (VirtualPath v1, VirtualPath v2) { 440 return !VirtualPath.Equals(v1, v2); 441 } 442 Equals(VirtualPath v1, VirtualPath v2)443 public static bool Equals(VirtualPath v1, VirtualPath v2) { 444 445 // Check if it's the same object 446 if ((Object)v1 == (Object)v2) { 447 return true; 448 } 449 450 if ((Object)v1 == null || (Object)v2 == null) { 451 return false; 452 } 453 454 return EqualsHelper(v1, v2); 455 } 456 Equals(object value)457 public override bool Equals(object value) { 458 459 if (value == null) 460 return false; 461 462 VirtualPath virtualPath = value as VirtualPath; 463 if ((object)virtualPath == null) { 464 Debug.Assert(false); 465 return false; 466 } 467 468 return EqualsHelper(virtualPath, this); 469 } 470 EqualsHelper(VirtualPath v1, VirtualPath v2)471 private static bool EqualsHelper(VirtualPath v1, VirtualPath v2) { 472 return StringComparer.InvariantCultureIgnoreCase.Compare( 473 v1.VirtualPathString, v2.VirtualPathString) == 0; 474 } 475 GetHashCode()476 public override int GetHashCode() { 477 return StringComparer.InvariantCultureIgnoreCase.GetHashCode(VirtualPathString); 478 } 479 ToString()480 public override String ToString() { 481 // If we only have the app relative path, and we don't know the app root, return 482 // the app relative path instead of accessing VirtualPathString, which would throw 483 if (_virtualPath == null && HttpRuntime.AppDomainAppVirtualPathObject == null) { 484 Debug.Assert(_appRelativeVirtualPath != null); 485 return _appRelativeVirtualPath; 486 } 487 488 return VirtualPathString; 489 } 490 491 // Copy a set of flags from another VirtualPath object CopyFlagsFrom(VirtualPath virtualPath, int mask)492 private void CopyFlagsFrom(VirtualPath virtualPath, int mask) { 493 flags.IntegerValue |= virtualPath.flags.IntegerValue & mask; 494 } 495 GetVirtualPathString(VirtualPath virtualPath)496 internal static string GetVirtualPathString(VirtualPath virtualPath) { 497 return virtualPath == null ? null : virtualPath.VirtualPathString; 498 } 499 GetVirtualPathStringNoTrailingSlash(VirtualPath virtualPath)500 internal static string GetVirtualPathStringNoTrailingSlash(VirtualPath virtualPath) { 501 return virtualPath == null ? null : virtualPath.VirtualPathStringNoTrailingSlash; 502 } 503 GetAppRelativeVirtualPathString(VirtualPath virtualPath)504 internal static string GetAppRelativeVirtualPathString(VirtualPath virtualPath) { 505 return virtualPath == null ? null : virtualPath.AppRelativeVirtualPathString; 506 } 507 508 // Same as GetAppRelativeVirtualPathString, but returns "" instead of null GetAppRelativeVirtualPathStringOrEmpty(VirtualPath virtualPath)509 internal static string GetAppRelativeVirtualPathStringOrEmpty(VirtualPath virtualPath) { 510 return virtualPath == null ? String.Empty : virtualPath.AppRelativeVirtualPathString; 511 } 512 513 // Default Create method Create(string virtualPath)514 public static VirtualPath Create(string virtualPath) { 515 return Create(virtualPath, VirtualPathOptions.AllowAllPath); 516 } 517 CreateTrailingSlash(string virtualPath)518 public static VirtualPath CreateTrailingSlash(string virtualPath) { 519 return Create(virtualPath, VirtualPathOptions.AllowAllPath | VirtualPathOptions.EnsureTrailingSlash); 520 } 521 CreateAllowNull(string virtualPath)522 public static VirtualPath CreateAllowNull(string virtualPath) { 523 return Create(virtualPath, VirtualPathOptions.AllowAllPath | VirtualPathOptions.AllowNull); 524 } 525 CreateAbsolute(string virtualPath)526 public static VirtualPath CreateAbsolute(string virtualPath) { 527 return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath); 528 } 529 CreateNonRelative(string virtualPath)530 public static VirtualPath CreateNonRelative(string virtualPath) { 531 return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath); 532 } 533 CreateAbsoluteTrailingSlash(string virtualPath)534 public static VirtualPath CreateAbsoluteTrailingSlash(string virtualPath) { 535 return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.EnsureTrailingSlash); 536 } 537 CreateNonRelativeTrailingSlash(string virtualPath)538 public static VirtualPath CreateNonRelativeTrailingSlash(string virtualPath) { 539 return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath | 540 VirtualPathOptions.EnsureTrailingSlash); 541 } 542 CreateAbsoluteAllowNull(string virtualPath)543 public static VirtualPath CreateAbsoluteAllowNull(string virtualPath) { 544 return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowNull); 545 } 546 CreateNonRelativeAllowNull(string virtualPath)547 public static VirtualPath CreateNonRelativeAllowNull(string virtualPath) { 548 return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath | VirtualPathOptions.AllowNull); 549 } 550 CreateNonRelativeTrailingSlashAllowNull(string virtualPath)551 public static VirtualPath CreateNonRelativeTrailingSlashAllowNull(string virtualPath) { 552 return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath | 553 VirtualPathOptions.AllowNull | VirtualPathOptions.EnsureTrailingSlash); 554 } 555 Create(string virtualPath, VirtualPathOptions options)556 public static VirtualPath Create(string virtualPath, VirtualPathOptions options) { 557 558 // Trim it first, so that blank strings (e.g. " ") get treated as empty 559 if (virtualPath != null) 560 virtualPath = virtualPath.Trim(); 561 562 // If it's empty, check whether we allow it 563 if (String.IsNullOrEmpty(virtualPath)) { 564 if ((options & VirtualPathOptions.AllowNull) != 0) 565 return null; 566 567 throw new ArgumentNullException("virtualPath"); 568 } 569 570 // Dev10 767308: optimize for normal paths, and scan once for 571 // i) invalid chars 572 // ii) slashes 573 // iii) '.' 574 575 bool slashes = false; 576 bool dot = false; 577 int len = virtualPath.Length; 578 unsafe { 579 fixed (char * p = virtualPath) { 580 for (int i = 0; i < len; i++) { 581 switch (p[i]) { 582 // need to fix slashes ? 583 case '/': 584 if (i > 0 && p[i-1] == '/') 585 slashes = true; 586 break; 587 case '\\': 588 slashes = true; 589 break; 590 // contains "." or ".." 591 case '.': 592 dot = true; 593 break; 594 // invalid chars 595 case '\0': 596 throw new HttpException(SR.GetString(SR.Invalid_vpath, virtualPath)); 597 default: 598 break; 599 } 600 } 601 } 602 } 603 604 if (slashes) { 605 // If we're supposed to fail on malformed path, then throw 606 if ((options & VirtualPathOptions.FailIfMalformed) != 0) { 607 throw new HttpException(SR.GetString(SR.Invalid_vpath, virtualPath)); 608 } 609 // Flip ----lashes, and remove duplicate slashes 610 virtualPath = UrlPath.FixVirtualPathSlashes(virtualPath); 611 } 612 613 // Make sure it ends with a trailing slash if requested 614 if ((options & VirtualPathOptions.EnsureTrailingSlash) != 0) 615 virtualPath = UrlPath.AppendSlashToPathIfNeeded(virtualPath); 616 617 VirtualPath virtualPathObject = new VirtualPath(); 618 619 if (UrlPath.IsAppRelativePath(virtualPath)) { 620 621 if (dot) 622 virtualPath = UrlPath.ReduceVirtualPath(virtualPath); 623 624 if (virtualPath[0] == UrlPath.appRelativeCharacter) { 625 if ((options & VirtualPathOptions.AllowAppRelativePath) == 0) { 626 throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowAppRelativePath, virtualPath)); 627 } 628 629 virtualPathObject._appRelativeVirtualPath = virtualPath; 630 } 631 else { 632 // It's possible for the path to become absolute after calling Reduce, 633 // even though it started with "~/". e.g. if the app is "/app" and the path is 634 // "~/../hello.aspx", it becomes "/hello.aspx", which is absolute 635 636 if ((options & VirtualPathOptions.AllowAbsolutePath) == 0) { 637 throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowAbsolutePath, virtualPath)); 638 } 639 640 virtualPathObject._virtualPath = virtualPath; 641 } 642 } 643 else { 644 if (virtualPath[0] != '/') { 645 if ((options & VirtualPathOptions.AllowRelativePath) == 0) { 646 throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowRelativePath, virtualPath)); 647 } 648 649 // Don't Reduce relative paths, since the Reduce method is broken (e.g. "../foo.aspx" --> "/foo.aspx!") 650 // 651 virtualPathObject._virtualPath = virtualPath; 652 } 653 else { 654 if ((options & VirtualPathOptions.AllowAbsolutePath) == 0) { 655 throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowAbsolutePath, virtualPath)); 656 } 657 658 if (dot) 659 virtualPath = UrlPath.ReduceVirtualPath(virtualPath); 660 661 virtualPathObject._virtualPath = virtualPath; 662 } 663 } 664 #if DBG 665 virtualPathObject.ValidateState(); 666 #endif 667 return virtualPathObject; 668 } 669 } 670 671 [Flags] 672 internal enum VirtualPathOptions { 673 AllowNull = 0x00000001, 674 EnsureTrailingSlash = 0x00000002, 675 AllowAbsolutePath = 0x00000004, 676 AllowAppRelativePath = 0x00000008, 677 AllowRelativePath = 0x00000010, 678 FailIfMalformed = 0x00000020, 679 680 AllowAllPath = AllowAbsolutePath | AllowAppRelativePath | AllowRelativePath, 681 } 682 } 683