diff options
| author | Claus Vium <cvium@users.noreply.github.com> | 2021-06-07 23:07:59 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-06-07 23:07:59 +0200 |
| commit | 93387ba235fc86861d4cf24c582f49fb33bb0787 (patch) | |
| tree | 5917003d31cf18a4c31a18fd04ec8a9a6f30e749 /Jellyfin.Server | |
| parent | 19a18899065aa2d50b0a989911a5f58a368b3b95 (diff) | |
| parent | 147612f59bb5870f04197087e3d5fcd954061471 (diff) | |
Merge pull request #5990 from BaronGreenback/UrlDecoding
Diffstat (limited to 'Jellyfin.Server')
| -rw-r--r-- | Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs | 10 | ||||
| -rw-r--r-- | Jellyfin.Server/Middleware/QueryStringDecodingMiddleware.cs | 35 | ||||
| -rw-r--r-- | Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 86 | ||||
| -rw-r--r-- | Jellyfin.Server/Startup.cs | 1 |
4 files changed, 132 insertions, 0 deletions
diff --git a/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs index 88e2b4152b..e291677475 100644 --- a/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs @@ -79,6 +79,16 @@ namespace Jellyfin.Server.Extensions } /// <summary> + /// Enables url decoding before binding to the application pipeline. + /// </summary> + /// <param name="appBuilder">The <see cref="IApplicationBuilder"/>.</param> + /// <returns>The updated application builder.</returns> + public static IApplicationBuilder UseQueryStringDecoding(this IApplicationBuilder appBuilder) + { + return appBuilder.UseMiddleware<QueryStringDecodingMiddleware>(); + } + + /// <summary> /// Adds base url redirection to the application pipeline. /// </summary> /// <param name="appBuilder">The application builder.</param> diff --git a/Jellyfin.Server/Middleware/QueryStringDecodingMiddleware.cs b/Jellyfin.Server/Middleware/QueryStringDecodingMiddleware.cs new file mode 100644 index 0000000000..fd0ebbf438 --- /dev/null +++ b/Jellyfin.Server/Middleware/QueryStringDecodingMiddleware.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; + +namespace Jellyfin.Server.Middleware +{ + /// <summary> + /// URL decodes the querystring before binding. + /// </summary> + public class QueryStringDecodingMiddleware + { + private readonly RequestDelegate _next; + + /// <summary> + /// Initializes a new instance of the <see cref="QueryStringDecodingMiddleware"/> class. + /// </summary> + /// <param name="next">The next delegate in the pipeline.</param> + public QueryStringDecodingMiddleware(RequestDelegate next) + { + _next = next; + } + + /// <summary> + /// Executes the middleware action. + /// </summary> + /// <param name="httpContext">The current HTTP context.</param> + /// <returns>The async task.</returns> + public async Task Invoke(HttpContext httpContext) + { + httpContext.Features.Set<IQueryFeature>(new UrlDecodeQueryFeature(httpContext.Features.Get<IQueryFeature>())); + + await _next(httpContext).ConfigureAwait(false); + } + } +} diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs new file mode 100644 index 0000000000..804e58b5ac --- /dev/null +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using MediaBrowser.Common.Extensions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Primitives; + +namespace Jellyfin.Server.Middleware +{ + /// <summary> + /// Defines the <see cref="UrlDecodeQueryFeature"/>. + /// </summary> + public class UrlDecodeQueryFeature : IQueryFeature + { + private IQueryCollection? _store; + + /// <summary> + /// Initializes a new instance of the <see cref="UrlDecodeQueryFeature"/> class. + /// </summary> + /// <param name="feature">The <see cref="IQueryFeature"/> instance.</param> + public UrlDecodeQueryFeature(IQueryFeature feature) + { + Query = feature.Query; + } + + /// <summary> + /// Gets or sets a value indicating the url decoded <see cref="IQueryCollection"/>. + /// </summary> + public IQueryCollection Query + { + get + { + return _store ?? QueryCollection.Empty; + } + + set + { + // Only interested in where the querystring is encoded which shows up as one key with nothing in the value. + if (value.Count != 1) + { + _store = value; + return; + } + + // Encoded querystrings have no value, so don't process anything if a value is present. + var (key, stringValues) = value.First(); + if (!string.IsNullOrEmpty(stringValues)) + { + _store = value; + return; + } + + // Unencode and re-parse querystring. + var unencodedKey = HttpUtility.UrlDecode(key); + + if (string.Equals(unencodedKey, key, System.StringComparison.Ordinal)) + { + // Don't do anything if it's not encoded. + _store = value; + return; + } + + var pairs = new Dictionary<string, StringValues>(); + var queryString = unencodedKey.SpanSplit('&'); + + foreach (var pair in queryString) + { + var i = pair.IndexOf('='); + + if (i == -1) + { + // encoded is an equals. + pairs.Add(pair[..i].ToString(), StringValues.Empty); + continue; + } + + pairs.Add(pair[..i].ToString(), new StringValues(pair[(i + 1)..].ToString())); + } + + _store = new QueryCollection(pairs); + } + } + } +} diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index f751398843..60cdc2f6fe 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -160,6 +160,7 @@ namespace Jellyfin.Server mainApp.UseAuthentication(); mainApp.UseJellyfinApiSwagger(_serverConfigurationManager); + mainApp.UseQueryStringDecoding(); mainApp.UseRouting(); mainApp.UseAuthorization(); |
