using System; using System.Collections.Generic; using System.Linq; using Jellyfin.Extensions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Primitives; namespace Jellyfin.Api.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 (key, stringValues) = value.First(); if (!string.IsNullOrEmpty(stringValues)) { _store = value; return; } if (!key.Contains('=', StringComparison.Ordinal)) { _store = value; return; } var pairs = new Dictionary(); foreach (var pair in key.SpanSplit('&')) { var i = pair.IndexOf('='); if (i == -1) { // encoded is an equals. // We use TryAdd so duplicate keys get ignored pairs.TryAdd(pair.ToString(), StringValues.Empty); continue; } var k = pair[..i].ToString(); var v = pair[(i + 1)..].ToString(); if (!pairs.TryAdd(k, new StringValues(v))) { pairs[k] = StringValues.Concat(pairs[k], v); } } _store = new QueryCollection(pairs); } } }