diff --git a/Base64.cs b/Base64.cs new file mode 100644 index 0000000..4f7f9b0 --- /dev/null +++ b/Base64.cs @@ -0,0 +1,163 @@ +using System; +using System.Text; + +namespace CMLeonOS +{ + /// + /// Cosmos裸机专属Base64编码/解码工具类 + /// 无.NET原生Convert依赖,纯手写实现,适配Cosmos System2 + /// + public static class Base64Helper + { + // Base64标准编码表(0-63对应),裸机环境直接硬编码,无需动态生成 + private const string Base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + /// + /// Base64编码(加密):将字符串转为Base64编码串(默认UTF8编码) + /// + /// 原始字符串 + /// Base64编码后的字符串 + public static string Encode(string input) + { + // 空值校验,裸机环境需严格判空 + if (string.IsNullOrEmpty(input)) + return string.Empty; + + // 将字符串转为UTF8字节数组(Cosmos支持基础UTF8/ASCII) + byte[] inputBytes = Encoding.UTF8.GetBytes(input); + // 调用字节数组的编码核心方法 + return EncodeBytes(inputBytes); + } + + /// + /// Base64编码(核心):将二进制字节数组转为Base64编码串 + /// 适配任意二进制数据(文件、网络流、字符串字节) + /// + /// 原始二进制字节数组 + /// Base64编码后的字符串 + public static string EncodeBytes(byte[] inputBytes) + { + if (inputBytes == null || inputBytes.Length == 0) + return string.Empty; + + // 构建结果字符串,裸机用StringBuilder更高效 + StringBuilder result = new StringBuilder(); + int inputLength = inputBytes.Length; + // 按3字节为一组遍历,处理所有完整组 + for (int i = 0; i < inputLength; i += 3) + { + // 取3个字节,不足的补0(位运算用,不修改原数组) + byte b1 = i < inputLength ? inputBytes[i] : (byte)0; + byte b2 = i + 1 < inputLength ? inputBytes[i + 1] : (byte)0; + byte b3 = i + 2 < inputLength ? inputBytes[i + 2] : (byte)0; + + // 核心位运算:3字节(24位)拆分为4个6位 + // 第一个6位:b1的高6位(右移2位,与0x3F过滤低2位) + int idx1 = (b1 >> 2) & 0x3F; + // 第二个6位:b1的低2位 + b2的高4位(左移4位 + b2右移4位,与0x3F过滤) + int idx2 = ((b1 & 0x03) << 4) | ((b2 >> 4) & 0x0F); + // 第三个6位:b2的低4位 + b3的高2位(左移2位 + b3右移6位,与0x3F过滤) + int idx3 = ((b2 & 0x0F) << 2) | ((b3 >> 6) & 0x03); + // 第四个6位:b3的低6位(与0x3F过滤高2位) + int idx4 = b3 & 0x3F; + + // 根据索引取Base64字符,添加到结果 + result.Append(Base64Table[idx1]); + result.Append(Base64Table[idx2]); + + // 补位处理:不足3字节时,用=替代 + result.Append(i + 1 < inputLength ? Base64Table[idx3] : '='); + result.Append(i + 2 < inputLength ? Base64Table[idx4] : '='); + } + + return result.ToString(); + } + + /// + /// Base64解码(解密):将Base64编码串转回原始字符串(默认UTF8编码) + /// + /// Base64编码串 + /// 解码后的原始字符串 + /// Base64格式错误时抛出 + public static string Decode(string input) + { + if (string.IsNullOrEmpty(input)) + return string.Empty; + + // 解码为字节数组,再转为UTF8字符串 + byte[] outputBytes = DecodeToBytes(input); + return Encoding.UTF8.GetString(outputBytes); + } + + /// + /// Base64解码(核心):将Base64编码串转回原始二进制字节数组 + /// 适配任意Base64编码的二进制数据 + /// + /// Base64编码串 + /// 解码后的二进制字节数组 + /// Base64格式错误时抛出 + public static byte[] DecodeToBytes(string input) + { + if (string.IsNullOrEmpty(input)) + return Array.Empty(); + + // 预处理:过滤所有非Base64有效字符(仅保留编码表字符和=) + StringBuilder cleanInput = new StringBuilder(); + foreach (char c in input) + { + if (Base64Table.Contains(c) || c == '=') + cleanInput.Append(c); + } + string base64 = cleanInput.ToString(); + int inputLength = base64.Length; + + // 基础格式校验:Base64长度必须是4的倍数 + if (inputLength % 4 != 0) + throw new ArgumentException("Invalid Base64 string: Length is not a multiple of 4"); + + // 计算补位符数量(=的个数,只能是0/1/2) + int padCount = 0; + if (base64[inputLength - 1] == '=') padCount++; + if (base64[inputLength - 2] == '=') padCount++; + + // 计算解码后的字节数:(4*分组数 - 补位符数) / 3 + int outputLength = (inputLength * 6) / 8 - padCount; + byte[] outputBytes = new byte[outputLength]; + int outputIndex = 0; + + // 按4个字符为一组遍历,处理所有组 + for (int i = 0; i < inputLength; i += 4) + { + // 取4个Base64字符,转换为对应的6位索引(0-63) + int idx1 = Base64Table.IndexOf(base64[i]); + int idx2 = Base64Table.IndexOf(base64[i + 1]); + // 补位的=索引为-1,转为0处理 + int idx3 = i + 2 < inputLength ? Base64Table.IndexOf(base64[i + 2]) : 0; + int idx4 = i + 3 < inputLength ? Base64Table.IndexOf(base64[i + 3]) : 0; + + // 基础校验:无效字符(索引为-1且非=) + if (idx1 == -1 || idx2 == -1 || (idx3 == -1 && base64[i+2] != '=') || (idx4 == -1 && base64[i+3] != '=')) + throw new ArgumentException("Invalid Base64 string: Contains invalid characters"); + + // 核心位运算:4个6位拼接为24位,拆分为3个字节 + uint combined = (uint)((idx1 << 18) | (idx2 << 12) | (idx3 << 6) | idx4); + // 第一个字节:24位的高8位 + byte b1 = (byte)((combined >> 16) & 0xFF); + // 第二个字节:24位的中间8位 + byte b2 = (byte)((combined >> 8) & 0xFF); + // 第三个字节:24位的低8位 + byte b3 = (byte)(combined & 0xFF); + + // 将字节写入结果数组,根据补位符数量跳过多余字节 + if (outputIndex < outputLength) + outputBytes[outputIndex++] = b1; + if (outputIndex < outputLength) + outputBytes[outputIndex++] = b2; + if (outputIndex < outputLength) + outputBytes[outputIndex++] = b3; + } + + return outputBytes; + } + } +} diff --git a/CMLeonOS.csproj b/CMLeonOS.csproj index cd7da58..bfb4d5c 100644 --- a/CMLeonOS.csproj +++ b/CMLeonOS.csproj @@ -42,7 +42,8 @@ - + + diff --git a/Shell.cs b/Shell.cs index 879b7da..30ef5f7 100644 --- a/Shell.cs +++ b/Shell.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Net; using System.Net.Sockets; using System.Text; +using CosmosHttp.Client; using Cosmos.Core; using Cosmos.Core.Memory; using Cosmos.HAL; @@ -194,6 +195,10 @@ namespace CMLeonOS " ping - Ping IP address (5 times)", " tcpserver - Start TCP server on specified port", " tcpclient - Connect to TCP server", + " wget - Download file from URL", + " whoami - Show current username", + " base64 encrypt - Encode text to Base64", + " base64 decrypt - Decode Base64 to text", " version - Show OS version", " about - Show about information", " help - Show help page (1-3)", @@ -451,6 +456,15 @@ namespace CMLeonOS case "tcpclient": ConnectTcpClient(args); break; + case "wget": + DownloadFile(args); + break; + case "whoami": + ShowCurrentUsername(); + break; + case "base64": + ProcessBase64Command(args); + break; default: ShowError($"Unknown command: {command}"); break; @@ -1888,5 +1902,186 @@ namespace CMLeonOS ShowError($"TCP client error: {ex.Message}"); } } + + private void DownloadFile(string args) + { + string[] parts = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length == 0) + { + ShowError("Error: Please specify URL"); + ShowError("Usage: wget [output]"); + return; + } + + string url = parts[0]; + string outputPath = parts.Length > 1 ? parts[1] : ""; + + Console.WriteLine("===================================="); + Console.WriteLine(" WGET - File Downloader"); + Console.WriteLine("===================================="); + Console.WriteLine(); + Console.WriteLine($"Downloading from: {url}"); + + try + { + HttpRequest request = new HttpRequest(); + + string domain = ""; + string path = "/"; + + if (url.StartsWith("http://")) + { + url = url.Substring(7); + } + if (url.StartsWith("https://")) + { + url = url.Substring(8); + } + + int slashIndex = url.IndexOf('/'); + if (slashIndex == -1) + { + domain = url; + } + else + { + domain = url.Substring(0, slashIndex); + path = url.Substring(slashIndex); + } + + if (string.IsNullOrWhiteSpace(domain)) + { + ShowError("Error: Invalid URL format"); + return; + } + + Console.WriteLine($"Domain: {domain}"); + Console.WriteLine($"Path: {path}"); + Console.WriteLine(); + + request.Domain = domain; + request.Path = path; + request.Method = "GET"; + + Console.WriteLine("Sending request..."); + request.Send(); + + if (request.Response == null) + { + ShowError("Error: No response received"); + return; + } + + byte[] content = Encoding.ASCII.GetBytes(request.Response.Content); + + if (content == null || content.Length == 0) + { + ShowError("Error: No content received"); + return; + } + + Console.WriteLine($"Downloaded {content.Length} bytes"); + Console.WriteLine(); + + if (string.IsNullOrWhiteSpace(outputPath)) + { + int lastSlash = path.LastIndexOf('/'); + if (lastSlash >= 0 && lastSlash < path.Length - 1) + { + outputPath = path.Substring(lastSlash + 1); + } + else + { + outputPath = "downloaded_file"; + } + } + + if (!outputPath.StartsWith("0:\\") && !outputPath.StartsWith("0:/")) + { + outputPath = Path.Combine(prompt, outputPath); + } + + Console.WriteLine($"Saving to: {outputPath}"); + + File.WriteAllBytes(outputPath, content); + + ShowSuccess($"File saved successfully: {outputPath}"); + } + catch (Exception ex) + { + ShowError($"Download error: {ex.Message}"); + } + } + + private void ShowCurrentUsername() + { + Console.WriteLine("===================================="); + Console.WriteLine(" Current User"); + Console.WriteLine("===================================="); + Console.WriteLine(); + Console.WriteLine($"Username: {userSystem.CurrentUsername}"); + Console.WriteLine(); + } + + private void ProcessBase64Command(string args) + { + string[] parts = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length == 0) + { + ShowError("Error: Please specify subcommand"); + ShowError("Usage: base64 encrypt | base64 decrypt "); + return; + } + + string subcommand = parts[0].ToLower(); + + if (subcommand != "encrypt" && subcommand != "decrypt") + { + ShowError("Error: Invalid subcommand"); + ShowError("Usage: base64 encrypt | base64 decrypt "); + return; + } + + if (parts.Length < 2) + { + ShowError("Error: Please specify text to process"); + ShowError($"Usage: base64 {subcommand} "); + return; + } + + string text = string.Join(" ", parts, 1, parts.Length - 1); + + Console.WriteLine("===================================="); + Console.WriteLine(" Base64"); + Console.WriteLine("===================================="); + Console.WriteLine(); + + try + { + if (subcommand == "encrypt") + { + string encoded = Base64Helper.Encode(text); + Console.WriteLine($"Original: {text}"); + Console.WriteLine(); + Console.WriteLine($"Encoded: {encoded}"); + } + if (subcommand == "decrypt") + { + string decoded = Base64Helper.Decode(text); + Console.WriteLine($"Encoded: {text}"); + Console.WriteLine(); + Console.WriteLine($"Decoded: {decoded}"); + } + + Console.WriteLine(); + ShowSuccess("Base64 operation completed"); + } + catch (Exception ex) + { + ShowError($"Base64 error: {ex.Message}"); + } + } } } \ No newline at end of file diff --git a/UserSystem.cs b/UserSystem.cs index 91b8df3..88c3e3e 100644 --- a/UserSystem.cs +++ b/UserSystem.cs @@ -159,6 +159,18 @@ namespace CMLeonOS } } + public string CurrentUsername + { + get + { + if (currentLoggedInUser != null) + { + return currentLoggedInUser.Username; + } + return "Not logged in"; + } + } + public void FirstTimeSetup() { Console.WriteLine("====================================");