diff --git a/Directory.Build.props b/Directory.Build.props index 1bc200d..d89e22d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,6 +16,8 @@ $(GITHUB_HEAD_REF) $(GITHUB_REF) $([System.Text.RegularExpressions.Regex]::Replace("$(BranchVersionLabel)", "^(.+/)?([^/]+)$", "ci.$2")) + $([System.Text.RegularExpressions.Regex]::Replace($(BranchVersionLabel), '[^0-9A-Za-z\-\.]+', '-')) + $([System.Text.RegularExpressions.Regex]::Replace($(BranchVersionLabel), '\.0+(\d+(\.|$))', '.$1')) local .$(GITHUB_RUN_NUMBER) diff --git a/src/TorSharp/ToolUtility.cs b/src/TorSharp/ToolUtility.cs index 1385516..2cddd1d 100644 --- a/src/TorSharp/ToolUtility.cs +++ b/src/TorSharp/ToolUtility.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; + using Knapcode.TorSharp.Tools; namespace Knapcode.TorSharp @@ -39,6 +40,8 @@ public static ToolSettings GetPrivoxyToolSettings(TorSharpSettings settings) return e; }, + TryFindInSystem = settings.TorSettings.AutomaticallyFindInSystem, + TryFindExecutableName = "privoxy.exe" }; } else if (settings.OSPlatform == TorSharpOSPlatform.Linux) @@ -92,6 +95,8 @@ public static ToolSettings GetPrivoxyToolSettings(TorSharpSettings settings) return null; } }, + TryFindInSystem = settings.TorSettings.AutomaticallyFindInSystem, + TryFindExecutableName = "privoxy" }; } else @@ -118,11 +123,15 @@ public static ToolSettings GetTorToolSettings(TorSharpSettings settings) GetEnvironmentVariables = t => new Dictionary(), ZippedToolFormat = ZippedToolFormat.TarGz, GetEntryPath = e => e, + TryFindInSystem = settings.TorSettings.AutomaticallyFindInSystem, + TryFindExecutableName = "tor.exe" }; } else if (settings.OSPlatform == TorSharpOSPlatform.Linux) { var prefix = default(string); + var archiveFormat = ZippedToolFormat.TarGz; + var getEntryPath = (string a) => a; if (settings.Architecture == TorSharpArchitecture.X86) { prefix = "tor-linux32-"; @@ -131,6 +140,41 @@ public static ToolSettings GetTorToolSettings(TorSharpSettings settings) { prefix = "tor-linux64-"; } + else if (settings.Architecture.IsArm()) + { + if (settings.Architecture == TorSharpArchitecture.Arm32) + { + prefix = "tor-browser-linux-armhf-"; + } + else if (settings.Architecture == TorSharpArchitecture.Arm64) + { + prefix = "tor-browser-linux-arm64-"; + } + archiveFormat = ZippedToolFormat.TarXz; + getEntryPath = e => + { + const string entryPrefix = "tor-browser/Browser/TorBrowser/"; + if (e.StartsWith(entryPrefix + "Data/Tor/")) + { + return e.Substring(entryPrefix.Length).ToLower(); + } + else if (e.StartsWith(entryPrefix + "Tor/")) + { + if (e.StartsWith(entryPrefix + "Tor/PluggableTransports/")) + { + return null; + } + else + { + return e.Substring(entryPrefix.Length).ToLower(); + } + } + else + { + return null; + } + }; + } else { settings.RejectRuntime("determine Linux Tor prefix"); @@ -169,8 +213,10 @@ public static ToolSettings GetTorToolSettings(TorSharpSettings settings) return output; }, - ZippedToolFormat = ZippedToolFormat.TarGz, - GetEntryPath = e => e, + ZippedToolFormat = archiveFormat, + GetEntryPath = getEntryPath, + TryFindInSystem = settings.TorSettings.AutomaticallyFindInSystem, + TryFindExecutableName = "tor" }; } else @@ -192,6 +238,9 @@ public static Tool GetLatestToolOrNull( TorSharpSettings settings, ToolSettings toolSettings) { + if (toolSettings.TryFindInSystem && TryFindToolInSystem(settings, toolSettings, out var tool)) + return tool; + if (!Directory.Exists(settings.ZippedToolsDirectory)) { return null; @@ -232,5 +281,36 @@ public static Tool GetLatestToolOrNull( .OrderByDescending(t => t.Version) .FirstOrDefault(); } + + public static bool TryFindToolInSystem(TorSharpSettings settings, ToolSettings toolSettings, out Tool tool) + { + tool = null; + + var toolPath = settings.OSPlatform == TorSharpOSPlatform.Linux ? + WhichUtility.Which(toolSettings.TryFindExecutableName) : // Linux + SearchInPathHelper.SearchInPathVariable(toolSettings.TryFindExecutableName); // Windows + var toolVariants = toolPath.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var binToolVariant = toolVariants.FirstOrDefault(); + if (!string.IsNullOrEmpty(binToolVariant)) + { + var directoryPath = Path.Combine(settings.ExtractedToolsDirectory, "local"); + var workingDirectory = Path.Combine(directoryPath, toolSettings.WorkingDirectory); + var configurationPath = Path.Combine(directoryPath, toolSettings.ConfigurationPath); + DirectoryUtility.CreateDirectoryIfNotExists(directoryPath, workingDirectory, + Path.GetDirectoryName(configurationPath)); + tool = new Tool() + { + Settings = toolSettings, + ZipPath = null, + DirectoryPath = directoryPath, + Version = null, + ExecutablePath = binToolVariant, + WorkingDirectory = workingDirectory, + ConfigurationPath = configurationPath, + AutomaticallyDetected = true + }; + } + return true; + } } } diff --git a/src/TorSharp/Tools/ArchiveUtility.cs b/src/TorSharp/Tools/ArchiveUtility.cs index cb7195e..e29f080 100644 --- a/src/TorSharp/Tools/ArchiveUtility.cs +++ b/src/TorSharp/Tools/ArchiveUtility.cs @@ -9,7 +9,7 @@ namespace Knapcode.TorSharp.Tools { - internal class ArchiveUtility + internal static class ArchiveUtility { public static async Task TestAsync(ZippedToolFormat format, string path) { @@ -61,19 +61,14 @@ public static async Task ExtractAsync( public static string GetFileExtension(ZippedToolFormat format) { - switch (format) + return format switch { - case ZippedToolFormat.Zip: - return ".zip"; - case ZippedToolFormat.Deb: - return ".deb"; - case ZippedToolFormat.TarXz: - return ".tar.xz"; - case ZippedToolFormat.TarGz: - return ".tar.gz"; - default: - throw new NotImplementedException($"The zipped tool format {format} does not have a known extension."); - } + ZippedToolFormat.Zip => ".zip", + ZippedToolFormat.Deb => ".deb", + ZippedToolFormat.TarXz => ".tar.xz", + ZippedToolFormat.TarGz => ".tar.gz", + _ => throw new NotImplementedException($"The zipped tool format {format} does not have a known extension."), + }; } public static async Task TestZipAsync(string zipPath) @@ -245,7 +240,7 @@ await ReadTarXzAsync( } } - private class ArFileHeader + private sealed class ArFileHeader { public ArFileHeader(string fileIdentifier, uint fileSize) { diff --git a/src/TorSharp/Tools/DirectoryUtility.cs b/src/TorSharp/Tools/DirectoryUtility.cs new file mode 100644 index 0000000..95e59cd --- /dev/null +++ b/src/TorSharp/Tools/DirectoryUtility.cs @@ -0,0 +1,19 @@ +using System.IO; + +namespace Knapcode.TorSharp.Tools +{ + internal static class DirectoryUtility + { + public static void CreateDirectoryIfNotExists(params string[] path) + { + foreach (var p in path) + CreateDirectoryIfNotExists(p); + } + + public static void CreateDirectoryIfNotExists(string path) + { + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + } + } +} diff --git a/src/TorSharp/Tools/EnumHelper.cs b/src/TorSharp/Tools/EnumHelper.cs new file mode 100644 index 0000000..a1dc2e8 --- /dev/null +++ b/src/TorSharp/Tools/EnumHelper.cs @@ -0,0 +1,12 @@ +using System; + +namespace Knapcode.TorSharp.Tools +{ + internal static class EnumHelper + { + public static bool IsArm(this TorSharpArchitecture arch) + { + return arch == TorSharpArchitecture.Arm32 || arch == TorSharpArchitecture.Arm64; + } + } +} diff --git a/src/TorSharp/Tools/FetcherHelpers.cs b/src/TorSharp/Tools/FetcherHelpers.cs index 8dbae7c..982e506 100644 --- a/src/TorSharp/Tools/FetcherHelpers.cs +++ b/src/TorSharp/Tools/FetcherHelpers.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.ServiceModel.Syndication; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -26,8 +27,7 @@ public static async Task GetStringAsync( public static async Task GetLatestDownloadableFileAsync( HttpClient httpClient, Uri baseUrl, - string fileNamePattern, - ZippedToolFormat format, + FileNamePatternAndFormat patternAndFormat, CancellationToken token) { var versionsContent = await httpClient.GetStringAsync(baseUrl, token).ConfigureAwait(false); @@ -44,7 +44,7 @@ public static async Task GetLatestDownloadableFileAsync( foreach (var link in GetLinks(listContent)) { - var match = Regex.Match(link, fileNamePattern, RegexOptions.IgnoreCase); + var match = Regex.Match(link, patternAndFormat.Pattern, RegexOptions.IgnoreCase); if (!match.Success) { continue; @@ -61,7 +61,7 @@ public static async Task GetLatestDownloadableFileAsync( return new DownloadableFile( parsedVersion, downloadUrl, - format); + patternAndFormat.Format); } } @@ -95,5 +95,28 @@ private static IEnumerable GetLinks(string content) .OfType() .Select(x => x.Groups["Link"].Value); } + + internal static DownloadableFile GetDownloadableFile( + FileNamePatternAndFormat fileNamePatternAndFormat, + SyndicationItem item) + { + var match = Regex.Match( + item.Title.Text, + fileNamePatternAndFormat.Pattern, + RegexOptions.IgnoreCase); + + if (!match.Success) + { + return null; + } + + if (!Version.TryParse(match.Groups["Version"].Value, out var parsedVersion)) + { + return null; + } + + var downloadUrl = item.Links[0].Uri; + return new DownloadableFile(parsedVersion, downloadUrl, fileNamePatternAndFormat.Format); + } } } \ No newline at end of file diff --git a/src/TorSharp/Tools/LineByLineConfigurer.cs b/src/TorSharp/Tools/LineByLineConfigurer.cs index 0df8fd2..e1d507e 100644 --- a/src/TorSharp/Tools/LineByLineConfigurer.cs +++ b/src/TorSharp/Tools/LineByLineConfigurer.cs @@ -26,7 +26,7 @@ public async Task ApplySettings(Tool tool, TorSharpSettings settings) try { // write first to a temporary file - temporaryPath = Path.GetTempFileName(); + temporaryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); TextReader reader; // read the existing configuration, if there is some @@ -57,15 +57,12 @@ public async Task ApplySettings(Tool tool, TorSharpSettings settings) // write the remaining lines foreach (var pair in dictionary.OrderBy(p => p.Key)) { - if (pair.Value != null && pair.Value.Any()) + if (pair.Value?.Any() == true) { - foreach (var value in pair.Value) + foreach (var value in pair.Value.Where(a => a != null)) { - if (value != null) - { - string newLine = _format.CreateLine(new KeyValuePair(pair.Key, value)); - await writer.WriteLineAsync(newLine).ConfigureAwait(false); - } + string newLine = _format.CreateLine(new KeyValuePair(pair.Key, value)); + await writer.WriteLineAsync(newLine).ConfigureAwait(false); } } } @@ -109,7 +106,6 @@ public async Task ApplySettings(Tool tool, TorSharpSettings settings) } } } - } } } diff --git a/src/TorSharp/Tools/Privoxy/PrivoxyFetcher.cs b/src/TorSharp/Tools/Privoxy/PrivoxyFetcher.cs index 7da6b49..a3b735c 100644 --- a/src/TorSharp/Tools/Privoxy/PrivoxyFetcher.cs +++ b/src/TorSharp/Tools/Privoxy/PrivoxyFetcher.cs @@ -91,7 +91,7 @@ private async Task> GetResultsAsync(bool takeFirst) await Task.WhenAll(faults).ConfigureAwait(false); } - throw new TorSharpException($"No version of Privoxy could be found."); + throw new TorSharpException("No version of Privoxy could be found."); } return results; @@ -121,8 +121,7 @@ private async Task GetLatestOrNullFromFileListingAsync( var downloadableFile = await FetcherHelpers.GetLatestDownloadableFileAsync( _httpClient, osBaseUrl, - fileNamePatternAndFormat.Pattern, - fileNamePatternAndFormat.Format, + fileNamePatternAndFormat, token).ConfigureAwait(false); if (downloadableFile == null) @@ -161,9 +160,8 @@ private async Task GetLatestOrNullFromRssAsync( var downloadableFile = syndicationFeed .Items - .Where(i => i.Links.Any()) - .Where(i => TitleStartWithDirectory(directory, i)) - .Select(i => GetDownloadableFile(fileNamePatternAndFormat.Pattern, fileNamePatternAndFormat.Format, i)) + .Where(i => i.Links.Any() && TitleStartWithDirectory(directory, i)) + .Select(i => FetcherHelpers.GetDownloadableFile(fileNamePatternAndFormat, i)) .Where(i => i != null) .OrderByDescending(x => x.Version) .FirstOrDefault(); @@ -186,30 +184,6 @@ private static bool TitleStartWithDirectory(string directory, SyndicationItem it RegexOptions.IgnoreCase); } - private DownloadableFile GetDownloadableFile( - string fileNamePattern, - ZippedToolFormat format, - SyndicationItem item) - { - var match = Regex.Match( - item.Title.Text, - fileNamePattern, - RegexOptions.IgnoreCase); - - if (!match.Success) - { - return null; - } - - if (!Version.TryParse(match.Groups["Version"].Value, out var parsedVersion)) - { - return null; - } - - var downloadUrl = item.Links.First().Uri; - return new DownloadableFile(parsedVersion, downloadUrl, format); - } - private string GetFileListingDirectory(Uri baseUrl) { string directory = null; diff --git a/src/TorSharp/Tools/SearchInPathHelper.cs b/src/TorSharp/Tools/SearchInPathHelper.cs new file mode 100644 index 0000000..509c972 --- /dev/null +++ b/src/TorSharp/Tools/SearchInPathHelper.cs @@ -0,0 +1,22 @@ +using System; +using System.IO; +using System.Linq; + +namespace Knapcode.TorSharp.Tools +{ + public static class SearchInPathHelper + { + public static string SearchInPathVariable(string pattern) + { + var variables = Environment.GetEnvironmentVariables(); + if (!variables.Contains("Path")) + return null; + + var what = variables["Path"].ToString() + .Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries) + .SelectMany(a => Directory.GetFiles(a, pattern, SearchOption.TopDirectoryOnly)); + + return what.FirstOrDefault(); + } + } +} diff --git a/src/TorSharp/Tools/SimpleToolRunner.cs b/src/TorSharp/Tools/SimpleToolRunner.cs index 86b88a0..8822cd4 100644 --- a/src/TorSharp/Tools/SimpleToolRunner.cs +++ b/src/TorSharp/Tools/SimpleToolRunner.cs @@ -34,26 +34,18 @@ public Task StartAsync(Tool tool) startInfo.EnvironmentVariables[pair.Key] = pair.Value; } - Process process = Process.Start(startInfo); + Process process = Process.Start(startInfo) ?? + throw new TorSharpException($"Unable to start the process '{tool.ExecutablePath}'."); - process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => - { + process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => Stdout?.Invoke(this, new DataEventArgs(tool.ExecutablePath, e.Data)); - }; - process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => - { + process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => Stderr?.Invoke(this, new DataEventArgs(tool.ExecutablePath, e.Data)); - }; process.BeginOutputReadLine(); process.BeginErrorReadLine(); - if (process == null) - { - throw new TorSharpException($"Unable to start the process '{tool.ExecutablePath}'."); - } - _processes.Add(process); return Task.CompletedTask; diff --git a/src/TorSharp/Tools/Tool.cs b/src/TorSharp/Tools/Tool.cs index c48e37d..fd2f8e2 100644 --- a/src/TorSharp/Tools/Tool.cs +++ b/src/TorSharp/Tools/Tool.cs @@ -41,5 +41,10 @@ internal class Tool /// The full path to the tool's configuration file. /// public string ConfigurationPath { get; set; } + + /// + /// Automate detected in system. + /// + public bool AutomaticallyDetected { get; set; } } } \ No newline at end of file diff --git a/src/TorSharp/Tools/ToolSettings.cs b/src/TorSharp/Tools/ToolSettings.cs index 70d2313..1a4ebf9 100644 --- a/src/TorSharp/Tools/ToolSettings.cs +++ b/src/TorSharp/Tools/ToolSettings.cs @@ -60,5 +60,15 @@ internal class ToolSettings /// executable name present in the PATH variable (e.g. installed to the local machine). /// public string ExecutablePathOverride { get; set; } + + /// + /// Try to find the tool without downloading it. + /// + public bool TryFindInSystem { get; set; } + + /// + /// In linux most binaries exists in any bin directory, e.g /bin/ /sbin/ /usr/bin/ + /// + public string TryFindExecutableName { get; set; } } } \ No newline at end of file diff --git a/src/TorSharp/Tools/Tor/TorConfigurationDictionary.cs b/src/TorSharp/Tools/Tor/TorConfigurationDictionary.cs index 4bcc509..8a023fc 100644 --- a/src/TorSharp/Tools/Tor/TorConfigurationDictionary.cs +++ b/src/TorSharp/Tools/Tor/TorConfigurationDictionary.cs @@ -88,14 +88,7 @@ public IDictionary> GetDictionary(Tool tool, TorSharpSettin dictionary["Bridge"] = settings.TorSettings.Bridge; } - if (settings.TorSettings.HashedControlPassword != null) - { - dictionary["HashedControlPassword"] = settings.TorSettings.HashedControlPassword; - } - else - { - dictionary["HashedControlPassword"] = null; - } + dictionary["HashedControlPassword"] = settings.TorSettings.HashedControlPassword; string dataDictionary; if (!string.IsNullOrWhiteSpace(settings.TorSettings.DataDirectory)) diff --git a/src/TorSharp/Tools/Tor/TorControlClient.cs b/src/TorSharp/Tools/Tor/TorControlClient.cs index 71ed9bf..3190046 100644 --- a/src/TorSharp/Tools/Tor/TorControlClient.cs +++ b/src/TorSharp/Tools/Tor/TorControlClient.cs @@ -51,7 +51,7 @@ public async Task>> GetInfoAsync(IEnumerable GetLatestAsync() { var fileNamePatternAndFormat = GetFileNamePatternAndFormat(); + DownloadableFile downloadableFile; - var downloadableFile = await FetcherHelpers.GetLatestDownloadableFileAsync( - _httpClient, - BaseUrl, - fileNamePatternAndFormat.Pattern, - fileNamePatternAndFormat.Format, - CancellationToken.None).ConfigureAwait(false); + if (_settings.Architecture.IsArm()) + { + if (!_settings.AllowUnofficialSources) + throw new TorSharpException($"{nameof(_settings.AllowUnofficialSources)} == false, there is no official tor distribution for {_settings.Architecture} platform."); + + downloadableFile = await GetLatestDownloadableArmFileAsync(_httpClient, + BaseArmUrl, + fileNamePatternAndFormat, + CancellationToken.None).ConfigureAwait(false); + } + else + { + downloadableFile = await FetcherHelpers.GetLatestDownloadableFileAsync( + _httpClient, + BaseUrl, + fileNamePatternAndFormat, + CancellationToken.None).ConfigureAwait(false); + } if (downloadableFile == null) { @@ -56,6 +75,7 @@ private FileNamePatternAndFormat GetFileNamePatternAndFormat() } else if (_settings.OSPlatform == TorSharpOSPlatform.Linux) { + format = ZippedToolFormat.TarGz; if (_settings.Architecture == TorSharpArchitecture.X86) { pattern = @"tor-expert-bundle-(?[\d\.]+)-linux-i686\.tar\.gz$"; @@ -64,7 +84,13 @@ private FileNamePatternAndFormat GetFileNamePatternAndFormat() { pattern = @"tor-expert-bundle-(?[\d\.]+)-linux-x86_64\.tar\.gz$"; } - format = ZippedToolFormat.TarGz; + else if (_settings.Architecture.IsArm()) + { + // Version twice to skip ALSA versions - https://github.com/alsa-project + var arch = _settings.Architecture == TorSharpArchitecture.Arm64 ? "arm64" : "armhf"; + pattern = @$"(?[\d\.]+)\/tor-browser-linux-{arch}-(?[\d\.]+)_ALL\.tar\.xz$"; + format = ZippedToolFormat.TarXz; + } } if (pattern == null) @@ -74,5 +100,52 @@ private FileNamePatternAndFormat GetFileNamePatternAndFormat() return new FileNamePatternAndFormat(pattern, format); } + + public async Task GetLatestDownloadableArmFileAsync(HttpClient httpClient, + Uri baseUrl, + FileNamePatternAndFormat patternAndFormat, + CancellationToken token) + { + using var response = await httpClient.GetAsync(baseUrl, HttpCompletionOption.ResponseContentRead, token).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + + using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var streamReader = new StreamReader(stream); + using var xmlReader = XmlReader.Create(streamReader); + var syndicationFeed = SyndicationFeed.Load(xmlReader); + if (syndicationFeed == null) + return null; + + return syndicationFeed.Items + .Where(i => i.Links.Any()) + .Select(i => GetDownloadableFile(patternAndFormat.Pattern, patternAndFormat.Format, i)) + .Where(i => i != null) + .OrderByDescending(x => x.Version) + .FirstOrDefault(); + } + + private DownloadableFile GetDownloadableFile( + string fileNamePattern, + ZippedToolFormat format, + SyndicationItem item) + { + var match = Regex.Match( + item.Title.Text, + fileNamePattern, + RegexOptions.IgnoreCase); + + if (!match.Success) + { + return null; + } + + if (!Version.TryParse(match.Groups["Version"].Value, out var parsedVersion)) + { + return null; + } + + var downloadUrl = item.Links[0].Uri; + return new DownloadableFile(parsedVersion, downloadUrl, format); + } } } \ No newline at end of file diff --git a/src/TorSharp/Tools/WhichUtility.cs b/src/TorSharp/Tools/WhichUtility.cs new file mode 100644 index 0000000..9ef3c0f --- /dev/null +++ b/src/TorSharp/Tools/WhichUtility.cs @@ -0,0 +1,43 @@ +using System.Diagnostics; +using System.Text; + +namespace Knapcode.TorSharp.Tools +{ + internal static class WhichUtility + { + public static string Which(string searchPattern) + { + using var process = new Process(); + + process.StartInfo.FileName = "which"; + process.StartInfo.Arguments = searchPattern; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = false; + + var output = new StringBuilder(); + var outputLock = new object(); + process.OutputDataReceived += GetOutputHandler(output, outputLock); + process.ErrorDataReceived += GetOutputHandler(output, outputLock); + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + + return output.ToString(); + } + + private static DataReceivedEventHandler GetOutputHandler(StringBuilder output, object outputLock) + { + return (object sender, DataReceivedEventArgs e) => + { + lock (outputLock) + { + output.AppendLine(e.Data); + } + }; + } + } +} diff --git a/src/TorSharp/TorSharp.csproj b/src/TorSharp/TorSharp.csproj index d95ce51..d487ef8 100644 --- a/src/TorSharp/TorSharp.csproj +++ b/src/TorSharp/TorSharp.csproj @@ -3,6 +3,7 @@ net462;net472;netstandard2.0 netstandard2.0 + latest Knapcode.TorSharp Knapcode.TorSharp true diff --git a/src/TorSharp/TorSharpArchitecture.cs b/src/TorSharp/TorSharpArchitecture.cs index 0393028..e6fbb90 100644 --- a/src/TorSharp/TorSharpArchitecture.cs +++ b/src/TorSharp/TorSharpArchitecture.cs @@ -8,5 +8,7 @@ public enum TorSharpArchitecture Unknown, X86, X64, + Arm32, + Arm64 } } \ No newline at end of file diff --git a/src/TorSharp/TorSharpPrivoxySettings.cs b/src/TorSharp/TorSharpPrivoxySettings.cs index 57fc9b4..9c0ca40 100644 --- a/src/TorSharp/TorSharpPrivoxySettings.cs +++ b/src/TorSharp/TorSharpPrivoxySettings.cs @@ -42,5 +42,10 @@ public TorSharpPrivoxySettings() /// See https://www.privoxy.org/user-manual/config.html#MAX-CLIENT-CONNECTIONS for more details. /// public int? MaxClientConnections { get; set; } + + /// + /// Automatically find privoxy in the system. May be helpful for linux users when you can install privoxy from a system repository. + /// + public bool AutomaticallyFindInSystem { get; set; } } } \ No newline at end of file diff --git a/src/TorSharp/TorSharpSettings.cs b/src/TorSharp/TorSharpSettings.cs index ef791e6..53ce40d 100644 --- a/src/TorSharp/TorSharpSettings.cs +++ b/src/TorSharp/TorSharpSettings.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Text; +using Knapcode.TorSharp.Tools; #if NETSTANDARD using System.Runtime.InteropServices; using SystemOSPlatform = System.Runtime.InteropServices.OSPlatform; @@ -40,18 +41,14 @@ public TorSharpSettings() OSPlatform = TorSharpOSPlatform.Unknown; } - switch (RuntimeInformation.ProcessArchitecture) + Architecture = RuntimeInformation.ProcessArchitecture switch { - case SystemArchitecture.X86: - Architecture = TorSharpArchitecture.X86; - break; - case SystemArchitecture.X64: - Architecture = TorSharpArchitecture.X64; - break; - default: - Architecture = TorSharpArchitecture.Unknown; - break; - } + SystemArchitecture.X86 => TorSharpArchitecture.X86, + SystemArchitecture.X64 => TorSharpArchitecture.X64, + SystemArchitecture.Arm => TorSharpArchitecture.Arm32, + SystemArchitecture.Arm64 => TorSharpArchitecture.Arm64, + _ => TorSharpArchitecture.Unknown, + }; #else OSPlatform = TorSharpOSPlatform.Windows; Architecture = Environment.Is64BitProcess ? TorSharpArchitecture.X64 : TorSharpArchitecture.X86; @@ -59,6 +56,19 @@ public TorSharpSettings() PrivoxySettings = new TorSharpPrivoxySettings(); TorSettings = new TorSharpTorSettings(); + +#if NETSTANDARD + if (OSPlatform == TorSharpOSPlatform.Linux) + { + // It's simply better for any linux distro to try find and use system binaries. + PrivoxySettings.AutomaticallyFindInSystem = true; + TorSettings.AutomaticallyFindInSystem = true; + } + if (Architecture.IsArm()) + { + PrivoxySettings.Disable = true; + } +#endif } /// @@ -175,6 +185,11 @@ public ToolRunnerType ToolRunnerType /// public TorSharpTorSettings TorSettings { get; set; } + /// + /// Allow non official download repository. + /// + public bool AllowUnofficialSources { get; set; } + [Obsolete("Use the " + nameof(TorSharpPrivoxySettings) + "." + nameof(TorSharpPrivoxySettings.Port) + " property instead.")] public int PrivoxyPort { @@ -233,20 +248,14 @@ public string TorDataDirectory private TorSharpTorSettings EnsureTorSettings() { - if (TorSettings == null) - { - TorSettings = new TorSharpTorSettings(); - } + TorSettings ??= new TorSharpTorSettings(); return TorSettings; } private TorSharpPrivoxySettings EnsurePrivoxySettings() { - if (PrivoxySettings == null) - { - PrivoxySettings = new TorSharpPrivoxySettings(); - } + PrivoxySettings ??= new TorSharpPrivoxySettings(); return PrivoxySettings; } diff --git a/src/TorSharp/TorSharpToolFetcher.cs b/src/TorSharp/TorSharpToolFetcher.cs index e014002..f077ecf 100644 --- a/src/TorSharp/TorSharpToolFetcher.cs +++ b/src/TorSharp/TorSharpToolFetcher.cs @@ -50,7 +50,7 @@ internal TorSharpToolFetcher( /// /// Checks for updates of the tools and returns what is found. Inspect the /// property to determine whether updates are available. This is - /// determined by checking the the latest versions are already downloads to the + /// determined by checking the latest versions are already downloads to the /// . /// /// Information about whether there are tool updates. @@ -89,7 +89,7 @@ private async Task CheckForUpdateAsync( bool useExistingTools) { var latestLocal = ToolUtility.GetLatestToolOrNull(_settings, toolSettings); - if (useExistingTools && latestLocal != null) + if (useExistingTools || latestLocal?.AutomaticallyDetected == true) { return null; } diff --git a/src/TorSharp/TorSharpTorSettings.cs b/src/TorSharp/TorSharpTorSettings.cs index 6791200..65befc6 100644 --- a/src/TorSharp/TorSharpTorSettings.cs +++ b/src/TorSharp/TorSharpTorSettings.cs @@ -178,5 +178,10 @@ public TorSharpTorSettings() /// See https://2019.www.torproject.org/docs/tor-manual-dev.html.en#ORPort for more details. /// public string ORPort { get; set; } + + /// + /// Automatically find tor in the system. May be helpful for linux users when you can install tor from a system repository. + /// + public bool AutomaticallyFindInSystem { get; set; } } } \ No newline at end of file diff --git a/test/PassThroughProxy/PassThroughProxy/Configurations/Rule.cs b/test/PassThroughProxy/PassThroughProxy/Configurations/Rule.cs index ac24127..60f6ef2 100644 --- a/test/PassThroughProxy/PassThroughProxy/Configurations/Rule.cs +++ b/test/PassThroughProxy/PassThroughProxy/Configurations/Rule.cs @@ -10,7 +10,6 @@ public Rule(string pattern, ActionEnum action) Pattern = new Regex(pattern, RegexOptions.Compiled); } - public Regex Pattern { get; private set; } public ActionEnum Action { get; private set; } diff --git a/test/PassThroughProxy/PassThroughProxy/Headers/HttpHeader.cs b/test/PassThroughProxy/PassThroughProxy/Headers/HttpHeader.cs index 5eea0eb..617a2e9 100644 --- a/test/PassThroughProxy/PassThroughProxy/Headers/HttpHeader.cs +++ b/test/PassThroughProxy/PassThroughProxy/Headers/HttpHeader.cs @@ -57,15 +57,12 @@ private static Address GetAddress(IEnumerable strings) .TrimStart() .Split(':'); - switch (split.Length) + return split.Length switch { - case 1: - return new Address(split[0], 80); - case 2: - return new Address(split[0], int.Parse(split[1])); - default: - throw new FormatException(string.Join(":", split)); - } + 1 => new Address(split[0], 80), + 2 => new Address(split[0], int.Parse(split[1])), + _ => throw new FormatException(string.Join(":", split)), + }; } private static long GetContentLength(IEnumerable strings) diff --git a/test/PassThroughProxy/PassThroughProxy/PassThroughProxy.csproj b/test/PassThroughProxy/PassThroughProxy/PassThroughProxy.csproj index bab1586..7faf5ba 100644 --- a/test/PassThroughProxy/PassThroughProxy/PassThroughProxy.csproj +++ b/test/PassThroughProxy/PassThroughProxy/PassThroughProxy.csproj @@ -1,8 +1,9 @@  - net6.0;net462;net472 + net462;net472;net6.0 net6.0 + latest false Proxy PassThroughProxy diff --git a/test/TorSharp.Tests/ARM/FetcherTests.cs b/test/TorSharp.Tests/ARM/FetcherTests.cs new file mode 100644 index 0000000..17a92ac --- /dev/null +++ b/test/TorSharp.Tests/ARM/FetcherTests.cs @@ -0,0 +1,66 @@ +using System.Net.Http; +using System.Threading.Tasks; + +using Knapcode.TorSharp.Tools.Tor; + +using Xunit; + +namespace Knapcode.TorSharp.Tests.ARM +{ + public class FetcherTests + { + private readonly HttpClient HttpClient = new HttpClient(); + + [Fact] + public async Task TorSharpToolFetcher_TestArm32() + { + var settings = new TorSharpSettings() + { + OSPlatform = TorSharpOSPlatform.Linux, + Architecture = TorSharpArchitecture.Arm32, + AllowUnofficialSources = true + }; + + var fetcher = new TorFetcher(settings, HttpClient); + var result = await fetcher.GetLatestAsync(); + + Assert.NotNull(result); + Assert.NotNull(result.Url); + } + + [Fact] + public async Task TorSharpToolFetcher_TestArm64() + { + var settings = new TorSharpSettings() + { + OSPlatform = TorSharpOSPlatform.Linux, + Architecture = TorSharpArchitecture.Arm64, + AllowUnofficialSources = true + }; + + var fetcher = new TorFetcher(settings, HttpClient); + var result = await fetcher.GetLatestAsync(); + + Assert.NotNull(result); + Assert.NotNull(result.Url); + } + + //[Fact] + //public async Task TorSharpToolFetcher_TestDownload() + //{ + // var settings = new TorSharpSettings() + // { + // OSPlatform = TorSharpOSPlatform.Linux, + // Architecture = TorSharpArchitecture.Arm64, + // AllowUnofficialSources = true + // }; + // settings.PrivoxySettings.Disable = true; + + // var toolFetcher = new TorSharpToolFetcher(settings, HttpClient); + // await toolFetcher.FetchAsync(); + + // var ts = new TorSharpProxy(settings); + // await ts.ConfigureAsync(); + //} + } +} diff --git a/test/TorSharp.Tests/TestAppTests.cs b/test/TorSharp.Tests/TestAppTests.cs index 3bd7b81..11eb076 100644 --- a/test/TorSharp.Tests/TestAppTests.cs +++ b/test/TorSharp.Tests/TestAppTests.cs @@ -56,7 +56,6 @@ public void SimpleToolRuner_OnlyWritesToStdoutIfSpecified(bool writeToConsole, s private void Execute(bool writeToConsole, ToolRunnerType toolRunnerType, string framework) { - // build the test app ExecuteDotnet( new[] diff --git a/test/TorSharp.Tests/TorSharp.Tests.csproj b/test/TorSharp.Tests/TorSharp.Tests.csproj index 1d77306..513d241 100644 --- a/test/TorSharp.Tests/TorSharp.Tests.csproj +++ b/test/TorSharp.Tests/TorSharp.Tests.csproj @@ -3,6 +3,7 @@ net462;net472;net6.0 net6.0 + latest false Knapcode.TorSharp.Tests Knapcode.TorSharp.Tests diff --git a/test/TorSharp.Tests/TorSharpFetcherTests.cs b/test/TorSharp.Tests/TorSharpFetcherTests.cs index 315db95..4cacaf4 100644 --- a/test/TorSharp.Tests/TorSharpFetcherTests.cs +++ b/test/TorSharp.Tests/TorSharpFetcherTests.cs @@ -5,6 +5,8 @@ using System.Net.Http; using System.Threading.Tasks; using Knapcode.TorSharp.Tests.TestSupport; +using Knapcode.TorSharp.Tools; + using xRetry; using Xunit; using Xunit.Abstractions; @@ -151,7 +153,7 @@ public static IEnumerable Platforms foreach (var architecture in Enum.GetValues(typeof(TorSharpArchitecture)).Cast()) { - if (architecture == TorSharpArchitecture.Unknown) + if (architecture == TorSharpArchitecture.Unknown || architecture.IsArm()) { continue; }