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