diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b499303f8..0f7f1fb62f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed multi-geo compatibility issues with `Get-PnPGeoAdministrator` response handling, `Set-PnPMultiGeoExperience` confirmation prompts, and unsupported-version errors for `Set-PnPMultiGeoCompanyAllowedDataLocation`. [#5401](https://github.com/pnp/powershell/pull/5401) - Fixed Power Apps cmdlets to use cloud-specific Power Apps service audiences and endpoints for government clouds. [#5404](https://github.com/pnp/powershell/pull/5404) - Fixed `Export-PnPPowerApp`, `Get-PnPPowerPlatformCustomConnector`, `Export-PnPFlow -AsZipPackage`, and `Import-PnPFlow` to use the Power Apps service audience when calling Power Apps and Business Applications endpoints in sovereign clouds. [#5408](https://github.com/pnp/powershell/pull/5408) +- Fixed `Get-PnPFile` returning the wrong file or a file not found error when the file name contains URL encoding sequences such as `%20` or a `+`. The provided Url is now tried as-is first and only decoded as a fallback, so both literal file names and URLs copied from a browser resolve correctly. [#5010](https://github.com/pnp/powershell/issues/5010) [#1864](https://github.com/pnp/powershell/issues/1864) ### Removed @@ -30,6 +31,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fabian Hutzli [fabianhutzli] - [reusto] - Patrick Schneider [pschneid] +- [svermaak] ## [3.3.0] diff --git a/src/Commands/Files/GetFile.cs b/src/Commands/Files/GetFile.cs index 4a7495d03b..d0d288cfd9 100644 --- a/src/Commands/Files/GetFile.cs +++ b/src/Commands/Files/GetFile.cs @@ -72,19 +72,19 @@ protected override void ExecuteCmdlet() // We can't deal with absolute URLs Url = UrlUtility.MakeRelativeUrl(Url); } - - // Remove URL decoding from the Url as that will not work. We will encode the + character specifically, because if that is part of the filename, it needs to stay and not be decoded. - Url = Utilities.UrlUtilities.UrlDecode(Url.Replace("+", "%2B")); var webUrl = CurrentWeb.EnsureProperty(w => w.ServerRelativeUrl); - if (!Url.ToLower().StartsWith(webUrl.ToLower())) + // The provided Url can be the literal name of the file or a URL encoded version of it. + // Try it as provided first so files with i.e. %20 or + in their actual name resolve, + // and only fall back to the decoded form so URLs copied from a browser keep working. + // The + character is excluded from decoding as it would otherwise turn into a space. + serverRelativeUrl = ToServerRelativeUrl(webUrl, Url); + var decodedUrl = Utilities.UrlUtilities.UrlDecode(Url.Replace("+", "%2B")); + var decodedServerRelativeUrl = ToServerRelativeUrl(webUrl, decodedUrl); + if (!decodedServerRelativeUrl.Equals(serverRelativeUrl, StringComparison.Ordinal) && !FileExists(serverRelativeUrl)) { - serverRelativeUrl = UrlUtility.Combine(webUrl, Url); - } - else - { - serverRelativeUrl = Url; + serverRelativeUrl = decodedServerRelativeUrl; } switch (ParameterSetName) @@ -163,6 +163,19 @@ protected override void ExecuteCmdlet() } } + private static string ToServerRelativeUrl(string webUrl, string url) + { + return !url.ToLower().StartsWith(webUrl.ToLower()) ? UrlUtility.Combine(webUrl, url) : url; + } + + private bool FileExists(string serverRelativeUrl) + { + var file = CurrentWeb.GetFileByServerRelativePath(ResourcePath.FromDecodedUrl(serverRelativeUrl)); + ClientContext.Load(file, f => f.Exists); + ClientContext.ExecuteQueryRetry(); + return file.Exists; + } + private static async Task SaveFileToLocal(IFile fileToDownload, string filePath) { // Start the download