using AndroidSideloader.Utilities; using JR.Utils.GUI.Forms; using System; using System.Diagnostics; using System.IO; using System.Management; using System.Text; using System.Windows.Forms; namespace AndroidSideloader { internal class RCLONE { private static readonly SettingsManager settings = SettingsManager.Instance; // Kill RCLONE Processes that were started from Rookie by looking for child processes. public static void killRclone() { var parentProcessId = Process.GetCurrentProcess().Id; var processes = Process.GetProcessesByName("rclone"); foreach (var process in processes) { try { using (ManagementObject obj = new ManagementObject($"win32_process.handle='{process.Id}'")) { obj.Get(); var ppid = Convert.ToInt32(obj["ParentProcessId"]); if (ppid == parentProcessId) { process.Kill(); } } } catch (Exception ex) { _ = Logger.Log($"Exception occured while attempting to shut down RCLONE with exception message: {ex.Message}", LogLevel.ERROR); } } } // For custom configs that use a password public static void Init() { string PwTxtPath = Path.Combine(Environment.CurrentDirectory, "rclone\\pw.txt"); if (File.Exists(PwTxtPath)) { rclonepw = File.ReadAllText(PwTxtPath); } } // Change if you want to use a config public static string downloadConfigPath = "vrp.download.config"; public static string uploadConfigPath = "vrp.upload.config"; public static string rclonepw = ""; private static readonly Process rclone = new Process(); // Run an RCLONE Command that accesses the Download Config. public static ProcessOutput runRcloneCommand_DownloadConfig(string command) { if (MainForm.isOffline) { return new ProcessOutput("", "No internet"); } ProcessOutput prcoutput = new ProcessOutput(); // Rclone output is unicode, else it will show garbage instead of unicode characters rclone.StartInfo.StandardOutputEncoding = Encoding.UTF8; string originalCommand = command; // set configpath if there is any if (downloadConfigPath.Length > 0) { command += $" --config {downloadConfigPath}"; } command += $" --inplace"; // set rclonepw if (rclonepw.Length > 0) { command += " --ask-password=false"; } string logcmd = Utilities.StringUtilities.RemoveEverythingBeforeFirst(command, "rclone.exe"); if (logcmd.Contains($"\"{settings.CurrentLogPath}\"")) { logcmd = logcmd.Replace($"\"{settings.CurrentLogPath}\"", $"\"{settings.CurrentLogName}\""); } if (logcmd.Contains(Environment.CurrentDirectory)) { logcmd = logcmd.Replace($"{Environment.CurrentDirectory}", $"CurrentDirectory"); } _ = Logger.Log($"Running Rclone command: {logcmd}"); rclone.StartInfo.FileName = Path.Combine(Environment.CurrentDirectory, "rclone", "rclone.exe"); rclone.StartInfo.Arguments = command; rclone.StartInfo.RedirectStandardInput = true; rclone.StartInfo.RedirectStandardError = true; rclone.StartInfo.RedirectStandardOutput = true; rclone.StartInfo.WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "rclone"); rclone.StartInfo.CreateNoWindow = true; setRcloneProxy(); // Display RCLONE Window if the binary is being run in Debug Mode. if (MainForm.debugMode) { rclone.StartInfo.CreateNoWindow = false; } rclone.StartInfo.UseShellExecute = false; _ = rclone.Start(); rclone.StandardInput.WriteLine(command); rclone.StandardInput.Close(); string output = rclone.StandardOutput.ReadToEnd(); string error = rclone.StandardError.ReadToEnd(); rclone.WaitForExit(); if (error.Contains("There is not enough space")) { Program.form.Invoke(() => { _ = FlexibleMessageBox.Show(Program.form, $"There isn't enough disk space to download this game.\r\nPlease ensure you have at least 200MB more the game size available in {settings.DownloadDir} and try again.", "NOT ENOUGH SPACE", MessageBoxButtons.OK, MessageBoxIcon.Error); }); return new ProcessOutput("Download failed.", ""); } // Switch mirror upon matching error output. if (error.Contains("400 Bad Request") || error.Contains("cannot fetch token") || error.Contains("authError") || error.Contains("quota") || error.Contains("exceeded") || error.Contains("directory not found") || error.Contains("Failed to")) { string oldRemote = MainForm.currentRemote; bool retSM = false; try { retSM = Program.form.SwitchMirrors(); } catch { return new ProcessOutput("All mirrors are on quota or down...", "All mirrors are on quota or down..."); } if (retSM) { prcoutput = runRcloneCommand_DownloadConfig(originalCommand.Replace(oldRemote, MainForm.currentRemote)); } } else { prcoutput.Output = output; prcoutput.Error = error; } if (!output.Contains("Game Name;Release Name;") && !output.Contains("package:") && !output.Contains(".meta")) { if (!string.IsNullOrWhiteSpace(error)) { _ = Logger.Log($"Rclone error: {error}\n", LogLevel.ERROR); } if (!string.IsNullOrWhiteSpace(output)) { _ = Logger.Log($"Rclone Output: {output}"); } } return prcoutput; } public static ProcessOutput runRcloneCommand_UploadConfig(string command) { ProcessOutput prcoutput = new ProcessOutput(); // Rclone output is unicode, else it will show garbage instead of unicode characters rclone.StartInfo.StandardOutputEncoding = Encoding.UTF8; // set configpath if there is any if (uploadConfigPath.Length > 0) { command += $" --config {uploadConfigPath}"; } string logcmd = Utilities.StringUtilities.RemoveEverythingBeforeFirst(command, "rclone.exe"); if (logcmd.Contains($"\"{settings.CurrentLogPath}\"")) { logcmd = logcmd.Replace($"\"{settings.CurrentLogPath}\"", $"\"{settings.CurrentLogName}\""); } if (logcmd.Contains(Environment.CurrentDirectory)) { logcmd = logcmd.Replace($"{Environment.CurrentDirectory}", $"CurrentDirectory"); } _ = Logger.Log($"Running Rclone command: {logcmd}"); command += " --checkers 1 --retries 2 --inplace"; rclone.StartInfo.FileName = Path.Combine(Environment.CurrentDirectory, "rclone", "rclone.exe"); rclone.StartInfo.Arguments = command; rclone.StartInfo.RedirectStandardInput = true; rclone.StartInfo.RedirectStandardError = true; rclone.StartInfo.RedirectStandardOutput = true; rclone.StartInfo.WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "rclone"); rclone.StartInfo.CreateNoWindow = true; setRcloneProxy(); // Display RCLONE Window if the binary is being run in Debug Mode. if (MainForm.debugMode) { rclone.StartInfo.CreateNoWindow = false; } rclone.StartInfo.UseShellExecute = false; _ = rclone.Start(); rclone.StandardInput.WriteLine(command); rclone.StandardInput.Flush(); rclone.StandardInput.Close(); string output = rclone.StandardOutput.ReadToEnd(); string error = rclone.StandardError.ReadToEnd(); rclone.WaitForExit(); // if there is one of these errors, we switch the mirrors if (error.Contains("400 Bad Request") || error.Contains("cannot fetch token") || error.Contains("authError") || error.Contains("quota") || error.Contains("exceeded") || error.Contains("directory not found") || error.Contains("Failed to")) { _ = Logger.Log(error, LogLevel.ERROR); return new ProcessOutput("Upload Failed.", "Upload failed."); } else { prcoutput.Output = output; prcoutput.Error = error; } if (!output.Contains("Game Name;Release Name;") && !output.Contains("package:") && !output.Contains(".meta")) { if (!string.IsNullOrWhiteSpace(error)) { _ = Logger.Log($"Rclone error: {error}\n", LogLevel.ERROR); } if (!string.IsNullOrWhiteSpace(output)) { _ = Logger.Log($"Rclone Output: {output}"); } } return prcoutput; } public static ProcessOutput runRcloneCommand_PublicConfig(string command) { if (MainForm.isOffline) { return new ProcessOutput("", "No internet"); } ProcessOutput prcoutput = new ProcessOutput(); // Rclone output is unicode, else it will show garbage instead of unicode characters rclone.StartInfo.StandardOutputEncoding = Encoding.UTF8; string logcmd = Utilities.StringUtilities.RemoveEverythingBeforeFirst(command, "rclone.exe"); if (logcmd.Contains($"\"{settings.CurrentLogPath}\"")) { logcmd = logcmd.Replace($"\"{settings.CurrentLogPath}\"", $"\"{settings.CurrentLogName}\""); } if (logcmd.Contains(Environment.CurrentDirectory)) { logcmd = logcmd.Replace($"{Environment.CurrentDirectory}", $"CurrentDirectory"); } command += $" --inplace"; _ = Logger.Log($"Running Rclone command: {logcmd}"); //set http source & args command += $" --http-url {MainForm.PublicConfigFile.BaseUri} {MainForm.PublicMirrorExtraArgs}"; rclone.StartInfo.FileName = Path.Combine(Environment.CurrentDirectory, "rclone", "rclone.exe"); rclone.StartInfo.Arguments = command; rclone.StartInfo.RedirectStandardInput = true; rclone.StartInfo.RedirectStandardError = true; rclone.StartInfo.RedirectStandardOutput = true; rclone.StartInfo.WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "rclone"); rclone.StartInfo.CreateNoWindow = true; setRcloneProxy(); // Display RCLONE Window if the binary is being run in Debug Mode. if (MainForm.debugMode) { rclone.StartInfo.CreateNoWindow = false; } rclone.StartInfo.UseShellExecute = false; _ = rclone.Start(); rclone.StandardInput.WriteLine(command); rclone.StandardInput.Flush(); rclone.StandardInput.Close(); string output = rclone.StandardOutput.ReadToEnd(); string error = rclone.StandardError.ReadToEnd(); rclone.WaitForExit(); if (error.Contains("There is not enough space")) { Program.form.Invoke(() => { _ = FlexibleMessageBox.Show(Program.form, $"There isn't enough disk space to download this game.\r\nPlease ensure you have at least 2x the game size available in {settings.DownloadDir} and try again.", "NOT ENOUGH SPACE", MessageBoxButtons.OK, MessageBoxIcon.Error); }); return new ProcessOutput("Download failed.", string.Empty); } if (error.Contains("Only one usage of each socket address (protocol/network address/port) is normally permitted")) { _ = Logger.Log(error, LogLevel.ERROR); return new ProcessOutput("Failed to fetch from public mirror.", "Failed to fetch from public mirror.\nYou may have a running RCLONE Task!\nCheck your Task Manager, Sort by Network Usage, and kill the process Rsync for Cloud Storage/Rclone"); } else if (error.Contains("400 Bad Request") || error.Contains("cannot fetch token") || error.Contains("authError") || error.Contains("quota") || error.Contains("exceeded") || error.Contains("directory not found") || error.Contains("Failed to")) { _ = Logger.Log(error, LogLevel.ERROR); return new ProcessOutput("Failed to fetch from public mirror.", "Failed to fetch from public mirror."); } else { prcoutput.Output = output; prcoutput.Error = error; } if (!output.Contains("Game Name;Release Name;") && !output.Contains("package:") && !output.Contains(".meta")) { if (!string.IsNullOrWhiteSpace(error)) { _ = Logger.Log($"Rclone error: {error}\n", LogLevel.ERROR); } if (!string.IsNullOrWhiteSpace(output)) { _ = Logger.Log($"Rclone Output: {output}"); } } return prcoutput; } private static void setRcloneProxy() { // Use the user's proxy settings if set, otherwise fallback to DNS fallback proxy if active string proxyUrl = DnsHelper.ProxyUrl; if (settings.useProxy) { // Use user's configured proxy var url = $"http://{settings.ProxyAddress}:{settings.ProxyPort}"; rclone.StartInfo.EnvironmentVariables["HTTP_PROXY"] = url; rclone.StartInfo.EnvironmentVariables["HTTPS_PROXY"] = url; rclone.StartInfo.EnvironmentVariables["http_proxy"] = url; rclone.StartInfo.EnvironmentVariables["https_proxy"] = url; } else if (!string.IsNullOrEmpty(proxyUrl)) { // Use our DNS-resolving proxy rclone.StartInfo.EnvironmentVariables["HTTP_PROXY"] = proxyUrl; rclone.StartInfo.EnvironmentVariables["HTTPS_PROXY"] = proxyUrl; rclone.StartInfo.EnvironmentVariables["http_proxy"] = proxyUrl; rclone.StartInfo.EnvironmentVariables["https_proxy"] = proxyUrl; } else { // No proxy rclone.StartInfo.EnvironmentVariables.Remove("HTTP_PROXY"); rclone.StartInfo.EnvironmentVariables.Remove("HTTPS_PROXY"); rclone.StartInfo.EnvironmentVariables.Remove("http_proxy"); rclone.StartInfo.EnvironmentVariables.Remove("https_proxy"); } } } }