diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index dd3ae8c10c..2d9d644514 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -1,323 +1,323 @@ -using System; -using System.IO; -using System.Linq; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; - -namespace Umbraco.Core -{ - /// - /// Provides extension methods to . - /// - public static class UriExtensions - { - /// - /// Checks if the current uri is a back office request - /// - /// - /// - /// The current application path or VirtualPath - /// - /// - /// - /// There are some special routes we need to check to properly determine this: - /// - /// If any route has an extension in the path like .aspx = back office - /// - /// These are def back office: - /// /Umbraco/RestServices = back office - /// /Umbraco/BackOffice = back office - /// If it's not any of the above, and there's no extension then we cannot determine if it's back office or front-end - /// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice - /// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute. - /// - /// These are def front-end: - /// /Umbraco/Surface = front-end - /// /Umbraco/Api = front-end - /// But if we've got this far we'll just have to assume it's front-end anyways. - /// - /// - internal static bool IsBackOfficeRequest(this Uri url, string applicationPath) - { - applicationPath = applicationPath ?? string.Empty; - - var fullUrlPath = url.AbsolutePath.TrimStart(new[] {'/'}); - var appPath = applicationPath.TrimStart(new[] {'/'}); - var urlPath = fullUrlPath.TrimStart(appPath).EnsureStartsWith('/'); - - //check if this is in the umbraco back office - var isUmbracoPath = urlPath.InvariantStartsWith(GlobalSettings.Path.EnsureStartsWith('/').TrimStart(appPath.EnsureStartsWith('/')).EnsureStartsWith('/')); - //if not, then def not back office - if (isUmbracoPath == false) return false; - - //if its the normal /umbraco path - if (urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea) - || urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea + "/")) - { - return true; - } - - //check for a file extension - var extension = Path.GetExtension(url.LocalPath); - //has an extension, def back office - if (extension.IsNullOrWhiteSpace() == false) return true; - //check for special case asp.net calls like: - // /umbraco/webservices/legacyAjaxCalls.asmx/js which will return a null file extension but are still considered extension'd requests - if (urlPath.InvariantContains(".asmx/") - || urlPath.InvariantContains(".aspx/") - || urlPath.InvariantContains(".ashx/") - || urlPath.InvariantContains(".axd/") - || urlPath.InvariantContains(".svc/")) - { - return true; - } - - //check for special back office paths - if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/BackOffice/") - || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/RestServices/")) - { - return true; - } - - //check for special front-end paths - if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/Surface/") - || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/Api/")) - { - return false; - } - - //if its none of the above, we will have to try to detect if it's a PluginController route, we can detect this by - // checking how many parts the route has, for example, all PluginController routes will be routed like - // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id} - // so if the path contains at a minimum 3 parts: Umbraco + MYPLUGINAREA + MYCONTROLLERNAME then we will have to assume it is a - // plugin controller for the front-end. - if (urlPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries).Length >= 3) - { - return false; - } - - //if its anything else we can assume it's back office - return true; - } - - /// - /// Checks if the current uri is an install request - /// - /// - /// - internal static bool IsInstallerRequest(this Uri url) - { - var authority = url.GetLeftPart(UriPartial.Authority); - var afterAuthority = url.GetLeftPart(UriPartial.Query) - .TrimStart(authority) - .TrimStart("/"); - - //check if this is in the umbraco back office - return afterAuthority.InvariantStartsWith(IOHelper.ResolveUrl("~/install").TrimStart("/")); - } - - /// - /// Checks if the uri is a request for the default back office page - /// - /// - /// - internal static bool IsDefaultBackOfficeRequest(this Uri url) - { - if (url.AbsolutePath.InvariantEquals(GlobalSettings.Path.TrimEnd("/")) - || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/')) - || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/') + "Default") - || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/') + "Default/")) - { - return true; - } - return false; - } - - /// - /// This is a performance tweak to check if this not an ASP.Net server file - /// .Net will pass these requests through to the module when in integrated mode. - /// We want to ignore all of these requests immediately. - /// - /// - /// - internal static bool IsClientSideRequest(this Uri url) - { - try - { - var ext = Path.GetExtension(url.LocalPath); - if (ext.IsNullOrWhiteSpace()) return false; - var toInclude = new[] {".aspx", ".ashx", ".asmx", ".axd", ".svc"}; - return toInclude.Any(ext.InvariantEquals) == false; - } - catch (ArgumentException ex) - { - LogHelper.Error(typeof(UriExtensions), "Failed to determine if request was client side", ex); - return false; - } - } - - /// - /// Rewrites the path of uri. - /// - /// The uri. - /// The new path, which must begin with a slash. - /// The rewritten uri. - /// Everything else remains unchanged, except for the fragment which is removed. - public static Uri Rewrite(this Uri uri, string path) - { - if (path.StartsWith("/") == false) - throw new ArgumentException("Path must start with a slash.", "path"); - - return uri.IsAbsoluteUri - ? new Uri(uri.GetLeftPart(UriPartial.Authority) + path + uri.Query) - : new Uri(path + uri.GetSafeQuery(), UriKind.Relative); - } - - /// - /// Rewrites the path and query of a uri. - /// - /// The uri. - /// The new path, which must begin with a slash. - /// The new query, which must be empty or begin with a question mark. - /// The rewritten uri. - /// Everything else remains unchanged, except for the fragment which is removed. - public static Uri Rewrite(this Uri uri, string path, string query) - { - if (path.StartsWith("/") == false) - throw new ArgumentException("Path must start with a slash.", "path"); - if (query.Length > 0 && query.StartsWith("?") == false) - throw new ArgumentException("Query must start with a question mark.", "query"); - if (query == "?") - query = ""; - - return uri.IsAbsoluteUri - ? new Uri(uri.GetLeftPart(UriPartial.Authority) + path + query) - : new Uri(path + query, UriKind.Relative); - } - - /// - /// Gets the absolute path of the uri, even if the uri is relative. - /// - /// The uri. - /// The absolute path of the uri. - /// Default uri.AbsolutePath does not support relative uris. - public static string GetSafeAbsolutePath(this Uri uri) - { - if (uri.IsAbsoluteUri) - return uri.AbsolutePath; - - // cannot get .AbsolutePath on relative uri (InvalidOperation) - var s = uri.OriginalString; - var posq = s.IndexOf("?", StringComparison.Ordinal); - var posf = s.IndexOf("#", StringComparison.Ordinal); - var pos = posq > 0 ? posq : (posf > 0 ? posf : 0); - var path = pos > 0 ? s.Substring(0, pos) : s; - return path; - } - - /// - /// Gets the decoded, absolute path of the uri. - /// - /// The uri. - /// The absolute path of the uri. - /// Only for absolute uris. - public static string GetAbsolutePathDecoded(this Uri uri) - { - return System.Web.HttpUtility.UrlDecode(uri.AbsolutePath); - } - - /// - /// Gets the decoded, absolute path of the uri, even if the uri is relative. - /// - /// The uri. - /// The absolute path of the uri. - /// Default uri.AbsolutePath does not support relative uris. - public static string GetSafeAbsolutePathDecoded(this Uri uri) - { - return System.Web.HttpUtility.UrlDecode(uri.GetSafeAbsolutePath()); - } - - /// - /// Rewrites the path of the uri so it ends with a slash. - /// - /// The uri. - /// The rewritten uri. - /// Everything else remains unchanged. - public static Uri EndPathWithSlash(this Uri uri) - { - var path = uri.GetSafeAbsolutePath(); - if (uri.IsAbsoluteUri) - { - if (path != "/" && path.EndsWith("/") == false) - uri = new Uri(uri.GetLeftPart(UriPartial.Authority) + path + "/" + uri.Query); - return uri; - } - - if (path != "/" && path.EndsWith("/") == false) - uri = new Uri(path + "/" + uri.Query, UriKind.Relative); - - return uri; - } - - /// - /// Rewrites the path of the uri so it does not end with a slash. - /// - /// The uri. - /// The rewritten uri. - /// Everything else remains unchanged. - public static Uri TrimPathEndSlash(this Uri uri) - { - var path = uri.GetSafeAbsolutePath(); - if (uri.IsAbsoluteUri) - { - if (path != "/") - uri = new Uri(uri.GetLeftPart(UriPartial.Authority) + path.TrimEnd('/') + uri.Query); - } - else - { - if (path != "/") - uri = new Uri(path.TrimEnd('/') + uri.Query, UriKind.Relative); - } - return uri; - } - - /// - /// Transforms a relative uri into an absolute uri. - /// - /// The relative uri. - /// The base absolute uri. - /// The absolute uri. - public static Uri MakeAbsolute(this Uri uri, Uri baseUri) - { - if (uri.IsAbsoluteUri) - throw new ArgumentException("Uri is already absolute.", "uri"); - - return new Uri(baseUri.GetLeftPart(UriPartial.Authority) + uri.GetSafeAbsolutePath() + uri.GetSafeQuery()); - } - - static string GetSafeQuery(this Uri uri) - { - if (uri.IsAbsoluteUri) - return uri.Query; - - // cannot get .Query on relative uri (InvalidOperation) - var s = uri.OriginalString; - var posq = s.IndexOf("?", StringComparison.Ordinal); - var posf = s.IndexOf("#", StringComparison.Ordinal); - var query = posq < 0 ? null : (posf < 0 ? s.Substring(posq) : s.Substring(posq, posf - posq)); - - return query; - } - - /// - /// Removes the port from the uri. - /// - /// The uri. - /// The same uri, without its port. - public static Uri WithoutPort(this Uri uri) - { - return new Uri(uri.GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped)); - } - } -} +using System; +using System.IO; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; + +namespace Umbraco.Core +{ + /// + /// Provides extension methods to . + /// + public static class UriExtensions + { + /// + /// Checks if the current uri is a back office request + /// + /// + /// + /// The current application path or VirtualPath + /// + /// + /// + /// There are some special routes we need to check to properly determine this: + /// + /// If any route has an extension in the path like .aspx = back office + /// + /// These are def back office: + /// /Umbraco/RestServices = back office + /// /Umbraco/BackOffice = back office + /// If it's not any of the above, and there's no extension then we cannot determine if it's back office or front-end + /// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice + /// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute. + /// + /// These are def front-end: + /// /Umbraco/Surface = front-end + /// /Umbraco/Api = front-end + /// But if we've got this far we'll just have to assume it's front-end anyways. + /// + /// + internal static bool IsBackOfficeRequest(this Uri url, string applicationPath) + { + applicationPath = applicationPath ?? string.Empty; + + var fullUrlPath = url.AbsolutePath.TrimStart(new[] {'/'}); + var appPath = applicationPath.TrimStart(new[] {'/'}); + var urlPath = fullUrlPath.TrimStart(appPath).EnsureStartsWith('/'); + + //check if this is in the umbraco back office + var isUmbracoPath = urlPath.InvariantStartsWith(GlobalSettings.Path.EnsureStartsWith('/').TrimStart(appPath.EnsureStartsWith('/')).EnsureStartsWith('/')); + //if not, then def not back office + if (isUmbracoPath == false) return false; + + //if its the normal /umbraco path + if (urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea) + || urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea + "/")) + { + return true; + } + + //check for a file extension + var extension = Path.GetExtension(url.LocalPath); + //has an extension, def back office + if (extension.IsNullOrWhiteSpace() == false) return true; + //check for special case asp.net calls like: + // /umbraco/webservices/legacyAjaxCalls.asmx/js which will return a null file extension but are still considered extension'd requests + if (urlPath.InvariantContains(".asmx/") + || urlPath.InvariantContains(".aspx/") + || urlPath.InvariantContains(".ashx/") + || urlPath.InvariantContains(".axd/") + || urlPath.InvariantContains(".svc/")) + { + return true; + } + + //check for special back office paths + if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/BackOffice/") + || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/RestServices/")) + { + return true; + } + + //check for special front-end paths + if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/Surface/") + || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/Api/")) + { + return false; + } + + //if its none of the above, we will have to try to detect if it's a PluginController route, we can detect this by + // checking how many parts the route has, for example, all PluginController routes will be routed like + // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id} + // so if the path contains at a minimum 3 parts: Umbraco + MYPLUGINAREA + MYCONTROLLERNAME then we will have to assume it is a + // plugin controller for the front-end. + if (urlPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries).Length >= 3) + { + return false; + } + + //if its anything else we can assume it's back office + return true; + } + + /// + /// Checks if the current uri is an install request + /// + /// + /// + internal static bool IsInstallerRequest(this Uri url) + { + var authority = url.GetLeftPart(UriPartial.Authority); + var afterAuthority = url.GetLeftPart(UriPartial.Query) + .TrimStart(authority) + .TrimStart("/"); + + //check if this is in the umbraco back office + return afterAuthority.InvariantStartsWith(IOHelper.ResolveUrl("~/install").TrimStart("/")); + } + + /// + /// Checks if the uri is a request for the default back office page + /// + /// + /// + internal static bool IsDefaultBackOfficeRequest(this Uri url) + { + if (url.AbsolutePath.InvariantEquals(GlobalSettings.Path.TrimEnd("/")) + || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/')) + || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/') + "Default") + || url.AbsolutePath.InvariantEquals(GlobalSettings.Path.EnsureEndsWith('/') + "Default/")) + { + return true; + } + return false; + } + + /// + /// This is a performance tweak to check if this not an ASP.Net server file + /// .Net will pass these requests through to the module when in integrated mode. + /// We want to ignore all of these requests immediately. + /// + /// + /// + internal static bool IsClientSideRequest(this Uri url) + { + try + { + var ext = Path.GetExtension(url.LocalPath); + if (ext.IsNullOrWhiteSpace()) return false; + var toInclude = new[] {".aspx", ".ashx", ".asmx", ".axd", ".svc"}; + return toInclude.Any(ext.InvariantEquals) == false; + } + catch (ArgumentException) + { + LogHelper.Debug(typeof(UriExtensions), "Failed to determine if request was client side. Due to invalid characters in path: {0}", () => url.LocalPath); + return false; + } + } + + /// + /// Rewrites the path of uri. + /// + /// The uri. + /// The new path, which must begin with a slash. + /// The rewritten uri. + /// Everything else remains unchanged, except for the fragment which is removed. + public static Uri Rewrite(this Uri uri, string path) + { + if (path.StartsWith("/") == false) + throw new ArgumentException("Path must start with a slash.", "path"); + + return uri.IsAbsoluteUri + ? new Uri(uri.GetLeftPart(UriPartial.Authority) + path + uri.Query) + : new Uri(path + uri.GetSafeQuery(), UriKind.Relative); + } + + /// + /// Rewrites the path and query of a uri. + /// + /// The uri. + /// The new path, which must begin with a slash. + /// The new query, which must be empty or begin with a question mark. + /// The rewritten uri. + /// Everything else remains unchanged, except for the fragment which is removed. + public static Uri Rewrite(this Uri uri, string path, string query) + { + if (path.StartsWith("/") == false) + throw new ArgumentException("Path must start with a slash.", "path"); + if (query.Length > 0 && query.StartsWith("?") == false) + throw new ArgumentException("Query must start with a question mark.", "query"); + if (query == "?") + query = ""; + + return uri.IsAbsoluteUri + ? new Uri(uri.GetLeftPart(UriPartial.Authority) + path + query) + : new Uri(path + query, UriKind.Relative); + } + + /// + /// Gets the absolute path of the uri, even if the uri is relative. + /// + /// The uri. + /// The absolute path of the uri. + /// Default uri.AbsolutePath does not support relative uris. + public static string GetSafeAbsolutePath(this Uri uri) + { + if (uri.IsAbsoluteUri) + return uri.AbsolutePath; + + // cannot get .AbsolutePath on relative uri (InvalidOperation) + var s = uri.OriginalString; + var posq = s.IndexOf("?", StringComparison.Ordinal); + var posf = s.IndexOf("#", StringComparison.Ordinal); + var pos = posq > 0 ? posq : (posf > 0 ? posf : 0); + var path = pos > 0 ? s.Substring(0, pos) : s; + return path; + } + + /// + /// Gets the decoded, absolute path of the uri. + /// + /// The uri. + /// The absolute path of the uri. + /// Only for absolute uris. + public static string GetAbsolutePathDecoded(this Uri uri) + { + return System.Web.HttpUtility.UrlDecode(uri.AbsolutePath); + } + + /// + /// Gets the decoded, absolute path of the uri, even if the uri is relative. + /// + /// The uri. + /// The absolute path of the uri. + /// Default uri.AbsolutePath does not support relative uris. + public static string GetSafeAbsolutePathDecoded(this Uri uri) + { + return System.Web.HttpUtility.UrlDecode(uri.GetSafeAbsolutePath()); + } + + /// + /// Rewrites the path of the uri so it ends with a slash. + /// + /// The uri. + /// The rewritten uri. + /// Everything else remains unchanged. + public static Uri EndPathWithSlash(this Uri uri) + { + var path = uri.GetSafeAbsolutePath(); + if (uri.IsAbsoluteUri) + { + if (path != "/" && path.EndsWith("/") == false) + uri = new Uri(uri.GetLeftPart(UriPartial.Authority) + path + "/" + uri.Query); + return uri; + } + + if (path != "/" && path.EndsWith("/") == false) + uri = new Uri(path + "/" + uri.Query, UriKind.Relative); + + return uri; + } + + /// + /// Rewrites the path of the uri so it does not end with a slash. + /// + /// The uri. + /// The rewritten uri. + /// Everything else remains unchanged. + public static Uri TrimPathEndSlash(this Uri uri) + { + var path = uri.GetSafeAbsolutePath(); + if (uri.IsAbsoluteUri) + { + if (path != "/") + uri = new Uri(uri.GetLeftPart(UriPartial.Authority) + path.TrimEnd('/') + uri.Query); + } + else + { + if (path != "/") + uri = new Uri(path.TrimEnd('/') + uri.Query, UriKind.Relative); + } + return uri; + } + + /// + /// Transforms a relative uri into an absolute uri. + /// + /// The relative uri. + /// The base absolute uri. + /// The absolute uri. + public static Uri MakeAbsolute(this Uri uri, Uri baseUri) + { + if (uri.IsAbsoluteUri) + throw new ArgumentException("Uri is already absolute.", "uri"); + + return new Uri(baseUri.GetLeftPart(UriPartial.Authority) + uri.GetSafeAbsolutePath() + uri.GetSafeQuery()); + } + + static string GetSafeQuery(this Uri uri) + { + if (uri.IsAbsoluteUri) + return uri.Query; + + // cannot get .Query on relative uri (InvalidOperation) + var s = uri.OriginalString; + var posq = s.IndexOf("?", StringComparison.Ordinal); + var posf = s.IndexOf("#", StringComparison.Ordinal); + var query = posq < 0 ? null : (posf < 0 ? s.Substring(posq) : s.Substring(posq, posf - posq)); + + return query; + } + + /// + /// Removes the port from the uri. + /// + /// The uri. + /// The same uri, without its port. + public static Uri WithoutPort(this Uri uri) + { + return new Uri(uri.GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped)); + } + } +}