diff --git a/Code/IPFilter/Apps/BitTorrentApplication.cs b/Code/IPFilter/Apps/BitTorrentApplication.cs index 708101e..432d83d 100644 --- a/Code/IPFilter/Apps/BitTorrentApplication.cs +++ b/Code/IPFilter/Apps/BitTorrentApplication.cs @@ -55,7 +55,7 @@ public async Task UpdateFilterAsync(FilterDownloadResult fil Trace.TraceInformation("Writing filter to " + destinationPath); using (var destination = File.Open(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None)) - using (var writer = new BitTorrentWriter(destination)) + using (var writer = new P2pWriter(destination)) { await writer.Write(filter.Entries, progress); } diff --git a/Code/IPFilter/Apps/DelugeApplication.cs b/Code/IPFilter/Apps/DelugeApplication.cs new file mode 100644 index 0000000..78d905e --- /dev/null +++ b/Code/IPFilter/Apps/DelugeApplication.cs @@ -0,0 +1,92 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using IPFilter.Formats; +using IPFilter.Models; +using IPFilter.Native; +using Microsoft.Win32; + +namespace IPFilter.Apps +{ + class DelugeApplication : IApplication { + + public Task DetectAsync() + { + using (var key = Registry.ClassesRoot.OpenSubKey(@"SOFTWARE\Deluge", false)) + { + if (key == null) + { + Trace.TraceInformation("Couldn't find Deluge key at HKCR\\SOFTWARE\\Deluge"); + return Task.FromResult(ApplicationDetectionResult.NotFound()); + } + + var startMenuFolder = (string)key.GetValue("Start Menu Folder"); + if (string.IsNullOrWhiteSpace(startMenuFolder)) + { + Trace.TraceInformation("Couldn't find Deluge start menu location"); + return Task.FromResult(ApplicationDetectionResult.NotFound()); + } + + // Get the link + var linkPath = Path.Combine(Environment.ExpandEnvironmentVariables(@"%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs"), startMenuFolder); + if (!Directory.Exists(linkPath)) + { + Trace.TraceInformation("Couldn't find Deluge shortcut folder: " + linkPath); + return Task.FromResult(ApplicationDetectionResult.NotFound()); + } + + var shortcut = Path.Combine(linkPath, "Deluge.lnk"); + if (!File.Exists(shortcut)) + { + Trace.TraceInformation("Couldn't find Deluge shortcut: " + shortcut); + return Task.FromResult(ApplicationDetectionResult.NotFound()); + } + + var path = ShellLinkHelper.ResolveShortcut(shortcut); + Trace.TraceInformation("Deluge location is " + path); + + var result = ApplicationDetectionResult.Create(this, "Deluge", Path.GetDirectoryName(path)); + + var exe = new FileInfo(path); + if (!exe.Exists) + { + Trace.TraceInformation("Deluge exe not found @ " + path); + result.IsPresent = false; + } + + var version = FileVersionInfo.GetVersionInfo(exe.FullName); + result.Description = version.ProductName; + result.Version = version.FileVersion; + + return Task.FromResult(result); + } + + // DisplayName: Deluge 1.3.15 + // UninstallString: C:\Program Files (x86)\Deluge\deluge-uninst.exe + var uninstallKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Deluge"; + var uinstallValue = @""; + + // Recent Apps GUID: {86B4A402-4897-48E8-8D82-0D19C33E1431} + // AppId: {7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Deluge\deluge.exe + // AppPath: C:\Program Files (x86)\Deluge\deluge.exe + + } + + public async Task UpdateFilterAsync(FilterDownloadResult filter, CancellationToken cancellationToken, IProgress progress) + { + var roamingPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create); + var destinationPath = Path.Combine(roamingPath, "deluge", "ipfilter.dat"); + + Trace.TraceInformation("Writing filter to " + destinationPath); + using (var destination = File.Open(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None)) + using (var writer = new P2pWriter(destination)) + { + await writer.Write(filter.Entries, progress); + } + + return new FilterUpdateResult { FilterTimestamp = filter.FilterTimestamp }; + } + } +} \ No newline at end of file diff --git a/Code/IPFilter/Apps/QBitTorrent.cs b/Code/IPFilter/Apps/QBitTorrent.cs index 3a5be3a..2e3c94b 100644 --- a/Code/IPFilter/Apps/QBitTorrent.cs +++ b/Code/IPFilter/Apps/QBitTorrent.cs @@ -55,7 +55,7 @@ public async Task UpdateFilterAsync(FilterDownloadResult fil Trace.TraceInformation("Writing filter to " + destinationPath); using (var destination = File.Open(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None)) - using (var writer = new BitTorrentWriter(destination)) + using (var writer = new P2pWriter(destination)) { await writer.Write(filter.Entries, progress); } diff --git a/Code/IPFilter/Cli/MultiFilterWriter.cs b/Code/IPFilter/Cli/MultiFilterWriter.cs new file mode 100644 index 0000000..27f4081 --- /dev/null +++ b/Code/IPFilter/Cli/MultiFilterWriter.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using IPFilter.Core; + +namespace IPFilter.Cli +{ + class MultiFilterWriter : IFilterWriter + { + readonly IList writers; + + public MultiFilterWriter(IEnumerable writers) + { + this.writers = writers.ToList(); + } + + public void Dispose() + { + foreach (var writer in writers) + { + writer.Dispose(); + } + } + + public Task WriteLineAsync(string line) + { + foreach (var writer in writers) + { + writer.WriteLineAsync(line); + } + + return Task.FromResult(1); + } + + public async Task Flush() + { + foreach (var writer in writers) + { + await writer.Flush(); + } + } + } +} \ No newline at end of file diff --git a/Code/IPFilter/Cli/TextFilterWriter.cs b/Code/IPFilter/Cli/TextFilterWriter.cs index 8aec458..1612a41 100644 --- a/Code/IPFilter/Cli/TextFilterWriter.cs +++ b/Code/IPFilter/Cli/TextFilterWriter.cs @@ -10,6 +10,8 @@ namespace IPFilter.Cli { class TextFilterWriter : IFilterWriter { + static readonly Task success = Task.FromResult(1); + readonly FileInfo file; readonly TextWriter writer; readonly TempFile temp; @@ -18,7 +20,6 @@ public TextFilterWriter(string path) { file = new FileInfo(path); temp = new TempFile(); - writer = new StreamWriter( temp.File.Open(FileMode.Create, FileAccess.Write, FileShare.Read)); } @@ -28,16 +29,17 @@ public void Dispose() temp?.Dispose(); } - public async Task WriteLineAsync(string line) + public Task WriteLineAsync(string line) { var parsed = DatParser.ParseLine(line); if (parsed == null) { - Trace.TraceWarning("Invalid line: " + line); - return; + if(!line.StartsWith("#")) Trace.TraceWarning("Invalid line: " + line); + return success; } - await writer.WriteLineAsync(parsed); + writer.WriteLine(parsed); + return success; } public async Task Flush() @@ -61,11 +63,13 @@ public async Task Flush() var list = FilterCollection.Merge(filters); // Flush the list out - using (var stream = file.Open(FileMode.Create, FileAccess.Write, FileShare.Read)) - using (var listWriter = new EmuleWriter(stream)) - { - await listWriter.Write(list, null); - } + using var stream = file.Open(FileMode.Create, FileAccess.Write, FileShare.Read); + + // Determine the desired format from the file extension + var format = file.Extension.StartsWith(".p2p") ? FilterFileFormat.P2p : FilterFileFormat.Emule; + //using var listWriter = (format == FilterFileFormat.Emule ? new EmuleWriter(stream) : (IFormatWriter)new BitTorrentWriter(stream)); + using var listWriter = new P2pWriter(stream); + await listWriter.Write(list, null); } } } \ No newline at end of file diff --git a/Code/IPFilter/Core/FileInfoExtensions.cs b/Code/IPFilter/Core/FileInfoExtensions.cs index f97c10d..7db51e6 100644 --- a/Code/IPFilter/Core/FileInfoExtensions.cs +++ b/Code/IPFilter/Core/FileInfoExtensions.cs @@ -21,7 +21,7 @@ public static void SafeDelete(this FileInfo file) } catch (Exception ex) { - Trace.TraceWarning($"Couldn't delete temporary file {file.FullName}: {ex}"); + Trace.TraceWarning($"Couldn't delete file {file.FullName}: {ex}"); } } diff --git a/Code/IPFilter/EntryPoint.cs b/Code/IPFilter/EntryPoint.cs index fa6454d..247c499 100644 --- a/Code/IPFilter/EntryPoint.cs +++ b/Code/IPFilter/EntryPoint.cs @@ -87,8 +87,6 @@ internal static void Main(string[] args) } else { - - CurateList(args).GetAwaiter().GetResult(); //Trace.TraceWarning("Invalid command line: " + commandLine); } @@ -194,7 +192,14 @@ static async Task CurateList(string[] args) // Configure outputs if (options.Outputs.Count > 0) { - context.Filter = new TextFilterWriter(options.Outputs.First()); + if (options.Outputs.Count > 1) + { + context.Filter = new MultiFilterWriter(options.Outputs.Select(x => new TextFilterWriter(x))); + } + else + { + context.Filter = new TextFilterWriter(options.Outputs.First()); + } } else { diff --git a/Code/IPFilter/Formats/EmuleWriter.cs b/Code/IPFilter/Formats/EmuleWriter.cs index 3f3c25a..e6daeee 100644 --- a/Code/IPFilter/Formats/EmuleWriter.cs +++ b/Code/IPFilter/Formats/EmuleWriter.cs @@ -11,7 +11,7 @@ namespace IPFilter.Formats /// /// Writes out ipfilter.dat for eMule, which aligns the data in space-padded columns e.g."1.2.8.0 - 1.2.8.255 , 0 , Some organization" /// - class EmuleWriter : IDisposable + class EmuleWriter : IFormatWriter { readonly Stream stream; @@ -54,13 +54,16 @@ public async Task Write(IList entries, IProgress pro sb.Append(entry.Description); - await writer.WriteLineAsync(sb.ToString()); + writer.WriteLine(sb.ToString()); if (progress == null) continue; var percent = (int) Math.Floor((double) (i / entries.Count * 100)); progress.Report(new ProgressModel(UpdateState.Decompressing, "Updating eMule...", percent)); } + progress?.Report(new ProgressModel(UpdateState.Decompressing, "Flushing...", 100)); + await writer.FlushAsync(); + progress?.Report(new ProgressModel(UpdateState.Decompressing, "Updated eMule.", 100)); } } diff --git a/Code/IPFilter/Formats/FilterFileFormat.cs b/Code/IPFilter/Formats/FilterFileFormat.cs new file mode 100644 index 0000000..fab443a --- /dev/null +++ b/Code/IPFilter/Formats/FilterFileFormat.cs @@ -0,0 +1,13 @@ +using System; + +namespace IPFilter.Formats +{ + [Flags] + enum FilterFileFormat + { + None = 0, + Emule = 1, + P2p = 2, + P2b = 4 + } +} \ No newline at end of file diff --git a/Code/IPFilter/Formats/IFormatWriter.cs b/Code/IPFilter/Formats/IFormatWriter.cs new file mode 100644 index 0000000..d734c0e --- /dev/null +++ b/Code/IPFilter/Formats/IFormatWriter.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using IPFilter.Models; + +namespace IPFilter.Formats +{ + interface IFormatWriter : IDisposable + { + Task Write(IList entries, IProgress progress); + } +} \ No newline at end of file diff --git a/Code/IPFilter/Formats/BitTorrentWriter.cs b/Code/IPFilter/Formats/P2pWriter.cs similarity index 96% rename from Code/IPFilter/Formats/BitTorrentWriter.cs rename to Code/IPFilter/Formats/P2pWriter.cs index 621eb6b..c710848 100644 --- a/Code/IPFilter/Formats/BitTorrentWriter.cs +++ b/Code/IPFilter/Formats/P2pWriter.cs @@ -11,7 +11,7 @@ namespace IPFilter.Formats /// /// Writes out eMule DAT format, 0-padding integers to 3 digits. e.g."001.009.096.105 - 001.009.096.105 , 000 , Some organization" /// - class BitTorrentWriter : IDisposable + class P2pWriter : IFormatWriter { readonly Stream stream; @@ -20,7 +20,7 @@ public void Dispose() stream.Dispose(); } - public BitTorrentWriter(Stream stream) + public P2pWriter(Stream stream) { this.stream = stream; } diff --git a/Code/IPFilter/Native/ShellLinkHelper.cs b/Code/IPFilter/Native/ShellLinkHelper.cs new file mode 100644 index 0000000..4b140ca --- /dev/null +++ b/Code/IPFilter/Native/ShellLinkHelper.cs @@ -0,0 +1,179 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace IPFilter.Native +{ + class ShellLinkHelper + { + [Flags()] + enum SLGP_FLAGS + { + /// Retrieves the standard short (8.3 format) file name + SLGP_SHORTPATH = 0x1, + /// Retrieves the Universal Naming Convention (UNC) path name of the file + SLGP_UNCPRIORITY = 0x2, + /// Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded + SLGP_RAWPATH = 0x4 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct WIN32_FIND_DATAW + { + public uint dwFileAttributes; + public long ftCreationTime; + public long ftLastAccessTime; + public long ftLastWriteTime; + public uint nFileSizeHigh; + public uint nFileSizeLow; + public uint dwReserved0; + public uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + public string cAlternateFileName; + } + + [Flags()] + enum SLR_FLAGS + { + /// + /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set, + /// the high-order word of fFlags can be set to a time-out value that specifies the + /// maximum amount of time to be spent resolving the link. The function returns if the + /// link cannot be resolved within the time-out duration. If the high-order word is set + /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds + /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out + /// duration, in milliseconds. + /// + SLR_NO_UI = 0x1, + /// Obsolete and no longer used + SLR_ANY_MATCH = 0x2, + /// If the link object has changed, update its path and list of identifiers. + /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine + /// whether or not the link object has changed. + SLR_UPDATE = 0x4, + /// Do not update the link information + SLR_NOUPDATE = 0x8, + /// Do not execute the search heuristics + SLR_NOSEARCH = 0x10, + /// Do not use distributed link tracking + SLR_NOTRACK = 0x20, + /// Disable distributed link tracking. By default, distributed link tracking tracks + /// removable media across multiple devices based on the volume name. It also uses the + /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter + /// has changed. Setting SLR_NOLINKINFO disables both types of tracking. + SLR_NOLINKINFO = 0x40, + /// Call the Microsoft Windows Installer + SLR_INVOKE_MSI = 0x80 + } + + + /// The IShellLink interface allows Shell links to be created, modified, and resolved + [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] + interface IShellLinkW + { + /// Retrieves the path and file name of a Shell link object + void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); + /// Retrieves the list of item identifiers for a Shell link object + void GetIDList(out IntPtr ppidl); + /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. + void SetIDList(IntPtr pidl); + /// Retrieves the description string for a Shell link object + void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); + /// Sets the description for a Shell link object. The description can be any application-defined string + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + /// Retrieves the name of the working directory for a Shell link object + void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + /// Sets the name of the working directory for a Shell link object + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + /// Retrieves the command-line arguments associated with a Shell link object + void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + /// Sets the command-line arguments for a Shell link object + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + /// Retrieves the hot key for a Shell link object + void GetHotkey(out short pwHotkey); + /// Sets a hot key for a Shell link object + void SetHotkey(short wHotkey); + /// Retrieves the show command for a Shell link object + void GetShowCmd(out int piShowCmd); + /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. + void SetShowCmd(int iShowCmd); + /// Retrieves the location (path and index) of the icon for a Shell link object + void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + /// Sets the location (path and index) of the icon for a Shell link object + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + /// Sets the relative path to the Shell link object + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + /// Attempts to find the target of a Shell link, even if it has been moved or renamed + void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); + /// Sets the path and file name of a Shell link object + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + + } + + [ComImport, Guid("0000010c-0000-0000-c000-000000000046"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPersist + { + [PreserveSig] + void GetClassID(out Guid pClassID); + } + + + [ComImport, Guid("0000010b-0000-0000-C000-000000000046"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPersistFile : IPersist + { + new void GetClassID(out Guid pClassID); + [PreserveSig] + int IsDirty(); + + [PreserveSig] + void Load([In, MarshalAs(UnmanagedType.LPWStr)] + string pszFileName, uint dwMode); + + [PreserveSig] + void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, + [In, MarshalAs(UnmanagedType.Bool)] bool fRemember); + + [PreserveSig] + void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName); + + [PreserveSig] + void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName); + } + + const uint STGM_READ = 0; + const int MAX_PATH = 260; + + // CLSID_ShellLink from ShlGuid.h + [ + ComImport(), + Guid("00021401-0000-0000-C000-000000000046") + ] + public class ShellLink + { + } + + + public static string ResolveShortcut(string filename) + { + var link = new ShellLink(); + + try + { + ((IPersistFile)link).Load(filename, STGM_READ); + var sb = new StringBuilder(MAX_PATH); + var data = new WIN32_FIND_DATAW(); + ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0); + return sb.ToString(); + } + finally + { + Marshal.FinalReleaseComObject(link); + } + } + } +} diff --git a/Code/IPFilter/Services/Config.cs b/Code/IPFilter/Services/Config.cs index e0b5fc6..33657ab 100644 --- a/Code/IPFilter/Services/Config.cs +++ b/Code/IPFilter/Services/Config.cs @@ -94,6 +94,8 @@ public class UpdateSettings public bool isDisabled { get; set; } public bool isPreReleaseEnabled { get; set; } + + public bool isCleanupDisabled { get; set; } } public class TaskSettings diff --git a/Code/IPFilter/ViewModels/MainWindowViewModel.cs b/Code/IPFilter/ViewModels/MainWindowViewModel.cs index d83c59b..1f51fb7 100644 --- a/Code/IPFilter/ViewModels/MainWindowViewModel.cs +++ b/Code/IPFilter/ViewModels/MainWindowViewModel.cs @@ -1,3 +1,5 @@ +using IPFilter.Cli; + namespace IPFilter.ViewModels { using System; @@ -377,6 +379,41 @@ async Task CheckForUpdates() Trace.TraceError("Failed to remove old ClickOnce app: " + ex); } + var applicationDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "IPFilter"); + var installerDir = new DirectoryInfo(Path.Combine(applicationDirectory, "installer")); + + // Detect the current running version. The ProductVersion contains the informational, semantic version e.g. "3.0.0-beta" + var versionInfo = Process.GetCurrentProcess().MainModule.FileVersionInfo; + var currentVersion = new SemanticVersion(versionInfo.ProductVersion); + + // Remove any old installers + try + { + if (!installerDir.Exists) + { + installerDir.Create(); + } + else if (!Config.Default.settings.update.isCleanupDisabled) + { + // Don't delete the MSI for the current installed version + var currentMsiName = "IPFilter." + currentVersion.ToNormalizedString() + ".msi"; + + // Scan the directory for all installers + foreach (var fileInfo in installerDir.GetFiles("IPFilter.*.msi")) + { + // Don't remove the installer for the installed version + if (fileInfo.Name.Equals(currentMsiName, StringComparison.OrdinalIgnoreCase)) continue; + + Trace.TraceInformation("Removing cached installer: " + fileInfo.Name); + fileInfo.SafeDelete(); + } + } + } + catch (Exception ex) + { + Trace.TraceError("Couldn't clean up old installers: " + ex); + } + Trace.TraceInformation("Checking for software updates..."); progress.Report(new ProgressModel(UpdateState.Downloading, "Checking for software updates...", -1)); @@ -385,10 +422,6 @@ async Task CheckForUpdates() var result = await updater.CheckForUpdateAsync(Config.Default.settings.update.isPreReleaseEnabled); if (result == null) return; - // The ProductVersion contains the informational, semantic version e.g. "3.0.0-beta" - var versionInfo = Process.GetCurrentProcess().MainModule.FileVersionInfo; - var currentVersion = new SemanticVersion(versionInfo.ProductVersion); - var latestVersion = new SemanticVersion(result.Version); Update.IsUpdateAvailable = latestVersion > currentVersion; @@ -423,7 +456,7 @@ async Task CheckForUpdates() { using (var process = new Process()) { - process.StartInfo = new ProcessStartInfo("https://davidmoore.github.io/ipfilter/") + process.StartInfo = new ProcessStartInfo("https://www.ipfilter.app/") { UseShellExecute = true }; @@ -434,7 +467,8 @@ async Task CheckForUpdates() } } - var msiPath = Path.Combine(Path.GetTempPath(), "IPFilter.msi"); + // Download the MSI to the installer directory + var msiPath = Path.Combine(installerDir.FullName, "IPFilter." + Update.AvailableVersion + ".msi"); // Download the installer using (var handler = new WebRequestHandler()) @@ -458,7 +492,7 @@ async Task CheckForUpdates() double bytesDownloaded = 0; using(var stream = await response.Content.ReadAsStreamAsync()) - using(var msi = File.Open( msiPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + using(var msi = File.Open(msiPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { var buffer = new byte[65535 * 4]; @@ -500,7 +534,8 @@ async Task CheckForUpdates() var sb = new StringBuilder("msiexec.exe "); // Enable logging for the installer - sb.AppendFormat(" /l*v \"{0}\"", Path.Combine(Path.GetTempPath(), "IPFilter.log")); + var installLog = Path.Combine(applicationDirectory, "install.log"); + sb.AppendFormat(" /l*v \"{0}\"", installLog); sb.AppendFormat(" /i \"{0}\"", msiPath); diff --git a/Code/IPFilter/Views/MainWindow.xaml b/Code/IPFilter/Views/MainWindow.xaml index 1cdbbd6..e3e4e64 100644 --- a/Code/IPFilter/Views/MainWindow.xaml +++ b/Code/IPFilter/Views/MainWindow.xaml @@ -44,11 +44,11 @@ - Support + Support - View Log - Options + View Log + Options diff --git a/Code/IPFilter/Views/MainWindow.xaml.cs b/Code/IPFilter/Views/MainWindow.xaml.cs index 58680f3..38a521e 100644 --- a/Code/IPFilter/Views/MainWindow.xaml.cs +++ b/Code/IPFilter/Views/MainWindow.xaml.cs @@ -36,6 +36,12 @@ public MainWindow() Closing += OnClosing; + Loaded += (sender, args) => + { + helper.EnsureHandle(); + Win32Api.BringToFront(helper.Handle); + }; + Activated += (sender, args) => { helper.EnsureHandle(); diff --git a/Tests/UnitTests/IPFilter.Tests/Apps/DelugeTests.cs b/Tests/UnitTests/IPFilter.Tests/Apps/DelugeTests.cs new file mode 100644 index 0000000..49ff1a2 --- /dev/null +++ b/Tests/UnitTests/IPFilter.Tests/Apps/DelugeTests.cs @@ -0,0 +1,28 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Web.Script.Serialization; +namespace IPFilter.Tests.Apps +{ + [TestClass] + public class DelugeTests + { + internal static readonly JavaScriptSerializer serializer = new JavaScriptSerializer(); + + [TestMethod, Ignore] + public void ParseConfig() + { + var conf = "{\n \"file\": 1, \n \"format\": 1\n}{\n \"check_after_days\": 1, \n \"timeout\": 180, \n \"url\": \"https://github.com/DavidMoore/ipfilter/releases/download/lists/ipfilter.dat.gz\", \n \"try_times\": 3, \n \"list_size\": 3053919, \n \"last_update\": 1583488027.858, \n \"list_type\": \"\", \n \"list_compression\": \"\", \n \"load_on_start\": false\n}"; + + + var obj = serializer.DeserializeObject(conf); + + Assert.IsNotNull(obj); + + } + + class DelugeSerializer + { + + } + } +} diff --git a/Tests/UnitTests/IPFilter.Tests/Formats/UTorrentWriterTests.cs b/Tests/UnitTests/IPFilter.Tests/Formats/P2pWriterTests.cs similarity index 89% rename from Tests/UnitTests/IPFilter.Tests/Formats/UTorrentWriterTests.cs rename to Tests/UnitTests/IPFilter.Tests/Formats/P2pWriterTests.cs index 33298d8..c85c606 100644 --- a/Tests/UnitTests/IPFilter.Tests/Formats/UTorrentWriterTests.cs +++ b/Tests/UnitTests/IPFilter.Tests/Formats/P2pWriterTests.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Net; using System.Text; using System.Threading.Tasks; using IPFilter.Formats; @@ -12,7 +11,7 @@ namespace IPFilter.Tests.Formats { [TestClass] - public class UTorrentWriterTests + public class P2pWriterTests { [TestMethod] public async Task Write() @@ -27,7 +26,7 @@ public async Task Write() using (var stream = new MemoryStream()) { - using (var writer = new BitTorrentWriter(stream)) + using (var writer = new P2pWriter(stream)) { var progress = new Mock>(); await writer.Write(entries, progress.Object); diff --git a/Tests/UnitTests/IPFilter.Tests/IPFilter.Tests.csproj b/Tests/UnitTests/IPFilter.Tests/IPFilter.Tests.csproj index 5d093bd..1b6150b 100644 --- a/Tests/UnitTests/IPFilter.Tests/IPFilter.Tests.csproj +++ b/Tests/UnitTests/IPFilter.Tests/IPFilter.Tests.csproj @@ -49,8 +49,10 @@ + + @@ -58,7 +60,7 @@ - + @@ -68,6 +70,7 @@ + @@ -96,6 +99,7 @@ 1.4.0 +