├── .gitattributes
├── .github
├── FUNDING.yml
├── release.yml
└── workflows
│ ├── azure-static-web-apps.yml
│ ├── build.yml
│ └── nightly-lists.yml
├── .gitignore
├── CHANGELOG.md
├── Code
└── IPFilter
│ ├── App.ico
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── Apps
│ ├── ApplicationEnumerator.cs
│ ├── BitTorrentApplication.cs
│ ├── DelugeApplication.cs
│ ├── EmuleApplication.cs
│ ├── IApplication.cs
│ ├── QBitTorrent.cs
│ └── UTorrentApplication.cs
│ ├── Cli
│ ├── CachingFileFetcher.cs
│ ├── CryptographicAlgorithmExtensions.cs
│ ├── FileFetcher.cs
│ ├── FileNode.cs
│ ├── GZipNode.cs
│ ├── INode.cs
│ ├── INodeVisitor.cs
│ ├── JsonNode.cs
│ ├── MultiFilterWriter.cs
│ ├── NodeVisitor.cs
│ ├── Options.cs
│ ├── TextFilterWriter.cs
│ ├── TextNode.cs
│ ├── UriNode.cs
│ └── ZipNode.cs
│ ├── Commands
│ ├── ScheduledTaskCommand.cs
│ └── Service.cs
│ ├── Core
│ ├── Benchmark.cs
│ ├── Blocklist.cs
│ ├── BlocklistBundle.cs
│ ├── BlocklistProvider.cs
│ ├── DataFormat.cs
│ ├── FileInfoExtensions.cs
│ ├── FileSystem.cs
│ ├── FilterCollection.cs
│ ├── FilterContext.cs
│ ├── FormatDetector.cs
│ ├── IFileSystem.cs
│ ├── IFilterEnumerator.cs
│ ├── IFilterWriter.cs
│ ├── IPAddressComparer.cs
│ ├── IUriResolver.cs
│ ├── NullWriter.cs
│ ├── StreamExtensions.cs
│ ├── TempFile.cs
│ ├── TempStream.cs
│ ├── UriResolver.cs
│ └── Win32Api.cs
│ ├── EntryPoint.cs
│ ├── FilterService.Designer.cs
│ ├── FilterService.cs
│ ├── FilterServiceInstaller.cs
│ ├── Formats
│ ├── Address.cs
│ ├── DatParser.cs
│ ├── EmuleWriter.cs
│ ├── FilterFileFormat.cs
│ ├── IFormatWriter.cs
│ ├── IpAddress.cs
│ └── P2pWriter.cs
│ ├── IPFilter.csproj
│ ├── ListProviders
│ ├── DefaultList.cs
│ ├── IMirrorProvider.cs
│ └── MirrorProvidersFactory.cs
│ ├── Logging
│ └── FileTraceListener.cs
│ ├── Models
│ ├── ApplicationDetectionResult.cs
│ ├── CompressionFormat.cs
│ ├── FileMirror.cs
│ ├── FilterDownloadResult.cs
│ ├── FilterEntry.cs
│ ├── FilterSource.cs
│ ├── FilterUpdateResult.cs
│ ├── ProgressModel.cs
│ ├── ProgressModelExtensions.cs
│ ├── UpdateInfo.cs
│ ├── UpdateModel.cs
│ └── UpdateState.cs
│ ├── Native
│ ├── PathHelper.cs
│ ├── ProcessInformation.cs
│ ├── ProcessManager.cs
│ ├── SafeLocalMemHandle.cs
│ ├── SecurityAttributes.cs
│ ├── ShellLinkHelper.cs
│ ├── StartupInfo.cs
│ └── Win32Api.cs
│ ├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ └── launchSettings.json
│ ├── Services
│ ├── CacheProvider.cs
│ ├── Config.cs
│ ├── CryptoHelper.cs
│ ├── DelegateTraceListener.cs
│ ├── Deployment
│ │ ├── ClickOnce
│ │ │ ├── ClickOnceRegistry.cs
│ │ │ ├── IUninstallStep.cs
│ │ │ ├── RemoveFiles.cs
│ │ │ ├── RemoveRegistryKeys.cs
│ │ │ ├── RemoveStartMenuEntry.cs
│ │ │ ├── RemoveUninstallEntry.cs
│ │ │ ├── UninstallInfo.cs
│ │ │ └── Uninstaller.cs
│ │ ├── DeploymentHelper.cs
│ │ ├── SemanticVersion.cs
│ │ └── Updater.cs
│ ├── DestinationPathsProvider.cs
│ ├── FilterDownloader.cs
│ ├── ICacheProvider.cs
│ └── PathSetting.cs
│ ├── ViewModels
│ ├── DelegateCommand.cs
│ ├── Interval.cs
│ ├── MainWindowViewModel.cs
│ ├── MessageBoxHelper.cs
│ └── OptionsViewModel.cs
│ ├── Views
│ ├── InverseConverter.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── OptionsWindow.xaml
│ └── OptionsWindow.xaml.cs
│ ├── ipfilter.json
│ └── lists.json
├── Directory.Build.props
├── IPFilter.sln
├── IPFilter.vsmdi
├── IPFilterUpdater.png
├── LICENSE.txt
├── Local.testsettings
├── README.md
├── Setup
├── Setup.IPFilter.CustomActions
│ ├── ClickOnceRegistry.cs
│ ├── CustomAction.config
│ ├── CustomAction.cs
│ ├── DeleteHelper.cs
│ ├── IO
│ │ ├── ErrorHelper.cs
│ │ ├── FileAttributes.cs
│ │ ├── FileSystemEnumerator.cs
│ │ ├── FileSystemInfoResultHandler.cs
│ │ ├── FileTime.cs
│ │ ├── FindData.cs
│ │ ├── Iterator.cs
│ │ ├── PathHelper.cs
│ │ ├── PathHelperMethods.cs
│ │ ├── SafeFindHandle.cs
│ │ ├── SearchData.cs
│ │ ├── SearchResult.cs
│ │ ├── SearchResultHandler.cs
│ │ ├── StringExtensions.cs
│ │ └── StringResultHandler.cs
│ ├── IUninstallStep.cs
│ ├── LoadLibraryExFlags.cs
│ ├── LoadLibraryException.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── RemoveFiles.cs
│ ├── RemoveRegistryKeys.cs
│ ├── RemoveStartMenuEntry.cs
│ ├── RemoveUninstallEntry.cs
│ ├── SafeLibraryHandle.cs
│ ├── Setup.IPFilter.CustomActions.csproj
│ ├── UninstallInfo.cs
│ ├── Uninstaller.cs
│ ├── Win32Api.cs
│ └── Win32Error.cs
└── Setup.IPFilter
│ ├── Product.wxs
│ └── Setup.IPFilter.wixproj
├── Tests
└── UnitTests
│ └── IPFilter.Tests
│ ├── AddressTests.cs
│ ├── Apps
│ └── DelugeTests.cs
│ ├── DestinationPathsProviderTests.cs
│ ├── FormatDetectorTests.cs
│ ├── Formats
│ ├── DatParserTests.cs
│ ├── EmuleWriterTests.cs
│ ├── FilterCollectionTests.cs
│ ├── FilterEntryComparerTests.cs
│ ├── IPAddressComparerTests.cs
│ ├── IpAddressTests.cs
│ └── P2pWriterTests.cs
│ ├── IPFilter.Tests.csproj
│ ├── ListMergerTests.cs
│ ├── PathHelperTests.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── Services
│ ├── ConfigTests.cs
│ └── SemanticVersionTests.cs
│ ├── ShellLinkHelperTests.cs
│ ├── StreamHelper.cs
│ ├── TestFilterData.cs
│ ├── UriResolverTests.cs
│ ├── ZipEnumeratorTests.cs
│ ├── ipfilter.dat
│ └── lists.json
├── TraceAndTestImpact.testsettings
└── install
└── Application Files
└── IPFilter.UI_1_0_0_10
├── IPFilter.UI.application
├── IPFilter.UI.exe.config.deploy
├── IPFilter.UI.exe.deploy
├── IPFilter.UI.exe.manifest
├── IPFilter.UI.pdb.deploy
├── IPFilter.dll.deploy
├── IPFilter.pdb.deploy
└── Ionic.Zip.Reduced.dll.deploy
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [DavidMoore] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: davidmoore # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | changelog:
2 | exclude:
3 | labels:
4 | - dependencies
5 | authors:
6 | - octocat
7 | categories:
8 | - title: Fixed 🛠
9 | labels:
10 | - bug
11 | - title: Added 🎉
12 | labels:
13 | - feature
14 | - title: Changed
15 | labels:
16 | - "*"
17 |
--------------------------------------------------------------------------------
/.github/workflows/azure-static-web-apps.yml:
--------------------------------------------------------------------------------
1 | name: Azure Static Web Apps CI/CD
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - docs
8 | pull_request:
9 | types: [opened, synchronize, reopened, closed]
10 | branches:
11 | - docs
12 | release:
13 | types: [published, edited]
14 | workflow_run:
15 | workflows:
16 | ["Nightly Lists"]
17 | types: [completed]
18 | branches:
19 | - 'master'
20 | - 'main'
21 |
22 | jobs:
23 | build_and_deploy_job:
24 | if: github.event_name == 'release' || github.event_name == 'push' || github.event_name == 'workflow_run' || (github.event_name == 'pull_request' && github.event.action != 'closed')
25 | runs-on: ubuntu-latest
26 | name: Build and Deploy Job
27 | steps:
28 | - uses: actions/checkout@v3
29 | with:
30 | submodules: true
31 | ref: docs
32 | - name: Build And Deploy
33 | id: builddeploy
34 | uses: Azure/static-web-apps-deploy@v1
35 | with:
36 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
37 | repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
38 | action: "upload"
39 | app_location: "/" # App source code path
40 | api_location: "" # Api source code path - optional
41 | output_location: "public" # Built app content directory - optional
42 |
43 | close_pull_request_job:
44 | if: github.event_name == 'pull_request' && github.event.action == 'closed'
45 | runs-on: ubuntu-latest
46 | name: Close Pull Request Job
47 | steps:
48 | - name: Close Pull Request
49 | id: closepullrequest
50 | uses: Azure/static-web-apps-deploy@v1
51 | with:
52 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
53 | action: "close"
54 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: 'Build & release'
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | version:
7 | description: 'Version'
8 | required: false
9 | type: string
10 |
11 | push:
12 | #branches:
13 | # - main
14 | tags:
15 | - '*'
16 | paths-ignore:
17 | - '.github/**'
18 |
19 | jobs:
20 | build:
21 | runs-on: windows-latest
22 | env:
23 | VERSION: ${{inputs.version || github.ref_name}}
24 | steps:
25 | - name: Checkout
26 | uses: actions/checkout@main
27 |
28 | - name: Restore
29 | run: |
30 | & "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\msbuild.exe" "/t:Restore" "/p:Configuration=Release" "/p:Version=$env:VERSION"
31 |
32 | - name: Build
33 | run: |
34 | & "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\msbuild.exe" "/p:Configuration=Release" "/p:Version=$env:VERSION"
35 |
36 | - name: Create Release
37 | env:
38 | GH_TOKEN: ${{ github.token }}
39 | run: |
40 | gh release create $env:VERSION --draft --prerelease --title "v$env:VERSION" --notes "Download and run **IPFilter.msi** to install. If you wish to just download and run the self-contained executable, you can download IPFilter.exe" ./Bin/IPFilter.msi ./Bin/IPFilter.exe
41 |
--------------------------------------------------------------------------------
/.github/workflows/nightly-lists.yml:
--------------------------------------------------------------------------------
1 | name: Nightly Lists
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '0 5 * * *'
7 |
8 | jobs:
9 | generate-list:
10 | runs-on: windows-latest
11 | name: Generate and upload lists
12 | env:
13 | GH_TOKEN: ${{ github.token }}
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Download release
17 | id: download-release
18 | shell: powershell
19 | run: |
20 | gh release download --pattern '*.exe'
21 | gh release download lists --pattern '*.json'
22 |
23 | Write-Host "Launching ipfilter..."
24 | .\IPFilter.exe https://raw.githubusercontent.com/DavidMoore/ipfilter/lists/default/lists.json '-o:ipfilter.dat'
25 |
26 | Write-Host "Waiting for ipfilter to finish..."
27 | Wait-Process -Name ipfilter
28 |
29 | ls
30 |
31 | Write-Host "Finished"
32 | exit 0
33 |
34 | - name: Archive
35 | shell: powershell
36 | run: |
37 |
38 | Write-Host "Creating ipfilter.dat.gz..."
39 | 7z a ipfilter.dat.gz ipfilter.dat
40 |
41 | Write-Host "Creating ipfilter.zip..."
42 | 7z a ipfilter.zip ipfilter.dat
43 |
44 | - name: Upload Release
45 | run: |
46 | gh release upload lists ipfilter.dat ipfilter.dat.gz ipfilter.zip --clobber
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2 | /Code/*/[Bb]in/
3 | /[Bb]in/
4 | [Oo]bj/
5 |
6 | # mstest test results
7 | TestResults
8 |
9 | ## Ignore Visual Studio temporary files, build results, and
10 | ## files generated by popular Visual Studio add-ons.
11 |
12 | # User-specific files
13 | *.suo
14 | *.user
15 | *.sln.docstates
16 |
17 | # Build results
18 | [Dd]ebug/
19 | [Rr]elease/
20 | x64/
21 | *_i.c
22 | *_p.c
23 | *.ilk
24 | *.meta
25 | *.obj
26 | *.pch
27 | *.pdb
28 | *.pgc
29 | *.pgd
30 | *.rsp
31 | *.sbr
32 | *.tlb
33 | *.tli
34 | *.tlh
35 | *.tmp
36 | *.log
37 | *.vspscc
38 | *.vssscc
39 | .builds
40 |
41 | # Visual C++ cache files
42 | ipch/
43 | *.aps
44 | *.ncb
45 | *.opensdf
46 | *.sdf
47 |
48 | # Visual Studio profiler
49 | *.psess
50 | *.vsp
51 | *.vspx
52 |
53 | # Guidance Automation Toolkit
54 | *.gpState
55 |
56 | # ReSharper is a .NET coding add-in
57 | _ReSharper*
58 |
59 | # Mindbench SASS cache
60 | .sass-cache/
61 |
62 | # NCrunch
63 | *.ncrunch*
64 | .*crunch*.local.xml
65 |
66 | # Installshield output folder
67 | [Ee]xpress
68 |
69 | # DocProject is a documentation generator add-in
70 | DocProject/buildhelp/
71 | DocProject/Help/*.HxT
72 | DocProject/Help/*.HxC
73 | DocProject/Help/*.hhc
74 | DocProject/Help/*.hhk
75 | DocProject/Help/*.hhp
76 | DocProject/Help/Html2
77 | DocProject/Help/html
78 |
79 | # Click-Once directory
80 | publish
81 |
82 | # Publish Web Output
83 | *.Publish.xml
84 |
85 | # NuGet Packages Directory
86 | packages/
87 |
88 | # Windows Azure Build Output
89 | csx
90 | *.build.csdef
91 |
92 | # Windows Store app package directory
93 | AppPackages/
94 |
95 | # Others
96 | sql
97 | TestResults
98 | [Tt]est[Rr]esult*
99 | *.Cache
100 | ClientBin
101 | [Ss]tyle[Cc]op.*
102 | ~$*
103 | *.dbmdl
104 | Generated_Code #added for RIA/Silverlight projects
105 |
106 | # Backup & report files from converting an old project file to a newer
107 | # Visual Studio version. Backup files are not needed, because we have git ;-)
108 | _UpgradeReport_Files/
109 | Backup*/
110 | UpgradeLog*.XML
111 |
112 | *.DotSettings
113 | /install
114 | /.vs
115 | /.idea
116 | *.binlog
117 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [Unreleased]
4 |
5 | ## [3.0.5-beta] - 2023-08-21
6 |
7 | ### Fixed
8 |
9 | * Options window should always enable OK and Cancel buttons (#74)
10 | * Fixed issue with deleting scheduled task after it's been disabled in options (#97)
11 |
12 | ## [3.0.4-beta] - 2023-08-19
13 |
14 | ### Fixed
15 |
16 | * Task Scheduler and Options fixes by @DavidMoore in https://github.com/DavidMoore/ipfilter/pull/95
17 | - Renamed "Save Settings" button in Options to "OK", and it automatically closes the window after saving (#74)
18 | - Scheduled task should be created or deleted according to the chosen option (#72)
19 | * The status / error message should auto-size to fit in the window (#67)
20 | * Corrected the URL in Add/Remove Programs entry
21 |
22 | ## [3.0.3-beta] - 2023-08-18
23 |
24 | ### Changed
25 | * Build & installer no longer signed with code signing certificate
26 | * Updated to .NET Framework 4.8
27 |
28 | ### Fixed
29 | * Fixed eMule alternative config locations #68
30 | * Fixed saving / loading of options including the scheduled task #89
31 | * Fix startup crash by @yfdyh000 in https://github.com/DavidMoore/ipfilter/pull/66
32 |
33 | ## Contributors
34 | * @DavidMoore
35 | * @yfdyh000 made their first contribution in https://github.com/DavidMoore/ipfilter/pull/66
36 |
37 | ## [3.0.2.9-beta]
38 |
39 | ### Changed
40 |
41 | - Changing the new upgrade logic
42 |
43 | ## [3.0.2.7-beta]
44 |
45 | ## [3.0.2.4-beta]
46 |
47 | ### Fixed
48 |
49 | - Fixing problem with empty paths when trying to detect installed torrent clients (#59)
50 |
51 | ## [3.0.2.3-beta] - 2019-08-30
52 |
53 | ### Fixed
54 |
55 | - Silent updating always installs 0 byte ipfilter.dat file (#58)
56 |
57 | ## [3.0.1.4-beta]
58 |
59 | [Unreleased]: https://github.com/DavidMoore/ipfilter/compare/3.0.5-beta...HEAD
60 | [3.0.5-beta]: https://github.com/DavidMoore/ipfilter/compare/3.0.4-beta...3.0.5-beta
61 | [3.0.4-beta]: https://github.com/DavidMoore/ipfilter/compare/3.0.3-beta...3.0.4-beta
62 | [3.0.3-beta]: https://github.com/DavidMoore/ipfilter/compare/3.0.2.9-beta...3.0.3-beta
63 | [3.0.2.9-beta]: https://github.com/DavidMoore/ipfilter/compare/3.0.2.7-beta...3.0.2.9-beta
64 | [3.0.2.7-beta]: https://github.com/DavidMoore/ipfilter/compare/3.0.2.4-beta...3.0.2.7-beta
65 | [3.0.2.4-beta]: https://github.com/DavidMoore/ipfilter/compare/3.0.2.3-beta...3.0.2.4-beta
66 | [3.0.2.3-beta]: https://github.com/DavidMoore/ipfilter/compare/3.0.1.4-beta...3.0.2.3-beta
67 | [3.0.1.4-beta]: https://github.com/DavidMoore/ipfilter/compare/3.0.0-beta1...3.0.1.4-beta
68 | [3.0.0-beta1]: https://github.com/DavidMoore/ipfilter/releases/tag/3.0.0-beta1
--------------------------------------------------------------------------------
/Code/IPFilter/App.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DavidMoore/ipfilter/96f15428b4fa4f21cc2bc4c8fe7d18f16ba3f381/Code/IPFilter/App.ico
--------------------------------------------------------------------------------
/Code/IPFilter/App.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/Code/IPFilter/App.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter
2 | {
3 | using System.Windows;
4 |
5 | ///
6 | /// Interaction logic for App.xaml
7 | ///
8 | public partial class App : Application
9 | {
10 | ///
11 | /// Raises the event.
12 | ///
13 | /// A that contains the event data.
14 | protected override void OnStartup(StartupEventArgs e)
15 | {
16 | base.OnStartup(e);
17 |
18 |
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Apps/ApplicationEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Apps
2 | {
3 | using System.Collections.Generic;
4 | using System.Threading.Tasks;
5 | using Models;
6 |
7 | public class ApplicationEnumerator
8 | {
9 | public async Task> GetInstalledApplications()
10 | {
11 | var results = new List();
12 |
13 | var applications = new List
14 | {
15 | new QBitTorrent(),
16 | new UTorrentApplication(),
17 | new BitTorrentApplication(),
18 | new EmuleApplication()
19 | };
20 |
21 | foreach (var application in applications)
22 | {
23 | var installed = await application.DetectAsync();
24 |
25 | if (installed != null && installed.IsPresent)
26 | {
27 | results.Add(installed);
28 | }
29 | }
30 |
31 | return results;
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Apps/BitTorrentApplication.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DavidMoore/ipfilter/96f15428b4fa4f21cc2bc4c8fe7d18f16ba3f381/Code/IPFilter/Apps/BitTorrentApplication.cs
--------------------------------------------------------------------------------
/Code/IPFilter/Apps/EmuleApplication.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using IPFilter.Formats;
7 | using IPFilter.Models;
8 | using Microsoft.Win32;
9 |
10 | namespace IPFilter.Apps
11 | {
12 | class EmuleApplication : IApplication
13 | {
14 | Environment.SpecialFolder configFolder;
15 |
16 | protected virtual string RegistryKeyName => @"Software\eMule";
17 |
18 | protected virtual string DefaultDisplayName => "eMule";
19 |
20 | protected virtual string FolderName => "eMule";
21 |
22 | public Task DetectAsync()
23 | {
24 | using var key = Registry.CurrentUser.OpenSubKey(RegistryKeyName, false);
25 | if (key == null) return Task.FromResult(ApplicationDetectionResult.NotFound());
26 |
27 | var installLocation = (string)key.GetValue("Install Path");
28 | if (string.IsNullOrWhiteSpace(installLocation)) return Task.FromResult(ApplicationDetectionResult.NotFound());
29 |
30 | var result = ApplicationDetectionResult.Create(this, DefaultDisplayName, installLocation);
31 |
32 | var exe = new FileInfo(Path.Combine(installLocation, "emule.exe"));
33 | if (!exe.Exists)
34 | {
35 | result.IsPresent = false;
36 | }
37 | else
38 | {
39 | var version = FileVersionInfo.GetVersionInfo(exe.FullName);
40 | result.Description = version.ProductName;
41 | result.Version = version.FileVersion;
42 | }
43 |
44 | // eMule can be configured to store config in the application folder or program data, instead of app data
45 | var useSharedConfigValue = key.GetValue("UsePublicUserDirectories") ?? 0;
46 | configFolder = (int) useSharedConfigValue switch
47 | {
48 | 1 => Environment.SpecialFolder.CommonApplicationData,
49 | 2 => Environment.SpecialFolder.ProgramFilesX86,
50 | _ => Environment.SpecialFolder.LocalApplicationData
51 | };
52 |
53 | return Task.FromResult(result);
54 | }
55 |
56 | public async Task UpdateFilterAsync(FilterDownloadResult filter, CancellationToken cancellationToken, IProgress progress)
57 | {
58 | var basePath = Environment.GetFolderPath( configFolder, Environment.SpecialFolderOption.Create);
59 | var destinationPath = Path.Combine(basePath, FolderName, "config", "ipfilter.dat");
60 |
61 | Trace.TraceInformation("Writing filter to " + destinationPath);
62 | using var destination = File.Open(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None);
63 | using var writer = new EmuleWriter(destination);
64 | await writer.Write(filter.Entries, progress);
65 |
66 | return new FilterUpdateResult { FilterTimestamp = filter.FilterTimestamp };
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Apps/IApplication.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Apps
2 | {
3 | using System;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using Models;
7 |
8 | public interface IApplication
9 | {
10 | Task DetectAsync();
11 |
12 | Task UpdateFilterAsync(FilterDownloadResult filter, CancellationToken cancellationToken, IProgress progress);
13 | }
14 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Apps/UTorrentApplication.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DavidMoore/ipfilter/96f15428b4fa4f21cc2bc4c8fe7d18f16ba3f381/Code/IPFilter/Apps/UTorrentApplication.cs
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/CachingFileFetcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Security.Cryptography;
4 | using System.Threading.Tasks;
5 | using IPFilter.Core;
6 |
7 | namespace IPFilter.Cli
8 | {
9 | class CachingFileFetcher : FileFetcher
10 | {
11 | static readonly MD5 hasher = MD5.Create();
12 |
13 | readonly DirectoryInfo path;
14 |
15 | public CachingFileFetcher()
16 | {
17 | var basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"ipfilter\cache\");
18 | path = new DirectoryInfo(basePath );
19 | if (!path.Exists) path.Create();
20 | }
21 |
22 | public override async Task Get(Uri uri, FilterContext context)
23 | {
24 | // The path to the cached version of the file (which may or may not exist, depending on if it's
25 | // already been downloaded and cached before).
26 | var cachePath = GetCachePath(uri);
27 |
28 | if (uri.IsFile)
29 | {
30 | return await GetLocalFile(uri, context, cachePath);
31 | }
32 |
33 | return await GetRemoteFile(uri, context, cachePath);
34 | }
35 |
36 | FileInfo GetCachePath(Uri uri)
37 | {
38 | var hash = hasher.ComputeHash(uri);
39 | var cachePath = Path.Combine(path.FullName, hash);
40 | return new FileInfo(cachePath);
41 | }
42 |
43 | public override void Dispose()
44 | {
45 | base.Dispose();
46 | hasher?.Dispose();
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/CryptographicAlgorithmExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 | using System.Text;
4 |
5 | namespace IPFilter.Cli
6 | {
7 | static class CryptographicAlgorithmExtensions
8 | {
9 | public static string ComputeHash(this HashAlgorithm hashAlgorithm, object value)
10 | {
11 | return ComputeHash(hashAlgorithm, value.ToString());
12 | }
13 |
14 | public static string ComputeHash(this HashAlgorithm hashAlgorithm, string value)
15 | {
16 | return BitConverter.ToString(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(value)))
17 | .Replace("-", "")
18 | .ToLowerInvariant();
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/FileNode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using IPFilter.Core;
6 |
7 | namespace IPFilter.Cli
8 | {
9 | class FileNode : INode
10 | {
11 | readonly FileInfo file;
12 |
13 | public FileNode(FileInfo file)
14 | {
15 | this.file = new FileInfo(file.FullName);
16 | }
17 |
18 | public string FullName => file.FullName;
19 |
20 | internal async Task DetectFormat()
21 | {
22 | using (var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
23 | {
24 | return await FormatDetector.DetectFormat(stream);
25 | }
26 | }
27 |
28 | public async Task Accept(INodeVisitor visitor)
29 | {
30 | // Find the file type
31 | var fileType = await DetectFormat();
32 |
33 | switch (fileType)
34 | {
35 | case DataFormat.GZip:
36 | await visitor.Visit(new GZipNode(file));
37 | break;
38 |
39 | case DataFormat.Zip:
40 | await visitor.Visit(new ZipNode(file));
41 | break;
42 |
43 | case DataFormat.Json:
44 | await visitor.Visit(new JsonNode(file));
45 | break;
46 |
47 | case DataFormat.Text:
48 | await visitor.Visit(new TextNode(file));
49 | break;
50 |
51 | case DataFormat.Binary:
52 | default:
53 | break;
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/GZipNode.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.IO.Compression;
3 | using System.Threading.Tasks;
4 |
5 | namespace IPFilter.Cli
6 | {
7 | ///
8 | /// Decompresses a GZipped file and processes the contents.
9 | ///
10 | class GZipNode : INode
11 | {
12 | readonly FileInfo file;
13 |
14 | public GZipNode(FileInfo file)
15 | {
16 | this.file = new FileInfo(file.FullName);
17 | }
18 |
19 | public async Task Accept(INodeVisitor visitor)
20 | {
21 | // Extract the file to a temporary file, and then parse that.
22 | using (var tempFile = new TempFile())
23 | {
24 | // Extract to the temporary file
25 | using (var tempStream = tempFile.OpenWrite())
26 | using (var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
27 | using (var gzipFile = new GZipStream(stream, CompressionMode.Decompress, false))
28 | {
29 | await gzipFile.CopyToAsync(tempStream);
30 | }
31 |
32 | // Process the extracted file
33 | await visitor.Visit(new FileNode(tempFile.File));
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/INode.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace IPFilter.Cli
4 | {
5 | public interface INode
6 | {
7 | Task Accept(INodeVisitor visitor);
8 | }
9 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/INodeVisitor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using IPFilter.Core;
4 |
5 | namespace IPFilter.Cli
6 | {
7 | public interface INodeVisitor : IDisposable
8 | {
9 | FilterContext Context { get; }
10 |
11 | Task Visit(T node) where T : INode;
12 | }
13 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/JsonNode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Runtime.Serialization.Json;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using IPFilter.Core;
8 |
9 | namespace IPFilter.Cli
10 | {
11 | class JsonNode : INode
12 | {
13 | readonly FileInfo file;
14 | static readonly DataContractJsonSerializer serializer;
15 |
16 | static JsonNode()
17 | {
18 | var settings = new DataContractJsonSerializerSettings
19 | {
20 | KnownTypes = new []{typeof(BlocklistBundle), typeof(BlocklistProvider), typeof(Blocklist)}
21 | };
22 |
23 | serializer = new DataContractJsonSerializer(typeof(BlocklistBundle), settings);
24 | }
25 |
26 | public JsonNode(FileInfo file)
27 | {
28 | this.file = new FileInfo(file.FullName);
29 | }
30 |
31 | public async Task Accept(INodeVisitor visitor)
32 | {
33 | BlocklistBundle bundle = null;
34 |
35 | var json = await file.ReadAllText();
36 |
37 | using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
38 | {
39 | try
40 | {
41 | bundle = (BlocklistBundle) serializer.ReadObject(stream);
42 | }
43 | catch (Exception ex)
44 | {
45 | Trace.TraceWarning("Couldn't read list json: " + ex);
46 | }
47 | }
48 |
49 | if (bundle == null)
50 | {
51 | Trace.TraceWarning($"Couldn't read JSON file {file.FullName}");
52 | return;
53 | }
54 |
55 | foreach (var provider in bundle.Lists)
56 | {
57 | if (visitor.Context.CancellationToken.IsCancellationRequested) return;
58 |
59 | if (provider?.Lists == null) continue;
60 |
61 | foreach (var list in provider.Lists)
62 | {
63 | var uri = ResolveUri(list, provider);
64 | if (uri == null) continue;
65 | if (visitor.Context.CancellationToken.IsCancellationRequested) return;
66 |
67 | // Process this list
68 | await visitor.Visit(new UriNode(uri));
69 | }
70 | }
71 | }
72 |
73 |
74 | Uri ResolveUri(Blocklist list, BlocklistProvider provider)
75 | {
76 | var uri = list.Uri;
77 |
78 | if (uri != null && uri.IsAbsoluteUri) return uri;
79 |
80 | if (provider.BaseUri == null)
81 | {
82 | Trace.TraceWarning("Skipping {0} as it doesn't have a full URI, and parent {1} has no base URI we can use.", list.Name, provider.Name);
83 | return null;
84 | }
85 |
86 | var resolved = string.Format(provider.BaseUri.ToString(), uri?.ToString() ?? list.Id);
87 | uri = new Uri(provider.BaseUri, new Uri(resolved));
88 |
89 | return uri;
90 | }
91 | }
92 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/MultiFilterWriter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using IPFilter.Core;
5 |
6 | namespace IPFilter.Cli
7 | {
8 | class MultiFilterWriter : IFilterWriter
9 | {
10 | readonly IList writers;
11 |
12 | public MultiFilterWriter(IEnumerable writers)
13 | {
14 | this.writers = writers.ToList();
15 | }
16 |
17 | public void Dispose()
18 | {
19 | foreach (var writer in writers)
20 | {
21 | writer.Dispose();
22 | }
23 | }
24 |
25 | public Task WriteLineAsync(string line)
26 | {
27 | foreach (var writer in writers)
28 | {
29 | writer.WriteLineAsync(line);
30 | }
31 |
32 | return Task.FromResult(1);
33 | }
34 |
35 | public async Task Flush()
36 | {
37 | foreach (var writer in writers)
38 | {
39 | await writer.Flush();
40 | }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/NodeVisitor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using IPFilter.Core;
4 |
5 | namespace IPFilter.Cli
6 | {
7 | class NodeVisitor : INodeVisitor
8 | {
9 | public NodeVisitor() : this(new FilterContext()) {}
10 |
11 | public NodeVisitor(FilterContext context)
12 | {
13 | Context = context;
14 | }
15 |
16 | public FilterContext Context { get; }
17 |
18 | public Task Visit(T node) where T : INode
19 | {
20 | Console.WriteLine("Visiting " + node);
21 | return node.Accept(this);
22 | }
23 |
24 | public void Dispose()
25 | {
26 | Context?.Dispose();
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/Options.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace IPFilter.Cli
5 | {
6 | class Options
7 | {
8 | public Options()
9 | {
10 | Inputs = new List();
11 | Outputs = new List();
12 | }
13 |
14 | public static Options Parse(params string[] args)
15 | {
16 | var options = new Options();
17 |
18 | if (args != null)
19 | {
20 | foreach (var arg in args)
21 | {
22 | // Skip empty arguments
23 | if (arg == null) continue;
24 | var trimmed = arg.Trim();
25 | if( trimmed.Length == 0) continue;
26 |
27 | if (arg.StartsWith("o:", StringComparison.OrdinalIgnoreCase))
28 | {
29 | options.Outputs.Add(arg.Substring(2));
30 | }
31 | else
32 | {
33 | options.Inputs.Add(arg);
34 | }
35 | }
36 | }
37 |
38 | return options;
39 | }
40 |
41 | public IList Inputs { get; set; }
42 |
43 | public IList Outputs { get; set; }
44 | }
45 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/TextFilterWriter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Threading.Tasks;
5 | using IPFilter.Core;
6 | using IPFilter.Formats;
7 | using IPFilter.Models;
8 |
9 | namespace IPFilter.Cli
10 | {
11 | class TextFilterWriter : IFilterWriter
12 | {
13 | static readonly Task success = Task.FromResult(1);
14 |
15 | readonly FileInfo file;
16 | readonly TextWriter writer;
17 | readonly TempFile temp;
18 |
19 | public TextFilterWriter(string path)
20 | {
21 | file = new FileInfo(path);
22 | temp = new TempFile();
23 | writer = new StreamWriter( temp.File.Open(FileMode.Create, FileAccess.Write, FileShare.Read));
24 | }
25 |
26 | public void Dispose()
27 | {
28 | writer?.Dispose();
29 | temp?.Dispose();
30 | }
31 |
32 | public Task WriteLineAsync(string line)
33 | {
34 | var parsed = DatParser.ParseLine(line);
35 | if (parsed == null)
36 | {
37 | if(!line.StartsWith("#")) Trace.TraceWarning("Invalid line: " + line);
38 | return success;
39 | }
40 |
41 | writer.WriteLine(parsed);
42 | return success;
43 | }
44 |
45 | public async Task Flush()
46 | {
47 | await writer.FlushAsync();
48 | writer.Dispose();
49 |
50 | var filters = new List();
51 |
52 | using(var input = new StreamReader(temp.OpenShareableRead()))
53 | {
54 | string line;
55 | while ((line = await input.ReadLineAsync()) != null)
56 | {
57 | var filter = DatParser.ParseEntry(line);
58 | if( filter != null) filters.Add(filter);
59 | }
60 | }
61 |
62 | // Sort and merge the list
63 | var list = FilterCollection.Merge(filters);
64 |
65 | // Flush the list out
66 | using var stream = file.Open(FileMode.Create, FileAccess.Write, FileShare.Read);
67 |
68 | // Determine the desired format from the file extension
69 | var format = file.Extension.StartsWith(".p2p") ? FilterFileFormat.P2p : FilterFileFormat.Emule;
70 | //using var listWriter = (format == FilterFileFormat.Emule ? new EmuleWriter(stream) : (IFormatWriter)new BitTorrentWriter(stream));
71 | using var listWriter = new P2pWriter(stream);
72 | await listWriter.Write(list, null);
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/TextNode.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using System.Threading.Tasks;
4 |
5 | namespace IPFilter.Cli
6 | {
7 | class TextNode : INode
8 | {
9 | readonly FileInfo file;
10 |
11 | public TextNode(FileInfo file)
12 | {
13 | this.file = new FileInfo(file.FullName);
14 | }
15 |
16 | public async Task Accept(INodeVisitor visitor)
17 | {
18 | using(var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
19 | using(var reader = new StreamReader(stream, Encoding.UTF8))
20 | {
21 | if (visitor.Context.CancellationToken.IsCancellationRequested) return;
22 |
23 | var line = await reader.ReadLineAsync();
24 | while (line != null)
25 | {
26 | var trimmed = line.Trim();
27 | if (trimmed.Length > 0)
28 | {
29 | // If we find any binary characters, skip this entire file.
30 | // foreach (var character in trimmed)
31 | // {
32 | // if (char.IsControl(character)) return;
33 | // }
34 |
35 | await visitor.Context.Filter.WriteLineAsync(line);
36 | }
37 |
38 | if (visitor.Context.CancellationToken.IsCancellationRequested) return;
39 | line = await reader.ReadLineAsync();
40 | }
41 | }
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/UriNode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 | using System.Threading.Tasks;
4 |
5 | namespace IPFilter.Cli
6 | {
7 | class UriNode : INode
8 | {
9 | static readonly MD5 hasher = MD5.Create();
10 | readonly Uri uri;
11 | string uriHash;
12 | readonly FileFetcher fileFetcher;
13 |
14 | public UriNode(Uri uri)
15 | {
16 | this.uri = uri;
17 | this.uriHash = hasher.ComputeHash(uri);
18 | fileFetcher = new CachingFileFetcher();
19 | }
20 |
21 | public async Task Accept(INodeVisitor visitor)
22 | {
23 | var node = await fileFetcher.Get(uri, visitor.Context);
24 | if (node == null) return;
25 | await visitor.Visit(node);
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Cli/ZipNode.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.IO.Compression;
3 | using System.Threading.Tasks;
4 |
5 | namespace IPFilter.Cli
6 | {
7 | class ZipNode : INode
8 | {
9 | readonly FileInfo file;
10 |
11 | public ZipNode(FileInfo file)
12 | {
13 | this.file = new FileInfo(file.FullName);
14 | }
15 |
16 | public async Task Accept(INodeVisitor visitor)
17 | {
18 | using(var zipSource = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
19 | using (var zipFile = new ZipArchive(zipSource, ZipArchiveMode.Read, true))
20 | {
21 | foreach (var entry in zipFile.Entries)
22 | {
23 | using (var entryStream = entry.Open())
24 | using (var tempFile = new TempFile())
25 | {
26 | using (var tempStream = tempFile.OpenWrite())
27 | {
28 | await entryStream.CopyToAsync(tempStream, visitor.Context.CancellationToken);
29 | }
30 |
31 | await visitor.Visit(new FileNode(tempFile.File));
32 | }
33 | }
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Commands/Service.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration.Install;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace IPFilter.Commands
9 | {
10 | class ServiceCommand
11 | {
12 | public static async Task ExecuteAsync()
13 | {
14 | var installer = new TransactedInstaller();
15 |
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Code/IPFilter/Core/Benchmark.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Core
2 | {
3 | using System;
4 | using System.Diagnostics;
5 |
6 | ///
7 | /// Helper class for timing an operation with a descriptive name. Traces
8 | /// the start and completion, with elapsed time.
9 | ///
10 | ///
11 | ///
12 | /// using(Benchmark.New("My operation name"))
13 | /// {
14 | /// // Do stuff
15 | /// }
16 | ///
17 | /// Result:
18 | ///
19 | /// Starting [My operation name]
20 | /// Finished [My operation name] in 00:00:01.2002472
21 | ///
22 | ///
23 | class Benchmark : IDisposable
24 | {
25 | readonly string operation;
26 | readonly Stopwatch stopwatch;
27 |
28 | public static Benchmark New(string operation, params object[] args)
29 | {
30 | return New(string.Format(operation, args));
31 | }
32 |
33 | public static Benchmark New(string operation)
34 | {
35 | return new Benchmark(operation);
36 | }
37 |
38 | Benchmark(string operation)
39 | {
40 | this.operation = operation;
41 | stopwatch = new Stopwatch();
42 | stopwatch.Start();
43 | }
44 |
45 | public void Dispose()
46 | {
47 | stopwatch.Stop();
48 | Trace.TraceInformation($"Finished [{operation}] in {stopwatch.Elapsed}");
49 | GC.SuppressFinalize(this);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Code/IPFilter/Core/Blocklist.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace IPFilter.Core
5 | {
6 | [DataContract]
7 | public class Blocklist
8 | {
9 | [DataMember(Name="id")]
10 | public string Id { get; set; }
11 |
12 | [DataMember(Name="name")]
13 | public string Name { get; set; }
14 |
15 | [DataMember(Name="description")]
16 | public string Description { get; set; }
17 |
18 | [DataMember(Name="uri")]
19 | public Uri Uri { get; set; }
20 | }
21 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/BlocklistBundle.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Runtime.Serialization;
3 |
4 | namespace IPFilter.Core
5 | {
6 | [DataContract]
7 | public class BlocklistBundle
8 | {
9 | [DataMember(Name = "name", IsRequired = false)]
10 | public string Name { get; set; }
11 |
12 | [DataMember(Name="description", IsRequired = false)]
13 | public string Description { get; set; }
14 |
15 | [DataMember(Name="lists")]
16 | public IList Lists { get; set; }
17 | }
18 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/BlocklistProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.Serialization;
4 |
5 | namespace IPFilter.Core
6 | {
7 | [DataContract]
8 | public class BlocklistProvider
9 | {
10 | [DataMember(Name="id")]
11 | public string Id { get; set; }
12 | [DataMember(Name="name")]
13 | public string Name { get; set; }
14 | [DataMember(Name="description")]
15 | public string Description { get; set; }
16 | [DataMember(Name="baseUri")]
17 | public Uri BaseUri { get; set; }
18 |
19 | [DataMember(Name="uri")]
20 | public Uri Uri { get; set; }
21 |
22 | [DataMember(Name="lists")]
23 | public IList Lists { get; set; }
24 | }
25 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/DataFormat.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Core
2 | {
3 | public enum DataFormat
4 | {
5 | None = 0,
6 | GZip,
7 | Zip,
8 | Json,
9 | Text,
10 | Binary
11 | }
12 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/FileInfoExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace IPFilter.Cli
8 | {
9 | public static class FileInfoExtensions
10 | {
11 | public static void SafeDelete(this FileInfo file)
12 | {
13 | if (file == null) return;
14 |
15 | try
16 | {
17 | file.Refresh();
18 | if (!file.Exists) return;
19 | if (file.IsReadOnly) file.IsReadOnly = false;
20 | file.Delete();
21 | }
22 | catch (Exception ex)
23 | {
24 | Trace.TraceWarning($"Couldn't delete file {file.FullName}: {ex}");
25 | }
26 | }
27 |
28 | public static async Task ReadAllText(this FileInfo file)
29 | {
30 | using (var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
31 | using(var reader = new StreamReader(stream, Encoding.UTF8, true))
32 | {
33 | return await reader.ReadToEndAsync();
34 | }
35 | }
36 |
37 | ///
38 | /// Writes the passed string to the file using UTF-8 encoding, creating the file if it doesn't exist,
39 | /// and overwriting if it does.
40 | ///
41 | ///
42 | ///
43 | ///
44 | public static async Task WriteAllText(this FileInfo file, string value)
45 | {
46 | using (var stream = file.Open(FileMode.Create, FileAccess.Write, FileShare.Read))
47 | using(var writer = new StreamWriter(stream, Encoding.UTF8))
48 | {
49 | await writer.WriteAsync(value);
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/FileSystem.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace IPFilter.Core
4 | {
5 | public class FileSystem : IFileSystem
6 | {
7 | public TempStream GetTempStream()
8 | {
9 | var file = new FileInfo(Path.GetTempFileName());
10 | return new TempStream(file);
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/FilterCollection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using IPFilter.Models;
5 |
6 | namespace IPFilter.Core
7 | {
8 | #pragma warning disable 618
9 | public class FilterCollection : SortedSet
10 | {
11 | static readonly IComparer comparer = new MutableFilterEntryComparer();
12 |
13 | public static IList Sort(IEnumerable source)
14 | {
15 | var list = new List(source);
16 | list.Sort(FilterEntry.Comparer);
17 | return list;
18 | }
19 |
20 | public static IList Merge(IEnumerable source)
21 | {
22 | var list = new List(source);
23 | list.Sort(FilterEntry.Comparer);
24 |
25 | for (var i = 0; i < list.Count; i++)
26 | {
27 | var filter = list[i];
28 |
29 | // Keep peeking at the next entries to see if they fit in range, in which case we will merge them.
30 | while ( i + 1 < list.Count )
31 | {
32 | var next = list[i + 1];
33 |
34 | if( comparer.Compare(filter, next) == 0)
35 | {
36 | list.RemoveAt(i + 1);
37 | }
38 | else
39 | {
40 | break;
41 | }
42 | }
43 | }
44 |
45 | return list.ToList();
46 | }
47 |
48 | class MutableFilterEntryComparer : IComparer
49 | {
50 | public int Compare(FilterEntry x, FilterEntry y)
51 | {
52 | if (x == null || y == null)
53 | {
54 | if (x == null && y == null) return 0;
55 | return x == null ? -1 : 1;
56 | }
57 |
58 | if ( FilterEntry.AddressComparer.Compare(x.From, y.From) < 1 && FilterEntry.AddressComparer.Compare(y.From, x.To) <= 1)
59 | {
60 | // The two ranges overlap, so merge them.
61 | x.From = Math.Min(x.From, y.From);
62 | x.To = Math.Max(x.To, y.To);
63 | return 0;
64 | }
65 |
66 | return FilterEntry.Comparer.Compare(x, y);
67 | }
68 | }
69 | }
70 | #pragma warning restore 618
71 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/FilterContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | namespace IPFilter.Core
5 | {
6 | public class FilterContext : IDisposable
7 | {
8 | public FilterContext()
9 | {
10 | UriResolver = new UriResolver();
11 | Filter = new NullWriter();
12 | FileSystem = new FileSystem();
13 | }
14 |
15 | public virtual CancellationToken CancellationToken { get; set; }
16 |
17 | //public IProgress Progress { get; set; }
18 |
19 | public IUriResolver UriResolver { get; set; }
20 |
21 | public IFilterWriter Filter { get; set; }
22 |
23 | public IFileSystem FileSystem { get; set; }
24 |
25 | public void Dispose()
26 | {
27 | Filter?.Dispose();
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/IFileSystem.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Core
2 | {
3 | public interface IFileSystem
4 | {
5 | TempStream GetTempStream();
6 | }
7 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/IFilterEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace IPFilter.Core
4 | {
5 | ///
6 | /// Contract for a type that reads in a list of filters
7 | /// and writes them to a destination.
8 | ///
9 | public interface IFilterEnumerator
10 | {
11 | ///
12 | /// Writes a list of filters to the specified .
13 | ///
14 | ///
15 | ///
16 | ///
17 | Task GetFilters(IFilterWriter writer, FilterContext context);
18 | }
19 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/IFilterWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace IPFilter.Core
5 | {
6 | public interface IFilterWriter : IDisposable
7 | {
8 | Task WriteLineAsync(string line);
9 |
10 | ///
11 | /// Finalizes and flushes the list to output. Typically this will
12 | /// be the file system, and will involve sorting the list and removing duplicates.
13 | ///
14 | Task Flush();
15 | }
16 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/IPAddressComparer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace IPFilter.Core
4 | {
5 | public class IPAddressComparer : IComparer, IEqualityComparer
6 | {
7 | public int Compare(uint x, uint y)
8 | {
9 | var xAddress = (long) x;
10 | var yAddress = (long) y;
11 |
12 | if (xAddress == yAddress) return 0;
13 |
14 | var distance = xAddress - yAddress;
15 |
16 | if (distance > int.MaxValue) return int.MaxValue;
17 | if (distance < int.MinValue) return int.MinValue;
18 |
19 | return (int)distance;
20 | }
21 |
22 | public bool Equals(uint x, uint y)
23 | {
24 | return x == y;
25 | }
26 |
27 | public int GetHashCode(uint obj)
28 | {
29 | return obj.GetHashCode();
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/IUriResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace IPFilter.Core
4 | {
5 | public interface IUriResolver
6 | {
7 | Uri Resolve(string url);
8 | }
9 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/NullWriter.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace IPFilter.Core
4 | {
5 | public class NullWriter : IFilterWriter
6 | {
7 | static readonly Task empty = Task.FromResult(1);
8 |
9 | public Task WriteLineAsync(string line)
10 | {
11 | return empty;
12 | }
13 |
14 | public void Flush()
15 | {
16 |
17 | }
18 |
19 | public void Dispose()
20 | {
21 | }
22 |
23 | Task IFilterWriter.Flush()
24 | {
25 | return empty;
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/StreamExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Security.Cryptography;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace IPFilter.Cli
9 | {
10 | public static class StreamExtensions
11 | {
12 | ///
13 | /// The default copy buffer size, used in the .NET source, is reputedly :
14 | /// "... the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
15 | /// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
16 | /// improvement in Copy performance".
17 | ///
18 | public const int DefaultCopyBufferSize = 81920;
19 |
20 | public static async Task ReadAndHashAsync(this Stream source, byte[] buffer, int offset, int count, HashAlgorithm algorithm, CancellationToken cancellationToken)
21 | {
22 | var bytes = await source.ReadAsync(buffer, offset, count, cancellationToken);
23 |
24 | if (bytes == 0)
25 | {
26 | algorithm.TransformFinalBlock(buffer, 0, 0);
27 | }
28 | else
29 | {
30 | algorithm.TransformBlock(buffer, 0, bytes, null, 0);
31 | }
32 |
33 | return bytes;
34 | }
35 |
36 | public static async Task CopyToAsync(this Stream source, Stream destination, CancellationToken cancellationToken)
37 | {
38 | if (source == null) throw new ArgumentNullException(nameof(source));
39 | if (destination == null) throw new ArgumentNullException(nameof(destination));
40 |
41 | Debug.Assert(source.CanRead);
42 | Debug.Assert(destination.CanWrite);
43 |
44 | byte[] buffer = new byte[DefaultCopyBufferSize];
45 | int bytesRead;
46 | while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
47 | {
48 | await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
49 | }
50 | }
51 |
52 | public static Task CopyToAndHashAsync(this Stream source, Stream destination, HashAlgorithm hasher, CancellationToken cancellationToken)
53 | {
54 | return CopyToAndHashAsync(source, destination, DefaultCopyBufferSize, hasher, cancellationToken);
55 | }
56 |
57 | public static async Task CopyToAndHashAsync(this Stream source, Stream destination, int bufferSize, HashAlgorithm hasher, CancellationToken cancellationToken)
58 | {
59 | if (source == null) throw new ArgumentNullException(nameof(source));
60 | if (destination == null) throw new ArgumentNullException(nameof(destination));
61 | if (hasher == null) throw new ArgumentNullException(nameof(hasher));
62 | if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize));
63 |
64 | Debug.Assert(source.CanRead);
65 | Debug.Assert(destination.CanWrite);
66 |
67 | byte[] buffer = new byte[bufferSize];
68 | int bytesRead;
69 | while ((bytesRead = await source.ReadAndHashAsync(buffer, 0, buffer.Length, hasher, cancellationToken).ConfigureAwait(false)) != 0)
70 | {
71 | await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
72 | }
73 |
74 | return hasher.Hash;
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/TempStream.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using IPFilter.Cli;
3 |
4 | namespace IPFilter.Core
5 | {
6 | public class TempStream : FileStream
7 | {
8 | readonly FileInfo file;
9 |
10 | public FileInfo File => file;
11 |
12 | public TempStream(FileInfo file) : base(file.FullName, FileMode.Create, FileAccess.Write, FileShare.Read)
13 | {
14 | this.file = new FileInfo(file.FullName);
15 | }
16 |
17 | ~TempStream()
18 | {
19 | Dispose(false);
20 | }
21 |
22 | protected override void Dispose(bool disposing)
23 | {
24 | base.Dispose(disposing);
25 | file.SafeDelete();
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Core/UriResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace IPFilter.Core
8 | {
9 | ///
10 | /// Resolves and validates the URIs for various list sources.
11 | ///
12 | public class UriResolver : IUriResolver
13 | {
14 | public Uri Resolve(string url)
15 | {
16 | if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out Uri uri)) return null;
17 | if (!uri.IsAbsoluteUri) return null;
18 |
19 | var builder = new UriBuilder(uri);
20 |
21 | switch (uri.Scheme)
22 | {
23 | case "http":
24 | case "https":
25 | return uri;
26 |
27 | case "file":
28 | return new Uri(Path.GetFullPath(uri.LocalPath));
29 |
30 | default:
31 | return null;
32 | }
33 |
34 | return null;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Code/IPFilter/FilterService.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter
2 | {
3 | partial class FilterService
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Component Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | components = new System.ComponentModel.Container();
32 | this.ServiceName = "IPFilter";
33 | }
34 |
35 | #endregion
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Code/IPFilter/FilterService.cs:
--------------------------------------------------------------------------------
1 | using System.ServiceProcess;
2 |
3 | namespace IPFilter
4 | {
5 | partial class FilterService : ServiceBase
6 | {
7 | public FilterService()
8 | {
9 | InitializeComponent();
10 | }
11 |
12 | protected override void OnStart(string[] args) {}
13 |
14 | protected override void OnStop() {}
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Code/IPFilter/FilterServiceInstaller.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Configuration.Install;
3 | using System.ServiceProcess;
4 |
5 | namespace IPFilter
6 | {
7 | [RunInstaller(true)]
8 | public class FilterServiceInstaller : Installer
9 | {
10 | readonly ServiceProcessInstaller installer;
11 | ServiceInstaller serviceInstaller;
12 |
13 | public FilterServiceInstaller()
14 | {
15 | installer = new ServiceProcessInstaller
16 | {
17 | Account = ServiceAccount.User
18 | };
19 |
20 | serviceInstaller = new ServiceInstaller
21 | {
22 | DelayedAutoStart = true,
23 | Description = "IPFilter Updater",
24 | DisplayName = "IPFilter",
25 | ServiceName = "ipfilter",
26 | StartType = ServiceStartMode.Automatic
27 | };
28 |
29 | Installers.Add(serviceInstaller);
30 | Installers.Add(installer);
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Formats/EmuleWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using IPFilter.Core;
7 | using IPFilter.Models;
8 |
9 | namespace IPFilter.Formats
10 | {
11 | ///
12 | /// 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"
13 | ///
14 | class EmuleWriter : IFormatWriter
15 | {
16 | readonly Stream stream;
17 |
18 | public void Dispose()
19 | {
20 | stream.Dispose();
21 | }
22 |
23 | public EmuleWriter(Stream stream)
24 | {
25 | this.stream = stream;
26 | }
27 |
28 | public async Task Write(IList entries, IProgress progress)
29 | {
30 | var sb = new StringBuilder(255);
31 | var address = new StringBuilder(15);
32 |
33 | using(Benchmark.New("Writing {0} entries", entries.Count))
34 | using (var writer = new StreamWriter(stream, Encoding.ASCII))
35 | {
36 | for (var i = 1; i <= entries.Count; i++)
37 | {
38 | var entry = entries[i-1];
39 | sb.Clear();
40 |
41 | var from = BitConverter.GetBytes(entry.From);
42 | address.Clear();
43 | address.Append(from[3]).Append(".").Append(from[2]).Append(".").Append(from[1]).Append(".").Append(from[0]);
44 | sb.Append(address.ToString().PadRight(16));
45 |
46 | sb.Append("- ");
47 |
48 | var to = BitConverter.GetBytes(entry.To);
49 | address.Clear();
50 | address.Append(to[3]).Append(".").Append(to[2]).Append(".").Append(to[1]).Append(".").Append(to[0]);
51 | sb.Append(address.ToString().PadRight(16));
52 |
53 | sb.Append(", ").Append(entry.Level.ToString().PadLeft(3)).Append(" , ");
54 |
55 | sb.Append(entry.Description);
56 |
57 | writer.WriteLine(sb.ToString());
58 |
59 | if (progress == null) continue;
60 | var percent = (int) Math.Floor((double) (i / entries.Count * 100));
61 | progress.Report(new ProgressModel(UpdateState.Decompressing, "Updating eMule...", percent));
62 | }
63 |
64 | progress?.Report(new ProgressModel(UpdateState.Decompressing, "Flushing...", 100));
65 | await writer.FlushAsync();
66 |
67 | progress?.Report(new ProgressModel(UpdateState.Decompressing, "Updated eMule.", 100));
68 | }
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Formats/FilterFileFormat.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace IPFilter.Formats
4 | {
5 | [Flags]
6 | enum FilterFileFormat
7 | {
8 | None = 0,
9 | Emule = 1,
10 | P2p = 2,
11 | P2b = 4
12 | }
13 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Formats/IFormatWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using IPFilter.Models;
5 |
6 | namespace IPFilter.Formats
7 | {
8 | interface IFormatWriter : IDisposable
9 | {
10 | Task Write(IList entries, IProgress progress);
11 | }
12 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Formats/P2pWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using IPFilter.Core;
7 | using IPFilter.Models;
8 |
9 | namespace IPFilter.Formats
10 | {
11 | ///
12 | /// Writes out eMule DAT format, 0-padding integers to 3 digits. e.g."001.009.096.105 - 001.009.096.105 , 000 , Some organization"
13 | ///
14 | class P2pWriter : IFormatWriter
15 | {
16 | readonly Stream stream;
17 |
18 | public void Dispose()
19 | {
20 | stream.Dispose();
21 | }
22 |
23 | public P2pWriter(Stream stream)
24 | {
25 | this.stream = stream;
26 | }
27 |
28 | public async Task Write(IList entries, IProgress progress)
29 | {
30 | var sb = new StringBuilder(255);
31 | var address = new StringBuilder(15);
32 |
33 | var currentPercentage = -1;
34 |
35 | using (Benchmark.New("Writing {0} entries", entries.Count))
36 | using (var writer = new StreamWriter(stream, Encoding.ASCII))
37 | {
38 | for (var i = 1; i <= entries.Count; i++)
39 | {
40 | sb.Clear();
41 |
42 | var from = BitConverter.GetBytes(entries[i - 1].From);
43 | address.Clear();
44 | address.Append(from[3].ToString("D3")).Append(".").Append(from[2].ToString("D3")).Append(".").Append(from[1].ToString("D3")).Append(".").Append(from[0].ToString("D3"));
45 | sb.Append(address);
46 |
47 | sb.Append(" - ");
48 |
49 | var to = BitConverter.GetBytes(entries[i - 1].To);
50 | address.Clear();
51 | address.Append(to[3].ToString("D3")).Append(".").Append(to[2].ToString("D3")).Append(".").Append(to[1].ToString("D3")).Append(".").Append(to[0].ToString("D3"));
52 | sb.Append(address);
53 |
54 | sb.Append(" , ").Append(entries[i - 1].Level.ToString("D3").PadLeft(3)).Append(" , ");
55 |
56 | sb.Append(entries[i - 1].Description);
57 |
58 | writer.WriteLine(sb.ToString());
59 |
60 | if (progress == null) continue;
61 | var percent = (int)Math.Floor((double)i / entries.Count * 100);
62 |
63 | if (percent > currentPercentage)
64 | {
65 | progress.Report(new ProgressModel(UpdateState.Downloading, "Writing...", percent));
66 | }
67 |
68 | currentPercentage = percent;
69 | }
70 |
71 | await writer.FlushAsync();
72 | }
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/Code/IPFilter/IPFilter.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net48
6 | true
7 | {34A454A5-5453-424A-81CE-30FF0A2B004B}
8 | Properties
9 | IPFilter
10 | IPFilter
11 | ..\..\Bin\
12 | ..\..\Bin\
13 |
14 |
15 | IPFilter.EntryPoint
16 |
17 |
18 | App.ico
19 | false
20 | preview
21 | True
22 | enable
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | MSBuild:Compile
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | MSBuild:Compile
45 |
46 |
47 | MSBuild:Compile
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 4.0
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | Component
72 |
73 |
74 | Component
75 |
76 |
77 | Component
78 |
79 |
80 |
81 |
82 | Never
83 |
84 |
85 | PreserveNewest
86 |
87 |
88 |
--------------------------------------------------------------------------------
/Code/IPFilter/ListProviders/DefaultList.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.ListProviders
2 | {
3 | public class DefaultList : IMirrorProvider
4 | {
5 | public string Name => "Default";
6 |
7 | public string GetUrlForMirror()
8 | {
9 | return "https://github.com/DavidMoore/ipfilter/releases/download/lists/ipfilter.dat.gz";
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Code/IPFilter/ListProviders/IMirrorProvider.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.ListProviders
2 | {
3 | ///
4 | /// Contract for a source that provides mirrors of the file
5 | ///
6 | public interface IMirrorProvider
7 | {
8 | ///
9 | /// The name of the mirror provider
10 | ///
11 | string Name { get; }
12 |
13 | string GetUrlForMirror();
14 | }
15 | }
--------------------------------------------------------------------------------
/Code/IPFilter/ListProviders/MirrorProvidersFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace IPFilter.ListProviders
4 | {
5 | static class MirrorProvidersFactory
6 | {
7 | private static readonly IList list = new List {new DefaultList()};
8 |
9 | public static IList Get()
10 | {
11 | return list;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/ApplicationDetectionResult.cs:
--------------------------------------------------------------------------------
1 | using IPFilter.Native;
2 |
3 | namespace IPFilter.Models
4 | {
5 | using System.IO;
6 | using Apps;
7 |
8 | public class ApplicationDetectionResult
9 | {
10 | public bool IsPresent { get; set; }
11 |
12 | public string Description { get; set; }
13 |
14 | public DirectoryInfo InstallLocation { get; set; }
15 |
16 | public string Version { get; set; }
17 |
18 | public IApplication Application { get; set; }
19 |
20 | public static ApplicationDetectionResult NotFound()
21 | {
22 | return new ApplicationDetectionResult();
23 | }
24 |
25 | public static ApplicationDetectionResult Create(IApplication application, string description, string installLocation)
26 | {
27 | var directory = PathHelper.GetDirectoryInfo(installLocation);
28 |
29 | return new ApplicationDetectionResult
30 | {
31 | Application = application,
32 | Description = description,
33 | InstallLocation = directory,
34 | IsPresent = true
35 | };
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/CompressionFormat.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Models
2 | {
3 | public enum CompressionFormat
4 | {
5 | None = 0,
6 | GZip = 1,
7 | Zip = 2
8 | }
9 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/FileMirror.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Models
2 | {
3 | public class FileMirror
4 | {
5 | public FileMirror(string id, string name)
6 | {
7 | Id = id;
8 | Name = name;
9 | }
10 |
11 | ///
12 | /// Descriptive name of the mirror e.g. Transact (Canberra, Australia)
13 | ///
14 | public string Name { get; set; }
15 |
16 | ///
17 | /// The id of the mirror e.g. transact
18 | ///
19 | public string Id { get; set; }
20 |
21 | ///
22 | /// The name of the mirror provider
23 | ///
24 | public string Provider { get; set; }
25 |
26 | ///
27 | /// Is the list selected
28 | ///
29 | public bool IsSelected { get; set; }
30 |
31 | public override string ToString()
32 | {
33 | return Name;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/FilterDownloadResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace IPFilter.Models
4 | {
5 | using System;
6 | using System.IO;
7 | using System.Net.Http.Headers;
8 | using ListProviders;
9 |
10 | public class FilterDownloadResult : IDisposable
11 | {
12 | public FilterDownloadResult()
13 | {
14 | Entries = new List(100000);
15 | }
16 |
17 | public DateTimeOffset? FilterTimestamp { get; set; }
18 |
19 | public IMirrorProvider MirrorProvider { get; set; }
20 |
21 | public string Uri { get; set; }
22 |
23 | public long? Length { get; set; }
24 |
25 | public Exception Exception { get; set; }
26 |
27 | public MemoryStream Stream { get; set; }
28 |
29 | public CompressionFormat CompressionFormat { get; set; }
30 |
31 | public EntityTagHeaderValue Etag { get; set; }
32 |
33 | public IList Entries { get; set; }
34 |
35 | ///
36 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
37 | ///
38 | public void Dispose()
39 | {
40 | Dispose(true);
41 | }
42 |
43 | void Dispose(bool disposing)
44 | {
45 | if (!disposing || Stream == null) return;
46 | Stream.Dispose();
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/FilterSource.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace IPFilter.Models
4 | {
5 | public class FilterSource
6 | {
7 | public bool IsSelected { get; set; }
8 |
9 | public string Provider { get; set; }
10 |
11 | public string Id { get; set; }
12 |
13 | public string Name { get; set; }
14 |
15 | public string Url { get; set; }
16 |
17 |
18 | public DateTimeOffset? LastModified { get; set; }
19 |
20 | public string Username { get; set; }
21 |
22 | public string Password { get; set; }
23 |
24 | private bool Equals(FilterSource other)
25 | {
26 | return string.Equals(Provider, other.Provider, StringComparison.OrdinalIgnoreCase) && string.Equals(Id, other.Id, StringComparison.OrdinalIgnoreCase);
27 | }
28 |
29 | public override bool Equals(object obj)
30 | {
31 | if (ReferenceEquals(null, obj)) return false;
32 | if (ReferenceEquals(this, obj)) return true;
33 | if (obj.GetType() != this.GetType()) return false;
34 | return Equals((FilterSource) obj);
35 | }
36 |
37 | public override int GetHashCode()
38 | {
39 | if (Provider == null || Id == null) return 0;
40 | return (StringComparer.OrdinalIgnoreCase.GetHashCode(Provider) * 397) ^ StringComparer.OrdinalIgnoreCase.GetHashCode(Id);
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/FilterUpdateResult.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Models
2 | {
3 | using System;
4 |
5 | public class FilterUpdateResult
6 | {
7 | public TimeSpan? UpdateTime { get; set; }
8 |
9 | public DateTimeOffset? FilterTimestamp { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/ProgressModel.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Models
2 | {
3 | public class ProgressModel
4 | {
5 | ///
6 | /// Initializes a new instance of the class.
7 | ///
8 | public ProgressModel(UpdateState state, string caption, int value)
9 | {
10 | State = state;
11 | Caption = caption;
12 | Value = value;
13 | }
14 |
15 | public UpdateState State { get; set; }
16 | public string Caption { get; set; }
17 | public int Value { get; set; }
18 | }
19 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/ProgressModelExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace IPFilter.Models
4 | {
5 | public static class ProgressModelExtensions
6 | {
7 | public static void Report(this IProgress progress, UpdateState state, string caption)
8 | {
9 | progress.Report(state, caption, -1);
10 | }
11 |
12 | public static void Report(this IProgress progress, UpdateState state, string caption, int value)
13 | {
14 | progress.Report(new ProgressModel(state, caption, value));
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/UpdateInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace IPFilter.Models
4 | {
5 | [DataContract]
6 | public class UpdateInfo
7 | {
8 | [DataMember(Name="version")]
9 | public string Version { get; set; }
10 |
11 | [DataMember(Name="uri")]
12 | public string Uri { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Models/UpdateState.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Models
2 | {
3 | public enum UpdateState
4 | {
5 | ///
6 | /// Ready to download
7 | ///
8 | Ready,
9 |
10 | ///
11 | /// The download is in progress
12 | ///
13 | Downloading,
14 |
15 | ///
16 | /// The download is done
17 | ///
18 | Done,
19 |
20 | ///
21 | /// Is download, but the user has clicked "Cancel" and the
22 | /// download should be stopped as soon as possible
23 | ///
24 | Cancelling,
25 |
26 | ///
27 | /// The download was cancelled
28 | ///
29 | Cancelled,
30 |
31 | Decompressing
32 | }
33 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Native/PathHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Globalization;
4 | using System.IO;
5 | using System.Text;
6 |
7 | namespace IPFilter.Native
8 | {
9 | static class PathHelper
10 | {
11 | public static DirectoryInfo GetDirectoryInfo(string path)
12 | {
13 | return GetDirectoryInfo(path, CultureInfo.CurrentCulture);
14 | }
15 |
16 | public static DirectoryInfo GetDirectoryInfo(string path, CultureInfo cultureInfo)
17 | {
18 | if (string.IsNullOrWhiteSpace(path)) return null;
19 |
20 | try
21 | {
22 | // On some cultures, the backslash character (0x5C / 92) is displayed differently, so we will use the culture's codepage
23 | // to read in the directory, and then convert it back, which should fix the backslashes.
24 |
25 | // For example, in Japanese codepage 932, the backslash character is displayed as the Yen symbol e.g C:¥Program Files¥qBittorrent
26 | // If we read in the path bytes using the Japanese codepage of 932, then convert those bytes to a string using the same codepage,
27 | // the yen character will actually get corrected to the backslash.
28 |
29 | // Get the culture's codepage
30 | var encoding = Encoding.GetEncoding(cultureInfo.TextInfo.ANSICodePage);
31 |
32 | var normalizedPath = encoding.GetString(encoding.GetBytes(path));
33 |
34 | return new DirectoryInfo(normalizedPath);
35 | }
36 | catch (Exception ex)
37 | {
38 | Trace.TraceWarning($"Couldn't normalize the path '{path}'. Culture: {cultureInfo}, CodePage: {cultureInfo.TextInfo.ANSICodePage}");
39 | Trace.TraceWarning("Error: " + ex);
40 | Trace.TraceInformation($"Falling back to un-normalized path of '{path}'");
41 |
42 | return new DirectoryInfo(path);
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Native/ProcessInformation.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Native
2 | {
3 | using System;
4 | using System.Runtime.InteropServices;
5 |
6 | [StructLayout(LayoutKind.Sequential)]
7 | public class ProcessInformation
8 | {
9 | public IntPtr hProcess = IntPtr.Zero;
10 | public IntPtr hThread = IntPtr.Zero;
11 | public int dwProcessId;
12 | public int dwThreadId;
13 | }
14 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Native/ProcessManager.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Native
2 | {
3 | using System;
4 | using System.Runtime.InteropServices;
5 | using System.Text;
6 |
7 | public class ProcessManager
8 | {
9 | [DllImport("Kernel32", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
10 | public static extern bool CreateProcess(
11 | [MarshalAs(UnmanagedType.LPTStr)]string applicationName,
12 | StringBuilder commandLine,
13 | SecurityAttributes processAttributes,
14 | SecurityAttributes threadAttributes,
15 | bool inheritHandles,
16 | int creationFlags,
17 | IntPtr environment,
18 | [MarshalAs(UnmanagedType.LPTStr)]string currentDirectory,
19 | StartupInfo startupInfo,
20 | ProcessInformation processInformation
21 | );
22 | }
23 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Native/SafeLocalMemHandle.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Native
2 | {
3 | using System;
4 | using System.Runtime.ConstrainedExecution;
5 | using System.Runtime.InteropServices;
6 | using System.Security;
7 | using System.Security.Permissions;
8 | using Microsoft.Win32.SafeHandles;
9 |
10 | [SuppressUnmanagedCodeSecurity, HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
11 | public sealed class SafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid
12 | {
13 | internal SafeLocalMemHandle() : base(true) {}
14 |
15 | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
16 | internal SafeLocalMemHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle)
17 | {
18 | base.SetHandle(existingHandle);
19 | }
20 |
21 | [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
22 | internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string stringSecurityDescriptor,
23 | int stringSDRevision, out SafeLocalMemHandle pSecurityDescriptor, IntPtr securityDescriptorSize);
24 |
25 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("kernel32.dll")]
26 | private static extern IntPtr LocalFree(IntPtr hMem);
27 |
28 | protected override bool ReleaseHandle()
29 | {
30 | return (LocalFree(base.handle) == IntPtr.Zero);
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Native/SecurityAttributes.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Native
2 | {
3 | using System;
4 | using System.Runtime.InteropServices;
5 |
6 | [StructLayout(LayoutKind.Sequential)]
7 | public class SecurityAttributes
8 | {
9 | public int nLength = 12;
10 | public SafeLocalMemHandle lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, false);
11 | public bool bInheritHandle;
12 | }
13 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Native/StartupInfo.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Native
2 | {
3 | using System;
4 | using System.Runtime.InteropServices;
5 | using Microsoft.Win32.SafeHandles;
6 |
7 | [StructLayout(LayoutKind.Sequential)]
8 | public class StartupInfo
9 | {
10 | public int cb;
11 | public IntPtr lpReserved = IntPtr.Zero;
12 | public IntPtr lpDesktop = IntPtr.Zero;
13 | public IntPtr lpTitle = IntPtr.Zero;
14 | public int dwX;
15 | public int dwY;
16 | public int dwXSize;
17 | public int dwYSize;
18 | public int dwXCountChars;
19 | public int dwYCountChars;
20 | public int dwFillAttribute;
21 | public int dwFlags;
22 | public short wShowWindow;
23 | public short cbReserved2;
24 | public IntPtr lpReserved2 = IntPtr.Zero;
25 | public SafeFileHandle hStdInput = new SafeFileHandle(IntPtr.Zero, false);
26 | public SafeFileHandle hStdOutput = new SafeFileHandle(IntPtr.Zero, false);
27 | public SafeFileHandle hStdError = new SafeFileHandle(IntPtr.Zero, false);
28 |
29 | public StartupInfo()
30 | {
31 | this.dwY = 0;
32 | this.cb = Marshal.SizeOf(this);
33 | }
34 |
35 | public void Dispose()
36 | {
37 | // close the handles created for child process
38 | if (this.hStdInput != null && !this.hStdInput.IsInvalid)
39 | {
40 | this.hStdInput.Close();
41 | this.hStdInput = null;
42 | }
43 |
44 | if (this.hStdOutput != null && !this.hStdOutput.IsInvalid)
45 | {
46 | this.hStdOutput.Close();
47 | this.hStdOutput = null;
48 | }
49 |
50 | if (this.hStdError == null || this.hStdError.IsInvalid) return;
51 |
52 | this.hStdError.Close();
53 | this.hStdError = null;
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Native/Win32Api.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace IPFilter.Native
5 | {
6 | public static class Win32Api
7 | {
8 | [DllImport("user32.dll")]
9 | [return: MarshalAs(UnmanagedType.Bool)]
10 | public static extern bool SetForegroundWindow(IntPtr hWnd);
11 |
12 | [DllImport("user32.dll")]
13 | static extern IntPtr GetForegroundWindow();
14 |
15 | [DllImport("user32.dll")]
16 | static extern bool AttachThreadInput(uint threadToBeAttached, uint threadToBeAttachedTo, bool attach);
17 |
18 | [DllImport("user32.dll")]
19 | static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
20 |
21 | [DllImport("kernel32.dll")]
22 | static extern uint GetCurrentThreadId();
23 |
24 | [DllImport("user32.dll")]
25 | static extern IntPtr SetFocus(IntPtr hWnd);
26 |
27 | public static void BringToFront(IntPtr handle)
28 | {
29 | // Get the handle of the current foreground window
30 | var foregroundWindow = GetForegroundWindow();
31 |
32 | if (foregroundWindow == IntPtr.Zero)
33 | {
34 | // If the foreground window can't be found (which can happen in some circumstances,
35 | // such as when focus is being switched, or screensaver is active), then the best
36 | // we can do is try to bring the requested window to the front anyway.
37 | SetForegroundWindow(handle);
38 | SetFocus(handle);
39 | return;
40 | }
41 |
42 | // If we're already the foreground window, set focus to make sure.
43 | if (foregroundWindow == handle)
44 | {
45 | SetFocus(handle);
46 | return;
47 | }
48 |
49 | // Get the thread handle for the window that's currently in the foreground.
50 | var windowThread = GetWindowThreadProcessId(foregroundWindow, IntPtr.Zero);
51 |
52 | // Get our current thread handle.
53 | var currentThread = GetCurrentThreadId();
54 |
55 | try
56 | {
57 | if (currentThread != windowThread)
58 | {
59 | // Attach our thread to the window that holds the thread input, so that
60 | // we now have authority over the foreground window and focus.
61 | AttachThreadInput(currentThread, windowThread, true);
62 | }
63 |
64 | // Move the requested window into the foreground, with focus.
65 | SetForegroundWindow(handle);
66 | SetFocus(handle);
67 | }
68 | finally
69 | {
70 | if (currentThread != windowThread)
71 | {
72 | // Don't forget to detach our thread from the thread input afterwards.
73 | AttachThreadInput(currentThread, windowThread, false);
74 | }
75 | }
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Windows;
4 |
5 | using System;
6 | using System.Runtime.InteropServices;
7 | using System.Runtime.CompilerServices;
8 |
9 | [assembly: AssemblyTrademark("")]
10 | [assembly: AssemblyCulture("")]
11 | [assembly: NeutralResourcesLanguage("en-US")]
12 | [assembly: ComVisible(false)]
13 | [assembly: CLSCompliant(false)]
14 | [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
15 | [assembly: InternalsVisibleTo("IPFilter.Tests")]
--------------------------------------------------------------------------------
/Code/IPFilter/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34014
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace IPFilter.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("IPFilter.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Code/IPFilter/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "IPFilter": {
4 | "commandName": "Project",
5 | "commandLineArgs": ""
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Services/CacheProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Headers;
2 | using IPFilter.Cli;
3 |
4 | namespace IPFilter.Services
5 | {
6 | using System;
7 | using System.Deployment.Application;
8 | using System.Diagnostics;
9 | using System.IO;
10 | using System.Threading.Tasks;
11 | using Models;
12 |
13 | class CacheProvider : ICacheProvider
14 | {
15 | static readonly string filterPath;
16 |
17 | static CacheProvider()
18 | {
19 | string dataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "IPFilter");
20 |
21 | if (ApplicationDeployment.IsNetworkDeployed)
22 | {
23 | dataPath = ApplicationDeployment.CurrentDeployment.DataDirectory;
24 | }
25 |
26 | filterPath = Path.Combine(dataPath, "ipfilter.dat");
27 | }
28 |
29 | public static string FilterPath
30 | {
31 | get { return filterPath; }
32 | }
33 |
34 | public async Task GetAsync(FilterDownloadResult filter)
35 | {
36 | var file = new FileInfo(filterPath);
37 | if (!file.Exists) return null;
38 |
39 | // Find the Etag
40 | var etagFile = new FileInfo(file.FullName + ".etag");
41 | if (!etagFile.Exists) return null;
42 |
43 | var result = new FilterDownloadResult
44 | {
45 | FilterTimestamp = file.LastWriteTimeUtc,
46 | Etag = EntityTagHeaderValue.Parse(File.ReadAllText(etagFile.FullName)),
47 | Stream = new MemoryStream((int) file.Length)
48 | };
49 |
50 | using (var stream = file.OpenRead())
51 | {
52 | await stream.CopyToAsync(result.Stream);
53 | }
54 |
55 | result.Length = result.Stream.Length;
56 |
57 | return result;
58 | }
59 |
60 | public async Task SetAsync(FilterDownloadResult filter)
61 | {
62 | try
63 | {
64 | if (filter == null || filter.Exception != null) return;
65 |
66 | var file = new FileInfo(filterPath);
67 |
68 | if (file.Directory != null && !file.Directory.Exists)
69 | {
70 | file.Directory.Create();
71 | }
72 |
73 | Trace.TraceInformation("Writing cached ipfilter to " + filterPath);
74 | filter.Stream.Seek(0, SeekOrigin.Begin);
75 | using (var cacheFile = File.Open(filterPath, FileMode.Create, FileAccess.Write,FileShare.Read))
76 | {
77 | await filter.Stream.CopyToAsync(cacheFile);
78 | }
79 |
80 | if (filter.FilterTimestamp != null)
81 | {
82 | file.LastWriteTimeUtc = filter.FilterTimestamp.Value.UtcDateTime;
83 | }
84 |
85 | File.WriteAllText(file.FullName + ".etag", filter.Etag.ToString());
86 | }
87 | catch (Exception ex)
88 | {
89 | Trace.TraceWarning("Couldn't write the cached ipfilter: " + ex.Message);
90 | }
91 | }
92 | }
93 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Services/Config.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Web.Script.Serialization;
6 |
7 | namespace IPFilter.Services
8 | {
9 | class Config
10 | {
11 | static readonly Lazy defaultConfig = new Lazy(LoadDefault);
12 |
13 | public static Configuration Default => defaultConfig.Value;
14 |
15 | internal const string DefaultSettings = "ipfilter.json";
16 |
17 | internal static readonly JavaScriptSerializer serializer = new JavaScriptSerializer();
18 |
19 | static Configuration LoadDefault()
20 | {
21 | try
22 | {
23 | if (File.Exists(DefaultSettings))
24 | {
25 | var json = File.ReadAllText(DefaultSettings);
26 | return Parse(json);
27 | }
28 | }
29 | catch (Exception ex)
30 | {
31 | Trace.TraceError($"Couldn't load {DefaultSettings}: " + ex);
32 | }
33 |
34 | return new Configuration();
35 | }
36 |
37 | public static void Reload()
38 | {
39 | var config = LoadDefault();
40 |
41 | Config.Default.settings.update.isPreReleaseEnabled = config.settings.update.isPreReleaseEnabled;
42 | Config.Default.settings.update.isDisabled = config.settings.update.isDisabled;
43 | Config.Default.settings.task.isEnabled = config.settings.task.isEnabled;
44 | Config.Default.settings.cache.isEnabled = config.settings.cache.isEnabled;
45 |
46 | Config.Default.outputs.Clear();
47 | foreach (var output in config.outputs)
48 | {
49 | Config.Default.outputs.Add(output);
50 | }
51 | }
52 |
53 | public static void Save(Configuration config, string path)
54 | {
55 | File.WriteAllText(path,serializer.Serialize(config));
56 | }
57 |
58 | public static Configuration Parse(string json)
59 | {
60 | return serializer.Deserialize(json);
61 | }
62 |
63 | public class Configuration
64 | {
65 | public Configuration()
66 | {
67 | settings = new Settings();
68 | outputs = new List();
69 | }
70 |
71 | public Settings settings { get; set; }
72 |
73 | public ICollection outputs { get; set; }
74 | }
75 |
76 | public class Settings
77 | {
78 | public Settings()
79 | {
80 | update = new UpdateSettings();
81 | task = new TaskSettings();
82 | cache = new CacheSettings();
83 | }
84 |
85 | public UpdateSettings update { get; set; }
86 |
87 | public TaskSettings task { get; set; }
88 |
89 | public CacheSettings cache { get; set; }
90 | }
91 |
92 | public class UpdateSettings
93 | {
94 | public bool isDisabled { get; set; }
95 |
96 | public bool isPreReleaseEnabled { get; set; }
97 |
98 | public bool isCleanupDisabled { get; set; }
99 | }
100 |
101 | public class TaskSettings
102 | {
103 | public bool isEnabled { get; set; }
104 | }
105 | public class CacheSettings
106 | {
107 | public bool isEnabled { get; set; }
108 | }
109 | }
110 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Services/CryptoHelper.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services
2 | {
3 | using System;
4 | using System.Runtime.InteropServices;
5 | using System.Security;
6 | using System.Security.Cryptography;
7 | using System.Text;
8 |
9 | class CryptoHelper
10 | {
11 | static readonly byte[] entropy = Encoding.Unicode.GetBytes("Salt Is Not A Password");
12 |
13 | public static string EncryptString(SecureString input)
14 | {
15 | byte[] encryptedData = ProtectedData.Protect(Encoding.Unicode.GetBytes(ToInsecureString(input)), entropy, DataProtectionScope.CurrentUser);
16 | return Convert.ToBase64String(encryptedData);
17 | }
18 |
19 | public static SecureString DecryptString(string encryptedData)
20 | {
21 | try
22 | {
23 | byte[] decryptedData = ProtectedData.Unprotect(Convert.FromBase64String(encryptedData), entropy, DataProtectionScope.CurrentUser);
24 | return ToSecureString(Encoding.Unicode.GetString(decryptedData));
25 | }
26 | catch
27 | {
28 | return new SecureString();
29 | }
30 | }
31 |
32 | public static SecureString ToSecureString(string input)
33 | {
34 | var secure = new SecureString();
35 | foreach (char c in input)
36 | {
37 | secure.AppendChar(c);
38 | }
39 | secure.MakeReadOnly();
40 | return secure;
41 | }
42 |
43 | public static string ToInsecureString(SecureString input)
44 | {
45 | string returnValue;
46 | IntPtr ptr = Marshal.SecureStringToBSTR(input);
47 | try
48 | {
49 | returnValue = Marshal.PtrToStringBSTR(ptr);
50 | }
51 | finally
52 | {
53 | Marshal.ZeroFreeBSTR(ptr);
54 | }
55 | return returnValue;
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Services/DelegateTraceListener.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services
2 | {
3 | using System;
4 | using System.Diagnostics;
5 |
6 | public class DelegateTraceListener : TraceListener
7 | {
8 | readonly Action writeAction;
9 | readonly Action writeLineAction;
10 |
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | public DelegateTraceListener(Action writeAction, Action writeLineAction)
15 | {
16 | this.writeAction = writeAction;
17 | this.writeLineAction = writeLineAction;
18 | }
19 |
20 | ///
21 | /// When overridden in a derived class, writes the specified message to the listener you create in the derived class.
22 | ///
23 | /// A message to write.
24 | public override void Write(string message)
25 | {
26 | writeAction?.Invoke(message);
27 | }
28 |
29 | ///
30 | /// When overridden in a derived class, writes a message to the listener you create in the derived class, followed by a line terminator.
31 | ///
32 | /// A message to write.
33 | public override void WriteLine(string message)
34 | {
35 | writeLineAction?.Invoke(message);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Services/Deployment/ClickOnce/IUninstallStep.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services.Deployment
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | public interface IUninstallStep : IDisposable
7 | {
8 | void Prepare(List componentsToRemove);
9 |
10 | void PrintDebugInformation();
11 |
12 | void Execute();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Code/IPFilter/Services/Deployment/ClickOnce/RemoveStartMenuEntry.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services.Deployment
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 |
8 | public class RemoveStartMenuEntry : IUninstallStep
9 | {
10 | private readonly UninstallInfo _uninstallInfo;
11 | private List _foldersToRemove;
12 | private List _filesToRemove;
13 |
14 | public RemoveStartMenuEntry(UninstallInfo uninstallInfo)
15 | {
16 | _uninstallInfo = uninstallInfo;
17 | }
18 |
19 | public void Prepare(List componentsToRemove)
20 | {
21 | var programsFolder = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
22 | var folder = Path.Combine(programsFolder, _uninstallInfo.ShortcutFolderName);
23 | var suiteFolder = Path.Combine(folder, _uninstallInfo.ShortcutSuiteName ?? string.Empty);
24 | var shortcut = Path.Combine(suiteFolder, _uninstallInfo.ShortcutFileName + ".appref-ms");
25 | var supportShortcut = Path.Combine(suiteFolder, _uninstallInfo.SupportShortcutFileName + ".url");
26 |
27 | var desktopFolder = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
28 | var desktopShortcut = Path.Combine(desktopFolder, _uninstallInfo.ShortcutFileName + ".appref-ms");
29 |
30 | _filesToRemove = new List();
31 | if (File.Exists(shortcut)) _filesToRemove.Add(shortcut);
32 | if (File.Exists(supportShortcut)) _filesToRemove.Add(supportShortcut);
33 | if (File.Exists(desktopShortcut)) _filesToRemove.Add(desktopShortcut);
34 |
35 | _foldersToRemove = new List();
36 | if (Directory.Exists(suiteFolder) && Directory.GetFiles(suiteFolder).All(d => _filesToRemove.Contains(d)))
37 | {
38 | _foldersToRemove.Add(suiteFolder);
39 |
40 | if (Directory.GetDirectories(folder).Count() == 1 && !Directory.GetFiles(folder).Any())
41 | _foldersToRemove.Add(folder);
42 | }
43 | }
44 |
45 | public void PrintDebugInformation()
46 | {
47 | if (_foldersToRemove == null)
48 | throw new InvalidOperationException("Call Prepare() first.");
49 |
50 | var programsFolder = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
51 | Console.WriteLine("Remove start menu entries from " + programsFolder);
52 |
53 | foreach (var file in _filesToRemove)
54 | {
55 | Console.WriteLine("Delete file " + file);
56 | }
57 |
58 | foreach (var folder in _foldersToRemove)
59 | {
60 | Console.WriteLine("Delete folder " + folder);
61 | }
62 |
63 | Console.WriteLine();
64 | }
65 |
66 | public void Execute()
67 | {
68 | if (_foldersToRemove == null)
69 | throw new InvalidOperationException("Call Prepare() first.");
70 |
71 | try
72 | {
73 | foreach (var file in _filesToRemove)
74 | {
75 | File.Delete(file);
76 | }
77 |
78 | foreach (var folder in _foldersToRemove)
79 | {
80 | Directory.Delete(folder, false);
81 | }
82 | }
83 | catch (IOException)
84 | {
85 | }
86 | }
87 |
88 | public void Dispose()
89 | {
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Code/IPFilter/Services/Deployment/ClickOnce/RemoveUninstallEntry.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services.Deployment
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using Microsoft.Win32;
6 |
7 | public class RemoveUninstallEntry : IUninstallStep
8 | {
9 | private readonly UninstallInfo _uninstallInfo;
10 | private RegistryKey _uninstall;
11 |
12 | public RemoveUninstallEntry(UninstallInfo uninstallInfo)
13 | {
14 | _uninstallInfo = uninstallInfo;
15 | }
16 |
17 | public void Prepare(List componentsToRemove)
18 | {
19 | _uninstall = Registry.CurrentUser.OpenSubKey(UninstallInfo.UninstallRegistryPath, true);
20 | }
21 |
22 | public void PrintDebugInformation()
23 | {
24 | if (_uninstall == null)
25 | throw new InvalidOperationException("Call Prepare() first.");
26 |
27 | Console.WriteLine("Remove uninstall info from " + _uninstall.OpenSubKey(_uninstallInfo.Key).Name);
28 |
29 | Console.WriteLine();
30 | }
31 |
32 | public void Execute()
33 | {
34 | if (_uninstall == null)
35 | throw new InvalidOperationException("Call Prepare() first.");
36 |
37 | _uninstall.DeleteSubKey(_uninstallInfo.Key);
38 | }
39 |
40 | public void Dispose()
41 | {
42 | if (_uninstall != null)
43 | {
44 | _uninstall.Close();
45 | _uninstall = null;
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Code/IPFilter/Services/Deployment/ClickOnce/UninstallInfo.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services.Deployment
2 | {
3 | using System;
4 | using System.Linq;
5 | using Microsoft.Win32;
6 |
7 | public class UninstallInfo
8 | {
9 | public const string UninstallRegistryPath = @"Software\Microsoft\Windows\CurrentVersion\Uninstall";
10 |
11 | UninstallInfo() {}
12 |
13 | public static UninstallInfo Find(string appName)
14 | {
15 | var uninstall = Registry.CurrentUser.OpenSubKey(UninstallRegistryPath);
16 | if (uninstall == null) return null;
17 |
18 | return (from app in uninstall.GetSubKeyNames() let sub = uninstall.OpenSubKey(app)
19 | where sub != null && sub.GetValue("DisplayName") as string == appName
20 | select new UninstallInfo
21 | {
22 | Key = app,
23 | UninstallString = sub.GetValue("UninstallString") as string,
24 | ShortcutFolderName = sub.GetValue("ShortcutFolderName") as string,
25 | ShortcutSuiteName = sub.GetValue("ShortcutSuiteName") as string,
26 | ShortcutFileName = sub.GetValue("ShortcutFileName") as string,
27 | SupportShortcutFileName = sub.GetValue("SupportShortcutFileName") as string,
28 | Version = sub.GetValue("DisplayVersion") as string
29 | }).FirstOrDefault();
30 | }
31 |
32 | public string Key { get; set; }
33 |
34 | public string UninstallString { get; private set; }
35 |
36 | public string ShortcutFolderName { get; set; }
37 |
38 | public string ShortcutSuiteName { get; set; }
39 |
40 | public string ShortcutFileName { get; set; }
41 |
42 | public string SupportShortcutFileName { get; set; }
43 |
44 | public string Version { get; set; }
45 |
46 | public string GetPublicKeyToken()
47 | {
48 | var token = UninstallString.Split(',')
49 | .First(s => s.Trim().StartsWith("PublicKeyToken=", StringComparison.Ordinal)).Substring(16);
50 | if (token.Length != 16) throw new ArgumentException();
51 | return token;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Code/IPFilter/Services/Deployment/ClickOnce/Uninstaller.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services.Deployment
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 |
7 | public class Uninstaller
8 | {
9 | private readonly ClickOnceRegistry _registry;
10 |
11 | public Uninstaller()
12 | : this(new ClickOnceRegistry())
13 | {
14 | }
15 |
16 | public Uninstaller(ClickOnceRegistry registry)
17 | {
18 | _registry = registry;
19 | }
20 |
21 | public void Uninstall(UninstallInfo uninstallInfo)
22 | {
23 | var toRemove = FindComponentsToRemove(uninstallInfo.GetPublicKeyToken());
24 |
25 | Console.WriteLine("Components to remove:");
26 | toRemove.ForEach(Console.WriteLine);
27 | Console.WriteLine();
28 |
29 | var steps = new List
30 | {
31 | new RemoveFiles(),
32 | new RemoveStartMenuEntry(uninstallInfo),
33 | new RemoveRegistryKeys(_registry, uninstallInfo),
34 | new RemoveUninstallEntry(uninstallInfo)
35 | };
36 |
37 | steps.ForEach(s => s.Prepare(toRemove));
38 | steps.ForEach(s => s.PrintDebugInformation());
39 | steps.ForEach(s => s.Execute());
40 |
41 | steps.ForEach(s => s.Dispose());
42 | }
43 |
44 | private List FindComponentsToRemove(string token)
45 | {
46 | var components = _registry.Components.Where(c => c.Key.Contains(token)).ToList();
47 |
48 | var toRemove = new List();
49 | foreach (var component in components)
50 | {
51 | toRemove.Add(component.Key);
52 |
53 | foreach (var dependency in component.Dependencies)
54 | {
55 | if (toRemove.Contains(dependency)) continue; // already in the list
56 | if (_registry.Components.All(c => c.Key != dependency)) continue; // not a public component
57 |
58 | var mark = _registry.Marks.FirstOrDefault(m => m.Key == dependency);
59 | if (mark != null && mark.Implications.Any(i => components.All(c => c.Key != i.Name)))
60 | {
61 | // don't remove because other apps depend on this
62 | continue;
63 | }
64 |
65 | toRemove.Add(dependency);
66 | }
67 | }
68 |
69 | return toRemove;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Code/IPFilter/Services/Deployment/Updater.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.Linq;
4 | using System.Net.Http.Headers;
5 | using System.Web.Script.Serialization;
6 |
7 | namespace IPFilter.Services
8 | {
9 | using System;
10 | using System.Net.Http;
11 | using System.Threading.Tasks;
12 | using Models;
13 |
14 | class Updater
15 | {
16 | public async Task CheckForUpdateAsync(bool isPreReleaseEnabled = false)
17 | {
18 | const string latestReleases = "https://api.github.com/repos/DavidMoore/IPFilter/releases";
19 |
20 | using(var handler = new WebRequestHandler())
21 | using (var client = new HttpClient(handler))
22 | {
23 | client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
24 | client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("DavidMooreIPFilter", EntryPoint.Version.ToString()));
25 |
26 | // Get the latest releases from GitHub
27 | using (var response = await client.GetAsync(latestReleases,HttpCompletionOption.ResponseHeadersRead))
28 | {
29 | var content = await response.Content.ReadAsStringAsync();
30 | response.EnsureSuccessStatusCode();
31 |
32 | var serializer = new JavaScriptSerializer();
33 | var results = serializer.Deserialize>(content);
34 |
35 | var latest = results.FirstOrDefault(x => (isPreReleaseEnabled || !x.prerelease) && !x.tag_name.Equals("lists", StringComparison.OrdinalIgnoreCase));
36 | if (latest == null)
37 | {
38 | Trace.TraceWarning("Couldn't find a release from the list returned by GitHub");
39 | return null;
40 | }
41 |
42 | var asset = latest.assets.FirstOrDefault(x => x.name.Equals("IPFilter.msi", StringComparison.OrdinalIgnoreCase));
43 | if (asset == null)
44 | {
45 | Trace.TraceWarning("Couldn't find installer in the release assets for " + latest.name);
46 | return null;
47 | }
48 |
49 | var info = new UpdateInfo
50 | {
51 | Version = latest.tag_name,
52 | Uri = asset.browser_download_url.ToString()
53 | };
54 |
55 | return info;
56 | }
57 | }
58 | }
59 |
60 | class GitHubRelease
61 | {
62 | public string name { get; set; }
63 |
64 | public string tag_name { get; set; }
65 |
66 | public ICollection assets { get; set; }
67 |
68 | public bool prerelease { get; set; }
69 | }
70 |
71 | class GitHubAsset
72 | {
73 | public long id { get; set; }
74 |
75 | public string name { get; set; }
76 |
77 | public Uri browser_download_url { get; set; }
78 |
79 | public string content_type { get; set; }
80 |
81 | public long size { get; set; }
82 |
83 | public DateTimeOffset created_at { get; set; }
84 |
85 | public DateTimeOffset updated_at { get; set; }
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Services/DestinationPathsProvider.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.IO;
7 | using System.Linq;
8 | using Properties;
9 |
10 | public class DestinationPathsProvider
11 | {
12 | public IEnumerable GetDestinations(params string[] values)
13 | {
14 | if (values == null) return Enumerable.Empty();
15 |
16 | return values.Select(Environment.ExpandEnvironmentVariables)
17 | .Select(TrimSeparatorsAndWhitespace)
18 | .Distinct(StringComparer.OrdinalIgnoreCase);
19 | }
20 |
21 | string TrimSeparatorsAndWhitespace(string value)
22 | {
23 | // TODO: Handle unicode whitespace?
24 | char[] trimChars = {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, ' ', '\t'};
25 |
26 | return value.TrimStart().TrimEnd(trimChars);
27 | }
28 |
29 | public IEnumerable GetDestinations()
30 | {
31 | try
32 | {
33 | // Try to combine our defaults with the custom ones
34 | if (Config.Default.outputs == null)
35 | {
36 | return Enumerable.Empty();
37 | }
38 |
39 | return Config.Default.outputs.Select(ParseCustomPath);
40 | }
41 | catch (Exception ex)
42 | {
43 | Trace.TraceWarning("Had trouble getting the custom paths: " + ex);
44 | return Enumerable.Empty();
45 | }
46 | }
47 |
48 | PathSetting ParseCustomPath(string arg)
49 | {
50 | var separatorIndex = arg.IndexOf(';');
51 | var name = "(Untitled)";
52 | string path;
53 |
54 | if (separatorIndex > -1)
55 | {
56 | name = arg.Substring(0, separatorIndex);
57 | path = arg.Substring(separatorIndex);
58 | }
59 | else
60 | {
61 | path = arg;
62 | }
63 |
64 | path = TrimSeparatorsAndWhitespace(path);
65 |
66 | return new PathSetting(name, path);
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Services/ICacheProvider.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services
2 | {
3 | using System.Threading.Tasks;
4 | using Models;
5 |
6 | public interface ICacheProvider
7 | {
8 | Task GetAsync(FilterDownloadResult filter);
9 | Task SetAsync(FilterDownloadResult filter);
10 | }
11 | }
--------------------------------------------------------------------------------
/Code/IPFilter/Services/PathSetting.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.Services
2 | {
3 | public class PathSetting
4 | {
5 | public PathSetting(string name, string path)
6 | {
7 | Name = name;
8 | Path = path;
9 | }
10 |
11 | public string Name { get; set; }
12 | public string Path { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/Code/IPFilter/ViewModels/DelegateCommand.cs:
--------------------------------------------------------------------------------
1 | namespace IPFilter.ViewModels
2 | {
3 | using System;
4 | using System.Windows.Input;
5 |
6 | ///
7 | /// Implements a command that when executed runs a delegate action.
8 | ///
9 | public class DelegateCommand : ICommand
10 | {
11 | readonly Action