Fix quoting issues with authored files

This commit is contained in:
Timothy Baldridge 2021-12-20 09:22:40 -07:00
parent 77bd85bb47
commit b503394de0
6 changed files with 75 additions and 8 deletions

View File

@ -1,3 +1,6 @@
using System;
using System.Text.Json.Serialization;
using System.Web;
using Wabbajack.Hashing.xxHash64; using Wabbajack.Hashing.xxHash64;
using Wabbajack.Paths; using Wabbajack.Paths;
@ -12,4 +15,5 @@ public class FileDefinition
public PartDefinition[] Parts { get; set; } = { }; public PartDefinition[] Parts { get; set; } = { };
public string? ServerAssignedUniqueId { get; set; } public string? ServerAssignedUniqueId { get; set; }
public string MungedName => $"{OriginalFileName}_{ServerAssignedUniqueId!}"; public string MungedName => $"{OriginalFileName}_{ServerAssignedUniqueId!}";
} }

View File

@ -16,6 +16,7 @@ using Wabbajack.DTOs.JsonConverters;
using Wabbajack.Networking.GitHub; using Wabbajack.Networking.GitHub;
using Wabbajack.Paths.IO; using Wabbajack.Paths.IO;
using Wabbajack.Server.DataModels; using Wabbajack.Server.DataModels;
using Wabbajack.Server.Extensions;
using Wabbajack.Server.Services; using Wabbajack.Server.Services;
namespace Wabbajack.BuildServer.Controllers; namespace Wabbajack.BuildServer.Controllers;
@ -89,7 +90,7 @@ public class AuthorControls : ControllerBase
{ {
var data = await KnownFolders.EntryPoint.Combine(@"Controllers\Templates\AuthorControls.html") var data = await KnownFolders.EntryPoint.Combine(@"Controllers\Templates\AuthorControls.html")
.ReadAllTextAsync(); .ReadAllTextAsync();
var func = NettleEngine.GetCompiler().Compile(data); var func = NettleEngine.GetCompiler().RegisterWJFunctions().Compile(data);
return func(o); return func(o);
} }

View File

