//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth { using System; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.RegularExpressions; using System.Web; using System.Web.UI; using DotNetOpenAuth.Messaging; using Validation; /// /// Utility methods for working with URIs. /// internal static class UriUtil { /// /// Tests a URI for the presence of an OAuth payload. /// /// The URI to test. /// The prefix. /// /// True if the URI contains an OAuth message. /// internal static bool QueryStringContainPrefixedParameters(this Uri uri, string prefix) { Requires.NotNullOrEmpty(prefix, "prefix"); if (uri == null) { return false; } NameValueCollection nvc = HttpUtility.ParseQueryString(uri.Query); Assumes.True(nvc != null); // BCL return nvc.Keys.OfType().Any(key => key.StartsWith(prefix, StringComparison.Ordinal)); } /// /// Determines whether some is using HTTPS. /// /// The Uri being tested for security. /// /// true if the URI represents an encrypted request; otherwise, false. /// internal static bool IsTransportSecure(this Uri uri) { Requires.NotNull(uri, "uri"); return string.Equals(uri.Scheme, "https", StringComparison.OrdinalIgnoreCase); } /// /// Equivalent to UriBuilder.ToString() but omits port # if it may be implied. /// Equivalent to UriBuilder.Uri.ToString(), but doesn't throw an exception if the Host has a wildcard. /// /// The UriBuilder to render as a string. /// The string version of the Uri. internal static string ToStringWithImpliedPorts(this UriBuilder builder) { Requires.NotNull(builder, "builder"); // We only check for implied ports on HTTP and HTTPS schemes since those // are the only ones supported by OpenID anyway. if ((builder.Port == 80 && string.Equals(builder.Scheme, "http", StringComparison.OrdinalIgnoreCase)) || (builder.Port == 443 && string.Equals(builder.Scheme, "https", StringComparison.OrdinalIgnoreCase))) { // An implied port may be removed. string url = builder.ToString(); // Be really careful to only remove the first :80 or :443 so we are guaranteed // we're removing only the port (and not something in the query string that // looks like a port. string result = Regex.Replace(url, @"^(https?://[^:]+):\d+", m => m.Groups[1].Value, RegexOptions.IgnoreCase); Assumes.True(result != null); // Regex.Replace never returns null return result; } else { // The port must be explicitly given anyway. return builder.ToString(); } } /// /// Validates that a URL will be resolvable at runtime. /// /// The page hosting the control that receives this URL as a property. /// If set to true the page is in design-time mode rather than runtime mode. /// The URI to check. /// Thrown if the given URL is not a valid, resolvable URI. [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Just to throw an exception on invalid input.")] internal static void ValidateResolvableUrl(Page page, bool designMode, string value) { if (string.IsNullOrEmpty(value)) { return; } if (page != null && !designMode) { Assumes.True(page.Request != null); // Validate new value by trying to construct a Realm object based on it. string relativeUrl = page.ResolveUrl(value); Assumes.True(page.Request.Url != null); Assumes.True(relativeUrl != null); new Uri(page.Request.Url, relativeUrl); // throws an exception on failure. } else { // We can't fully test it, but it should start with either ~/ or a protocol. if (Regex.IsMatch(value, @"^https?://")) { new Uri(value); // make sure it's fully-qualified, but ignore wildcards } else if (value.StartsWith("~/", StringComparison.Ordinal)) { // this is valid too } else { throw new UriFormatException(); } } } } }