From 3ec610d65f92b788bc35390c8ba8e5be09039a3b Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:15:38 -0600 Subject: [PATCH] properly encode xmltv fragments (#1597) --- .../Commands/RefreshChannelDataHandler.cs | 103 ++++++++++-------- .../Commands/RefreshChannelListHandler.cs | 25 +++-- .../Channels/XmlTemplateContext.cs | 14 +++ 3 files changed, 88 insertions(+), 54 deletions(-) create mode 100644 ErsatzTV.Application/Channels/XmlTemplateContext.cs diff --git a/ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs b/ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs index 201527b6..a58d42c3 100644 --- a/ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs +++ b/ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.Logging; using Microsoft.IO; using Newtonsoft.Json; using Scriban; +using Scriban.Runtime; using WebMarkupMin.Core; namespace ErsatzTV.Application.Channels; @@ -54,6 +55,8 @@ public class RefreshChannelDataHandler : IRequestHandler RemoveXmlComments = true, CollapseTagsWithoutContent = true }); + + var templateContext = new XmlTemplateContext(); string movieText = await File.ReadAllTextAsync(movieTemplateFileName, cancellationToken); var movieTemplate = Template.Parse(movieText, movieTemplateFileName); @@ -213,26 +216,31 @@ public class RefreshChannelDataHandler : IRequestHandler .HeadOrNone() .Match(a => GetArtworkUrl(a, ArtworkKind.Poster), () => string.Empty); - string result = await movieTemplate.RenderAsync( - new - { - ProgrammeStart = start, - ProgrammeStop = stop, - ChannelNumber = request.ChannelNumber, - HasCustomTitle = hasCustomTitle, - CustomTitle = displayItem.CustomTitle, - MovieTitle = title, - MovieHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot), - MoviePlot = metadata.Plot, - MovieHasYear = metadata.Year.HasValue, - MovieYear = metadata.Year, - MovieGenres = metadata.Genres.Map(g => g.Name).OrderBy(n => n), - MovieHasArtwork = !string.IsNullOrWhiteSpace(poster), - MovieArtworkUrl = poster, - MovieHasContentRating = !string.IsNullOrWhiteSpace(metadata.ContentRating), - MovieContentRating = metadata.ContentRating, - MovieGuids = metadata.Guids.Map(g => g.Guid) - }); + var data = new + { + ProgrammeStart = start, + ProgrammeStop = stop, + ChannelNumber = request.ChannelNumber, + HasCustomTitle = hasCustomTitle, + CustomTitle = displayItem.CustomTitle, + MovieTitle = title, + MovieHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot), + MoviePlot = metadata.Plot, + MovieHasYear = metadata.Year.HasValue, + MovieYear = metadata.Year, + MovieGenres = metadata.Genres.Map(g => g.Name).OrderBy(n => n), + MovieHasArtwork = !string.IsNullOrWhiteSpace(poster), + MovieArtworkUrl = poster, + MovieHasContentRating = !string.IsNullOrWhiteSpace(metadata.ContentRating), + MovieContentRating = metadata.ContentRating, + MovieGuids = metadata.Guids.Map(g => g.Guid) + }; + + var scriptObject = new ScriptObject(); + scriptObject.Import(data); + templateContext.PushGlobal(scriptObject); + + string result = await movieTemplate.RenderAsync(templateContext); MarkupMinificationResult minified = minifier.Minify(result); await xml.WriteRawAsync(minified.MinifiedContent); @@ -257,31 +265,36 @@ public class RefreshChannelDataHandler : IRequestHandler string artworkPath = GetPrioritizedArtworkPath(metadata); - string result = await episodeTemplate.RenderAsync( - new - { - ProgrammeStart = start, - ProgrammeStop = stop, - ChannelNumber = request.ChannelNumber, - HasCustomTitle = hasCustomTitle, - CustomTitle = displayItem.CustomTitle, - ShowTitle = title, - EpisodeHasTitle = !string.IsNullOrWhiteSpace(subtitle), - EpisodeTitle = subtitle, - EpisodeHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot), - EpisodePlot = metadata.Plot, - ShowHasYear = showMetadata.Year.HasValue, - ShowYear = showMetadata.Year, - ShowGenres = showMetadata.Genres.Map(g => g.Name).OrderBy(n => n), - EpisodeHasArtwork = !string.IsNullOrWhiteSpace(artworkPath), - EpisodeArtworkUrl = artworkPath, - SeasonNumber = templateEpisode.Season?.SeasonNumber ?? 0, - EpisodeNumber = metadata.EpisodeNumber, - ShowHasContentRating = !string.IsNullOrWhiteSpace(showMetadata.ContentRating), - ShowContentRating = showMetadata.ContentRating, - ShowGuids = showMetadata.Guids.Map(g => g.Guid), - EpisodeGuids = metadata.Guids.Map(g => g.Guid) - }); + var data = new + { + ProgrammeStart = start, + ProgrammeStop = stop, + ChannelNumber = request.ChannelNumber, + HasCustomTitle = hasCustomTitle, + CustomTitle = displayItem.CustomTitle, + ShowTitle = title, + EpisodeHasTitle = !string.IsNullOrWhiteSpace(subtitle), + EpisodeTitle = subtitle, + EpisodeHasPlot = !string.IsNullOrWhiteSpace(metadata.Plot), + EpisodePlot = metadata.Plot, + ShowHasYear = showMetadata.Year.HasValue, + ShowYear = showMetadata.Year, + ShowGenres = showMetadata.Genres.Map(g => g.Name).OrderBy(n => n), + EpisodeHasArtwork = !string.IsNullOrWhiteSpace(artworkPath), + EpisodeArtworkUrl = artworkPath, + SeasonNumber = templateEpisode.Season?.SeasonNumber ?? 0, + EpisodeNumber = metadata.EpisodeNumber, + ShowHasContentRating = !string.IsNullOrWhiteSpace(showMetadata.ContentRating), + ShowContentRating = showMetadata.ContentRating, + ShowGuids = showMetadata.Guids.Map(g => g.Guid), + EpisodeGuids = metadata.Guids.Map(g => g.Guid) + }; + + var scriptObject = new ScriptObject(); + scriptObject.Import(data); + templateContext.PushGlobal(scriptObject); + + string result = await episodeTemplate.RenderAsync(templateContext); MarkupMinificationResult minified = minifier.Minify(result); await xml.WriteRawAsync(minified.MinifiedContent); diff --git a/ErsatzTV.Application/Channels/Commands/RefreshChannelListHandler.cs b/ErsatzTV.Application/Channels/Commands/RefreshChannelListHandler.cs index 76642504..a46cf386 100644 --- a/ErsatzTV.Application/Channels/Commands/RefreshChannelListHandler.cs +++ b/ErsatzTV.Application/Channels/Commands/RefreshChannelListHandler.cs @@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.IO; using Scriban; +using Scriban.Runtime; using WebMarkupMin.Core; namespace ErsatzTV.Application.Channels; @@ -65,6 +66,7 @@ public class RefreshChannelListHandler : IRequestHandler string text = await File.ReadAllTextAsync(templateFileName, cancellationToken); var template = Template.Parse(text, templateFileName); + var templateContext = new XmlTemplateContext(); await using RecyclableMemoryStream ms = _recyclableMemoryStreamManager.GetStream(); await using var xml = XmlWriter.Create( @@ -73,15 +75,20 @@ public class RefreshChannelListHandler : IRequestHandler await foreach (ChannelResult channel in GetChannels(dbContext).WithCancellation(cancellationToken)) { - string result = await template.RenderAsync( - new - { - ChannelNumber = channel.Number, - ChannelName = channel.Name, - ChannelCategories = GetCategories(channel.Categories), - ChannelHasArtwork = !string.IsNullOrWhiteSpace(channel.ArtworkPath), - ChannelArtworkPath = channel.ArtworkPath - }); + var data = new + { + ChannelNumber = channel.Number, + ChannelName = channel.Name, + ChannelCategories = GetCategories(channel.Categories), + ChannelHasArtwork = !string.IsNullOrWhiteSpace(channel.ArtworkPath), + ChannelArtworkPath = channel.ArtworkPath + }; + + var scriptObject = new ScriptObject(); + scriptObject.Import(data); + templateContext.PushGlobal(scriptObject); + + string result = await template.RenderAsync(templateContext); MarkupMinificationResult minified = minifier.Minify(result); await xml.WriteRawAsync(minified.MinifiedContent); diff --git a/ErsatzTV.Application/Channels/XmlTemplateContext.cs b/ErsatzTV.Application/Channels/XmlTemplateContext.cs new file mode 100644 index 00000000..c40e3dbb --- /dev/null +++ b/ErsatzTV.Application/Channels/XmlTemplateContext.cs @@ -0,0 +1,14 @@ +using System.Net; +using Scriban; +using Scriban.Parsing; + +namespace ErsatzTV.Application.Channels; + +public class XmlTemplateContext : TemplateContext +{ + public override TemplateContext Write(SourceSpan span, object textAsObject) + => base.Write(span, textAsObject is string text ? WebUtility.HtmlEncode(text) : textAsObject); + + public override ValueTask WriteAsync(SourceSpan span, object textAsObject) + => base.WriteAsync(span, textAsObject is string text ? WebUtility.HtmlEncode(text) : textAsObject); +}