using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
using AyaNova.Util;
using System.IO;
namespace AyaNova.Api.Controllers
{
///
/// Logo controller
///
[ApiController]
[Asp.Versioning.ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/logo")]
[Produces("application/json")]
[Authorize]
public class LogoController : ControllerBase
{
private readonly AyContext ct;
private readonly ILogger log;
private readonly ApiServerState serverState;
// private const int MAXIMUM_LOGO_SIZE = 512000;//We really don't want it too big or it will slow the fuck down for everything
///
/// ctor
///
///
///
///
public LogoController(AyContext dbcontext, ILogger logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
///
/// Get Logo
///
/// One of "small", "medium", "large"
/// A single Logo and it's values
[AllowAnonymous]
[HttpGet("{size}")]
public async Task DownloadLogo([FromRoute] string size)
{
//allowing this because it messes up the login form needlessly
// if (serverState.IsClosed)
// return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
if (string.IsNullOrWhiteSpace(size))
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, "size", "Size is required and must be one of 'small', 'medium' or 'large'"));
size = size.ToLowerInvariant();
if (size != "small" && size != "medium" && size != "large")
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, "size", "Size parameter must be one of 'small', 'medium' or 'large'"));
var logo = await ct.Logo.SingleOrDefaultAsync();
if (logo == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
switch (size)
{
case "small":
if (logo.Small == null)
return NotFound();
return LogoFile(logo.Small);
case "medium":
if (logo.Medium == null)
return NotFound();
return LogoFile(logo.Medium);
case "large":
if (logo.Large == null)
return NotFound();
return LogoFile(logo.Large);
}
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
}
//case 4727 (F23): the type comes from the bytes, never from what was sent or stored, and the response may not run
//script: an SVG opened directly by its URL is shown, its script is not run (an tag is not affected)
private FileStreamResult LogoFile(byte[] bytes)
{
Response.Headers["X-Content-Type-Options"] = "nosniff";
Response.Headers["Content-Security-Policy"] = "default-src 'none'; style-src 'unsafe-inline'; sandbox";
return new FileStreamResult(new MemoryStream(bytes), ImageTypeOrNull(bytes) ?? "application/octet-stream");
}
//case 4727 (F23): PNG, JPEG, GIF, WebP or SVG, recognised by their content; null for anything else
internal static string ImageTypeOrNull(byte[] b)
{
if (b == null || b.Length < 12)
return null;
if (b[0] == 0x89 && b[1] == 0x50 && b[2] == 0x4E && b[3] == 0x47 && b[4] == 0x0D && b[5] == 0x0A && b[6] == 0x1A && b[7] == 0x0A)
return "image/png";
if (b[0] == 0xFF && b[1] == 0xD8 && b[2] == 0xFF)
return "image/jpeg";
if (b[0] == 'G' && b[1] == 'I' && b[2] == 'F' && b[3] == '8' && (b[4] == '7' || b[4] == '9') && b[5] == 'a')
return "image/gif";
if (b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F' && b[8] == 'W' && b[9] == 'E' && b[10] == 'B' && b[11] == 'P')
return "image/webp";
var head = System.Text.Encoding.UTF8.GetString(b, 0, System.Math.Min(b.Length, 4096)).TrimStart((char)0xFEFF, ' ', '\t', '\r', '\n');
if (head.StartsWith("<") && head.Contains("