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 { /// /// Defines the . /// public class UrlDecodeQueryFeature : IQueryFeature { private IQueryCollection? _store; /// /// Initializes a new instance of the class. /// /// The instance. public UrlDecodeQueryFeature(IQueryFeature feature) { Query = feature.Query; } /// /// Gets or sets a value indicating the url decoded . /// 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 kvp = value.First(); if (!string.IsNullOrEmpty(kvp.Value)) { _store = value; return; } // Unencode and re-parse querystring. var unencodedKey = HttpUtility.UrlDecode(kvp.Key); if (string.Equals(unencodedKey, kvp.Key, System.StringComparison.Ordinal)) { // Don't do anything if it's not encoded. _store = value; return; } var pairs = new Dictionary(); var queryString = unencodedKey.SpanSplit('&'); foreach (var pair in queryString) { var section = pair.ToString(); var i = section.IndexOf('=', System.StringComparison.Ordinal); if (i == -1) { // encoded is an equals. pairs.Add(section, new StringValues(string.Empty)); continue; } pairs.Add(section[0..i], new StringValues(section[(i + 1)..])); } _store = new QueryCollection(pairs); } } } }