@ -18,6 +18,7 @@ using Wabbajack.DTOs.JsonConverters;
using Wabbajack.Hashing.xxHash64; using Wabbajack.Hashing.xxHash64;
using Wabbajack.Server.DataModels; using Wabbajack.Server.DataModels;
using Wabbajack.Server.DTOs; using Wabbajack.Server.DTOs;
using Wabbajack.Server.Extensions;
using Wabbajack.Server.Services; using Wabbajack.Server.Services;
namespace Wabbajack.BuildServer.Controllers; namespace Wabbajack.BuildServer.Controllers;
@ -26,16 +27,16 @@ namespace Wabbajack.BuildServer.Controllers;
[Route("/authored_files")] [Route("/authored_files")]
public class AuthoredFiles : ControllerBase public class AuthoredFiles : ControllerBase
{ {
private static readonly Func<object, string> HandleGetListTemplate = NettleEngine.GetCompiler().Compile(@" private static readonly Func<object, string> HandleGetListTemplate = NettleEngine.GetCompiler().RegisterWJFunctions().Compile(@"
<html><body> <html><body>
<table> <table>
{{each $.files }} {{each $.files }}
<tr> <tr>
<td><a href='https://authored-files.wabbajack.org/{{$.Definition.MungedName}}'>{{$.Definition.OriginalFileName}}</a></td> <td><a href='https://authored-files.wabbajack.org/{{@UrlEncode($.Definition.MungedName)}}'>{{$.Definition.OriginalFileName}}</a></td>
<td>{{$.HumanSize}}</td> <td>{{$.HumanSize}}</td>
<td>{{$.Definition.Author}}</td> <td>{{$.Definition.Author}}</td>
<td>{{$.Updated}}</td> <td>{{$.Updated}}</td>
<td><a href='/authored_files/direct_link/{{$.Definition.MungedName}}'>(Slow) HTTP Direct Link</a></td> <td><a href='/authored_files/direct_link/{{@UrlEncode($.Definition.MungedName)}}'>(Slow) HTTP Direct Link</a></td>
</tr> </tr>
{{/each}} {{/each}}
</table> </table>
@ -178,6 +179,7 @@ public class AuthoredFiles : ControllerBase
[Route("direct_link/{mungedName}")] [Route("direct_link/{mungedName}")]
public async Task DirectLink(string mungedName) public async Task DirectLink(string mungedName)
{ {
mungedName = _authoredFiles.DecodeName(mungedName);
var definition = await _authoredFiles.ReadDefinition(mungedName); var definition = await _authoredFiles.ReadDefinition(mungedName);
Response.Headers.ContentDisposition = Response.Headers.ContentDisposition =
new StringValues($"attachment; filename={definition.OriginalFileName}"); new StringValues($"attachment; filename={definition.OriginalFileName}");

View File

@ -19,7 +19,7 @@
{{each $.WabbajackFiles }} {{each $.WabbajackFiles }}
<tr> <tr>
<td> <td>
<button onclick="deleteFile('{{$.MangledName}}');">Delete</button> <button onclick="deleteFile('{{@Escape($.MangledName)}}');">Delete</button>
</td> </td>
<td>{{$.Name}}</td> <td>{{$.Name}}</td>
<td>{{$.Size}}</td> <td>{{$.Size}}</td>
@ -43,7 +43,7 @@
{{each $.OtherFiles }} {{each $.OtherFiles }}
<tr> <tr>
<td> <td>
<button onclick="deleteFile('{{$.MangledName}}');">Delete</button> <button onclick="deleteFile('{{@Escape($.MangledName)}}');">Delete</button>
</td> </td>
<td>{{$.Name}}</td> <td>{{$.Name}}</td>
<td>{{$.Size}}</td> <td>{{$.Size}}</td>

View File

@ -1,4 +1,5 @@
using System.IO.Compression; using System.IO.Compression;
using System.Web;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Wabbajack.BuildServer; using Wabbajack.BuildServer;
using Wabbajack.Common; using Wabbajack.Common;
@ -44,9 +45,9 @@ public class AuthorFiles
return defs.ToArray(); return defs.ToArray();
} }
public async Task<Stream> StreamForPart(string hashAsHex, int part) public async Task<Stream> StreamForPart(string mungedName, int part)
{ {
return AuthorFilesLocation.Combine(hashAsHex, "parts", part.ToString()).Open(FileMode.Open); return AuthorFilesLocation.Combine(mungedName, "parts", part.ToString()).Open(FileMode.Open);
} }
public async Task<Stream> CreatePart(string mungedName, int part) public async Task<Stream> CreatePart(string mungedName, int part)
@ -74,6 +75,11 @@ public class AuthorFiles
return await ReadDefinition(AuthorFilesLocation.Combine(mungedName, "definition.json.gz")); return await ReadDefinition(AuthorFilesLocation.Combine(mungedName, "definition.json.gz"));
} }
public bool IsDefinition(string mungedName)
{
return AuthorFilesLocation.Combine(mungedName, "definition.json.gz").FileExists();
}
private async Task<FileDefinition> ReadDefinition(AbsolutePath file) private async Task<FileDefinition> ReadDefinition(AbsolutePath file)
{ {
var gz = new GZipStream(new MemoryStream(await file.ReadAllBytesAsync()), CompressionMode.Decompress); var gz = new GZipStream(new MemoryStream(await file.ReadAllBytesAsync()), CompressionMode.Decompress);
@ -101,4 +107,10 @@ public class AuthorFiles
await AllAuthoredFiles(); await AllAuthoredFiles();
return _byServerId[serverAssignedUniqueId]; return _byServerId[serverAssignedUniqueId];
} }
public string DecodeName(string mungedName)
{
var decoded = HttpUtility.UrlDecode(mungedName);
return IsDefinition(decoded) ? decoded : mungedName;
}
} }

View File

@ -0,0 +1,48 @@
using System.Text.RegularExpressions;
using System.Web;
using Nettle.Compiler;
using Nettle.Functions;
namespace Wabbajack.Server.Extensions;
public static class NettleFunctions
{
public static INettleCompiler RegisterWJFunctions(this INettleCompiler compiler)
{
compiler.RegisterFunction(new UrlEncode());
compiler.RegisterFunction(new Escape());
return compiler;
}
private sealed class UrlEncode : FunctionBase
{
public UrlEncode() : base()
{
DefineRequiredParameter("text", "text to encode", typeof(string));
}
protected override object GenerateOutput(TemplateContext context, params object[] parameterValues)
{
var value = GetParameterValue<string>("text", parameterValues);
return HttpUtility.UrlEncode(value);
}
public override string Description => "URL encodes a string";
}
private sealed class Escape : FunctionBase
{
public Escape() : base()
{
DefineRequiredParameter("text", "text to escape", typeof(string));
}
protected override object GenerateOutput(TemplateContext context, params object[] parameterValues)
{
var value = GetParameterValue<string>("text", parameterValues);
return Regex.Escape(value);
}
public override string Description => "Escapes a string";
}
}