diff --git a/tools/ImportBuddy/source/ImportBuddy/ImportBuddy/Program.cs b/tools/ImportBuddy/source/ImportBuddy/ImportBuddy/Program.cs index 07e682249..be03c655c 100644 --- a/tools/ImportBuddy/source/ImportBuddy/ImportBuddy/Program.cs +++ b/tools/ImportBuddy/source/ImportBuddy/ImportBuddy/Program.cs @@ -48,7 +48,7 @@ static IHostBuilder CreateHostBuilder(string[] args) => config.AddJsonFile("appsettings.json", optional: false); if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - config.AddJsonFile("appSettings.linux.json", optional: false); + config.AddJsonFile("appsettings.linux.json", optional: false); } }) .ConfigureLogging(logging => diff --git a/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/CsvEnumerator.cs b/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/CsvEnumerator.cs new file mode 100644 index 000000000..2bd989964 --- /dev/null +++ b/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/CsvEnumerator.cs @@ -0,0 +1,165 @@ +namespace MakeMkv; + +/// +/// Iterates over comma separated values in a single line of text. +/// +public sealed class CsvEnumerator +{ + private readonly String _span; + + private readonly Boolean _isInitialized; + + private Int32 _currentStart; + private Int32 _currentEnd; + + /// + public ReadOnlySpan Current => _span.AsSpan()[_currentStart.._currentEnd]; + + /// + /// Creates a new over a span of characters. + /// + public CsvEnumerator(String span) + { + _isInitialized = true; + _span = span; + _currentStart = 0; + _currentEnd = -1; + } + + /// + public Boolean MoveNext() + { + Int32 start = _currentEnd + 1; + if (!_isInitialized || start > _span.Length) + { + return false; + } + + var slice = _span[start..]; + _currentStart = start; + + Boolean quoted = false; + Boolean escaped = false; + Int32 i; + for (i = 0; i < slice.Length; i++) + { + Char curChar = slice[i]; + if (curChar == '\\') + { + if (!escaped) + { + escaped = true; + continue; + } + } + else if (curChar == '"') + { + if (!escaped) + { + quoted = !quoted; + } + } + else if (curChar == ',') + { + if (!quoted) + { + break; + } + } + + escaped = false; + } + + Int32 elementLength = i; + + _currentEnd = _currentStart + elementLength; + return true; + } + + /// + /// Returns an enumerator suitable for use in foreach loops. + /// + public CsvEnumerator GetEnumerator() => this; + + /// + /// Attempts to advance the enumerator and parse the value as an . + /// + /// The parsed value, or the value if enumeration or parsing failed. + public int TryParseInt() + { + if (MoveNext()) + { + if (Int32.TryParse(Current, out int val)) + { + return val; + } + } + + return default; + } + + /// + /// Attempts to advance the enumerator and parse the value as a . + /// + /// The parsed value, or if enumeration or parsing failed. + public bool TryParseBoolean() + { + if (MoveNext()) + { + if (Int32.TryParse(Current, out int val)) + { + return val != 256 && val != 999 && val > 0; + } + } + + return default; + } + + /// + /// Attempts to advance the enumerator and parse the value as a . + /// + /// The parsed value, or if enumeration or parsing failed. + public string? GetString() + { + if (MoveNext()) + { + return Current.ToString().Replace("\"", string.Empty); + } + + return default; + } + + /// + /// Attempts to advance the enumerator and parse the value as a . + /// + /// The parsed value, or the value if enumeration or parsing failed. + public DateTime TryParseDateTime() + { + if (MoveNext()) + { + if (DateTime.TryParse(Current, out DateTime val)) + { + return val; + } + } + + return default; + } + + /// + /// Attempts to advance the enumerator and parse the value as a . + /// + /// The parsed value, or the value if enumeration or parsing failed. + public long TryParseLong() + { + if (MoveNext()) + { + if (Int64.TryParse(Current, out long val)) + { + return val; + } + } + + return default; + } +} diff --git a/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/LogLines/LogLine.cs b/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/LogLines/LogLine.cs index c9fc38904..f2499e1bf 100644 --- a/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/LogLines/LogLine.cs +++ b/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/LogLines/LogLine.cs @@ -28,7 +28,7 @@ protected static int TryParseInt(int index, string[] parts) { if (index < parts.Length) { - if(Int32.TryParse(parts[index], out int val)) + if (Int32.TryParse(parts[index], out int val)) { return val; } diff --git a/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/LogLines/MessageLogLine.cs b/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/LogLines/MessageLogLine.cs index ca89011c9..41d38a5b6 100644 --- a/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/LogLines/MessageLogLine.cs +++ b/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/LogParser/LogLines/MessageLogLine.cs @@ -18,25 +18,21 @@ public MessageLogLine() : base("MSG") public static MessageLogLine Parse(string line) { - string[] parts = line.Substring(4).Split(','); + var enumerator = new CsvEnumerator(line[4..]); var result = new MessageLogLine { - Code = GetString(0, parts), - Flags = GetString(1, parts), - ParamCount = TryParseInt(2, parts), - Message = GetString(3, parts), - MessageTemplate = GetString(4, parts), + Code = enumerator.GetString(), + Flags = enumerator.GetString(), + ParamCount = enumerator.TryParseInt(), + Message = enumerator.GetString(), + MessageTemplate = enumerator.GetString(), OriginalLine = line }; - for (int i = 5; i < parts.Length; i++) + while (enumerator.GetString() is { } val) { - string? val = GetString(i, parts); - if (val != null) - { - result.Params.Add(val); - } + result.Params.Add(val); } return result; diff --git a/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/MakeMkvHelper.cs b/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/MakeMkvHelper.cs index 8e55352fb..42d7b8d1a 100644 --- a/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/MakeMkvHelper.cs +++ b/tools/ImportBuddy/source/ImportBuddy/TheDiscDb.MakeMkv/MakeMkvHelper.cs @@ -1,4 +1,6 @@ -namespace TheDiscDb.Tools.MakeMkv +using MakeMkv; + +namespace TheDiscDb.Tools.MakeMkv { using System; using System.Collections.Generic; @@ -42,7 +44,7 @@ public async Task WriteLogs(int driveIndex, string path, bool cleanLogs = true) await Task.Delay(200); // wait for file handle to be released try { - await CleanLogs(path); + await CleanLogs(driveIndex, path); } catch (IOException e) { @@ -51,19 +53,77 @@ public async Task WriteLogs(int driveIndex, string path, bool cleanLogs = true) } } - public async Task CleanLogs(string path, CancellationToken cancellationToken = default) + public async Task CleanLogs(int driveIndex, string path, CancellationToken cancellationToken = default) { var output = new List(); var lines = await this.fileSystem.File.ReadAllLines(path, cancellationToken); bool fileChanged = false; - foreach (var line in lines) + foreach (var originalLine in lines) { - if (string.IsNullOrWhiteSpace(line) || line.StartsWith("MSG") || line.StartsWith("DRV")) + // Remove empty lines from the output + if (originalLine == string.Empty) { fileChanged = true; continue; } + string? line = null; + if (originalLine.StartsWith("MSG")) + { + var msgLine = MessageLogLine.Parse(originalLine); + var (toRedact, replacement) = msgLine.Code switch + { + // The debug file path is usually in the user's home directory + "1004" => (msgLine.Params[0], "***"), + // The drive name can sometimes contain a serial number + "2003" => (msgLine.Params[1], "***"), + // The .MakeMKV folder is usually in the user's home directory + "3338" => (msgLine.Params[1], "***"), + // No other messages are known to contain sensitive information + _ => (null, ""), + }; + + if (toRedact is not null) + { + line = originalLine.Replace(toRedact, replacement); + } + } + else if (originalLine.StartsWith("DRV")) + { + // DRV:1,2,999,12,"BD-ROM HL-DT-ST BDDVDRW UH12NS30 1.03","42","E:" + // becomes + // DRV:1,2,999,12,"redacted by ImportBuddy","42","/redacted/by/ImportBuddy" + var driveLine = DriveScanLogLine.Parse(originalLine); + if (!string.IsNullOrEmpty(driveLine.DriveName)) + { + // Always redact the drive name, since it could contain a serial number + line = originalLine.Replace(driveLine.DriveName, "***"); + + // Only keep the disc name for the active drive + if (driveLine.Index != driveIndex && !string.IsNullOrEmpty(driveLine.DiscName)) + { + line = line.Replace(driveLine.DiscName, "***"); + } + + // Always redact the drive letter or path + if (!string.IsNullOrEmpty(driveLine.DriveLetter)) + { + line = line.Replace(driveLine.DriveLetter, "***"); + } + } + } + + if (line is null) + { + // This line wasn't modified, use the original line + line = originalLine; + } + else + { + // Something was changed, ensure the file is overwritten + fileChanged = true; + } + output.Add(line); }