using KbinXml.Net; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Server; using Server.Extensions; using Server.Middlewares; using Server.Request; using Server.Services; using Server.Utils; using System.Security.Cryptography; using System.Text; using System.Xml.Linq; using Abstractions.Entities; using Abstractions.Services; using Server.Authentication; var key = Convert.FromHexString("00000000000069D74627D985EE2187161570D08D93B12455035B6DF0D8205DF5"); var builder = WebApplication.CreateBuilder(args); var loggerFactory = LoggerFactory.Create(b => b.AddConsole()); var logger = loggerFactory.CreateLogger("MedusaLogger"); var pluginService = new PluginService(logger); // Add services to the container. pluginService.RegisterPlugins(); var plugins = pluginService.GetPlugins(); foreach (var plugin in plugins) { await plugin.OnBuilderInitialize(builder); } builder.Services.AddAuthorization(); builder.Services.AddIdentityApiEndpoints() .AddEntityFrameworkStores(); builder.Services.AddHandlers(); builder.Services.AddDbContext(options => options.UseSqlite("Data Source=Medusa.db;")); builder.Services.AddIdentityCore(config => { config.Password.RequiredLength = 8; config.SignIn.RequireConfirmedEmail = true; config.Lockout.AllowedForNewUsers = true; }).AddEntityFrameworkStores(); builder.Services.AddTransient(); builder.Services.AddSingleton(pluginService); builder.Services.AddSingleton(); var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles(); // Configure the HTTP request pipeline. //app.UseHttpsRedirection(); app.UseMiddleware(); app.UseHandlers(); foreach (var plugin in plugins) { await plugin.OnAppInitialize(app); } app.MapIdentityApi(); var eamuseGroup = app.MapGroup("eamuse"); eamuseGroup.MapPost("/{model}/{module}/{method}", (string model, string module, string method, [FromServices] IHttpContextAccessor httpContextAccessor) => { }); eamuseGroup.MapPost("/{m}", async (string m, [FromQuery] string model, [FromQuery] string? module, [FromQuery] string? method, [FromQuery] string? f, HttpContext httpContext, [FromServices] ILogger logger, [FromServices] IHandlerService handlerService) => { // Enable buffering to allow multiple reads of the request body httpContext.Request.EnableBuffering(); // The body is 932 encoded xml using var reader = new StreamReader(httpContext.Request.Body, Encoding.GetEncoding(932), false, 1024, true); var body = await reader.ReadToEndAsync(); httpContext.Request.Body.Position = 0; var compress = httpContext.Request.Headers["X-Compress"].ToString().Contains("lz77"); var encrypt = httpContext.Request.Headers["X-Eamuse-Info"].FirstOrDefault() is not null; var amusementRequest = new AmusementRequest() { Model = model, Module = module ?? "", Method = method ?? "" }; if(!string.IsNullOrEmpty(f)) { var fParts = f.Split('.'); amusementRequest.Module = fParts[0]; amusementRequest.Method = fParts[1]; } logger.LogInformation("Handling {Module} {Method}", amusementRequest.Module, amusementRequest.Method); var document = new XDocument(); if (!string.IsNullOrEmpty(body)) document = XDocument.Parse(body); var responseXml = await handlerService.Handle(amusementRequest.Model, amusementRequest.Module, amusementRequest.Method, document); var encoding = httpContext.Items["Encoding"]?.ToString() ?? "ShiftJIS"; encoding = encoding switch { "shift_jis" => "ShiftJIS", "us-ascii" => "ASCII", "utf-8" => "UTF8", "euc-jp" => "EUC_JP", _ => encoding }; var encodedBody = KbinConverter.Write(responseXml, Enum.Parse(encoding, true)); if(compress) { encodedBody = LZ77.CompressEmpty(encodedBody); } if (!encrypt) return TypedResults.Bytes(encodedBody, "application/octet-stream"); var originalInfo = httpContext.Request.Headers["X-Eamuse-Info"].FirstOrDefault()?.Split('-') ?? []; var part = Convert.FromHexString((originalInfo[1] + originalInfo[2])); for(var i = 0; i < 6; i++) key[i] = part[i]; var rc4Key = MD5.HashData(key); encodedBody = RC4.Encrypt(rc4Key, encodedBody); httpContext.Response.Headers.Append("X-Eamuse-Info", string.Join('-', originalInfo)); return TypedResults.Bytes(encodedBody, "application/octet-stream"); }); eamuseGroup.MapPost("/", async ([FromQuery] string model, [FromQuery] string? module, [FromQuery] string? method, [FromQuery] string? f, HttpContext httpContext, [FromServices] ILogger logger, [FromServices] IHandlerService handlerService) => { // Enable buffering to allow multiple reads of the request body httpContext.Request.EnableBuffering(); var body = ""; // The body is 932 encoded xml if(httpContext.Request.Body.Length != 0) { using var reader = new StreamReader(httpContext.Request.Body, Encoding.GetEncoding(932), false, 1024, true); body = await reader.ReadToEndAsync(); } httpContext.Request.Body.Position = 0; var compress = httpContext.Request.Headers["X-Compress"].ToString().Contains("lz77"); var encrypt = httpContext.Request.Headers["X-Eamuse-Info"].FirstOrDefault() is not null; var amusementRequest = new AmusementRequest() { Model = model, Module = module ?? "", Method = method ?? ""}; if (!string.IsNullOrEmpty(f)) { var fParts = f.Split('.'); amusementRequest.Module = fParts[0]; amusementRequest.Method = fParts[1]; } logger.LogInformation("Handling {Module} {Method}", amusementRequest.Module, amusementRequest.Method); var document = new XDocument(); if(!string.IsNullOrEmpty(body)) document = XDocument.Parse(body); var responseXml = await handlerService.Handle(amusementRequest.Model, amusementRequest.Module, amusementRequest.Method, document); var encoding = httpContext.Items["Encoding"]?.ToString() ?? "ShiftJIS"; encoding = encoding switch { "shift_jis" => "ShiftJIS", "us-ascii" => "ASCII", "utf-8" => "UTF8", "euc-jp" => "EUC_JP", _ => encoding }; var encodedBody = KbinConverter.Write(responseXml, (KnownEncodings)Enum.Parse(typeof(KnownEncodings), encoding, true)); if(compress) { encodedBody = LZ77.CompressEmpty(encodedBody); } if (!encrypt) return TypedResults.Bytes(encodedBody, "application/octet-stream"); var originalInfo = httpContext.Request.Headers["X-Eamuse-Info"].FirstOrDefault()?.Split('-') ?? []; var part = Convert.FromHexString((originalInfo[1] + originalInfo[2])); for(var i = 0; i < 6; i++) key[i] = part[i]; var rc4Key = MD5.HashData(key); encodedBody = RC4.Encrypt(rc4Key, encodedBody); httpContext.Response.Headers.Append("X-Eamuse-Info", string.Join('-', originalInfo)); return TypedResults.Bytes(encodedBody, "application/octet-stream"); }); app.MapFallbackToFile("/index.html"); await using var scope = app.Services.CreateAsyncScope(); var appDbContext = scope.ServiceProvider.GetRequiredService(); await appDbContext.Database.MigrateAsync(); app.Run();