using System.Security.Cryptography; using System.Text; using System.Windows; using AxCopilot.SDK; using AxCopilot.Themes; namespace AxCopilot.Handlers; /// /// 인코딩/해싱 변환 핸들러. "encode " 프리픽스로 사용합니다. /// 예: encode base64 hello → aGVsbG8= /// encode decode base64 aGVsbG8= → hello /// encode url https://example.com → URL 인코딩 /// encode hex hello → 68656c6c6f /// encode md5 hello → 5d41402abc4b2a76b9719d911017c592 /// encode sha256 hello → 2cf24dba... /// encode sha1 hello → aaf4c61d... /// encode (빈 쿼리) → 지원 목록 /// public class EncodeHandler : IActionHandler { public string? Prefix => "encode "; // 뒤에 공백 포함 public PluginMetadata Metadata => new( "Encoder", "인코딩/해싱 — encode base64/url/hex/md5/sha256 텍스트", "1.0", "AX"); public Task> GetItemsAsync(string query, CancellationToken ct) { var q = query.Trim(); if (string.IsNullOrWhiteSpace(q)) return Task.FromResult(HelpItems()); // "decode <타입> <값>" 파턴 if (q.StartsWith("decode ", StringComparison.OrdinalIgnoreCase)) { var rest = q[7..].Trim(); return Task.FromResult(HandleDecode(rest)); } // "<타입> <값>" 패턴 var spaceIdx = q.IndexOf(' '); if (spaceIdx < 0) return Task.FromResult(HelpItems()); var type = q[..spaceIdx].Trim().ToLowerInvariant(); var input = q[(spaceIdx + 1)..]; return Task.FromResult(HandleEncode(type, input)); } private static IEnumerable HandleEncode(string type, string input) { try { return type switch { "base64" or "b64" => SingleResult("Base64 인코딩", Convert.ToBase64String(Encoding.UTF8.GetBytes(input))), "url" => SingleResult("URL 인코딩", Uri.EscapeDataString(input)), "hex" => SingleResult("HEX 인코딩", Convert.ToHexString(Encoding.UTF8.GetBytes(input)).ToLowerInvariant()), "md5" => SingleResult("MD5 해시", MD5Hash(input)), "sha1" => SingleResult("SHA-1 해시", SHA1Hash(input)), "sha256" => SingleResult("SHA-256 해시", SHA256Hash(input)), "sha512" => SingleResult("SHA-512 해시", SHA512Hash(input)), "html" => SingleResult("HTML 엔티티 인코딩", System.Net.WebUtility.HtmlEncode(input)), _ => [ new LauncherItem( $"알 수 없는 타입: {type}", "base64, url, hex, md5, sha1, sha256, sha512, html 중 선택", null, null, Symbol: Symbols.Warning) ] }; } catch (Exception ex) { return [ new LauncherItem("변환 오류", ex.Message, null, null, Symbol: Symbols.Error) ]; } } private static IEnumerable HandleDecode(string rest) { var spaceIdx = rest.IndexOf(' '); if (spaceIdx < 0) return [new LauncherItem("사용법: encode decode <타입> <값>", "타입: base64, url, hex, html", null, null, Symbol: Symbols.Info)]; var type = rest[..spaceIdx].Trim().ToLowerInvariant(); var input = rest[(spaceIdx + 1)..]; try { return type switch { "base64" or "b64" => SingleResult("Base64 디코딩", Encoding.UTF8.GetString(Convert.FromBase64String(input))), "url" => SingleResult("URL 디코딩", Uri.UnescapeDataString(input)), "hex" => SingleResult("HEX 디코딩", Encoding.UTF8.GetString(Convert.FromHexString(input))), "html" => SingleResult("HTML 엔티티 디코딩", System.Net.WebUtility.HtmlDecode(input)), _ => [ new LauncherItem( $"디코딩 불가 타입: {type}", "디코딩 지원: base64, url, hex, html", null, null, Symbol: Symbols.Warning) ] }; } catch (Exception ex) { return [ new LauncherItem("디코딩 오류", ex.Message, null, null, Symbol: Symbols.Error) ]; } } private static IEnumerable SingleResult(string title, string result) { var preview = result.Length > 80 ? result[..77] + "…" : result; return [ new LauncherItem( title, $"{preview} · Enter로 복사", null, result, Symbol: Symbols.EncodeIcon) ]; } private static IEnumerable HelpItems() { return [ new LauncherItem("encode base64 <텍스트>", "Base64 인코딩/디코딩", null, null, Symbol: Symbols.EncodeIcon), new LauncherItem("encode url <텍스트>", "URL 퍼센트 인코딩/디코딩", null, null, Symbol: Symbols.EncodeIcon), new LauncherItem("encode hex <텍스트>", "16진수 인코딩/디코딩", null, null, Symbol: Symbols.EncodeIcon), new LauncherItem("encode md5 <텍스트>", "MD5 해시 (단방향)", null, null, Symbol: Symbols.EncodeIcon), new LauncherItem("encode sha256 <텍스트>", "SHA-256 해시 (단방향)", null, null, Symbol: Symbols.EncodeIcon), new LauncherItem("encode html <텍스트>", "HTML 엔티티 인코딩/디코딩", null, null, Symbol: Symbols.EncodeIcon), new LauncherItem("encode decode base64 <값>", "Base64 디코딩 예시", null, null, Symbol: Symbols.EncodeIcon), ]; } // ─── 해시 헬퍼 ───────────────────────────────────────────────────────────── private static string MD5Hash(string input) { var bytes = MD5.HashData(Encoding.UTF8.GetBytes(input)); return Convert.ToHexString(bytes).ToLowerInvariant(); } private static string SHA1Hash(string input) { var bytes = SHA1.HashData(Encoding.UTF8.GetBytes(input)); return Convert.ToHexString(bytes).ToLowerInvariant(); } private static string SHA256Hash(string input) { var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(input)); return Convert.ToHexString(bytes).ToLowerInvariant(); } private static string SHA512Hash(string input) { var bytes = SHA512.HashData(Encoding.UTF8.GetBytes(input)); return Convert.ToHexString(bytes).ToLowerInvariant(); } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { if (item.Data is string text) { try { Clipboard.SetText(text); } catch (Exception) { } } return Task.CompletedTask; } }