332 Commits

Author SHA1 Message Date
Gary Sharp d1aee95d35 qol: user photos to honour image orientation metadata 2026-03-09 18:59:20 +11:00
Gary Sharp 72a709c11d qol: Saved Exports: informational message 2026-03-05 15:36:18 +11:00
Gary Sharp 4f7f6db804 qol: option to view/print generated document inline 2026-03-01 14:15:27 +11:00
Gary Sharp 892299a791 bug: device enrolment match initially match on account name instead of account id 2026-02-25 15:35:02 +11:00
Gary Sharp 48512fa9d1 qol: offline domain join to reuse AD computer accounts
Replaces old behaviour of deleting and creating new accounts. Now when a device has a new name, its existing account is renamed and reused.
2026-02-25 14:34:34 +11:00
Gary Sharp 204d57a4a5 feature: display pdf attachments inline 2026-01-26 16:34:26 +11:00
Gary Sharp f807d75162 #180: bulk download device/job/user attachments 2026-01-26 15:04:04 +11:00
Gary Sharp e809c63e37 #184: optionally set managed group descriptions 2026-01-26 12:31:01 +11:00
Gary Sharp e1f1973520 feature: Bootstrapper secure server discovery 2026-01-22 15:26:23 +11:00
Gary Sharp 71fa53bfb2 bug: on the home page a white bar appeared at the bottom of the screen when there was a pending enrolment 2026-01-22 15:22:23 +11:00
Gary Sharp 834d2f8fae feature: #UserDetails[] expression helper ignores hide/obfuscate mode symbols 2026-01-22 15:20:23 +11:00
Gary Sharp 2ab765f2d7 bug: edge case where the assigned user is incorrectly set for login 2026-01-22 15:17:19 +11:00
Gary Sharp 04be92a1df feature: online session authentication and one-way decryption 2026-01-01 19:21:00 +11:00
Gary Sharp 4e7c7c117b fix activation registration after local-network-access policy introduction 2025-12-29 13:14:52 +11:00
Gary Sharp f975c55b8a bug: whitespace missing in device/user flag tooltips 2025-12-10 14:38:58 +11:00
Gary Sharp 9e0d832aa1 bug fix: save non-warranty purchase order reference 2025-12-04 14:48:34 +11:00
Gary Sharp 94a31282eb bug fix #185: fix document package generation 2025-12-03 15:49:10 +11:00
Gary Sharp 529bba5c72 resolves #179: filter noticeboard by job queue 2025-10-31 17:45:04 +11:00
Gary Sharp 8424a9a9a2 feature #180: bulk download document attachment instances 2025-10-31 14:58:54 +11:00
Gary Sharp 202bbb163b qol: lazy-loading of device profile organisation units 2025-10-31 13:58:59 +11:00
Gary Sharp 0e15d2a880 fix: device batch timeline link 2025-10-19 19:19:28 +11:00
Gary Sharp 89c14084f5 fix #181 - explicitly fetch DeviceComments when showing a device 2025-10-19 19:11:00 +11:00
Gary Sharp e2c07f019c v2.5 release 2025-09-28 11:24:29 +10:00
Gary Sharp 7603cac01a feature: scheduled flag removal 2025-09-19 12:18:45 +10:00
Gary Sharp 356762c811 feature: enrol devices bound to another domain 2025-09-18 18:27:29 +10:00
Gary Sharp deaac0f073 bug fixes 2025-09-11 15:57:16 +10:00
Gary Sharp de10f92c02 minor bug fixes 2025-09-10 15:23:06 +10:00
Gary Sharp 3e514fdf9a feature: device profiles - set assigned user for logon 2025-09-07 12:48:10 +10:00
Gary Sharp 36c24542e1 update: minor changes to document template bulk generation 2025-09-07 12:06:23 +10:00
Gary Sharp ca7193a8fc feature: device document template bulk generation 2025-08-17 18:56:56 +10:00
Gary Sharp 676ff82e4b qol: user device assignment helper methods for expressions 2025-08-13 16:15:53 +10:00
Gary Sharp 53fdea5325 update dependencies: jQuery, jQuery UI, jQuery Validate, jQuery Validate Unobtrusive, SignalR 2025-08-09 19:00:29 +10:00
Gary Sharp 0c0a7bf297 use fetch over $.getJSON 2025-08-09 18:43:44 +10:00
Gary Sharp 57aeaa3eaa remove disconnected dialogs 2025-08-09 16:17:10 +10:00
Gary Sharp d291ad464f qol: online upload: remove dialog when attachment received 2025-08-01 11:40:02 +10:00
Gary Sharp be7ee4cae8 feature: flag permissions
feature: flag permissions
2025-07-31 19:03:33 +10:00
Gary Sharp 7deead494b security: use more antiforgery tokens 2025-07-31 16:18:32 +10:00
Gary Sharp fd43d85778 qol: simplify and Tuple.Create() 2025-07-20 15:58:42 +10:00
Gary Sharp 4c27b0ff3c qol: use type keywords and more interpolated strings 2025-07-20 15:44:44 +10:00
Gary Sharp 58546ed16d qol: simplify default 2025-07-20 15:31:35 +10:00
Gary Sharp 9656c15c4b qol: simplify calls 2025-07-20 15:20:22 +10:00
Gary Sharp 1add4ee0f5 qol: inline variable declaration 2025-07-20 15:12:33 +10:00
Gary Sharp 5792771ea1 qol: formatting 2025-07-20 13:47:56 +10:00
Gary Sharp 49da389c2c qol: unnecessary usings 2025-07-20 12:00:57 +10:00
Gary Sharp 7faebe56a8 qol: interpolated strings 2025-07-20 11:59:28 +10:00
Gary Sharp 4e518d6684 qol: remove this 2025-07-20 11:39:42 +10:00
Gary Sharp b4e54c9edf qol: simplify accessors 2025-07-20 11:24:04 +10:00
Gary Sharp 3aeb9374a9 feature: job export supports individual user details 2025-07-17 13:09:10 +10:00
Gary Sharp 0905a02aea #172 attempt reconnect if signalR disconnects 2025-07-17 12:44:06 +10:00
Gary Sharp f8fd1a58a3 #145 add comments for devices 2025-07-17 11:40:50 +10:00
Gary Sharp 2184c9e22e add comments for users [#145] 2025-07-12 19:55:58 +10:00
Gary Sharp 42e9045d5e bug fixed: users with Remove Own Attachment permission had to refresh before they could remove 2025-07-12 19:51:54 +10:00
Gary Sharp 092c6249ee refactor job logs/comments [#145] 2025-07-12 16:48:27 +10:00
Gary Sharp b56e82d5c4 resolves #151: BYOD sub type added to User - Management jobs 2025-07-11 13:25:45 +10:00
Gary Sharp 54a578f4a1 feature: custom device models 2025-07-11 12:55:50 +10:00
Gary Sharp 3f1fa3d7de Resolves #173: Feature Request: Filter box for device/user flag assignment 2025-07-05 17:01:19 +10:00
Gary Sharp caa0bedc93 Update #9: by default, hide decommissioned devices from search 2025-07-05 14:14:07 +10:00
Gary Sharp 6e99b4503b feature: device model/profile decommissioning 2025-07-05 13:55:44 +10:00
Gary Sharp 583552ffdd feature: batch device decommissioning 2025-07-03 19:13:52 +10:00
Gary Sharp 4660425ccc qol: quick search input to have fixed width 2025-07-03 12:53:47 +10:00
Gary Sharp 8b8b26116e feature: specify device profile/batch during pending enrolment 2025-06-27 14:51:03 +10:00
Gary Sharp 57ae665070 bug: remove stray braces from the Device Batch and Profile views 2025-06-18 13:18:57 +10:00
Gary Sharp f06a37ebb4 bug: remove stray brace from device model view 2025-06-18 13:09:31 +10:00
Gary Sharp ee273ffe03 bug: show correct error message if Online Upload session fails 2025-06-18 13:03:47 +10:00
Gary Sharp 0672bdb7c0 qol: avoid license/activation confusion by renaming license Activate button to Apply 2025-06-18 13:03:20 +10:00
Gary Sharp 662e1ed231 feature: Upload Online Attachments front end 2025-06-15 18:01:01 +10:00
Gary Sharp cc1f224456 feature: Upload Online Attachments 2025-06-15 17:56:13 +10:00
Gary Sharp 6e2c36d4ae bug: enforce device details scope 2025-06-15 17:51:35 +10:00
Gary Sharp bccbb20b84 bug fix #171 device batch attachments not working 2025-03-14 16:40:55 +11:00
Gary Sharp fd3eb80648 bug fix: online services authentication token not expiring 2025-03-10 14:35:57 +11:00
Gary Sharp 9a171a8417 bug fix #168 device export: batch filtering inverted 2025-03-04 17:34:51 +11:00
Gary Sharp eb1ffae212 bug fix: export user details without User.ShowDetails claim 2025-02-27 13:54:43 +11:00
Gary Sharp d31c2db43b regression fix: device page header overflow 2025-02-27 13:17:04 +11:00
Gary Sharp 62a5da7513 regression fix: quick search 2025-02-27 13:16:37 +11:00
Gary Sharp eb3ed7f819 regression fix: ajax icons not appearing 2025-02-26 17:59:08 +11:00
Gary Sharp a9687b5f25 feature: user details are individually exported; shared export field renderer 2025-02-26 13:23:24 +11:00
Gary Sharp 539503133a qol: fix System.Net.Http dependencies 2025-02-26 13:16:29 +11:00
Gary Sharp d2b5e7ab6a exporting: remember last options for documents/user flags/device flags 2025-02-26 13:06:34 +11:00
Gary Sharp 04430bba50 bug fix #162 document template id closure not updated on subsequent invoke 2025-02-17 18:08:22 +11:00
Gary Sharp 162ca20046 fix: enrollment > enrolment 2025-02-16 17:13:55 +11:00
Gary Sharp 786d4809b5 feature: document instance exporting
resolves #154
2025-02-14 16:05:23 +11:00
Gary Sharp d3cef11796 feature: exports api refactoring 2025-02-14 13:07:30 +11:00
Gary Sharp 2c215e4856 feature: saved exports; include jobs and flags 2025-02-13 18:20:47 +11:00
Gary Sharp eec8ba438c feature: saved exports view/edit/trigger 2025-02-12 18:36:30 +11:00
Gary Sharp 59dc0b9d5a regression bug: csv export was failing 2025-02-12 15:39:18 +11:00
Gary Sharp ac24055365 feature: saved exports
initial - not feature complete
2025-02-09 17:14:04 +11:00
Gary Sharp 2fce645066 refactor: simplify export metadata construction 2025-02-07 16:12:19 +11:00
Gary Sharp 67f1c2a5d1 refactor: make exporting consistent 2025-02-06 19:14:36 +11:00
Gary Sharp f946f3250c feature: collect MDM Hardware Data (Autopilot hash) during device enrolment
resolves #160
2025-02-05 17:38:57 +11:00
Gary Sharp 0853bcee50 feature: initial comments template
completes #150
2025-01-24 17:28:27 +11:00
Gary Sharp 408e1c4c14 qol: make Online Services Connect more reliable 2025-01-23 15:14:30 +11:00
Gary Sharp dee54bb6d7 feature: choose which noticeboard components to display with the link builder
also restyle noticeboard using flex
2025-01-22 17:58:45 +11:00
Gary Sharp 50603210b0 update expression browser route 2025-01-19 21:44:50 +11:00
Gary Sharp b640e51874 feature: add clipboard link to various identifiers (resolves #148) 2025-01-18 13:33:55 +11:00
Gary Sharp 7a336e699a request: device profile OUs should be sorted alphabetically 2025-01-17 13:05:06 +11:00
Gary Sharp a3e1e1d030 feature: update expression browser and add plugin expression extensions 2025-01-16 12:36:01 +11:00
Gary Sharp 963970feeb update list of shipped binaries for plugins 2025-01-15 15:36:44 +11:00
Gary Sharp 296f7a13fd feature: online services push notifications 2025-01-01 19:23:18 +11:00
Gary Sharp 4006bdbcc6 bug: AD ignore foreign security principals 2024-12-30 14:25:08 +11:00
Gary Sharp 26bac92e5a qol: online services authentication expiration offset 2024-12-30 14:24:28 +11:00
Gary Sharp b6dfaa3445 feature: online activation 2024-12-28 08:12:31 +11:00
Gary Sharp 39ba206831 qol: attachments newest to oldest and adjustments so attachments are shown 2024-12-18 20:09:10 +11:00
Gary Sharp 7550e0e45d qol: wait for attachment thumbnail generation and show additional file type-specific icons 2024-12-18 19:37:15 +11:00
Gary Sharp 9f10eeeb70 qol: make attachment comments optional 2024-12-18 18:40:23 +11:00
Gary Sharp 77acf0bfdb bug fix #104 hardware mac addresses not saving on MacOSX enrol 2024-12-15 18:00:40 +11:00
Gary Sharp f0dfe45cd4 bug fix #153 save job log message when bulk generating document templates 2024-12-15 17:42:32 +11:00
Gary Sharp fb6432a5c8 feature: add email address to organisation address (resolves #156) 2024-12-15 17:08:49 +11:00
Gary Sharp b15917254f clean up: remove unused namespaces 2024-12-14 16:55:37 +11:00
Gary Sharp 8abe31f430 qol: use unified exporting for logs 2024-12-14 16:49:31 +11:00
Gary Sharp a6b9cd1af2 feature: job exporting (resolves #155) 2024-12-12 16:02:12 +11:00
Gary Sharp 90c709c4c1 feature: #158 adds job preference option to include all attachments on lodgment 2024-12-11 17:01:42 +11:00
Gary Sharp 25e3a8e1f1 feature: add select all/none attachments when submitting waranty/repair/insurance job 2024-12-11 15:15:22 +11:00
Gary Sharp 343f20980d qol: impove plugin installation/update api 2024-12-11 15:13:26 +11:00
Gary Sharp f36871abe2 feature: dont disable a computer account if a duplicate, commissioned device exists 2024-12-11 15:11:33 +11:00
Gary Sharp cb354cd13e skip disabling computer account during decommissioning if another active device with the same name exists 2024-10-10 16:17:38 +11:00
Gary Sharp 09f9f2d427 version bump for v2.4 release 2024-09-26 16:33:55 +10:00
Gary Sharp 390857e065 attempt to preserve IIS bindings so future installations can restore them 2024-09-25 18:16:32 +10:00
Gary Sharp e0d620bf67 feature: lodge insurance 2024-09-25 16:21:51 +10:00
Gary Sharp 78b7b059ea feature: expression helpers - basic email sending 2024-05-16 22:11:10 +10:00
Gary Sharp bb846d14c5 feature: job preferences - On Device Ready For Return expression 2024-05-16 22:09:42 +10:00
Gary Sharp 565e1707ce fix: importing new devices fails for domain joined machines with assigned users 2024-04-08 18:11:03 +10:00
Gary Sharp a675e4a6e9 Feature: Provide CurrentDeviceUserAssignments for use by document expressions 2024-03-28 11:30:39 +11:00
Gary Sharp d8eb8fec83 global support license and validation 2024-01-26 16:23:26 +11:00
Gary Sharp f90eda4101 add pending enrollment identifier 2024-01-24 16:38:50 +11:00
Gary Sharp 37e2e5a08c feature: pending device enrollment approval 2024-01-21 14:06:11 +11:00
Gary Sharp 6af83cbdb2 Merge branch 'device-flags' 2024-01-14 19:13:51 +11:00
Gary Sharp 5f4cb20900 #138 display device flags where appropriate in the UI 2024-01-14 19:13:09 +11:00
Gary Sharp aee467cb53 #138 add/remove individual device flags UI 2024-01-14 18:29:58 +11:00
Gary Sharp 6ce6e2cccf #138 device flags configuration API and UI 2024-01-14 17:59:30 +11:00
Gary Sharp cd858c2215 #138 device flag searching 2024-01-14 17:56:20 +11:00
Gary Sharp 7e07c07171 #138 device flag export service 2024-01-14 17:55:20 +11:00
Gary Sharp 15e2806731 #138 device flag services 2024-01-14 17:54:06 +11:00
Gary Sharp 53baf4eb78 #138 device flag authorization claims 2024-01-13 16:56:23 +11:00
Gary Sharp 8c48ab6ecd #138 device flag models 2024-01-13 16:33:03 +11:00
Gary Sharp 8afe4195a9 #107 allow moving of device OU when changing profile 2024-01-12 12:53:50 +11:00
Gary Sharp dcc4fcb984 #107 support moving profiles devices when changing the organisational unit 2024-01-12 12:52:39 +11:00
Gary Sharp 4631903019 refactor: split windows/mac enrolment 2024-01-12 11:13:48 +11:00
Gary Sharp 85d51c0e45 Fix #141 preserve computer names for non-domain joined devices 2024-01-11 16:27:40 +11:00
Gary Sharp 974a07f3bb show a warning when a device profile OU couuld not be found 2023-11-24 12:58:12 +11:00
Gary Sharp b576aec641 feature: computer name template testing 2023-11-24 11:57:26 +11:00
Gary Sharp a2aaa4c913 #140 make additional fontawesome icons available for user flags 2023-11-12 21:25:16 +11:00
Gary Sharp 318a70d9a3 #140 upgrade fontawesome 2023-11-12 21:24:16 +11:00
Gary Sharp 53e57d4017 feature: user flag assignment exporting 2023-11-11 20:04:52 +11:00
Gary Sharp 143dc1b3e6 bug fix: when exporting devices refresh details before extracting records instead of after 2023-11-11 18:59:59 +11:00
Gary Sharp a4f18d1d49 initial refactor of device export so it can be reused for future exporting 2023-11-11 17:52:24 +11:00
Gary Sharp 46222f2a78 hide decommissioned device profiles and batches by default when updating a devices batch or profile 2023-11-09 20:48:16 +11:00
Gary Sharp 2e091383ec fixes #134 - hide decommissioned device profiles by default 2023-11-09 20:05:25 +11:00
Gary Sharp 7ace4d1c7e add padding row to device batch list to maintain alternating row styles 2023-11-09 19:55:22 +11:00
Gary Sharp f901d45d78 support Firefox checkbox inside anchor element when logging warranty 2023-11-09 14:05:48 +11:00
Gary Sharp 028d1ccc64 remove localhost-only requirement for post upgrade maintenance 2023-11-09 13:43:51 +11:00
Gary Sharp 7b6a44921d plugin library: link to configuration instead of disabled "installed" button 2023-11-09 13:43:09 +11:00
Gary Sharp 23406e5e39 include more inner exception data in system logging 2023-11-08 16:51:08 +11:00
Gary Sharp 83557e6f0c include external job references in detailed search 2023-11-08 16:49:19 +11:00
Gary Sharp 1c90c89158 upgrade ssh.net 2023-11-06 11:25:38 +11:00
Gary Sharp 9924153e82 upgrade dotnet framework 4.5 to 4.6 2023-11-06 11:24:58 +11:00
Gary Sharp 3ec2ea7d37 add job log when re-opening 2023-11-03 15:50:01 +11:00
Gary Sharp b6945d9bbd fix #135 avoid failing device import/enrol when unable to set AD description 2023-10-12 17:37:42 +11:00
Gary Sharp 6740a7479a bug: disable image (camera) attachment upload unless over HTTPS
Browser restriction: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security
2023-10-12 16:34:35 +11:00
Gary Sharp a3fb09440d feature: document template QR Code image extensions 2023-10-12 16:07:14 +11:00
Gary Sharp 7353405b16 use native media devices to capture webcam images 2023-10-08 16:25:32 +11:00
Gary Sharp 6fcb1a1eae fixup Disco -> Disco ICT 2023-07-26 19:01:31 +10:00
Gary Sharp e204ea4d5f version bump for release candidate 2023-06-10 17:18:04 +10:00
Gary Sharp 0a4a2816a0 feature: bulk generate documents for device batches, models and profiles 2023-05-04 15:30:04 +10:00
Gary Sharp 473b02f718 bug fix: TryBinaryNumericEncode on small numbers 2023-05-04 15:27:46 +10:00
Gary Sharp 5a2c29ea0b bug: unable to bulk generate documents for devices/job; honor the id sort order 2023-05-04 13:06:04 +10:00
Gary Sharp 1a8ec32f0f view batch devices from index
requested by @RB
2023-04-28 15:39:44 +10:00
Gary Sharp 9e7daf2043 data tables remember pagination size preference 2023-04-28 14:36:01 +10:00
Gary Sharp 0c690cff5a bulk generation refactoring 2023-04-27 14:54:32 +10:00
Gary Sharp b55b77de9f initialize plugin scheduled tasks when installed 2023-04-26 18:16:46 +10:00
Gary Sharp 937508c440 document template user flag rules should use the creator user for flag assignment 2023-04-16 15:14:14 +10:00
Gary Sharp cfe4c4b912 refactor expression caching; add document fields 2023-04-16 14:02:35 +10:00
Gary Sharp d75663a219 document template on import user flag rules 2023-04-16 09:50:19 +10:00
Gary Sharp 1e24844e88 respect whitespace when rendering logs 2023-04-14 16:40:30 +10:00
Gary Sharp 273b67c422 support user details category in document bulk generation 2023-04-14 16:40:13 +10:00
Gary Sharp 13549e7ec4 bug fix: calculate task progress offsets and multiplier correctly 2023-04-14 16:39:14 +10:00
Gary Sharp bbb40bd5c4 register plugin references before initialization 2023-04-14 16:38:38 +10:00
Gary Sharp 22dad072b9 refactor user details plugin interface 2023-04-14 16:37:42 +10:00
Gary Sharp 215e9863a2 bug: client was unable to run djoin.exe
perhaps a behaviour change between dotnet 4.5 and 4.5.2. It was looking for djoin.exe in the SysWow directory.
2023-02-12 18:42:19 +11:00
Gary Sharp 190abc72dd bug: when adding computer offline, computer name should not be required 2023-02-09 20:03:20 +11:00
Gary Sharp af75a55d44 AddFlag helper method for expressions 2023-02-08 18:03:55 +11:00
Gary Sharp e05206d405 bug: correctly store email settings 2023-02-08 16:57:49 +11:00
Gary Sharp 53eed02a4f globally enable TLS1.2 2023-02-08 16:52:28 +11:00
Gary Sharp 57a7f67c3a remove device custom details
this plugin functionality has never been used
2023-02-08 16:24:21 +11:00
Gary Sharp 47e11c0fd6 bugs: minor 2023-01-29 10:10:37 +11:00
Gary Sharp 708ca16de8 minor framework updates 2023-01-29 10:07:19 +11:00
Gary Sharp 535a3f2967 bug: dont throw if unable to resolve online services 2023-01-28 13:37:27 +11:00
Gary Sharp ebd1c79f64 fix: increase pdf generated image resolution 2022-12-15 16:23:54 +11:00
Gary Sharp 7194dd612d bug: image montage layout 2022-12-15 16:08:24 +11:00
Gary Sharp 584bc07824 minor: when creating a job scoped document template default to all job types 2022-12-15 15:00:31 +11:00
Gary Sharp 2bd9238090 feature: support hiding user details with asterisk and ampersand suffixes 2022-12-09 12:23:41 +11:00
Gary Sharp b11fa5b1d6 Newtonsoft.Json 13.0.2 update 2022-12-08 11:15:34 +11:00
Gary Sharp 7667eaa49e maintenance: improved css minify 2022-12-04 15:21:04 +11:00
Gary Sharp 9f7c0f965e Microsoft.Owin update 2022-12-04 15:12:33 +11:00
Gary Sharp a274a33cf6 Newtonsoft.Json update 2022-12-04 15:05:51 +11:00
Gary Sharp 4acfd54319 Merge pull request #119 from garysharp/dependabot/nuget/Disco.Web.Extensions/Newtonsoft.Json-13.0.1
Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Web.Extensions
2022-12-04 14:20:40 +11:00
dependabot[bot] 941f1cf13e Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Web.Extensions
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 6.0.3 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/6.0.3...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-04 03:20:30 +00:00
Gary Sharp f225edd2d4 Merge pull request #118 from garysharp/dependabot/nuget/Disco.Data/Newtonsoft.Json-13.0.1
Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Data
2022-12-04 14:20:06 +11:00
Gary Sharp 5824680e2a Merge pull request #117 from garysharp/dependabot/nuget/Disco.Models/Newtonsoft.Json-13.0.1
Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Models
2022-12-04 14:19:55 +11:00
Gary Sharp ed4ec890da Merge pull request #120 from garysharp/dependabot/nuget/Disco.Client/Newtonsoft.Json-13.0.1
Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Client
2022-12-04 14:19:39 +11:00
Gary Sharp 56453fa081 Merge pull request #121 from garysharp/dependabot/nuget/Disco.Services/Newtonsoft.Json-13.0.1
Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Services
2022-12-04 14:19:25 +11:00
dependabot[bot] 11c824518f Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Client
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 6.0.3 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/6.0.3...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-04 03:18:33 +00:00
dependabot[bot] d720303a83 Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Data
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 6.0.3 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/6.0.3...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-04 03:18:32 +00:00
dependabot[bot] 02b29948ce Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Models
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 6.0.3 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/6.0.3...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-04 03:18:29 +00:00
dependabot[bot] a152365312 Bump Newtonsoft.Json from 6.0.3 to 13.0.1 in /Disco.Services
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 6.0.3 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/6.0.3...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-04 03:18:22 +00:00
Gary Sharp dba6c383f6 moment.js update 2022-12-04 14:17:31 +11:00
dependabot[bot] ba32b3b717 Bump Moment.js from 2.7.0 to 2.29.4 in /Disco.Web
Bumps [Moment.js](https://github.com/timrwood/moment) from 2.7.0 to 2.29.4.
- [Release notes](https://github.com/timrwood/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/timrwood/moment/compare/2.7.0...2.29.4)

---
updated-dependencies:
- dependency-name: Moment.js
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-04 14:00:00 +11:00
Gary Sharp 51cebd0706 maintenance: update T4MVC 2022-12-04 14:00:00 +11:00
Gary Sharp 983bdbefb9 maintenance: rebuild bundling/minify 2022-12-04 13:59:59 +11:00
Gary Sharp be99083d6a feature: bulk generating user documents 2022-12-04 13:47:24 +11:00
Gary Sharp 4c19a1ef63 minor: return correct content type for jpg 2022-12-04 13:47:24 +11:00
Gary Sharp 4f9f0fd0a8 maintenance: plugin refactoring 2022-12-04 13:47:24 +11:00
Gary Sharp 05593a1466 maintenance: document template refactoring 2022-12-04 13:47:23 +11:00
Gary Sharp 2873b4be8a bug: log detected thumbnail after collecting more information 2022-12-04 13:47:23 +11:00
Gary Sharp 7a1ff211a0 feature: user contact details 2022-12-04 13:47:23 +11:00
Gary Sharp fcf70f724e maintenance: dependency updates 2022-12-04 13:47:22 +11:00
Gary Sharp 99be87ed9c maintenance: AD integration refactoring 2022-12-04 13:47:22 +11:00
Gary Sharp 261baf669e fix: add accept attribute to file pickers 2022-12-04 13:47:22 +11:00
Gary Sharp b0fddd491b fix: simplify document template managed groups 2022-12-04 13:47:21 +11:00
Gary Sharp dff47c0e8b feature: export device custom details 2022-12-04 13:47:21 +11:00
Gary Sharp ed58619919 fix: configuration optimization 2022-12-04 13:47:20 +11:00
Gary Sharp 2a2731b9f3 feature: task-based document bulk generation 2022-12-04 13:47:20 +11:00
Gary Sharp 13e666d95a feature: document handlers 2022-12-04 13:47:19 +11:00
Gary Sharp 96cccab958 feature: pdf field locations cached 2022-12-04 13:47:18 +11:00
Gary Sharp e463a7361e feature: email address and phone numbers as links 2022-12-04 13:47:17 +11:00
Gary Sharp 58e467b6d6 feature: support importing device computer name 2022-12-04 13:47:17 +11:00
Gary Sharp 81bd6ccd83 bug: importing a PDF attachment fails if the template or target no longer exist 2022-09-07 18:08:28 +10:00
Gary Sharp 3e7db6b552 qol: remove exceptionless 2022-02-18 13:57:42 +11:00
Gary Sharp 09a6369693 qol: update javascript bundling 2022-02-18 13:52:31 +11:00
Gary Sharp 9bfeff8c42 feature: expose custom details to expressions
custom details can now be easily retrieved from any expression
2021-02-07 18:17:03 +11:00
Gary Sharp 3e57af394d feature: custom details first-class
custom details (such as those from the UserDetails plugin) can now be more deeply integrated throughtout the system
2021-02-07 18:15:52 +11:00
Gary Sharp e11d0871c4 feature: add reply-to address for email service 2021-02-05 15:24:20 +11:00
Gary Sharp f7fdfb0c8a maintenance unify document generation ui 2021-01-13 15:41:51 +11:00
Gary Sharp 806aadd161 feature: email service and configuration 2021-01-10 13:57:25 +11:00
Gary Sharp af4a94870e additional device details 2020-12-04 15:51:23 +11:00
Gary Sharp 486ce17857 Feature: Export device hardware audit information
Allows processor, memory, disk and network adapter information to be included in device exports
2020-11-29 16:46:20 +11:00
Gary Sharp f82b47dd46 Feature: Display detailed device hardware audit information
The device Details tab now displays processor, memory, disk and network adapter information collected at the previous enrolment
2020-11-29 16:45:10 +11:00
Gary Sharp f1d27732c7 Feature: Collect additional hardware audit information
When devices enrol the client collects processor, memory, disk and network information which is persisted in the database
2020-11-29 16:43:44 +11:00
Gary Sharp 28e5901929 Feature: Device Batch Attachments
allows for attachments to be uploaded and associated with Device Batches
2020-11-29 16:41:56 +11:00
Gary Sharp e531ffe2b7 support active directory trust relationships 2020-11-26 15:01:03 +11:00
Gary Sharp 4fca015afa bug fix: delete devices with assignment history 2020-11-13 20:15:31 +11:00
Gary Sharp a80ed14038 bug fix: csv device import first record was skipped 2020-09-10 12:43:03 +10:00
Gary Sharp f3e0939a06 Bug Fix: Ldap Query Escaping 2018-05-21 16:22:41 +10:00
Gary Sharp 2cd6ddfa24 Bug: EntityFramework does not understand Tuple.Create 2018-04-26 15:38:25 +10:00
Gary Sharp b4a30061e3 Bug fix #109 SQL Server 2016 Support 2018-04-23 18:21:44 +10:00
Gary Sharp cb033c0b70 Allow remote maintenance via web.config setting 2018-04-19 17:11:19 +10:00
Gary Sharp 3c197e7b2f Bug: Correctly write enrolment log device info 2018-04-19 17:09:34 +10:00
Gary Sharp 9f62da9ccd Bug: Stale cache when adding user flag in expression 2018-04-19 17:08:43 +10:00
Gary Sharp df721d7d6e Searching: Add detail to device search results 2018-04-19 17:06:58 +10:00
Gary Sharp 8278efb9a6 Bug: AD distinguishedName attribute name 2018-04-19 17:05:02 +10:00
Gary Sharp d477ad5d9c Client: Use UUID if no serial number is found 2018-04-19 17:04:25 +10:00
Gary Sharp 0755b6fc8a Workaround: Dont remove client certificates 2018-04-19 17:03:21 +10:00
Gary Sharp 78dd494304 Fix: Device import case-sensitive serial numbers 2018-04-19 17:02:38 +10:00
Gary Sharp 0497e145ea Bug fix: Allow for unexpected cert subject names 2017-05-25 13:47:17 +10:00
Gary Sharp 4d6aa18095 Perf: Save only when user updated 2017-03-29 13:00:15 +11:00
Gary Sharp fdf1bd4bc6 UI: Device models list, default hide decommissioned 2017-03-29 12:55:07 +11:00
Gary Sharp 82183f6508 UI: Device Table hide/show decommissioned devices 2017-03-29 12:18:37 +11:00
Gary Sharp d6ee70b860 Fix: NetBIOS computer name limit correction
16 character limit, not 24 as previously thought. Due to appended '$'
for machine accounts, a character limit of 15 is now enforced.
2017-03-29 12:17:01 +11:00
Gary Sharp 5ce9e51ae7 Feature: MS Excel (xlsx) Import/Export
Microsoft Excel files can be used to import/export devices. Several
import bugs were also fixed in the process.
2017-03-25 15:37:28 +11:00
Gary Sharp ed66f4f285 Tidy: Sort/remove usings, simplify names 2017-03-25 15:29:51 +11:00
Gary Sharp 526f8547f7 Client/Bootstrapper Fix: Try gathering model from BaseBoard 2017-03-24 15:50:18 +11:00
Gary Sharp d8e93cb31b Fix: Generate pdf for unimported users
If a user wasn't previously imported into the database generating of
PDFs failed. This change attempts to import the user if its not found.
2017-03-24 15:36:05 +11:00
Gary Sharp 5fa74a8cc0 Bug fix #106 - job queues: case-sensitive usernames 2016-12-08 17:01:13 +11:00
Gary Sharp a0d643eda1 Version bump for #103 2016-11-21 16:40:50 +11:00
Gary Sharp 0887eb447e Bug fix #103 Save User Flag assignment to DB 2016-11-21 16:02:45 +11:00
Gary Sharp d9cade74b6 Version bump for v2.2 2016-11-17 00:26:46 +11:00
Gary Sharp 67261cd5b8 Fix: pdf insert blank pages 2016-11-16 00:19:29 +11:00
Gary Sharp aca037ecf8 Feature: Document Template Packages
Document Templates can be grouped into a package and generated on-demand
in the same was as individual document templates. Packages can be
generated in bulk.
2016-11-14 01:21:23 +11:00
Gary Sharp ef8df08e29 Page Identifiers in OnAttachmentImportExpression 2016-11-11 12:07:51 +11:00
Gary Sharp 4c91d03385 Hide Document Templates & UI Tweaks
Flag Document Templates as hidden. UI changes aim to improve visibility
of used features in lists.
2016-11-10 17:42:10 +11:00
Gary Sharp b52cbcb94a Job Expressions
Expressions can be triggered when jobs are created and closed
2016-11-09 22:26:43 +11:00
Gary Sharp 065b14b158 User Flag Expressions
Adds the ability to have expressions evaluated when flags are added and
removed.
2016-11-09 20:06:24 +11:00
Gary Sharp cbf16a41a6 Additional AD Helper Methods
Useful when referenced from expressions
2016-11-09 20:03:50 +11:00
Gary Sharp f74d49b96e Fix Device Batch Managed Groups 2016-11-09 16:27:07 +11:00
Gary Sharp afa548fb70 Device enrolment with no model information
An exception was previously thrown when a device lacking
manufacturer/model information attempted to enrol.
2016-11-09 15:43:44 +11:00
Gary Sharp c72c18e825 Bug Fix #98 Ensure Usernames are not case-sensitive 2016-11-03 17:12:50 +11:00
Gary Sharp 6df2e16a7f Bug Fix #97 - Removed Organisation Address References
Remove Device Profile references to an address when its removed, and
anticipate missing addresses where referenced.
2016-11-03 16:47:07 +11:00
Gary Sharp 3e4dda683d Bug Fix: #88 Update PList Parsing
Various versions of OSX/MacOS are producing slightly different PList
files. The PList library was having difficulty with some versions. An
updated PList library has been included and source changed to utilise
this library.
2016-11-02 17:04:00 +11:00
Gary Sharp dc8a5ab806 Bug Fix #101 - Rename ZXing Dependency 2016-11-02 15:28:41 +11:00
Gary Sharp 0f20b16f41 Decommission AD descriptions #96 2016-10-10 20:11:03 +11:00
Gary Sharp 23975f8fc3 Add date-based managed group filtering 2016-10-10 19:21:01 +11:00
Gary Sharp 062769a7e3 Bug Fix #102 - Ignore WLAN service disabled 2016-10-10 15:35:32 +11:00
Gary Sharp 1dfa3f4f15 Use Json.Net for MVC ValueProviderFactory 2016-10-06 19:39:19 +11:00
Gary Sharp e082c22983 Critical function message wording
Critical functions require a localhost connection. Instead of reporting
503 Service Unavailable, report a useful message.
2016-10-06 19:33:34 +11:00
Gary Sharp b286f8473a Changed wireless profile client messages
These messages are intended to be informative, but were interpretted as
errors by some.
2016-10-06 18:33:29 +11:00
Gary Sharp 0a67e9544d Bug Fix #87 - Scroll multiple devices on create job 2016-09-29 20:02:37 +10:00
Gary Sharp 687713428b Consistent style of attachments 2016-09-29 19:37:42 +10:00
Gary Sharp aeb9eb4d3a Style bundling update
Using VS extension: 'Web Compiler'
2016-09-29 19:28:57 +10:00
Gary Sharp 9baca7f633 Javascript bundling updated
Using VS extension: 'Bundler & Minifier'
2016-09-29 18:34:06 +10:00
Gary Sharp 5fcb81e9e8 Bug Fix #80 - Prompt for attachment comment 2016-09-29 18:32:52 +10:00
Gary Sharp 3c521541fd Bug Fix #79 Ban '/' from serial numbers 2016-09-29 17:55:39 +10:00
Gary Sharp 669de7e46b Bug Fix: #76 importing devices 2016-09-29 17:35:41 +10:00
Gary Sharp a15f3daad5 Bug Fix: Disco Client 32-bit regression 2016-09-29 17:01:23 +10:00
Gary Sharp 86faf02cf0 scheduled tasks tidy up 2016-09-28 21:20:18 +10:00
Gary Sharp 27c21175d7 Certificate/wireless plugins; major refactoring
Migrate much of BI to Services.
Added Wireless Profile Provider plugin feature.
Added Certificate Authority Provider plugin feature.
Modified Certificate Provider plugin feature.
Database migration v17, for Device Profiles.
Enrolment Client Updated to support CA Certificates, Wireless Profiles
and Hardware Info.
New Client Enrolment Protocol to support new features.
Plugin Manifest Generator added to main solution.
Improved AD search performance.
2016-09-28 20:17:55 +10:00
Gary Sharp 489a5df7cc Preview document template pdf
Renders document template pdfs to an image which is displayed in the
configuration ui.
2016-09-21 19:55:57 +10:00
Gary Sharp 85425d2a1f Improve QR binary encoding compression 2016-09-21 19:55:00 +10:00
Gary Sharp 4b866c9486 Escape AD Distinguished Names that contain "/"
for EFC
2016-09-15 19:45:14 +10:00
Gary Sharp acfa0e5094 Document Template Binary QR Codes
The QR Code now stores the data using a custom binary encoding format.
This reduces the amount of data stored by ~30%. This increases the
chance of a lower-version QR Code being used and the likelyhood of even
small QR Codes being detected. Compatibility with previously generated
documents is maintained.
2016-09-15 19:27:52 +10:00
Gary Sharp 5ea9a814d6 Pdf Import Rewrite
Pdf Import rewritten to greatly improve QR Code detection, reduce
reliance on iTextSharp and improve thumbnails. Fixes #50
2016-09-01 18:31:35 +10:00
Gary Sharp 44f6d325db Update: Mac Enrol using SshNet
SshNet supports required cryptographic algorithms. Simplified data
gathering using plist/xml format from system_profiler. Fixes #88 and #92
2016-09-01 18:31:22 +10:00
Gary Sharp 4e6093702d Improvement: Simplify JobTable SQL Generation
New EF expressions which simplify SQL and avoid some strange behaviour
on SQL Express.
2016-05-30 14:37:12 +10:00
Gary Sharp d955addc26 AD Performance Improvement
When searching very large Active Directories prefix wildcards greatly
reduce performance. A configuration switch is implemented when results
in only suffix wildcards being used.
2016-05-02 18:54:27 +10:00
Gary Sharp dee347128e Add RazorGenerator Directives 2015-12-23 10:55:04 +11:00
Gary Sharp d6de5e5958 Add TempPath parameter to bootstrapper install
Allows a temporary path to be passed to the Bootstrapper to be used when
mounting WIM images during the offline install phase. Allows mounting to
be done on another (faster, SSD) volume.
2015-12-23 10:55:03 +11:00
Gary Sharp 80f7cd0275 Merge pull request #95 from ahmedshash/master
Fix for ability to import keyboard in device import #94
2015-12-23 10:52:59 +11:00
ahmedshash 538fd2e394 Fix for ability to import keyboard in device import #94
As per request #94 this extends functionality when importing/exporting
devices by adding a keyboard field.
2015-12-22 21:16:34 +11:00
Gary Sharp ad5aea6df2 Initial config CSS animation
Uses CSS animation instead of GIF; GIF animation stops when browser
navigation begins, CSS continues.
2015-05-26 21:01:46 +07:00
Gary Sharp b070283270 Razor Generator Directives
Enable `GeneratePrettyNames` so that namespaces and class names are
preserved as in the past (after changes in RazorGenerator Extension)
2015-05-26 12:46:02 +07:00
Gary Sharp 6a017c7be9 Version Bump
Basis of a future release (2.1)
2015-05-14 15:02:51 +10:00
Gary Sharp 6fe8d9c8e0 Bug Fix: Assemblies missing from deployment 2015-05-14 13:11:11 +10:00
Gary Sharp 7eb17a91c9 Removed minify source maps
These files have no route in mvc, so only cause 404 errors in browser
dev tools.
2015-02-02 20:58:47 +11:00
Gary Sharp d2acf31b81 Migrate VS2013 version incrementing extension
"Build Version Increment" doesn't support VS2013, migrates to "Automatic
Versions".
2015-02-02 20:17:58 +11:00
Gary Sharp 50399f4f48 Bug Fix #82: Dereference managed groups on delete
Managed Group references (and subscriptions) continued to exist after
Device Profiles, Device Batches and Document Templates were deleted.
This prevented assigning the same group to another
Profile/Batch/Template without first recycling the IIS App Pool.
2015-02-02 19:23:27 +11:00
Gary Sharp 23db0b6111 Bug Fix #83: Correct case in attachment usernames 2015-02-02 19:00:58 +11:00
Gary Sharp a7b3f045d6 Project updated to VS2013 2015-02-02 18:43:01 +11:00
Gary Sharp 76318d8d00 Release v2.0.0918.1700 2014-09-18 19:11:21 +10:00
Gary Sharp f2ac35a7fa Bug Fix #75: Display errors creating device batch
Thanks Patrick Connell
2014-09-15 13:24:28 +10:00
Gary Sharp b1575fa321 Unified SignalR disconnected/error dialogs
Dialogs (with a refresh option) appear whenever the SignalR client
disconnects or encounters an error. Nonsensical error messages replaced.
Page refresh technique changed to allow for urls containing fragment
hashes.
2014-09-11 17:21:39 +10:00
Gary Sharp 4283b62803 Bug Fix: Job searching with username
When searching jobs, the default domain is now assumed if none is
provided when checking for job users.
2014-09-11 16:48:45 +10:00
Gary Sharp 7551b39b8e Generated Job Log to Markdown
Generated job logs are formatted with Markdown. Includes other minor css
changes.
2014-09-09 13:54:14 +10:00
Gary Sharp 3ae99f45bb Update device model type on Enrolment 2014-09-08 14:53:10 +10:00
Gary Sharp c846fa053a Dialog height reduced & remove js minify maps
Provide better support for lower resolution devices (buttons became
hidden in tall dialogs). Remove references to JavaScript minification
source maps which aren't deployed and caused confusion.
2014-09-08 14:51:51 +10:00
Gary Sharp 57c2e062fc Update: Plugin provided assemblies
Update assemblies which are available to all plugins.
2014-09-02 14:45:44 +10:00
Gary Sharp e940c24522 Bug Fix: Document template bulk examples 2014-09-02 14:45:02 +10:00
Gary Sharp 73d6160db6 Fix typo: AvilableDomainControllers
AvilableDomainControllers -> AvailableDomainControllers
Thanks Boris Pekez
2014-08-29 14:46:29 +10:00
Gary Sharp 13c5efe5d6 Added new UI Icons in FontAwesome 4.2.0 2014-08-28 15:32:57 +10:00
Gary Sharp bbe4cccc91 Update #74: Friendly disconnected messages
When the live connection to the server is interrupted some ui elements
are disabled. If the connection fails (due to errors or failed
reconnection) a dialog instructs the user to check their connection and
refresh the browser. Relates to Device, Job and User pages (logs and
attachments).
2014-08-28 14:59:39 +10:00
Gary Sharp 41e061df54 Update: Font Awesome 4.2.0 2014-08-28 13:53:35 +10:00
Gary Sharp 35a07344cc Bug Fix #71: Update managed groups on device enrol 2014-08-28 12:50:53 +10:00
Gary Sharp 4b6604df5b Feature #69 #72: Noticeboard themes and filtering 2014-08-26 16:27:37 +10:00
Gary Sharp 0de162fce3 Update: Update Job Identifiers 2014-08-19 10:53:59 +10:00
Gary Sharp f26474fa4d Device decommissioning 'returned' reason 2014-08-07 13:43:59 +10:00
1541 changed files with 240992 additions and 96955 deletions
+4
View File
@@ -0,0 +1,4 @@
[*.cs]
# VSSpell001: Spell Check
dotnet_diagnostic.VSSpell001.severity = suggestion
+2
View File
@@ -3,6 +3,8 @@
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons. ## files generated by popular Visual Studio add-ons.
.vs/
# User-specific files # User-specific files
*.suo *.suo
*.user *.user
-102
View File
@@ -1,102 +0,0 @@
using Disco.BI.Extensions;
using Exceptionless;
using iTextSharp.text.pdf;
using System;
using System.Drawing;
using System.IO;
using System.Linq;
namespace Disco.BI.AttachmentBI
{
public static class Utilities
{
public static bool GenerateThumbnail(Stream Source, string SourceMimeType, Stream OutStream)
{
if (Source != null)
{
// GDI+ (jpg, png, gif, bmp)
if (SourceMimeType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("jpg") ||
SourceMimeType.Equals("image/png", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("png") ||
SourceMimeType.Equals("image/gif", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("gif") ||
SourceMimeType.Equals("image/bmp", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("bmp"))
{
try
{
using (Image sourceImage = Image.FromStream(Source))
{
using (Image thumbImage = sourceImage.ResizeImage(48, 48))
{
using (Image mimeTypeIcon = Disco.Properties.Resources.MimeType_img16)
thumbImage.EmbedIconOverlay(mimeTypeIcon);
thumbImage.SaveJpg(90, OutStream);
return true;
}
}
}
catch (Exception ex)
{
ex.ToExceptionless().Submit();
// Ignore Thumbnail Generation exceptions for images
}
}
// PDF
if (SourceMimeType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("pdf"))
{
PdfReader pdfReader = new PdfReader(Source);
try
{
using (DisposableImageCollection pdfPageImages = pdfReader.PdfPageImages(1))
{
if (pdfPageImages.Count() > 0)
{
// Find Biggest Image on Page
Image biggestImage = pdfPageImages.OrderByDescending(i => i.Height * i.Width).First();
using (Image thumbImage = biggestImage.ResizeImage(48, 48, Brushes.White))
{
using (Image mimeTypeIcon = Disco.Properties.Resources.MimeType_pdf16)
thumbImage.EmbedIconOverlay(mimeTypeIcon);
thumbImage.SaveJpg(90, OutStream);
return true;
}
}
}
}
catch (Exception ex)
{
ex.ToExceptionless().Submit();
}
finally
{
if (pdfReader != null)
pdfReader.Close();
}
}
}
return false;
}
public static bool GenerateThumbnail(string SourceFilename, string SourceMimeType, string DestinationFilename)
{
using (FileStream sourceStream = new FileStream(SourceFilename, FileMode.Open, FileAccess.Read))
{
return GenerateThumbnail(sourceStream, SourceMimeType, DestinationFilename);
}
}
public static bool GenerateThumbnail(Stream Source, string SourceMimeType, string DestinationFilename)
{
bool result;
using (FileStream destinationStream = new FileStream(DestinationFilename, FileMode.Create, FileAccess.Write, FileShare.None))
{
result = GenerateThumbnail(Source, SourceMimeType, destinationStream);
}
if (!result && File.Exists(DestinationFilename))
File.Delete(DestinationFilename);
return result;
}
}
}
-31
View File
@@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Data.Repository;
using Disco.Data.Configuration;
namespace Disco.BI
{
public static class DataStore
{
public static string CreateLocation(DiscoDataContext Database, string SubLocation, DateTime? SubSubLocationTimestamp = null)
{
return CreateLocation(Database.DiscoConfiguration, SubLocation, SubSubLocationTimestamp);
}
public static string CreateLocation(SystemConfiguration DiscoConfiguration, string SubLocation, DateTime? SubSubLocationTimestamp = null)
{
string SubSubLocation = string.Empty;
if (SubSubLocationTimestamp.HasValue)
SubSubLocation = SubSubLocationTimestamp.Value.ToString(@"yyyy\\MM");
string storeDirectory = System.IO.Path.Combine(DiscoConfiguration.DataStoreLocation, SubLocation, SubSubLocation);
if (!System.IO.Directory.Exists(storeDirectory))
System.IO.Directory.CreateDirectory(storeDirectory);
return storeDirectory;
}
}
}
-61
View File
@@ -1,61 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Disco.Data.Repository;
using Disco.Models.Repository;
namespace Disco.BI
{
public static class DeviceModelBI
{
// Added: 2013-02-07 G#
// Ensure Duplicate Device Models are not created by creating only one Device Model at a time
// http://www.discoict.com.au/forum/support/2013/2/duplicate-device-models.aspx
// Thanks to Michael Vorster for reporting this problem.
private static object _CreateDeviceModelLock = new object();
public static Tuple<DeviceModel, bool> GetOrCreateDeviceModel(this DbSet<DeviceModel> DeviceModelsSet, string Manufacturer, string Model, string ModelType)
{
// Already Exists?
var deviceModel = DeviceModelsSet.FirstOrDefault(dm => dm.Manufacturer == Manufacturer && dm.Model == Model);
if (deviceModel == null)
{
// Ensure only one thread/request at a time
lock (_CreateDeviceModelLock)
{
// Check again now that lock is enforced
deviceModel = DeviceModelsSet.FirstOrDefault(dm => dm.Manufacturer == Manufacturer && dm.Model == Model);
if (deviceModel == null)
{
// Create the Device Model in a different DataContext so we don't have to commit unrelated changes
using (DiscoDataContext database = new DiscoDataContext())
{
var addDeviceModel = new DeviceModel
{
Manufacturer = Manufacturer,
Model = Model,
ModelType = ModelType,
Description = string.Format("{0} {1}", Manufacturer, Model)
};
database.DeviceModels.Add(addDeviceModel);
database.SaveChanges();
}
// Obtain the Device Model with the in-scope DataContext
// - Overhead acknowledged, but reasonable given the infrequency of occurrence
deviceModel = DeviceModelsSet.FirstOrDefault(dm => dm.Manufacturer == Manufacturer && dm.Model == Model);
return new Tuple<DeviceModel, bool>(deviceModel, true);
}
}
}
return new Tuple<DeviceModel,bool>(deviceModel, false);
}
// Added: 2013-02-07 G#
}
}
-850
View File
@@ -1,850 +0,0 @@
using Disco.BI.Extensions;
using Disco.Data.Repository;
using Disco.Models.ClientServices;
using Disco.Models.Repository;
using Disco.Services.Authorization;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Users;
using Exceptionless;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Tamir.SharpSsh;
namespace Disco.BI.DeviceBI
{
public class DeviceEnrol
{
public enum EnrolmentTypes
{
Normal,
Mac = 5,
MacSecure,
Register = 30
}
private static Regex SshPromptRegEx = new Regex("[\\$,\\#]", RegexOptions.Multiline);
public static MacSecureEnrolResponse MacSecureEnrol(DiscoDataContext Database, string Host)
{
MacEnrol trustedRequest = new MacEnrol();
string sessionId = System.Guid.NewGuid().ToString("B");
MacSecureEnrolResponse MacSecureEnrol;
try
{
EnrolmentLog.LogSessionStarting(sessionId, Host, EnrolmentTypes.MacSecure);
EnrolmentLog.LogSessionProgress(sessionId, 0, string.Format("Connecting to '{0}' as '{1}'", Host, Database.DiscoConfiguration.Bootstrapper.MacSshUsername));
SshShell shell = new SshShell(Host, Database.DiscoConfiguration.Bootstrapper.MacSshUsername, Database.DiscoConfiguration.Bootstrapper.MacSshPassword);
try
{
shell.ExpectPattern = "#";
shell.Connect();
EnrolmentLog.LogSessionProgress(sessionId, 10, "Connected, Authenticating");
var output = shell.Expect(SshPromptRegEx);
bool sessionElevated = false;
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
if (!output.TrimEnd(new char[0]).EndsWith("#"))
{
EnrolmentLog.LogSessionProgress(sessionId, 22, "Connected, Elevating Credentials");
shell.WriteLine("sudo -k");
System.Threading.Thread.Sleep(250);
output = shell.Expect(SshPromptRegEx);
EnrolmentLog.LogSessionProgress(sessionId, 25, "Connected, Elevating Credentials");
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
shell.WriteLine("sudo -s -S");
System.Threading.Thread.Sleep(250);
output = shell.Expect(":");
EnrolmentLog.LogSessionProgress(sessionId, 27, "Connected, Elevating Credentials");
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
shell.WriteLine(Database.DiscoConfiguration.Bootstrapper.MacSshPassword);
System.Threading.Thread.Sleep(250);
output = shell.Expect(SshPromptRegEx);
sessionElevated = true;
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
}
EnrolmentLog.LogSessionProgress(sessionId, 20, "Retrieving Serial Number");
trustedRequest.DeviceSerialNumber = ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Serial Number\" | cut -d \":\" -f 2-", sessionId);
EnrolmentLog.LogSessionDevice(sessionId, trustedRequest.DeviceSerialNumber, null);
EnrolmentLog.LogSessionProgress(sessionId, 30, "Retrieving Hardware UUID");
trustedRequest.DeviceUUID = ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Hardware UUID:\" | cut -d \":\" -f 2-", sessionId);
EnrolmentLog.LogSessionProgress(sessionId, 40, "Retrieving Computer Name");
trustedRequest.DeviceComputerName = ParseMacShellCommand(shell, "scutil --get ComputerName", sessionId);
EnrolmentLog.LogSessionProgress(sessionId, 50, "Retrieving Ethernet MAC Address");
string lanNicId = ParseMacShellCommand(shell, "system_profiler SPEthernetDataType | egrep -o \"en0|en1|en2|en3|en4|en5|en6\"", sessionId);
if (!string.IsNullOrWhiteSpace(lanNicId))
{
trustedRequest.DeviceLanMacAddress = ParseMacShellCommand(shell, string.Format("ifconfig {0} | grep ether | cut -d \" \" -f 2-", lanNicId), sessionId);
}
EnrolmentLog.LogSessionProgress(sessionId, 65, "Retrieving Wireless MAC Address");
string wlanNicId = ParseMacShellCommand(shell, "system_profiler SPAirPortDataType | egrep -o \"en0|en1|en2|en3|en4|en5|en6\"", sessionId);
if (!string.IsNullOrWhiteSpace(wlanNicId))
{
trustedRequest.DeviceWlanMacAddress = ParseMacShellCommand(shell, string.Format("ifconfig {0} | grep ether | cut -d \" \" -f 2-", wlanNicId), sessionId);
}
trustedRequest.DeviceManufacturer = "Apple Inc.";
EnrolmentLog.LogSessionProgress(sessionId, 80, "Retrieving Model");
trustedRequest.DeviceModel = ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Model Identifier:\" | cut -d \":\" -f 2-", sessionId);
EnrolmentLog.LogSessionProgress(sessionId, 90, "Retrieving Model Type");
trustedRequest.DeviceModelType = ParseMacModelType(ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Model Name:\" | cut -d \":\" -f 2-", sessionId));
EnrolmentLog.LogSessionProgress(sessionId, 99, "Disconnecting");
output = ParseMacModelType(ParseMacShellCommand(shell, "exit", sessionId));
if (sessionElevated)
{
output = ParseMacModelType(ParseMacShellCommand(shell, "exit", sessionId));
}
if (shell.Connected)
{
shell.Close();
}
EnrolmentLog.LogSessionProgress(sessionId, 100, "Disconnected, Starting Disco Enrolment");
MacSecureEnrolResponse response = MacSecureEnrolResponse.FromMacEnrolResponse(MacEnrol(Database, trustedRequest, true, sessionId));
EnrolmentLog.LogSessionFinished(sessionId);
MacSecureEnrol = response;
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
if (shell != null)
{
bool connected = shell.Connected;
if (connected)
{
shell.Close();
}
}
}
}
catch (System.Exception ex)
{
ex.ToExceptionless().Submit();
EnrolmentLog.LogSessionError(sessionId, ex);
throw ex;
}
return MacSecureEnrol;
}
#region "Mac Enrol Helpers"
private static string ParseMacModelType(string ModelName)
{
string ParseMacModelType;
if (!string.IsNullOrWhiteSpace(ModelName))
{
string mn = ModelName.ToLower();
if (mn.Contains("imac") || mn.Contains("mini"))
{
ParseMacModelType = "Desktop";
return ParseMacModelType;
}
if (mn.Contains("macbook"))
{
ParseMacModelType = "Mobile";
return ParseMacModelType;
}
if (mn.Contains("xserve"))
{
ParseMacModelType = "Server";
return ParseMacModelType;
}
}
ParseMacModelType = "Unknown";
return ParseMacModelType;
}
private static string ParseMacShellCommand(SshShell Shell, string Command, string LogSessionId)
{
Shell.WriteLine(Command);
System.Threading.Thread.Sleep(250);
string Response = Shell.Expect(SshPromptRegEx);
Response = Response.Replace("\r", string.Empty);
EnrolmentLog.LogSessionDiagnosticInformation(LogSessionId, Response);
bool flag = Response.Contains("\n");
string ParseMacShellCommand;
if (flag)
{
string[] ResponseLines = Response.Split(new char[]
{
'\n'
});
switch (ResponseLines.Length)
{
case 0:
case 1:
{
ParseMacShellCommand = string.Empty;
break;
}
case 2:
case 3:
{
ParseMacShellCommand = ResponseLines[1].Trim();
break;
}
default:
{
System.Text.StringBuilder ResponseBuilder = new System.Text.StringBuilder();
int num = ResponseLines.Length - 2;
int lineIndex = 1;
while (true)
{
int arg_111_0 = lineIndex;
int num2 = num;
if (arg_111_0 > num2)
{
break;
}
ResponseBuilder.AppendLine(ResponseLines[lineIndex]);
lineIndex++;
}
ParseMacShellCommand = ResponseBuilder.ToString().Trim();
break;
}
}
}
else
{
ParseMacShellCommand = Response;
}
return ParseMacShellCommand;
}
#endregion
public static MacEnrolResponse MacEnrol(DiscoDataContext Database, MacEnrol Request, bool Trusted, string OpenSessionId = null)
{
string sessionId;
if (OpenSessionId == null)
{
sessionId = System.Guid.NewGuid().ToString("B");
EnrolmentLog.LogSessionStarting(sessionId, Request.DeviceSerialNumber, EnrolmentTypes.Mac);
}
else
{
sessionId = OpenSessionId;
}
EnrolmentLog.LogSessionDeviceInfo(sessionId, Request);
MacEnrolResponse response = new MacEnrolResponse();
try
{
EnrolmentLog.LogSessionProgress(sessionId, 10, "Querying Database");
Device RepoDevice = Database.Devices.Include("AssignedUser").Include("DeviceProfile").Include("DeviceProfile").Where(d => d.SerialNumber == Request.DeviceSerialNumber).FirstOrDefault();
if (!Trusted)
{
if (RepoDevice == null)
throw new EnrolSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.DeviceSerialNumber));
if (!RepoDevice.AllowUnauthenticatedEnrol)
throw new EnrolSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.DeviceSerialNumber));
}
if (RepoDevice == null)
{
EnrolmentLog.LogSessionProgress(sessionId, 50, "New Device, Building Disco Instance");
EnrolmentLog.LogSessionTaskAddedDevice(sessionId, Request.DeviceSerialNumber);
DeviceProfile deviceProfile = Database.DeviceProfiles.Find(Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
RepoDevice = new Device
{
SerialNumber = Request.DeviceSerialNumber,
DeviceDomainId = Request.DeviceComputerName,
DeviceProfile = deviceProfile,
DeviceModel = deviceModel,
AllowUnauthenticatedEnrol = false,
CreatedDate = DateTime.Now,
EnrolledDate = DateTime.Now
};
Database.Devices.Add(RepoDevice);
}
else
{
EnrolmentLog.LogSessionProgress(sessionId, 50, "Existing Device, Updating Disco Instance");
EnrolmentLog.LogSessionTaskUpdatingDevice(sessionId, Request.DeviceSerialNumber);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
RepoDevice.DeviceModel = deviceModel;
RepoDevice.DeviceDomainId = Request.DeviceComputerName;
if (!RepoDevice.EnrolledDate.HasValue)
{
RepoDevice.EnrolledDate = DateTime.Now;
}
}
RepoDevice.LastEnrolDate = DateTime.Now;
RepoDevice.AllowUnauthenticatedEnrol = false;
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
//DeviceProfileConfiguration RepoDeviceProfileContext = RepoDevice.DeviceProfile.Configuration(Context);
EnrolmentLog.LogSessionProgress(sessionId, 90, "Building Response");
//if (RepoDeviceProfileContext.DistributionType == DeviceProfileConfiguration.DeviceProfileDistributionTypes.OneToOne && RepoDevice.AssignedUser != null)
if (RepoDevice.DeviceProfile.DistributionType == DeviceProfile.DistributionTypes.OneToOne && RepoDevice.AssignedUser != null)
{
ADUserAccount AssignedUserInfo = ActiveDirectory.RetrieveADUserAccount(RepoDevice.AssignedUser.UserId);
EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.SamAccountName, AssignedUserInfo.Domain.NetBiosName, AssignedUserInfo.SecurityIdentifier.ToString());
response.DeviceAssignedUserUsername = AssignedUserInfo.SamAccountName;
response.DeviceAssignedUserDomain = AssignedUserInfo.Domain.NetBiosName;
response.DeviceAssignedUserName = AssignedUserInfo.DisplayName;
response.DeviceAssignedUserSID = AssignedUserInfo.SecurityIdentifier.ToString();
}
response.DeviceComputerName = RepoDevice.DeviceDomainId;
EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully");
}
catch (EnrolSafeException ex)
{
EnrolmentLog.LogSessionError(sessionId, ex);
return new MacEnrolResponse { ErrorMessage = ex.Message };
}
catch (System.Exception ex2)
{
ex2.ToExceptionless().Submit();
EnrolmentLog.LogSessionError(sessionId, ex2);
throw ex2;
}
finally
{
if (OpenSessionId == null)
EnrolmentLog.LogSessionFinished(sessionId);
}
return response;
}
public static EnrolResponse Enrol(DiscoDataContext Database, string Username, Models.ClientServices.Enrol Request)
{
ADMachineAccount adMachineAccount = null;
EnrolResponse response = new EnrolResponse();
AuthorizationToken authenticatedToken = null;
bool isAuthenticated = false;
ADDomain domain = null;
Lazy<ADDomainController> domainController = new Lazy<ADDomainController>(() =>
{
if (domain == null)
throw new InvalidOperationException("The [domain] variable must be initialized first");
return domain.GetAvailableDomainController(RequireWritable: true);
});
string sessionId = System.Guid.NewGuid().ToString("B");
response.SessionId = sessionId;
EnrolmentLog.LogSessionStarting(sessionId, Request.DeviceSerialNumber, EnrolmentTypes.Normal);
EnrolmentLog.LogSessionDeviceInfo(sessionId, Request);
try
{
EnrolmentLog.LogSessionProgress(sessionId, 10, "Loading User Data");
if (!string.IsNullOrWhiteSpace(Username))
{
authenticatedToken = UserService.GetAuthorization(Username, Database);
isAuthenticated = (authenticatedToken != null);
}
EnrolmentLog.LogSessionProgress(sessionId, 13, "Loading Device Data");
Device RepoDevice = Database.Devices.Include("AssignedUser").Include("DeviceModel").Include("DeviceProfile").Where(d => d.SerialNumber == Request.DeviceSerialNumber).FirstOrDefault();
EnrolmentLog.LogSessionProgress(sessionId, 15, "Discovering User/Device Disco Permissions");
if (isAuthenticated)
{
if (!authenticatedToken.Has(Claims.Device.Actions.EnrolDevices))
{
if (!authenticatedToken.Has(Claims.ComputerAccount))
throw new EnrolSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.DeviceSerialNumber, authenticatedToken.User.UserId));
if (domain == null)
domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName);
if (!authenticatedToken.User.UserId.Equals(string.Format(@"{0}\{1}$", domain.NetBiosName, Request.DeviceComputerName), System.StringComparison.OrdinalIgnoreCase))
throw new EnrolSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.DeviceSerialNumber, authenticatedToken.User.UserId));
}
}
else
{
if (RepoDevice == null)
{
throw new EnrolSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.DeviceSerialNumber));
}
if (!RepoDevice.AllowUnauthenticatedEnrol)
{
if (RepoDevice.DeviceProfile.AllowUntrustedReimageJobEnrolment)
{
if (Database.Jobs.Count(j => j.DeviceSerialNumber == RepoDevice.SerialNumber && j.JobTypeId == JobType.JobTypeIds.SImg && !j.ClosedDate.HasValue) == 0)
{
throw new EnrolSafeException(string.Format("Device has no open 'Software - Reimage' job (SN: '{0}')", Request.DeviceSerialNumber));
}
}
else
{
throw new EnrolSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.DeviceSerialNumber));
}
}
}
if (Request.DeviceIsPartOfDomain && !string.IsNullOrWhiteSpace(Request.DeviceComputerName))
{
EnrolmentLog.LogSessionProgress(sessionId, 20, "Loading Active Directory Computer Account");
System.Guid? uuidGuid = null;
System.Guid? macAddressGuid = null;
if (!string.IsNullOrEmpty(Request.DeviceUUID))
uuidGuid = ADMachineAccount.NetbootGUIDFromUUID(Request.DeviceUUID);
if (!string.IsNullOrEmpty(Request.DeviceLanMacAddress))
macAddressGuid = ADMachineAccount.NetbootGUIDFromMACAddress(Request.DeviceLanMacAddress);
if (domain == null)
domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName);
var requestDeviceId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.DeviceComputerName);
adMachineAccount = domainController.Value.RetrieveADMachineAccount(requestDeviceId, uuidGuid, macAddressGuid);
}
if (RepoDevice == null)
{
EnrolmentLog.LogSessionProgress(sessionId, 30, "New Device, Creating Disco Instance");
EnrolmentLog.LogSessionTaskAddedDevice(sessionId, Request.DeviceSerialNumber);
DeviceProfile deviceProfile = Database.DeviceProfiles.Find(Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
if (domain == null)
domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName);
RepoDevice = new Device
{
SerialNumber = Request.DeviceSerialNumber,
DeviceDomainId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.DeviceComputerName),
DeviceProfile = deviceProfile,
DeviceModel = deviceModel,
AllowUnauthenticatedEnrol = false,
CreatedDate = DateTime.Now,
EnrolledDate = DateTime.Now,
LastEnrolDate = DateTime.Now,
DeviceDetails = new List<DeviceDetail>()
};
Database.Devices.Add(RepoDevice);
if (!string.IsNullOrEmpty(Request.DeviceLanMacAddress))
RepoDevice.DeviceDetails.LanMacAddress(RepoDevice, Request.DeviceLanMacAddress);
if (!string.IsNullOrEmpty(Request.DeviceWlanMacAddress))
RepoDevice.DeviceDetails.WLanMacAddress(RepoDevice, Request.DeviceWlanMacAddress);
}
else
{
EnrolmentLog.LogSessionProgress(sessionId, 30, "Existing Device, Updating Disco Instance");
EnrolmentLog.LogSessionTaskUpdatingDevice(sessionId, Request.DeviceSerialNumber);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
RepoDevice.DeviceModel = deviceModel;
if (!string.IsNullOrEmpty(Request.DeviceLanMacAddress))
RepoDevice.DeviceDetails.LanMacAddress(RepoDevice, Request.DeviceLanMacAddress);
if (!string.IsNullOrEmpty(Request.DeviceWlanMacAddress))
RepoDevice.DeviceDetails.WLanMacAddress(RepoDevice, Request.DeviceWlanMacAddress);
if (!RepoDevice.EnrolledDate.HasValue)
RepoDevice.EnrolledDate = DateTime.Now;
RepoDevice.LastEnrolDate = DateTime.Now;
}
if (adMachineAccount == null)
{
if (RepoDevice.DeviceProfile.ProvisionADAccount)
{
EnrolmentLog.LogSessionProgress(sessionId, 50, "Provisioning an Active Directory Computer Account");
if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit))
throw new InvalidOperationException("No Organisational Unit has been set in the device profile");
if (domain == null)
domain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit);
if (string.IsNullOrEmpty(RepoDevice.DeviceDomainId) || RepoDevice.DeviceProfile.EnforceComputerNameConvention)
RepoDevice.DeviceDomainId = RepoDevice.ComputerNameRender(Database, domain);
string offlineProvisionDiagnosicInfo;
EnrolmentLog.LogSessionTaskProvisioningADAccount(sessionId, RepoDevice.SerialNumber, RepoDevice.DeviceDomainId);
adMachineAccount = domainController.Value.RetrieveADMachineAccount(RepoDevice.DeviceDomainId);
response.OfflineDomainJoin = domainController.Value.OfflineDomainJoinProvision(RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo);
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo);
response.RequireReboot = true;
}
if (adMachineAccount != null)
{
response.DeviceComputerName = adMachineAccount.Name;
response.DeviceDomainName = adMachineAccount.Domain.NetBiosName;
}
else if (ActiveDirectory.IsValidDomainAccountId(RepoDevice.DeviceDomainId))
{
string accountUsername;
ADDomain accountDomain;
ActiveDirectory.ParseDomainAccountId(RepoDevice.DeviceDomainId, out accountUsername, out accountDomain);
response.DeviceDomainName = accountDomain == null ? null : accountDomain.NetBiosName;
response.DeviceComputerName = accountUsername;
}
else
{
response.DeviceDomainName = Request.DeviceDNSDomainName;
response.DeviceComputerName = Request.DeviceComputerName;
}
}
else
{
RepoDevice.DeviceDomainId = adMachineAccount.Id.Trim('$');
response.DeviceComputerName = adMachineAccount.Name;
response.DeviceDomainName = adMachineAccount.Domain.NetBiosName;
// Enforce Computer Name Convention
if (!adMachineAccount.IsCriticalSystemObject && RepoDevice.DeviceProfile.EnforceComputerNameConvention)
{
if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit))
throw new InvalidOperationException("No Organisational Unit has been set in the device profile");
if (domain == null)
domain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit);
var calculatedComputerName = RepoDevice.ComputerNameRender(Database, domain);
string calculatedAccountUsername;
ActiveDirectory.ParseDomainAccountId(calculatedComputerName, out calculatedAccountUsername);
if (!Request.DeviceComputerName.Equals(calculatedAccountUsername, StringComparison.OrdinalIgnoreCase))
{
EnrolmentLog.LogSessionProgress(sessionId, 50, string.Format("Renaming Device: {0} -> {1}", Request.DeviceComputerName, calculatedComputerName));
EnrolmentLog.LogSessionTaskRenamingDevice(sessionId, Request.DeviceComputerName, calculatedComputerName);
RepoDevice.DeviceDomainId = calculatedComputerName;
response.DeviceDomainName = domain.NetBiosName;
response.DeviceComputerName = calculatedAccountUsername;
// Create New Account
string offlineProvisionDiagnosicInfo;
response.OfflineDomainJoin = domainController.Value.OfflineDomainJoinProvision(RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo);
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo);
response.RequireReboot = true;
}
}
// Enforce Organisational Unit
if (!adMachineAccount.IsCriticalSystemObject && response.OfflineDomainJoin == null && RepoDevice.DeviceProfile.EnforceOrganisationalUnit)
{
var parentDistinguishedName = adMachineAccount.ParentDistinguishedName;
if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit))
throw new InvalidOperationException(string.Format("The Organisational Unit for the Device Profile '{0}' [{1}] is not set.", RepoDevice.DeviceProfile.Name, RepoDevice.DeviceProfile.Id));
if (!parentDistinguishedName.Equals(RepoDevice.DeviceProfile.OrganisationalUnit, StringComparison.OrdinalIgnoreCase)) // Custom OU
{
var proposedDomain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit);
var currentDomain = ActiveDirectory.Context.GetDomainFromDistinguishedName(parentDistinguishedName);
if (currentDomain != proposedDomain)
throw new NotSupportedException("Unable to move the devices organisational unit when the source and destination domains are different.");
if (domain == null)
domain = proposedDomain;
else if (domain != proposedDomain)
throw new NotSupportedException("To many domains involved in this enrolment, contact support regarding your scenario.");
EnrolmentLog.LogSessionProgress(sessionId, 65, string.Format("Moving Device Organisational Unit: {0} -> {1}", parentDistinguishedName, RepoDevice.DeviceProfile.OrganisationalUnit));
EnrolmentLog.LogSessionTaskMovingDeviceOrganisationUnit(sessionId, parentDistinguishedName, RepoDevice.DeviceProfile.OrganisationalUnit);
adMachineAccount.MoveOrganisationalUnit(domainController.Value, RepoDevice.DeviceProfile.OrganisationalUnit);
response.RequireReboot = true;
}
}
}
if (adMachineAccount != null && !adMachineAccount.IsCriticalSystemObject)
{
EnrolmentLog.LogSessionProgress(sessionId, 75, "Updating Active Directory Computer Account Properties");
adMachineAccount.UpdateNetbootGUID(Request.DeviceUUID, Request.DeviceLanMacAddress);
if (RepoDevice.AssignedUser != null)
adMachineAccount.SetDescription(RepoDevice);
}
if (RepoDevice.DeviceProfile.DistributionType == DeviceProfile.DistributionTypes.OneToOne)
{
if (RepoDevice.AssignedUser == null)
{
response.AllowBootstrapperUninstall = false;
}
else
{
EnrolmentLog.LogSessionProgress(sessionId, 80, "Retrieving Active Directory Assigned User Account");
ADUserAccount AssignedUserInfo = ActiveDirectory.RetrieveADUserAccount(RepoDevice.AssignedUser.UserId);
EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.SamAccountName, AssignedUserInfo.Domain.NetBiosName, AssignedUserInfo.SecurityIdentifier.ToString());
response.AllowBootstrapperUninstall = true;
response.DeviceAssignedUserIsLocalAdmin = RepoDevice.DeviceProfile.AssignedUserLocalAdmin;
response.DeviceAssignedUserUsername = AssignedUserInfo.SamAccountName;
response.DeviceAssignedUserDomain = AssignedUserInfo.Domain.NetBiosName;
response.DeviceAssignedUserName = AssignedUserInfo.DisplayName;
response.DeviceAssignedUserSID = AssignedUserInfo.SecurityIdentifier.ToString();
}
}
else
{
response.AllowBootstrapperUninstall = true;
}
if (!string.IsNullOrEmpty(Request.DeviceWlanMacAddress) && !string.IsNullOrEmpty(RepoDevice.DeviceProfile.CertificateProviderId))
{
EnrolmentLog.LogSessionProgress(sessionId, 90, "Provisioning a Wireless Certificate");
var allocationResult = RepoDevice.AllocateCertificate(Database);
var deviceCertificate = allocationResult.Item1;
if (deviceCertificate != null)
{
bool certAlreadyInstalled = false;
if (Request.DeviceCertificates != null && Request.DeviceCertificates.Count > 0)
{
foreach (string existingCertName in Request.DeviceCertificates)
{
if (existingCertName.Contains(deviceCertificate.Name))
{
certAlreadyInstalled = true;
break;
}
}
}
if (!certAlreadyInstalled)
{
EnrolmentLog.LogSessionTaskProvisioningWirelessCertificate(sessionId, RepoDevice.SerialNumber, deviceCertificate.Name);
response.DeviceCertificate = System.Convert.ToBase64String(deviceCertificate.Content);
}
}
response.DeviceCertificateRemoveExisting = allocationResult.Item2;
}
// Reset 'AllowUnauthenticatedEnrol'
if (RepoDevice.AllowUnauthenticatedEnrol)
RepoDevice.AllowUnauthenticatedEnrol = false;
EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully");
}
catch (EnrolSafeException ex)
{
EnrolmentLog.LogSessionError(sessionId, ex);
return new EnrolResponse
{
SessionId = sessionId,
ErrorMessage = ex.Message
};
}
catch (System.Exception ex2)
{
ex2.ToExceptionless().Submit();
EnrolmentLog.LogSessionError(sessionId, ex2);
throw ex2;
}
finally
{
EnrolmentLog.LogSessionFinished(sessionId);
}
return response;
}
public static RegisterResponse Register(DiscoDataContext Database, string Username, Register Request)
{
RegisterResponse response = new RegisterResponse();
ADMachineAccount adMachineAccount = null;
AuthorizationToken authenticatedToken = null;
ADDomain domain = null;
Lazy<ADDomainController> domainController = new Lazy<ADDomainController>(() =>
{
if (domain == null)
throw new InvalidOperationException("The [domain] variable must be initialized first");
return domain.GetAvailableDomainController(RequireWritable: true);
});
string sessionId = System.Guid.NewGuid().ToString("B");
response.SessionId = sessionId;
EnrolmentLog.LogSessionStarting(sessionId, Request.DeviceSerialNumber, EnrolmentTypes.Register);
EnrolmentLog.LogSessionDeviceInfo(sessionId, Request);
try
{
EnrolmentLog.LogSessionProgress(sessionId, 10, "Loading User Data");
if (!string.IsNullOrWhiteSpace(Username))
{
authenticatedToken = UserService.GetAuthorization(Username, Database);
}
EnrolmentLog.LogSessionProgress(sessionId, 13, "Loading Device Data");
Device RepoDevice = Database.Devices.Include("AssignedUser").Include("DeviceModel").Include("DeviceProfile").Where(d => d.SerialNumber == Request.DeviceSerialNumber).FirstOrDefault();
EnrolmentLog.LogSessionProgress(sessionId, 15, "Discovering User/Device Disco Permissions");
if (authenticatedToken != null)
{
if (!authenticatedToken.Has(Claims.Device.Actions.EnrolDevices))
{
if (!authenticatedToken.Has(Claims.ComputerAccount))
throw new EnrolSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.DeviceSerialNumber, authenticatedToken.User.UserId));
if (domain == null)
domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName);
if (!authenticatedToken.User.UserId.Equals(string.Format(@"{0}\{1}$", domain.NetBiosName, Request.DeviceComputerName), System.StringComparison.OrdinalIgnoreCase))
throw new EnrolSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.DeviceSerialNumber, authenticatedToken.User.UserId));
}
}
else
{
if (RepoDevice == null)
{
throw new EnrolSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.DeviceSerialNumber));
}
if (!RepoDevice.AllowUnauthenticatedEnrol)
{
if (RepoDevice.DeviceProfile.AllowUntrustedReimageJobEnrolment)
{
if (Database.Jobs.Count(j => j.DeviceSerialNumber == RepoDevice.SerialNumber && j.JobTypeId == JobType.JobTypeIds.SImg && !j.ClosedDate.HasValue) == 0)
{
throw new EnrolSafeException(string.Format("Device has no open 'Software - Reimage' job (SN: '{0}')", Request.DeviceSerialNumber));
}
}
else
{
throw new EnrolSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.DeviceSerialNumber));
}
}
}
if (Request.DeviceIsPartOfDomain && !string.IsNullOrWhiteSpace(Request.DeviceComputerName))
{
EnrolmentLog.LogSessionProgress(sessionId, 20, "Loading Active Directory Computer Account");
System.Guid? uuidGuid = null;
if (!string.IsNullOrEmpty(Request.DeviceUUID))
uuidGuid = ADMachineAccount.NetbootGUIDFromUUID(Request.DeviceUUID);
if (domain == null)
domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName);
var requestDeviceId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.DeviceComputerName);
adMachineAccount = domainController.Value.RetrieveADMachineAccount(requestDeviceId, uuidGuid);
}
if (RepoDevice == null)
{
EnrolmentLog.LogSessionProgress(sessionId, 30, "New Device, Creating Disco Instance");
EnrolmentLog.LogSessionTaskAddedDevice(sessionId, Request.DeviceSerialNumber);
DeviceProfile deviceProfile = Database.DeviceProfiles.Find(Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
if (domain == null)
domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName);
RepoDevice = new Device
{
SerialNumber = Request.DeviceSerialNumber,
DeviceDomainId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.DeviceComputerName),
DeviceProfile = deviceProfile,
DeviceModel = deviceModel,
AllowUnauthenticatedEnrol = false,
CreatedDate = DateTime.Now,
EnrolledDate = DateTime.Now,
LastEnrolDate = DateTime.Now,
DeviceDetails = new List<DeviceDetail>()
};
Database.Devices.Add(RepoDevice);
}
else
{
EnrolmentLog.LogSessionProgress(sessionId, 30, "Existing Device, Updating Disco Instance");
EnrolmentLog.LogSessionTaskUpdatingDevice(sessionId, Request.DeviceSerialNumber);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
RepoDevice.DeviceModel = deviceModel;
if (!RepoDevice.EnrolledDate.HasValue)
RepoDevice.EnrolledDate = DateTime.Now;
RepoDevice.LastEnrolDate = DateTime.Now;
}
if (adMachineAccount == null)
{
if (adMachineAccount != null)
{
response.DeviceComputerName = adMachineAccount.Name;
response.DeviceDomainName = adMachineAccount.Domain.NetBiosName;
}
else if (ActiveDirectory.IsValidDomainAccountId(RepoDevice.DeviceDomainId))
{
string accountUsername;
ADDomain accountDomain;
ActiveDirectory.ParseDomainAccountId(RepoDevice.DeviceDomainId, out accountUsername, out accountDomain);
response.DeviceDomainName = accountDomain == null ? null : accountDomain.NetBiosName;
response.DeviceComputerName = accountUsername;
}
else
{
response.DeviceDomainName = Request.DeviceDNSDomainName;
response.DeviceComputerName = Request.DeviceComputerName;
}
}
else
{
RepoDevice.DeviceDomainId = adMachineAccount.Id.Trim('$');
response.DeviceComputerName = adMachineAccount.Name;
response.DeviceDomainName = adMachineAccount.Domain.NetBiosName;
}
// Reset 'AllowUnauthenticatedEnrol'
if (RepoDevice.AllowUnauthenticatedEnrol)
RepoDevice.AllowUnauthenticatedEnrol = false;
EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully");
}
catch (EnrolSafeException ex)
{
EnrolmentLog.LogSessionError(sessionId, ex);
return new RegisterResponse
{
SessionId = sessionId,
ErrorMessage = ex.Message
};
}
catch (System.Exception ex2)
{
ex2.ToExceptionless().Submit();
EnrolmentLog.LogSessionError(sessionId, ex2);
throw ex2;
}
finally
{
EnrolmentLog.LogSessionFinished(sessionId);
}
return response;
}
}
}
@@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Disco.BI
{
public class EnrolSafeException : System.Exception
{
public EnrolSafeException(string Message) : base(Message)
{
}
}
}
-486
View File
@@ -1,486 +0,0 @@
using Disco.Services.Logging;
using Disco.Services.Logging.Models;
using Disco.Models.ClientServices;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Disco.BI.DeviceBI
{
public class EnrolmentLog : LogBase
{
public enum EventTypeIds
{
SessionStarting = 10,
SessionProgress,
SessionDevice,
SessionDeviceInfo,
SessionFinished = 20,
SessionDiagnosticInformation,
SessionWarning,
SessionError,
SessionErrorWithInner,
SessionClientError,
SessionTaskAddedDevice = 50,
SessionTaskUpdatingDevice,
SessionTaskCreatedDeviceModel = 56,
SessionTaskProvisioningADAccount = 58,
SessionTaskAssigningUser = 60,
SessionTaskProvisioningWirelessCertificate = 62,
SessionTaskRenamingDevice = 64,
SessionTaskMovingDeviceOrganisationUnit = 66,
ClientError = 400
}
private const int _ModuleId = 50;
public static EnrolmentLog Current
{
get
{
return (EnrolmentLog)LogContext.LogModules[50];
}
}
public override string ModuleDescription
{
get
{
return "Device Enrolment";
}
}
public override int ModuleId
{
get
{
return 50;
}
}
public override string ModuleName
{
get
{
return "DeviceEnrolment";
}
}
[System.Diagnostics.DebuggerNonUserCode]
public EnrolmentLog()
{
}
private static void Log(EnrolmentLog.EventTypeIds EventTypeId, params object[] Args)
{
EnrolmentLog.Current.Log((int)EventTypeId, Args);
}
public static void LogSessionStarting(string SessionId, string HostId, DeviceEnrol.EnrolmentTypes EnrolmentType)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionStarting, new object[]
{
SessionId,
HostId,
System.Enum.GetName(EnrolmentType.GetType(), EnrolmentType)
});
}
public static void LogSessionDevice(string SessionId, string DeviceSerialNumber, int? DeviceModelId)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionDevice, new object[]
{
SessionId,
DeviceSerialNumber,
DeviceModelId
});
}
public static void LogSessionDeviceInfo(string SessionId, string SerialNumber, string UUID, string ComputerName, string LanMacAddress, string WlanMacAddress, string Manufacturer, string Model, string ModelType)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionDeviceInfo, new object[]
{
SessionId,
SerialNumber,
UUID,
ComputerName,
LanMacAddress,
WlanMacAddress,
Manufacturer,
Model,
ModelType
});
}
public static void LogSessionDeviceInfo(string SessionId, MacEnrol Request)
{
EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.DeviceSerialNumber, Request.DeviceUUID, Request.DeviceComputerName, Request.DeviceLanMacAddress, Request.DeviceWlanMacAddress, Request.DeviceManufacturer, Request.DeviceModel, Request.DeviceModelType);
}
public static void LogSessionDeviceInfo(string SessionId, Models.ClientServices.Enrol Request)
{
EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.DeviceSerialNumber, Request.DeviceUUID, Request.DeviceComputerName, Request.DeviceLanMacAddress, Request.DeviceWlanMacAddress, Request.DeviceManufacturer, Request.DeviceModel, Request.DeviceModelType);
}
public static void LogSessionDeviceInfo(string SessionId, Models.ClientServices.Register Request)
{
EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.DeviceSerialNumber, Request.DeviceUUID, Request.DeviceComputerName, null, null, Request.DeviceManufacturer, Request.DeviceModel, Request.DeviceModelType);
}
public static void LogSessionProgress(string SessionId, int Progress, string Status)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionProgress, new object[]
{
SessionId,
Progress,
Status
});
}
public static void LogSessionFinished(string SessionId)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionFinished, new object[]
{
SessionId
});
}
public static void LogSessionDiagnosticInformation(string SessionId, string Message)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionDiagnosticInformation, new object[]
{
SessionId,
Message
});
}
public static void LogSessionWarning(string SessionId, string Message)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionWarning, new object[]
{
SessionId,
Message
});
}
public static void LogSessionError(string SessionId, System.Exception Ex)
{
if (Ex.InnerException == null)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionError, new object[]
{
SessionId,
Ex.GetType().Name,
Ex.Message,
Ex.StackTrace
});
}
else
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionErrorWithInner, new object[]
{
SessionId,
Ex.GetType().Name,
Ex.Message,
Ex.InnerException.GetType().Name,
Ex.InnerException.Message,
Ex.StackTrace,
Ex.InnerException.StackTrace
});
}
}
public static void LogSessionClientError(string SessionId, string ClientIP, string ClientIdentifier, string ClientVersion, string Error, string RawError)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionClientError, new object[]
{
SessionId,
ClientIP,
ClientIdentifier,
ClientVersion,
Error,
RawError
});
}
public static void LogSessionTaskAddedDevice(string SessionId, string DeviceSerialNumber)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskAddedDevice, new object[]
{
SessionId,
DeviceSerialNumber
});
}
public static void LogSessionTaskUpdatingDevice(string SessionId, string DeviceSerialNumber)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskUpdatingDevice, new object[]
{
SessionId,
DeviceSerialNumber
});
}
public static void LogSessionTaskCreatedDeviceModel(string SessionId, string DeviceSerialNumber, string Manufacturer, string Model)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskCreatedDeviceModel, new object[]
{
SessionId,
DeviceSerialNumber,
Manufacturer,
Model
});
}
public static void LogSessionTaskProvisioningADAccount(string SessionId, string DeviceSerialNumber, string ADAccountName)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskProvisioningADAccount, new object[]
{
SessionId,
DeviceSerialNumber,
ADAccountName
});
}
public static void LogSessionTaskAssigningUser(string SessionId, string DeviceSerialNumber, string UserDisplayName, string UserUsername, string UserDomain, string UserSID)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskAssigningUser, new object[]
{
SessionId,
DeviceSerialNumber,
UserDisplayName,
UserUsername,
UserDomain,
UserSID
});
}
public static void LogSessionTaskProvisioningWirelessCertificate(string SessionId, string DeviceSerialNumber, string CertificateName)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskProvisioningWirelessCertificate, new object[]
{
SessionId,
DeviceSerialNumber,
CertificateName
});
}
public static void LogSessionTaskRenamingDevice(string SessionId, string OldComputerName, string NewComputerName)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskRenamingDevice, new object[]
{
SessionId,
OldComputerName,
NewComputerName
});
}
public static void LogSessionTaskMovingDeviceOrganisationUnit(string SessionId, string OldOrganisationUnit, string NewOrganisationUnit)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionTaskMovingDeviceOrganisationUnit, new object[]
{
SessionId,
OldOrganisationUnit,
NewOrganisationUnit
});
}
public static void LogClientError(string ClientIP, string ClientIdentifier, string ClientVersion, string Error, string RawError)
{
EnrolmentLog.Log(EnrolmentLog.EventTypeIds.ClientError, new object[]
{
ClientIP,
ClientIdentifier,
ClientVersion,
Error,
RawError
});
}
protected override System.Collections.Generic.List<LogEventType> LoadEventTypes()
{
return new System.Collections.Generic.List<LogEventType>
{
new LogEventType
{
Id = 10,
ModuleId = 50,
Name = "Session Starting",
Format = "Starting '{2}' Enrolment for {1} (Session# {0})",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 12,
ModuleId = 50,
Name = "Session Device",
Format = null,
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = false
},
new LogEventType
{
Id = 11,
ModuleId = 50,
Name = "Session Progress",
Format = "Processing Session# {0}; {1}% Complete; Status: {2}",
Severity = 0,
UseLive = true,
UsePersist = false,
UseDisplay = false
},
new LogEventType
{
Id = 13,
ModuleId = 50,
Name = "Session Device Info",
Format = null,
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 20,
ModuleId = 50,
Name = "Session Finished",
Format = "Finished Session# {0}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 21,
ModuleId = 50,
Name = "Session Diagnostic Information",
Format = null,
Severity = 0,
UseLive = true,
UsePersist = false,
UseDisplay = false
},
new LogEventType
{
Id = 22,
ModuleId = 50,
Name = "Session Warning",
Format = null,
Severity = 1,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 23,
ModuleId = 50,
Name = "Session Error",
Format = "An Error Occurred: [{1}] {2}",
Severity = 2,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 24,
ModuleId = 50,
Name = "Session Error with Internal",
Format = "An Error Occurred: [{1}] {2}; Internal Error: [{3}] {4}",
Severity = 2,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionClientError,
ModuleId = _ModuleId,
Name = "Client Error",
Format = "IP: {1}; Device ID: {2}; Version: {3} Error: {4}; Session# {0}",
Severity = (int)LogEventType.Severities.Error,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 50,
ModuleId = 50,
Name = "Task - Added Device",
Format = "Creating Disco Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 51,
ModuleId = 50,
Name = "Task - Updating Device",
Format = "Updating Disco Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 56,
ModuleId = 50,
Name = "Task - Creating Device Model",
Format = "Creating Device Model '{2} {3}' for Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 58,
ModuleId = 50,
Name = "Task - Provisioning Active Directory Account",
Format = "Provisioning Active Directory Account '{2}' for Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 60,
ModuleId = 50,
Name = "Task - Assigning User",
Format = "Assigning User '{2}' ({4}\\{3} {{{5}}}) for Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 62,
ModuleId = 50,
Name = "Task - Provisioning Wireless Certificate",
Format = "Provisioning Wireless Certificate '{2}' for Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 64,
ModuleId = 50,
Name = "Task - Renaming Device",
Format = "Renaming Device '{1}' to '{2}'",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = 66,
ModuleId = 50,
Name = "Task - Moving Device Organisation Unit",
Format = "Moving Device Organisation Unit '{1}' to '{2}'",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ClientError,
ModuleId = _ModuleId,
Name = "Client Error",
Format = "IP: {0}; Device ID: {1}; Version: {2} Error: {3}",
Severity = (int)LogEventType.Severities.Error,
UseLive = true,
UsePersist = true,
UseDisplay = true
}
};
}
}
}
-21
View File
@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace Disco.BI
{
public class DisposableImageCollection : List<Bitmap>, IDisposable
{
public void Dispose()
{
foreach (Image i in this)
{
if (i != null)
i.Dispose();
}
}
}
}
@@ -1,78 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using Disco.Models.Repository;
using Disco.Data.Repository;
using Disco.BI.Extensions;
using System.Web;
using System.Drawing;
using iTextSharp.text.pdf;
namespace Disco.BI.DocumentTemplateBI
{
class DocumentTemplateQRCodeLocationCache
{
private static ConcurrentDictionary<string, List<RectangleF>> _Cache = new ConcurrentDictionary<string, List<RectangleF>>();
public static List<RectangleF> GetLocations(DocumentTemplate dt, DiscoDataContext Database)
{
// Check Cache
List<RectangleF> locations;
if (_Cache.TryGetValue(dt.Id, out locations))
{
return locations;
}
// Generate Cache
return GenerateLocations(dt, Database);
}
public static bool InvalidateLocations(DocumentTemplate dt)
{
List<RectangleF> locations;
return _Cache.TryRemove(dt.Id, out locations);
}
private static bool SetValue(string DocumentTemplateId, List<RectangleF> Locations)
{
if (_Cache.ContainsKey(DocumentTemplateId))
{
List<RectangleF> oldLocations;
if (_Cache.TryGetValue(DocumentTemplateId, out oldLocations))
{
return _Cache.TryUpdate(DocumentTemplateId, Locations, oldLocations);
}
}
return _Cache.TryAdd(DocumentTemplateId, Locations);
}
internal static List<RectangleF> GenerateLocations(DocumentTemplate dt, DiscoDataContext Database)
{
string templateFilename = dt.RepositoryFilename(Database);
PdfReader pdfReader = new PdfReader(templateFilename);
List<RectangleF> locations = new List<RectangleF>();
if (pdfReader.AcroFields.Fields.ContainsKey("DiscoAttachmentId"))
{
foreach (var pdfFieldPosition in pdfReader.AcroFields.GetFieldPositions("DiscoAttachmentId"))
{
var pdfPageSize = pdfReader.GetPageSize(pdfFieldPosition.page);
// Original Position
locations.Add(new RectangleF(
(float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(pdfFieldPosition.position.Left / pdfPageSize.Width) - 0.05)),
(float)System.Math.Min(1.0, System.Math.Max(0.0, (double)((pdfPageSize.Height - pdfFieldPosition.position.Top) / pdfPageSize.Height) - 0.05)),
(float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(pdfFieldPosition.position.Width / pdfPageSize.Width) + 0.1)),
(float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(pdfFieldPosition.position.Height / pdfPageSize.Height) + 0.1))
));
}
}
pdfReader.Close();
// Update Cache
SetValue(dt.Id, locations);
return locations;
}
}
}
@@ -1,204 +0,0 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Interop.ActiveDirectory;
using System;
namespace Disco.BI.DocumentTemplateBI
{
public class DocumentUniqueIdentifier
{
private bool? _loadedComponentsOk;
private DocumentTemplate _documentTemplate;
private object _data;
private string _dataDescription;
public string TemplateTypeId { get; private set; }
public string DataId { get; private set; }
public string DocumentUniqueId
{
get
{
return string.Format("{0}|{1}", this.TemplateTypeId, this.DataId);
}
}
public string CreatorId { get; private set; }
public System.DateTime TimeStamp { get; private set; }
public int Page { get; private set; }
public string Tag { get; private set; }
public DocumentTemplate DocumentTemplate
{
get
{
bool flag = this._loadedComponentsOk.HasValue && this._loadedComponentsOk.Value;
if (flag)
{
return this._documentTemplate;
}
throw new System.Exception("Document Unique Identifier Components not loaded or invalid");
}
}
public object Data
{
get
{
bool flag = this._loadedComponentsOk.HasValue && this._loadedComponentsOk.Value;
if (flag)
{
return this._data;
}
throw new System.Exception("Document Unique Identifier Components not loaded or invalid");
}
}
public string DataDescription
{
get
{
bool flag = this._loadedComponentsOk.HasValue && this._loadedComponentsOk.Value;
if (flag)
{
return this._dataDescription;
}
throw new System.Exception("Document Unique Identifier Components not loaded or invalid");
}
}
public string DataScope { get; private set; }
public static bool IsDocumentUniqueIdentifier(string UniqueIdentifier)
{
return UniqueIdentifier.StartsWith("Disco|", System.StringComparison.OrdinalIgnoreCase);
}
public DocumentUniqueIdentifier(string TemplateTypeId, string DataId, string CreatorId, DateTime TimeStamp, int? Page = null, string Tag = null)
{
this.Tag = Tag;
this.TemplateTypeId = TemplateTypeId;
this.DataId = DataId;
this.CreatorId = ActiveDirectory.ParseDomainAccountId(CreatorId);
this.TimeStamp = TimeStamp;
this.Page = Page ?? 0;
}
public DocumentUniqueIdentifier(string UniqueIdentifier, string Tag)
{
if (!DocumentUniqueIdentifier.IsDocumentUniqueIdentifier(UniqueIdentifier))
{
throw new System.ArgumentException("Invalid Document Unique Identifier", "UniqueIdentifier");
}
this.Tag = Tag;
string[] s = UniqueIdentifier.Split(new char[] { '|' });
string left = s[1].ToUpper();
if (left == "AT" || left == "1")
{
if (s.Length >= 3)
{
this.TemplateTypeId = s[2];
}
if (s.Length >= 4)
{
this.DataId = s[3];
}
if (s.Length >= 5)
{
this.CreatorId = ActiveDirectory.ParseDomainAccountId(s[4]);
}
if (s.Length >= 6)
{
System.DateTime timeStamp;
if (System.DateTime.TryParse(s[5], out timeStamp))
{
this.TimeStamp = timeStamp;
}
}
if (s.Length >= 7)
{
int page = 0;
if (int.TryParse(s[6], out page))
{
this.Page = page;
}
}
return;
}
throw new System.ArgumentException(string.Format("Invalid Document Unique Identifier Version ({0})", s[1]), "UniqueIdentifier");
}
public bool LoadComponents(DiscoDataContext Database)
{
bool LoadComponents;
if (!this._loadedComponentsOk.HasValue)
{
string scopeType;
if (this.TemplateTypeId.StartsWith("--"))
{
string templateTypeId = this.TemplateTypeId;
switch (this.TemplateTypeId)
{
case "--DEVICE":
scopeType = DocumentTemplate.DocumentTemplateScopes.Device;
break;
case "--JOB":
scopeType = DocumentTemplate.DocumentTemplateScopes.Job;
break;
case "--USER":
scopeType = DocumentTemplate.DocumentTemplateScopes.User;
break;
default:
scopeType = null;
break;
}
}
else
{
this._documentTemplate = Database.DocumentTemplates.Find(this.TemplateTypeId);
if (this._documentTemplate != null)
{
scopeType = this._documentTemplate.Scope;
}
else
{
scopeType = null;
}
}
if (scopeType != null)
{
this.DataScope = scopeType;
switch (scopeType)
{
case DocumentTemplate.DocumentTemplateScopes.Device:
Device d = Database.Devices.Find(this.DataId);
if (d != null)
{
this._data = d;
this._dataDescription = d.SerialNumber;
this._loadedComponentsOk = true;
LoadComponents = true;
return LoadComponents;
}
break;
case DocumentTemplate.DocumentTemplateScopes.Job:
Job i = Database.Jobs.Find(int.Parse(this.DataId));
if (i != null)
{
this._data = i;
this._dataDescription = i.Id.ToString();
this._loadedComponentsOk = true;
LoadComponents = true;
return LoadComponents;
}
break;
case DocumentTemplate.DocumentTemplateScopes.User:
User u = Database.Users.Find(ActiveDirectory.ParseDomainAccountId(this.DataId));
if (u != null)
{
this._data = u;
this._dataDescription = u.DisplayName;
this._loadedComponentsOk = true;
LoadComponents = true;
return LoadComponents;
}
break;
default:
break;
}
}
this._loadedComponentsOk = false;
}
LoadComponents = this._loadedComponentsOk.Value;
return LoadComponents;
}
}
}
@@ -1,478 +0,0 @@
using Disco.Models.Repository;
using Disco.Services.Logging;
using Disco.Services.Logging.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Disco.BI.DocumentTemplateBI
{
public class DocumentsLog : LogBase
{
public enum EventTypeIds
{
ImportStarting = 10,
ImportProgress,
ImportFinished,
ImportWarning = 15,
ImportError,
ImportAttachmentExpressionEvaluated = 50,
ImportPageStarting = 100,
ImportPageImageUpdate = 104,
ImportPageProgress,
ImportPageDetected = 110,
ImportPageUndetected = 115,
ImportPageError = 120,
ImportPageUndetectedStored = 150,
DocumentGenerated = 500,
DocumentGeneratedWithExpression
}
private const int _ModuleId = 40;
public static DocumentsLog Current
{
get
{
return (DocumentsLog)LogContext.LogModules[_ModuleId];
}
}
public override string ModuleDescription
{
get
{
return "Documents";
}
}
public override int ModuleId
{
get
{
return _ModuleId;
}
}
public override string ModuleName
{
get
{
return "Documents";
}
}
private static void Log(DocumentsLog.EventTypeIds EventTypeId, params object[] Args)
{
DocumentsLog.Current.Log((int)EventTypeId, Args);
}
public static void LogImportStarting(string SessionId, string DocumentName)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportStarting, new object[]
{
SessionId,
DocumentName
});
}
public static void LogImportProgress(string SessionId, int? Progress, string Status)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportProgress, new object[]
{
SessionId,
Progress,
Status
});
}
public static void LogImportFinished(string SessionId)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportFinished, new object[]
{
SessionId
});
}
public static void LogImportWarning(string SessionId, string Message)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportWarning, new object[]
{
SessionId,
Message
});
}
public static void LogImportError(string SessionId, string Message)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportError, new object[]
{
SessionId,
Message
});
}
public static void LogImportAttachmentExpressionEvaluated(DocumentTemplate template, Device device, DeviceAttachment attachment, string Result)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportAttachmentExpressionEvaluated, new object[]
{
template.Id,
device.SerialNumber,
attachment.Id,
Result
});
}
public static void LogImportAttachmentExpressionEvaluated(DocumentTemplate template, Job job, JobAttachment attachment, string Result)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportAttachmentExpressionEvaluated, new object[]
{
template.Id,
job.Id,
attachment.Id,
Result
});
}
public static void LogImportAttachmentExpressionEvaluated(DocumentTemplate template, User user, UserAttachment attachment, string Result)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportAttachmentExpressionEvaluated, new object[]
{
template.Id,
user.UserId,
attachment.Id,
Result
});
}
public static void LogImportAttachmentExpressionEvaluated(DocumentTemplate Template, object Data, object Attachment, string Result)
{
if (Data is Job)
LogImportAttachmentExpressionEvaluated(Template, (Job)Data, (JobAttachment)Attachment, Result);
else if (Data is User)
LogImportAttachmentExpressionEvaluated(Template, (User)Data, (UserAttachment)Attachment, Result);
else if (Data is Device)
LogImportAttachmentExpressionEvaluated(Template, (Device)Data, (DeviceAttachment)Attachment, Result);
else
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportAttachmentExpressionEvaluated, new object[]
{
Template.Id,
Data.ToString(),
Attachment.ToString(),
Result
});
}
public static void LogImportPageStarting(string SessionId, int PageNumber)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageStarting, new object[]
{
SessionId,
PageNumber
});
}
public static void LogImportPageImageUpdate(string SessionId, int PageNumber)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageImageUpdate, new object[]
{
SessionId,
PageNumber
});
}
public static void LogImportPageProgress(string SessionId, int PageNumber, int? Progress, string Status)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageProgress, new object[]
{
SessionId,
PageNumber,
Progress,
Status
});
}
public static void LogImportPageDetected(string SessionId, int PageNumber, string DocumentTypeId, string DocumentTypeName, string TargetType, string AssignedId, string AssignedName)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageDetected, new object[]
{
SessionId,
PageNumber,
DocumentTypeId,
DocumentTypeName,
TargetType,
AssignedId,
AssignedName
});
}
public static void LogImportPageUndetected(string SessionId, int PageNumber)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageUndetected, new object[]
{
SessionId,
PageNumber
});
}
public static void LogImportPageError(string SessionId, int PageNumber, string Message)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageError, new object[]
{
SessionId,
PageNumber,
Message
});
}
public static void LogImportPageUndetectedStored(string SessionId, int PageNumber)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageUndetectedStored, new object[]
{
SessionId,
PageNumber
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, Device Device, User Author, string ExpressionResult)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
{
Template.Id,
Device.SerialNumber,
Author.UserId,
ExpressionResult
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, Job Job, User Author, string ExpressionResult)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
{
Template.Id,
Job.Id,
Author.UserId,
ExpressionResult
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, User User, User Author, string ExpressionResult)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
{
Template.Id,
User.UserId,
Author.UserId,
ExpressionResult
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, object Data, User Author, string ExpressionResult)
{
if (Data is Job)
LogDocumentGenerated(Template, (Job)Data, Author, ExpressionResult);
else if (Data is User)
LogDocumentGenerated(Template, (User)Data, Author, ExpressionResult);
else if (Data is Device)
LogDocumentGenerated(Template, (Device)Data, Author, ExpressionResult);
else
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
{
Template.Id,
"UNKNOWN",
Author.UserId,
ExpressionResult
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, Device Device, User Author)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
{
Template.Id,
Device.SerialNumber,
Author.UserId
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, Job Job, User Author)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
{
Template.Id,
Job.Id,
Author.UserId
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, User User, User Author)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
{
Template.Id,
User.UserId,
Author.UserId
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, object Data, User Author)
{
if (Data is Job)
LogDocumentGenerated(Template, (Job)Data, Author);
else if (Data is User)
LogDocumentGenerated(Template, (User)Data, Author);
else if (Data is Device)
LogDocumentGenerated(Template, (Device)Data, Author);
else
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
{
Template.Id,
"UNKNOWN",
Author.UserId
});
}
protected override System.Collections.Generic.List<LogEventType> LoadEventTypes()
{
return new System.Collections.Generic.List<LogEventType>
{
new LogEventType
{
Id = (int)EventTypeIds.ImportStarting,
ModuleId = _ModuleId,
Name = "Import Starting",
Format = "Starting import of document: {1} (SessionId: {0})",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ImportProgress,
ModuleId = _ModuleId,
Name = "Import Progress",
Format = "Processing: {1}% Complete; Status: {2}",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = false,
UseDisplay = false
},
new LogEventType
{
Id = (int)EventTypeIds.ImportFinished,
ModuleId = _ModuleId,
Name = "Import Finished",
Format = "Import of document complete (SessionId: {0})",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ImportWarning,
ModuleId = _ModuleId,
Name = "Import Warning",
Format = "Import Warning: {1} (SessionId: {0})",
Severity = (int)LogEventType.Severities.Warning,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ImportError,
ModuleId = _ModuleId,
Name = "Import Error",
Format = "Import Error: {1} (SessionId: {0})",
Severity = (int)LogEventType.Severities.Error,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ImportAttachmentExpressionEvaluated,
ModuleId = _ModuleId,
Name = "Import Attachment Expression Evaluated",
Format = "The import attachment expression for '{0}' was evaluated for '{1}' (attachment id: {2}) with the result: {3}",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ImportPageStarting,
ModuleId = _ModuleId,
Name = "Import Page Starting",
Format = "Starting import of page: {1} (SessionId: {0})",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ImportPageImageUpdate,
ModuleId = _ModuleId,
Name = "Import Page Image Update",
Format = null,
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = false,
UseDisplay = false
},
new LogEventType
{
Id = (int)EventTypeIds.ImportPageProgress,
ModuleId = _ModuleId,
Name = "Import Page Progress",
Format = "Processing: Page {1}; {2}% Complete; Status: {3}",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = false,
UseDisplay = false
},
new LogEventType
{
Id = (int)EventTypeIds.ImportPageDetected,
ModuleId = _ModuleId,
Name = "Import Page Assigned",
Format = "Page {1} of type '{3}' assigned to {4}: '{6}'",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ImportPageUndetected,
ModuleId = _ModuleId,
Name = "Import Page Undetected",
Format = "Page {1} not detected",
Severity = (int)LogEventType.Severities.Warning,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ImportPageError,
ModuleId = _ModuleId,
Name = "Import Page Error",
Format = "Page {1}, Import Error: {2}",
Severity = (int)LogEventType.Severities.Error,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ImportPageUndetectedStored,
ModuleId = _ModuleId,
Name = "Import Page Undetected Stored",
Format = null,
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = false,
UseDisplay = false
},
new LogEventType
{
Id = (int)EventTypeIds.DocumentGenerated,
ModuleId = _ModuleId,
Name = "Document Generated",
Format = "A '{0}' document was generated for '{1}' by '{2}'",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.DocumentGeneratedWithExpression,
ModuleId = _ModuleId,
Name = "Document Generated with Expression",
Format = "A '{0}' document was generated for '{1}' by '{2}'. The expression returned: {3}",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = true,
UseDisplay = true
}
};
}
}
}
@@ -1,85 +0,0 @@
using System;
using System.IO;
using System.Web.Caching;
using Disco.Data.Repository;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Triggers;
namespace Disco.BI.DocumentTemplateBI.Importer
{
public class DocumentDropBoxMonitor : System.IDisposable
{
private IScheduler _scheduler;
private FileSystemWatcher _fsw;
private Cache _httpCache;
public const string WatcherFilter = "*.pdf";
public string DropBoxLocation { get; private set; }
public DocumentDropBoxMonitor(DiscoDataContext Database, ISchedulerFactory SchedulerFactory, Cache HttpCache)
{
if (Database == null)
throw new System.ArgumentNullException("Context");
this._httpCache = HttpCache;
var location = DataStore.CreateLocation(Database, "DocumentDropBox");
this.DropBoxLocation = location.EndsWith(@"\") ? location : string.Concat(location, @"\");
this._scheduler = SchedulerFactory.GetScheduler();
this._scheduler.Start();
}
public void ScheduleCurrentFiles(int Delay)
{
foreach (var filename in System.IO.Directory.GetFiles(this.DropBoxLocation, "*.pdf"))
{
this.ScheduleFile(filename, Delay);
}
}
public void StartWatching()
{
if (this._fsw == null)
{
this._fsw = new FileSystemWatcher(this.DropBoxLocation, "*.pdf");
this._fsw.Created += new FileSystemEventHandler(this.FSW_Created);
}
this._fsw.EnableRaisingEvents = true;
}
public void StopWatching()
{
if (this._fsw != null)
{
this._fsw.EnableRaisingEvents = false;
}
}
public void ScheduleFile(string Filename, int Delay)
{
System.Guid guid = System.Guid.NewGuid();
JobDetailImpl jd = new JobDetailImpl(guid.ToString(), typeof(DocumentImporterJob));
jd.JobDataMap.Add("Filename", Filename);
jd.JobDataMap.Add("RetryCount", 0);
jd.JobDataMap.Add("HttpCache", this._httpCache);
guid = System.Guid.NewGuid();
System.DateTimeOffset startTimeUtc = new System.DateTimeOffset(DateTime.Now.AddSeconds((double)Delay));
SimpleTriggerImpl trig = new SimpleTriggerImpl(guid.ToString(), startTimeUtc);
this._scheduler.ScheduleJob(jd, trig);
}
private void FSW_Created(object sender, FileSystemEventArgs e)
{
if ((e.ChangeType & WatcherChangeTypes.Deleted) != WatcherChangeTypes.Deleted)
this.ScheduleFile(e.FullPath, 5);
}
public void Dispose()
{
this.StopWatching();
if (this._fsw != null)
this._fsw.Dispose();
if (this._scheduler != null)
this._scheduler.Shutdown(false);
}
}
}
@@ -1,123 +0,0 @@
using Disco.Data.Repository;
using Exceptionless;
using Quartz;
using Quartz.Impl.Triggers;
using System;
using System.IO;
using System.Web.Caching;
namespace Disco.BI.DocumentTemplateBI.Importer
{
[PersistJobDataAfterExecution]
public class DocumentImporterJob : IJob
{
void IJob.Execute(IJobExecutionContext context)
{
string sessionId = context.JobDetail.JobDataMap["SessionId"] as string;
if (string.IsNullOrEmpty(sessionId))
{
sessionId = Guid.NewGuid().ToString();
context.JobDetail.JobDataMap["SessionId"] = sessionId;
}
string filename = context.JobDetail.JobDataMap["Filename"] as string;
int retryCount = (int)context.JobDetail.JobDataMap["RetryCount"];
Cache httpCache = context.JobDetail.JobDataMap["HttpCache"] as Cache;
var friendlyFilename = filename;
if (!string.IsNullOrEmpty(friendlyFilename))
friendlyFilename = System.IO.Path.GetFileName(friendlyFilename);
DocumentsLog.LogImportStarting(sessionId, friendlyFilename);
if (!File.Exists(filename))
{
DocumentsLog.LogImportWarning(sessionId, string.Format("File not found: {0}", filename));
DocumentsLog.LogImportFinished(sessionId);
context.Scheduler.DeleteJob(context.JobDetail.Key);
return;
}
try
{
using (DiscoDataContext database = new DiscoDataContext())
{
if (retryCount < 18)
{
context.JobDetail.JobDataMap["RetryCount"] = (++retryCount);
bool processResult = Interop.Pdf.PdfImporter.ProcessPdfAttachment(filename, database, sessionId, httpCache);
if (processResult)
{
// Import Successful - Delete
if (File.Exists(filename))
File.Delete(filename);
}
else
{
// Import Failed - Move to Errors Folder
if (File.Exists(filename))
{
try
{
string folderError = DataStore.CreateLocation(database, "DocumentDropBox_Errors");
string filenameError = Path.Combine(folderError, Path.GetFileName(filename));
int filenameErrorCount = 0;
while (File.Exists(filenameError))
{
filenameError = Path.Combine(folderError, string.Format("{0} ({1}){2}", Path.GetFileNameWithoutExtension(filename), ++filenameErrorCount, Path.GetExtension(filename)));
}
File.Move(filename, filenameError);
}
catch (Exception ex)
{
ex.ToExceptionless().Submit();
// Ignore Errors
}
}
}
}
else
{
// To Many Errors
DocumentsLog.LogImportError(sessionId, string.Format("To many errors occurred trying to import '{1}' (SessionId: {0})", sessionId, friendlyFilename));
// Move to Errors Folder
if (File.Exists(filename))
{
try
{
string folderError = DataStore.CreateLocation(database, "DocumentDropBox_Errors");
string filenameError = Path.Combine(folderError, Path.GetFileName(filename));
int filenameErrorCount = 0;
while (File.Exists(filenameError))
{
filenameError = Path.Combine(folderError, string.Format("{0} ({1}){2}", Path.GetFileNameWithoutExtension(filename), ++filenameErrorCount, Path.GetExtension(filename)));
}
File.Move(filename, filenameError);
}
catch
{
// Ignore Errors
}
}
}
}
DocumentsLog.LogImportFinished(sessionId);
// All Done
context.Scheduler.DeleteJob(context.JobDetail.Key);
}
catch (Exception ex)
{
ex.ToExceptionless().Submit();
DocumentsLog.LogImportWarning(sessionId, string.Format("{0}; Will try again in 10 Seconds", ex.Message));
// Reschedule Job for 10 seconds
SimpleTriggerImpl trig = new SimpleTriggerImpl(Guid.NewGuid().ToString(), new DateTimeOffset(DateTime.Now.AddSeconds(10)));
context.Scheduler.RescheduleJob(context.Trigger.Key, trig);
}
}
}
}
@@ -1,49 +0,0 @@
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace Disco.BI.DocumentTemplateBI
{
public static class Utilities
{
public static System.IO.Stream JoinPdfs(params System.IO.Stream[] Pdfs)
{
if (Pdfs.Length == 0)
throw new System.ArgumentNullException("Pdfs");
// Only One PDF - Possible Reference Bug v's Memory/Speed (Returning Param Memory Stream)
if (Pdfs.Length == 1)
return Pdfs[0];
// Join Pdfs
System.IO.MemoryStream msBuilder = new System.IO.MemoryStream();
Document pdfDoc = new Document();
PdfCopy pdfCopy = new PdfCopy(pdfDoc, msBuilder);
pdfDoc.Open();
pdfCopy.CloseStream = false;
for (int i = 0; i < Pdfs.Length; i++)
{
System.IO.Stream pdf = Pdfs[i];
PdfReader pdfReader = new PdfReader(pdf);
for (int indexPage = 1; indexPage <= pdfReader.NumberOfPages; indexPage++)
{
iTextSharp.text.Rectangle pageSize = pdfReader.GetPageSizeWithRotation(indexPage);
PdfImportedPage page = pdfCopy.GetImportedPage(pdfReader, indexPage);
pdfDoc.SetPageSize(pageSize);
pdfDoc.NewPage();
pdfCopy.AddPage(page);
}
pdfReader.Close();
}
pdfDoc.Close();
pdfCopy.Close();
msBuilder.Position = 0;
return msBuilder;
}
}
}
-259
View File
@@ -1,259 +0,0 @@
using Disco.Data.Repository;
using Disco.Models.BI.DocumentTemplates;
using Disco.Models.Repository;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using Disco.Models.BI.Expressions;
namespace Disco.BI.Expressions
{
public sealed class Expression : System.Collections.Generic.List<IExpressionPart>
{
public string Name { get; private set; }
public string Source { get; private set; }
public bool IsDynamic { get; private set; }
public int Ordinal { get; private set; }
private Expression(string Name, string Source, int Ordinal)
{
this.Name = Name;
this.Source = Source;
this.Ordinal = Ordinal;
}
public static void InitializeExpressions()
{
Spring.Core.TypeResolution.TypeRegistry.RegisterType("DataExt", typeof(Extensions.DataExt));
Spring.Core.TypeResolution.TypeRegistry.RegisterType("UserExt", typeof(Extensions.UserExt));
Spring.Core.TypeResolution.TypeRegistry.RegisterType("DeviceExt", typeof(Extensions.DeviceExt));
Spring.Core.TypeResolution.TypeRegistry.RegisterType("ImageExt", typeof(Extensions.ImageExt));
}
public T EvaluateFirst<T>(object ExpressionContext, System.Collections.IDictionary Variables)
{
T result = default(T);
if (this.Count > 0)
{
try
{
object expressionResult = this[0].Evaluate(ExpressionContext, Variables);
if (expressionResult != null)
{
if (expressionResult is T)
{
result = (T)expressionResult;
}
else
{
throw new InvalidOperationException("Expression returned an invalid type");
}
}
}
catch (System.Exception ex)
{
throw new InvalidOperationException("Expression evaluation resulted in an error", ex);
}
}
return result;
}
public Tuple<string, bool, object> Evaluate(object ExpressionContext, System.Collections.IDictionary Variables)
{
System.Text.StringBuilder resultValue = new System.Text.StringBuilder();
object resultObject = null;
bool resultError = false;
foreach (var expressionPart in this)
{
try
{
object partValue = expressionPart.Evaluate(ExpressionContext, Variables);
if (partValue != null)
{
// Check for Result Objects
if (partValue is IImageExpressionResult)
resultObject = partValue;
else
resultValue.Append(partValue.ToString());
}
}
catch (System.Exception ex)
{
if (!expressionPart.ErrorsAllowed)
{
resultValue.Append("## ERROR # ");
resultValue.Append(ex.Message);
resultValue.Append(" ##");
resultError = true;
}
}
}
return new Tuple<string, bool, object>(resultValue.ToString(), resultError, resultObject);
}
public static Expression TokenizeSingleDynamic(string Name, string ExpressionSource, int Ordinal)
{
Expression e = new Expression(Name, ExpressionSource, Ordinal);
if (ExpressionSource != null && !string.IsNullOrWhiteSpace(ExpressionSource))
e.Add(new EvaluateExpressionPart(ExpressionSource));
e.IsDynamic = true;
return e;
}
public static Expression Tokenize(string Name, string ExpressionSource, int Ordinal)
{
Expression e = new Expression(Name, ExpressionSource, Ordinal);
if (!ExpressionSource.Contains("{") || !ExpressionSource.Contains("}"))
{
e.Add(new TextExpressionPart(ExpressionSource));
}
else
{
System.Text.StringBuilder token = new System.Text.StringBuilder();
bool tokenEval = false;
int tokenEvalDepth = 0;
foreach (char c in ExpressionSource)
{
switch (c)
{
case '{':
{
if (!tokenEval)
{
if (token.Length > 0)
{
e.Add(new TextExpressionPart(token.ToString()));
token = new System.Text.StringBuilder();
}
tokenEval = true;
tokenEvalDepth = 0;
}
tokenEvalDepth++;
token.Append(c);
break;
}
case '}':
{
token.Append(c);
if (tokenEval)
{
tokenEvalDepth--;
if (tokenEvalDepth <= 0)
{
if (token.Length != 2 && (token.Length != 3 || token[1] != '@'))
{
e.Add(new EvaluateExpressionPart(token.ToString()));
e.IsDynamic = true;
token = new System.Text.StringBuilder();
}
tokenEval = false;
}
}
break;
}
default:
{
token.Append(c);
break;
}
}
}
if (token.Length > 0)
{
e.Add(new TextExpressionPart(token.ToString()));
}
}
return e;
}
public static IDictionary StandardVariables(DocumentTemplate AttachmentType, DiscoDataContext Database, User User, System.DateTime TimeStamp, DocumentState DocumentState)
{
return new Hashtable
{
{
"DataContext",
Database
},
{
"User",
User
},
{
"TimeStamp",
TimeStamp
},
{
"AttachmentType",
AttachmentType
},
{
"State",
DocumentState
}
};
}
public static Dictionary<string, string> StandardVariableTypes()
{
return new Dictionary<string, string>
{
{
"#DataContext",
typeof(DiscoDataContext).AssemblyQualifiedName
},
{
"#User",
typeof(User).AssemblyQualifiedName
},
{
"#TimeStamp",
typeof(System.DateTime).AssemblyQualifiedName
},
{
"#AttachmentType",
typeof(DocumentTemplate).AssemblyQualifiedName
},
{
"#State",
typeof(DocumentState).AssemblyQualifiedName
}
};
}
public static Dictionary<string, string> ExtensionLibraryTypes()
{
return new Dictionary<string, string>
{
{
"DataExt",
typeof(Extensions.DataExt).AssemblyQualifiedName
},
{
"DeviceExt",
typeof(Extensions.DeviceExt).AssemblyQualifiedName
},
{
"ImageExt",
typeof(Extensions.ImageExt).AssemblyQualifiedName
},
{
"UserExt",
typeof(Extensions.UserExt).AssemblyQualifiedName
}
};
}
}
}
-103
View File
@@ -1,103 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
namespace Disco.BI.Expressions
{
public static class ExpressionCache
{
private static ConcurrentDictionary<string, ConcurrentDictionary<string, Expression>> _Cache = new ConcurrentDictionary<string, ConcurrentDictionary<string, Expression>>();
public delegate Expression CreateValueDelegate();
public static ConcurrentDictionary<string, Expression> GetModule(string Module, bool Create = false)
{
ConcurrentDictionary<string, Expression> moduleCache;
if (_Cache.TryGetValue(Module, out moduleCache))
return moduleCache;
else
{
if (Create)
{
moduleCache = new ConcurrentDictionary<string, Expression>();
_Cache.TryAdd(Module, moduleCache);
return moduleCache;
}
else
return null;
}
}
private static Expression GetModuleValue(string Module, string Key, CreateValueDelegate CreateValue)
{
ConcurrentDictionary<string, Expression> moduleCache = GetModule(Module, (CreateValue != null));
if (moduleCache != null)
{
Expression expression;
if (moduleCache.TryGetValue(Key, out expression))
{
return expression;
}
if (CreateValue != null)
{
expression = CreateValue();
Expression oldExpression;
if (moduleCache.TryGetValue(Key, out oldExpression))
moduleCache.TryUpdate(Key, expression, oldExpression);
else
moduleCache.TryAdd(Key, expression);
return expression;
}
}
return null;
}
public static Expression GetValue(string Module, string Key, CreateValueDelegate CreateValue)
{
return GetModuleValue(Module, Key, CreateValue);
}
public static Expression GetValue(string Module, string Key)
{
return GetModuleValue(Module, Key, null);
}
public static bool InvalidModule(string Module)
{
ConcurrentDictionary<string, Expression> moduleCache;
return _Cache.TryRemove(Module, out moduleCache);
}
public static bool InvalidateKey(string Module, string Key)
{
Expression expression;
ConcurrentDictionary<string, Expression> moduleCache = GetModule(Module, false);
if (moduleCache != null)
{
bool removeResult = moduleCache.TryRemove(Key, out expression);
if (moduleCache.Count == 0)
InvalidModule(Module);
return removeResult;
}
else
return false;
}
public static bool SetValue(string Module, string Key, Expression Expression)
{
ConcurrentDictionary<string, Expression> moduleCache = GetModule(Module, true);
if (moduleCache.ContainsKey(Key))
{
Expression oldExpression;
if (moduleCache.TryGetValue(Key, out oldExpression))
{
return moduleCache.TryUpdate(Key, Expression, oldExpression);
}
}
return moduleCache.TryAdd(Key, Expression);
}
}
}
@@ -1,61 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Disco.BI.Expressions
{
public class ExpressionTypeMemberDescriptor
{
public const string FunctionKind = "function";
public const string PropertyKind = "property";
public const string ParameterKind = "parameter";
public string Kind {get;set;}
public string Name {get;set;}
public string ReturnType {get;set;}
public string ReturnExpressionType{get;set;}
public List<ExpressionTypeMemberDescriptor> Parameters{get;set;}
public static ExpressionTypeMemberDescriptor Build(System.Reflection.MethodInfo m)
{
ExpressionTypeMemberDescriptor md = new ExpressionTypeMemberDescriptor
{
Kind = "function",
Name = m.Name,
ReturnType = m.ReturnType.Name,
ReturnExpressionType = m.ReturnType.AssemblyQualifiedName
};
md.Parameters = (
from mdp in m.GetParameters()
select ExpressionTypeMemberDescriptor.Build(mdp)).ToList<ExpressionTypeMemberDescriptor>();
return md;
}
public static ExpressionTypeMemberDescriptor Build(System.Reflection.PropertyInfo p)
{
ExpressionTypeMemberDescriptor md = new ExpressionTypeMemberDescriptor
{
Kind = "property",
Name = p.Name,
ReturnType = p.PropertyType.Name,
ReturnExpressionType = p.PropertyType.AssemblyQualifiedName
};
md.Parameters = (
from mdp in p.GetIndexParameters()
select ExpressionTypeMemberDescriptor.Build(mdp)).ToList<ExpressionTypeMemberDescriptor>();
return md;
}
public static ExpressionTypeMemberDescriptor Build(System.Reflection.ParameterInfo pi)
{
return new ExpressionTypeMemberDescriptor
{
Kind = "parameter",
Name = pi.Name,
ReturnType = pi.ParameterType.Name,
ReturnExpressionType = pi.ParameterType.AssemblyQualifiedName
};
}
}
}
@@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Models.BI.Expressions;
using System.IO;
using System.Drawing;
using Disco.BI.Extensions;
namespace Disco.BI.Expressions.Extensions.ImageResultImplementations
{
public abstract class BaseImageExpressionResult : IImageExpressionResult
{
public byte Quality { get; set; }
public bool LosslessFormat { get; set; }
public bool ShowField { get; set; }
public string BackgroundColour { get; set; }
public bool BackgroundPreferTransparent { get; set; }
public BaseImageExpressionResult()
{
this.LosslessFormat = false;
this.Quality = 90;
this.ShowField = false;
this.BackgroundPreferTransparent = true;
}
public abstract Stream GetImage(int Width, int Height);
protected Stream RenderImage(Image SourceImage, int Width, int Height)
{
if (SourceImage == null)
throw new ArgumentNullException("SourceImage");
if (Width <= 0)
throw new ArgumentOutOfRangeException("Width", "Width must be > 0");
if (Height <= 0)
throw new ArgumentOutOfRangeException("Height", "Height must be > 0");
Brush backgroundBrush = null;
if (!LosslessFormat || !BackgroundPreferTransparent)
{
if (string.IsNullOrEmpty(this.BackgroundColour))
backgroundBrush = Brushes.White;
else
backgroundBrush = new SolidBrush(ColorTranslator.FromHtml(this.BackgroundColour));
}
using (Image resizedImage = SourceImage.ResizeImage(Width, Height, backgroundBrush))
{
return OutputImage(resizedImage);
}
}
protected Stream OutputImage(Image SourceImage)
{
MemoryStream imageStream = new MemoryStream();
if (LosslessFormat)
{ // Lossless Format - PNG
SourceImage.SavePng(imageStream);
}
else
{ // Lossy Format - JPG
byte quality = Math.Min((byte)100, Math.Max((byte)1, this.Quality));
SourceImage.SaveJpg(quality, imageStream);
}
imageStream.Position = 0;
return imageStream;
}
}
}
@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
namespace Disco.BI.Expressions.Extensions.ImageResultImplementations
{
public class BitmapImageExpressionResult : BaseImageExpressionResult
{
public Image Image { get; set; }
public BitmapImageExpressionResult(Image Image)
{
if (Image == null)
throw new ArgumentNullException("Image");
this.Image = Image;
}
public override Stream GetImage(int Width, int Height)
{
return this.RenderImage(this.Image, Width, Height);
}
}
}
@@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
namespace Disco.BI.Expressions.Extensions.ImageResultImplementations
{
public class FileImageExpressionResult : BaseImageExpressionResult
{
public string AbsoluteFilePath { get; set; }
public FileImageExpressionResult(string AbsoluteFilePath)
{
if (string.IsNullOrWhiteSpace(AbsoluteFilePath))
throw new ArgumentNullException("AbsoluteFilePath");
if (!File.Exists(AbsoluteFilePath))
throw new FileNotFoundException("Image not found", AbsoluteFilePath);
this.AbsoluteFilePath = AbsoluteFilePath;
}
public override Stream GetImage(int Width, int Height)
{
using (Image SourceImage = Bitmap.FromFile(this.AbsoluteFilePath))
{
return this.RenderImage(SourceImage, Width, Height);
}
}
}
}
@@ -1,16 +0,0 @@
using System;
using System.Collections;
namespace Disco.BI.Expressions
{
public interface IExpressionPart
{
string RawSource { get; set; }
string Source { get; set; }
bool ErrorsAllowed { get; set; }
bool ParseError { get; }
string ParseErrorMessage { get; }
bool IsDynamic { get; set; }
object Evaluate(object ExpressionContext, System.Collections.IDictionary Variables);
}
}
@@ -1,95 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Models.Repository;
using Disco.Data.Repository;
using Disco.Services.Users;
using Disco.Services.Authorization;
using Disco.BI.DocumentTemplateBI.ManagedGroups;
namespace Disco.BI.Extensions
{
public static class AttachmentActionExtensions
{
#region Delete
public static bool CanDelete(this DeviceAttachment da)
{
if (UserService.CurrentAuthorization.Has(Claims.Device.Actions.RemoveAnyAttachments))
return true;
if (UserService.CurrentAuthorization.Has(Claims.Device.Actions.RemoveOwnAttachments)
&& da.TechUserId == UserService.CurrentUserId)
return true;
return false;
}
public static void OnDelete(this DeviceAttachment da, DiscoDataContext Database)
{
if (!da.CanDelete())
throw new InvalidOperationException("Deletion of Attachment is Denied");
var attachmentId = da.Id;
var documentTemplateId = da.DocumentTemplateId;
var deviceSerialNumber = da.DeviceSerialNumber;
da.RepositoryDelete(Database);
Database.DeviceAttachments.Remove(da);
DocumentTemplateManagedGroups.TriggerDeviceAttachmentDeleted(Database, attachmentId, documentTemplateId, deviceSerialNumber);
}
public static bool CanDelete(this JobAttachment ja)
{
if (UserService.CurrentAuthorization.Has(Claims.Job.Actions.RemoveAnyAttachments))
return true;
if (UserService.CurrentAuthorization.Has(Claims.Job.Actions.RemoveOwnAttachments)
&& ja.TechUserId == UserService.CurrentUserId)
return true;
return false;
}
public static void OnDelete(this JobAttachment ja, DiscoDataContext Database)
{
if (!ja.CanDelete())
throw new InvalidOperationException("Deletion of Attachment is Denied");
var attachmentId = ja.Id;
var documentTemplateId = ja.DocumentTemplateId;
var jobId = ja.JobId;
ja.RepositoryDelete(Database);
Database.JobAttachments.Remove(ja);
DocumentTemplateManagedGroups.TriggerJobAttachmentDeleted(Database, attachmentId, documentTemplateId, jobId);
}
public static bool CanDelete(this UserAttachment ua)
{
if (UserService.CurrentAuthorization.Has(Claims.User.Actions.RemoveAnyAttachments))
return true;
if (UserService.CurrentAuthorization.Has(Claims.User.Actions.RemoveOwnAttachments)
&& ua.TechUserId == UserService.CurrentUserId)
return true;
return false;
}
public static void OnDelete(this UserAttachment ua, DiscoDataContext Database)
{
if (!ua.CanDelete())
throw new InvalidOperationException("Deletion of Attachment is Denied");
var attachmentId = ua.Id;
var documentTemplateId = ua.DocumentTemplateId;
var userId = ua.UserId;
ua.RepositoryDelete(Database);
Database.UserAttachments.Remove(ua);
DocumentTemplateManagedGroups.TriggerUserAttachmentDeleted(Database, attachmentId, documentTemplateId, userId);
}
#endregion
}
}
@@ -1,209 +0,0 @@
using Disco.BI.DocumentTemplateBI;
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Logging;
using Disco.Services.Users;
using Exceptionless;
using System;
using System.IO;
namespace Disco.BI.Extensions
{
public static class AttachmentExtensions
{
public static bool ImportPdfAttachment(this DocumentUniqueIdentifier UniqueIdentifier, DiscoDataContext Database, System.IO.Stream PdfContent, byte[] PdfThumbnail)
{
UniqueIdentifier.LoadComponents(Database);
DocumentTemplate documentTemplate = UniqueIdentifier.DocumentTemplate;
string filename;
string comments;
object attachment;
if (documentTemplate == null)
{
filename = string.Format("{0}_{1:yyyyMMdd-HHmmss}.pdf", UniqueIdentifier.DataId.Replace('\\', '_'), UniqueIdentifier.TimeStamp);
comments = string.Format("Uploaded: {0:s}", UniqueIdentifier.TimeStamp);
}
else
{
filename = string.Format("{0}_{1:yyyyMMdd-HHmmss}.pdf", UniqueIdentifier.TemplateTypeId, UniqueIdentifier.TimeStamp);
comments = string.Format("Generated: {0:s}", UniqueIdentifier.TimeStamp);
}
User creatorUser = UserService.GetUser(UniqueIdentifier.CreatorId, Database);
if (creatorUser == null)
{
// No Creator User (or Username invalid)
creatorUser = UserService.CurrentUser;
}
switch (UniqueIdentifier.DataScope)
{
case DocumentTemplate.DocumentTemplateScopes.Device:
Device d = (Device)UniqueIdentifier.Data;
attachment = d.CreateAttachment(Database, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, documentTemplate, PdfThumbnail);
break;
case DocumentTemplate.DocumentTemplateScopes.Job:
Job j = (Job)UniqueIdentifier.Data;
attachment = j.CreateAttachment(Database, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, documentTemplate, PdfThumbnail);
break;
case DocumentTemplate.DocumentTemplateScopes.User:
User u = (User)UniqueIdentifier.Data;
attachment = u.CreateAttachment(Database, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, documentTemplate, PdfThumbnail);
break;
default:
return false;
}
if (documentTemplate != null && !string.IsNullOrWhiteSpace(documentTemplate.OnImportAttachmentExpression))
{
try
{
var expressionResult = documentTemplate.EvaluateOnAttachmentImportExpression(attachment, Database, creatorUser, UniqueIdentifier.TimeStamp);
DocumentsLog.LogImportAttachmentExpressionEvaluated(documentTemplate, UniqueIdentifier.Data, attachment, expressionResult);
}
catch (Exception ex)
{
SystemLog.LogException("Document Importer - OnImportAttachmentExpression", ex);
}
}
return true;
}
public static string RepositoryFilename(this DeviceAttachment da, DiscoDataContext Database)
{
return Path.Combine(DataStore.CreateLocation(Database, "DeviceAttachments", da.Timestamp), string.Format("{0}_{1}_file", da.DeviceSerialNumber, da.Id));
}
public static string RepositoryFilename(this JobAttachment ja, DiscoDataContext Database)
{
return Path.Combine(DataStore.CreateLocation(Database, "JobAttachments", ja.Timestamp), string.Format("{0}_{1}_file", ja.JobId, ja.Id));
}
public static string RepositoryFilename(this UserAttachment ua, DiscoDataContext Database)
{
return Path.Combine(DataStore.CreateLocation(Database, "UserAttachments", ua.Timestamp), string.Format("{0}_{1}_file", ua.UserId.Replace('\\', '_'), ua.Id));
}
private static string RepositoryThumbnailFilenameInternal(string DirectoryPath, string Filename)
{
return Path.Combine(DirectoryPath, Filename);
}
public static string RepositoryThumbnailFilename(this DeviceAttachment da, DiscoDataContext Database)
{
return RepositoryThumbnailFilenameInternal(DataStore.CreateLocation(Database, "DeviceAttachments", da.Timestamp), string.Format("{0}_{1}_thumb.jpg", da.DeviceSerialNumber, da.Id));
}
public static string RepositoryThumbnailFilename(this JobAttachment ja, DiscoDataContext Database)
{
return RepositoryThumbnailFilenameInternal(DataStore.CreateLocation(Database, "JobAttachments", ja.Timestamp), string.Format("{0}_{1}_thumb.jpg", ja.JobId, ja.Id));
}
public static string RepositoryThumbnailFilename(this UserAttachment ua, DiscoDataContext Database)
{
return RepositoryThumbnailFilenameInternal(DataStore.CreateLocation(Database, "UserAttachments", ua.Timestamp), string.Format("{0}_{1}_thumb.jpg", ua.UserId.Replace('\\', '_'), ua.Id));
}
public static void RepositoryDelete(this DeviceAttachment da, DiscoDataContext Database)
{
RepositoryDelete(da.RepositoryFilename(Database), da.RepositoryThumbnailFilename(Database));
}
public static void RepositoryDelete(this JobAttachment ja, DiscoDataContext Database)
{
RepositoryDelete(ja.RepositoryFilename(Database), ja.RepositoryThumbnailFilename(Database));
}
public static void RepositoryDelete(this UserAttachment ua, DiscoDataContext Database)
{
RepositoryDelete(ua.RepositoryFilename(Database), ua.RepositoryThumbnailFilename(Database));
}
private static void RepositoryDelete(params string[] filePaths)
{
foreach (string filePath in filePaths)
{
if (File.Exists(filePath))
File.Delete(filePath);
}
}
public static string SaveAttachment(this DeviceAttachment da, DiscoDataContext Database, Stream FileContent)
{
string filePath = da.RepositoryFilename(Database);
SaveAttachment(filePath, FileContent);
return filePath;
}
public static string SaveAttachment(this JobAttachment ja, DiscoDataContext Database, Stream FileContent)
{
string filePath = ja.RepositoryFilename(Database);
SaveAttachment(filePath, FileContent);
return filePath;
}
public static string SaveAttachment(this UserAttachment ua, DiscoDataContext Database, Stream FileContent)
{
string filePath = ua.RepositoryFilename(Database);
SaveAttachment(filePath, FileContent);
return filePath;
}
public static string SaveThumbnailAttachment(this DeviceAttachment da, DiscoDataContext Database, byte[] FileContent)
{
string filePath = da.RepositoryThumbnailFilename(Database);
File.WriteAllBytes(filePath, FileContent);
return filePath;
}
public static string SaveThumbnailAttachment(this JobAttachment ja, DiscoDataContext Database, byte[] FileContent)
{
string filePath = ja.RepositoryThumbnailFilename(Database);
File.WriteAllBytes(filePath, FileContent);
return filePath;
}
public static string SaveThumbnailAttachment(this UserAttachment ua, DiscoDataContext Database, byte[] FileContent)
{
string filePath = ua.RepositoryThumbnailFilename(Database);
File.WriteAllBytes(filePath, FileContent);
return filePath;
}
private static void SaveAttachment(string FilePath, Stream FileContent)
{
using (FileStream sw = new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
FileContent.CopyTo(sw);
sw.Flush();
sw.Close();
}
}
public static string GenerateThumbnail(this DeviceAttachment da, DiscoDataContext Database)
{
string filePath = da.RepositoryThumbnailFilename(Database);
AttachmentBI.Utilities.GenerateThumbnail(da.RepositoryFilename(Database), da.MimeType, filePath);
return filePath;
}
public static string GenerateThumbnail(this JobAttachment ja, DiscoDataContext Database)
{
string filePath = ja.RepositoryThumbnailFilename(Database);
AttachmentBI.Utilities.GenerateThumbnail(ja.RepositoryFilename(Database), ja.MimeType, filePath);
return filePath;
}
public static string GenerateThumbnail(this UserAttachment ua, DiscoDataContext Database)
{
string filePath = ua.RepositoryThumbnailFilename(Database);
AttachmentBI.Utilities.GenerateThumbnail(ua.RepositoryFilename(Database), ua.MimeType, filePath);
return filePath;
}
public static string GenerateThumbnail(this DeviceAttachment da, DiscoDataContext Database, Stream SourceFile)
{
string filePath = da.RepositoryThumbnailFilename(Database);
AttachmentBI.Utilities.GenerateThumbnail(SourceFile, da.MimeType, filePath);
return filePath;
}
public static string GenerateThumbnail(this JobAttachment ja, DiscoDataContext Database, Stream SourceFile)
{
string filePath = ja.RepositoryThumbnailFilename(Database);
AttachmentBI.Utilities.GenerateThumbnail(SourceFile, ja.MimeType, filePath);
return filePath;
}
public static string GenerateThumbnail(this UserAttachment ua, DiscoDataContext Database, Stream SourceFile)
{
string filePath = ua.RepositoryThumbnailFilename(Database);
AttachmentBI.Utilities.GenerateThumbnail(SourceFile, ua.MimeType, filePath);
return filePath;
}
}
}
@@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Models.Repository;
using Disco.Data.Repository;
using Disco.Services.Users;
using Disco.Services.Authorization;
namespace Disco.BI.Extensions
{
public static class DeviceBatchExtensions
{
public static bool CanDelete(this DeviceBatch db, DiscoDataContext Database)
{
if (!UserService.CurrentAuthorization.Has(Claims.Config.DeviceBatch.Delete))
return false;
// Can't Delete if Contains Devices
var deviceCount = Database.Devices.Count(d => d.DeviceBatchId == db.Id);
if (deviceCount > 0)
return false;
return true;
}
public static void Delete(this DeviceBatch db, DiscoDataContext Database)
{
if (!db.CanDelete(Database))
throw new InvalidOperationException("The state of this Device Batch doesn't allow it to be deleted");
// Delete Batch
Database.DeviceBatches.Remove(db);
}
}
}
@@ -1,38 +0,0 @@
using System.Linq;
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Plugins;
using Disco.Services.Plugins.Features.CertificateProvider;
using System;
using System.Collections.Generic;
namespace Disco.BI.Extensions
{
public static class DeviceCertificateExtensions
{
public static Tuple<DeviceCertificate, List<string>> AllocateCertificate(this Device device, DiscoDataContext Database)
{
if (!string.IsNullOrEmpty(device.DeviceProfile.CertificateProviderId))
{
// Load Plugin
PluginFeatureManifest featureManifest = Plugins.GetPluginFeature(device.DeviceProfile.CertificateProviderId, typeof(CertificateProviderFeature));
using (CertificateProviderFeature providerFeature = featureManifest.CreateInstance<CertificateProviderFeature>())
{
// REMOVED 2012-07-18 G# - Plugin is responsible for checking
// Already Allocated Certificate
//if (deviceCertificates.Count > 0)
// return new Tuple<DeviceCertificate, List<string>>(deviceCertificates[0], providerPlugin.RemoveExistingCertificateNames());
//else
return providerFeature.AllocateCertificate(Database, device);
}
}
// Device Profile does not allow certificate allocation
return null;
}
}
}
@@ -1,162 +0,0 @@
using Disco.Models.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Disco.BI.Extensions
{
public static class DeviceDetailExtensions
{
#region Helpers
private static string GetDetail(this IEnumerable<DeviceDetail> details, string Scope, string Key)
{
if (details == null)
throw new ArgumentNullException("details");
if (string.IsNullOrEmpty(Scope))
throw new ArgumentNullException("Scope");
if (string.IsNullOrEmpty(Key))
throw new ArgumentNullException("Key");
var detail = details.Where(d => d.Key == Key).FirstOrDefault();
if (detail == null)
return null;
else
return detail.Value;
}
private static void SetDetail(this Device device, string Scope, string Key, string Value)
{
if (device == null)
throw new ArgumentNullException("device");
if (string.IsNullOrEmpty(Scope))
throw new ArgumentNullException("Scope");
if (string.IsNullOrEmpty(Key))
throw new ArgumentNullException("Key");
var detail = device.DeviceDetails.Where(d => d.Scope == Scope && d.Key == Key).FirstOrDefault();
// No Detail Stored & Set to Null
if (detail == null && Value == null)
return;
if (detail == null)
{
detail = new DeviceDetail()
{
DeviceSerialNumber = device.SerialNumber,
Scope = Scope,
Key = Key,
Value = Value
};
device.DeviceDetails.Add(detail);
}
if (detail.Value != Value)
{
if (Value == null)
{
device.DeviceDetails.Remove(detail);
}
else
{
detail.Value = Value;
}
}
}
#endregion
#region LanMacAddress
/// <summary>
/// Gets the LanMacAddress Device Detail Value
/// </summary>
/// <returns>The LanMacAddress or null</returns>
public static string LanMacAddress(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyLanMacAddress);
}
/// <summary>
/// Sets the LanMacAddress Device Detail Value
/// </summary>
public static void LanMacAddress(this IEnumerable<DeviceDetail> details, Device device, string LanMacAddress)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyLanMacAddress, LanMacAddress);
}
#endregion
#region WLanMacAddress
/// <summary>
/// Gets the WLanMacAddress Device Detail Value
/// </summary>
/// <returns>The WLanMacAddress or null</returns>
public static string WLanMacAddress(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyWLanMacAddress);
}
/// <summary>
/// Sets the WLanMacAddress Device Detail Value
/// </summary>
public static void WLanMacAddress(this IEnumerable<DeviceDetail> details, Device device, string WLanMacAddress)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyWLanMacAddress, WLanMacAddress);
}
#endregion
#region ACAdapter
/// <summary>
/// Gets the ACAdapter Device Detail Value
/// </summary>
/// <returns>The ACAdapter or null</returns>
public static string ACAdapter(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyACAdapter);
}
/// <summary>
/// Sets the ACAdapter Device Detail Value
/// </summary>
public static void ACAdapter(this IEnumerable<DeviceDetail> details, Device device, string ACAdapter)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyACAdapter, ACAdapter);
}
#endregion
#region Battery
/// <summary>
/// Gets the Battery Device Detail Value
/// </summary>
/// <returns>The Battery or null</returns>
public static string Battery(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyBattery);
}
/// <summary>
/// Sets the Battery Device Detail Value
/// </summary>
public static void Battery(this IEnumerable<DeviceDetail> details, Device device, string Battery)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyBattery, Battery);
}
#endregion
#region Keyboard
/// <summary>
/// Gets the Keyboard Device Detail Value
/// </summary>
/// <returns>The Keyboard or null</returns>
public static string Keyboard(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyKeyboard);
}
/// <summary>
/// Sets the Keyboard Device Detail Value
/// </summary>
public static void Keyboard(this IEnumerable<DeviceDetail> details, Device device, string Keyboard)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyKeyboard, Keyboard);
}
#endregion
}
}
@@ -1,66 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Models.Repository;
using Disco.Data.Repository;
using Disco.Data.Configuration.Modules;
using Disco.Models.BI.Config;
using Disco.Services.Users;
using Disco.Services.Authorization;
namespace Disco.BI.Extensions
{
public static class DeviceProfileExtensions
{
public const string ComputerNameExpressionCacheModule = "ComputerNameTemplate";
public static void ComputerNameInvalidateCache(this DeviceProfile deviceProfile)
{
Expressions.ExpressionCache.InvalidateKey(ComputerNameExpressionCacheModule, deviceProfile.Id.ToString());
}
public static OrganisationAddress DefaultOrganisationAddressDetails(this DeviceProfile deviceProfile, DiscoDataContext Database)
{
if (deviceProfile.DefaultOrganisationAddress.HasValue)
{
return Database.DiscoConfiguration.OrganisationAddresses.GetAddress(deviceProfile.DefaultOrganisationAddress.Value);
}
else
{
return null;
}
}
public static bool CanDelete(this DeviceProfile dp, DiscoDataContext Database)
{
if (!UserService.CurrentAuthorization.Has(Claims.Config.DeviceProfile.Delete))
return false;
// Can't Delete Default Profile (Id: 1)
if (dp.Id == 1)
return false;
// Can't Delete if Contains Devices
if (Database.Devices.Count(d => d.DeviceProfileId == dp.Id) > 0)
return false;
return true;
}
public static void Delete(this DeviceProfile dp, DiscoDataContext Database)
{
if (!dp.CanDelete(Database))
throw new InvalidOperationException("The state of this Device Profile doesn't allow it to be deleted");
// Update Defaults
if (Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId == dp.Id)
Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId = 1;
if (Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId == dp.Id)
Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId = 1;
// Delete Profile
Database.DeviceProfiles.Remove(dp);
}
}
}
@@ -1,285 +1,163 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Web;
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.BI.DocumentTemplates;
using Disco.Models.Repository; using Disco.Models.Repository;
using System.Collections; using Disco.Models.Services.Documents;
using System.Collections.Generic; using Disco.Services;
using Disco.Services.Documents;
using Disco.Services.Documents.ManagedGroups;
using Disco.Services.Expressions;
using Disco.Services.Interop.ActiveDirectory;
using iTextSharp.text.pdf; using iTextSharp.text.pdf;
using Disco.BI.Expressions; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using Disco.BI.DocumentTemplateBI; using System.Linq;
namespace Disco.BI.Extensions namespace Disco.BI.Extensions
{ {
public static class DocumentTemplateExtensions public static class DocumentTemplateExtensions
{ {
private const string DocumentTemplateExpressionCacheTemplate = "DocumentTemplate_{0}"; public static Tuple<Dictionary<string, Expression>, List<DocumentField>> CreateExpressions(string templateFileName, DiscoDataContext database)
{
Dictionary<string, Expression> expressions = new Dictionary<string, Expression>();
List<DocumentField> fields = new List<DocumentField>();
public static string RepositoryFilename(this DocumentTemplate dt, DiscoDataContext Database) PdfReader pdfReader = new PdfReader(templateFileName);
{
return System.IO.Path.Combine(DataStore.CreateLocation(Database, "DocumentTemplates"), string.Format("{0}.pdf", dt.Id));
}
public static string SavePdfTemplate(this DocumentTemplate dt, DiscoDataContext Database, Stream TemplateFile)
{
string filePath = dt.RepositoryFilename(Database);
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
TemplateFile.CopyTo(fs);
}
Expressions.ExpressionCache.InvalidModule(string.Format(DocumentTemplateExpressionCacheTemplate, dt.Id));
return filePath;
}
public static DisposableImageCollection PdfPageImages(this PdfReader pdfReader, int PageNumber)
{
return Interop.Pdf.PdfImporter.GetPageImages(pdfReader, PageNumber);
}
public static ConcurrentDictionary<string, Expression> PdfExpressionsFromCache(this DocumentTemplate dt, DiscoDataContext Database)
{
string cacheModuleKey = string.Format(DocumentTemplateExpressionCacheTemplate, dt.Id);
var module = Expressions.ExpressionCache.GetModule(cacheModuleKey);
if (module == null)
{
// Cache
string templateFilename = dt.RepositoryFilename(Database);
PdfReader pdfReader = new PdfReader(templateFilename);
int pdfFieldOrdinal = 0; int pdfFieldOrdinal = 0;
foreach (string pdfFieldKey in pdfReader.AcroFields.Fields.Keys) foreach (string pdfFieldKey in pdfReader.AcroFields.Fields.Keys)
{ {
var pdfField = pdfReader.AcroFields.Fields[pdfFieldKey];
var pdfFieldPositions = pdfReader.AcroFields.GetFieldPositions(pdfFieldKey);
var pdfFieldFlags = pdfField.GetMerged(0).GetAsNumber(PdfName.FF)?.IntValue ?? 0;
var isRequired = (pdfFieldFlags & 2) == 2;
var isReadOnly = (pdfFieldFlags & 1) == 1;
var pdfFieldValue = pdfReader.AcroFields.GetField(pdfFieldKey); var pdfFieldValue = pdfReader.AcroFields.GetField(pdfFieldKey);
Expressions.ExpressionCache.SetValue(cacheModuleKey, pdfFieldKey, Expressions.Expression.Tokenize(pdfFieldKey, pdfFieldValue, pdfFieldOrdinal)); var pdfFieldPosition = default(RectangleF?);
if (pdfFieldPositions != null && pdfFieldPositions.Count > 0)
{
var position = pdfFieldPositions.First().position;
pdfFieldPosition = new RectangleF(position.Left, position.Top, position.Width, position.Height);
}
var fieldTypeId = pdfReader.AcroFields.GetFieldType(pdfFieldKey);
var fieldType = DocumentFieldType.None;
if (fieldTypeId <= 8 && fieldTypeId > 0)
fieldType = (DocumentFieldType)fieldTypeId;
var fixedValues = default(List<string>);
if (fieldType == DocumentFieldType.RadioButton || fieldType == DocumentFieldType.Checkbox)
{
fixedValues = pdfReader.AcroFields.GetAppearanceStates(pdfFieldKey).ToList();
}
expressions[pdfFieldKey] = Expression.Tokenize(pdfFieldKey, pdfFieldValue, pdfFieldOrdinal, isRequired, isReadOnly, pdfFieldPosition);
fields.Add(new DocumentField(pdfFieldKey, pdfFieldValue, pdfFieldOrdinal, fieldType, isRequired, isReadOnly, fixedValues));
pdfFieldOrdinal++; pdfFieldOrdinal++;
} }
pdfReader.Close(); pdfReader.Close();
module = Expressions.ExpressionCache.GetModule(cacheModuleKey, true);
} return Tuple.Create(expressions, fields);
return module;
} }
public static List<BI.Expressions.Expression> ExtractPdfExpressions(this DocumentTemplate dt, DiscoDataContext Database) private static Tuple<Dictionary<string, Expression>, List<DocumentField>> CreateExpressions(DocumentTemplate dt, DiscoDataContext database)
{ {
return dt.PdfExpressionsFromCache(Database).Values.OrderBy(e => e.Ordinal).ToList(); string templateFileName = dt.RepositoryFilename(database);
return CreateExpressions(templateFileName, database);
} }
public static System.IO.Stream GeneratePdfBulk(this DocumentTemplate dt, DiscoDataContext Database, User CreatorUser, System.DateTime Timestamp, params string[] DataObjectsIds)
public static Dictionary<string, Expression> PdfExpressionsFromCache(this DocumentTemplate dt, DiscoDataContext database)
{ {
return Interop.Pdf.PdfGenerator.GenerateBulkFromTemplate(dt, Database, CreatorUser, Timestamp, DataObjectsIds); return ExpressionCache.GetOrCreateExpressions(dt, () => CreateExpressions(dt, database));
} }
public static System.IO.Stream GeneratePdfBulk(this DocumentTemplate dt, DiscoDataContext Database, User CreatorUser, System.DateTime Timestamp, params object[] DataObjects)
public static List<DocumentField> PdfFieldsFromCache(this DocumentTemplate dt, DiscoDataContext database)
{ {
return Interop.Pdf.PdfGenerator.GenerateBulkFromTemplate(dt, Database, CreatorUser, Timestamp, DataObjects); return ExpressionCache.GetOrCreateFields(dt, () => CreateExpressions(dt, database));
} }
public static System.IO.Stream GeneratePdf(this DocumentTemplate dt, DiscoDataContext Database, object Data, User CreatorUser, System.DateTime TimeStamp, DocumentState State, bool FlattenFields = false)
public static List<Expression> ExtractPdfExpressions(this DocumentTemplate dt, DiscoDataContext database)
{
return dt.PdfExpressionsFromCache(database).Values.OrderBy(e => e.Ordinal).ToList();
}
public static Stream GeneratePdf(this DocumentTemplate dt, DiscoDataContext database, IAttachmentTarget target, User creatorUser, DateTime timeStamp, DocumentState state, bool flattenFields = false)
{ {
bool generateExpression = !string.IsNullOrEmpty(dt.OnGenerateExpression); bool generateExpression = !string.IsNullOrEmpty(dt.OnGenerateExpression);
string generateExpressionResult = null; string generateExpressionResult = null;
if (generateExpression) if (generateExpression)
generateExpressionResult = dt.EvaluateOnGenerateExpression(Data, Database, CreatorUser, TimeStamp, State); generateExpressionResult = dt.EvaluateOnGenerateExpression(target, database, creatorUser, timeStamp, state);
var pdfStream = Interop.Pdf.PdfGenerator.GenerateFromTemplate(dt, Database, Data, CreatorUser, TimeStamp, State, FlattenFields); var pdfStream = Interop.Pdf.PdfGenerator.GenerateFromTemplate(dt, database, target, creatorUser, timeStamp, state, flattenFields);
if (generateExpression) if (generateExpression)
DocumentsLog.LogDocumentGenerated(dt, Data, CreatorUser, generateExpressionResult); DocumentsLog.LogDocumentGenerated(dt, target, creatorUser, generateExpressionResult);
else else
DocumentsLog.LogDocumentGenerated(dt, Data, CreatorUser); DocumentsLog.LogDocumentGenerated(dt, target, creatorUser);
return pdfStream; return pdfStream;
} }
public static Stream GeneratePdfPackage(this DocumentTemplatePackage package, DiscoDataContext database, IAttachmentTarget target, User creatorUser, DateTime timeStamp, DocumentState state)
public static Expression FilterExpressionFromCache(this DocumentTemplate dt)
{ {
return ExpressionCache.GetValue("DocumentTemplate_FilterExpression", dt.Id, () => { return Expression.TokenizeSingleDynamic(null, dt.FilterExpression, 0); }); return Interop.Pdf.PdfGenerator.GenerateFromPackage(package, database, target, creatorUser, timeStamp, state);
} }
public static void FilterExpressionInvalidateCache(this DocumentTemplate dt) public static Stream GeneratePdfPackageBulk(this DocumentTemplatePackage package, DiscoDataContext database, User creatorUser, DateTime timestamp, bool? insertBlankPages, List<string> dataObjectsIds)
{ {
ExpressionCache.InvalidateKey("DocumentTemplate_FilterExpression", dt.Id); return Interop.Pdf.PdfGenerator.GenerateBulkFromPackage(package, database, creatorUser, timestamp, insertBlankPages, dataObjectsIds);
} }
public static bool FilterExpressionMatches(this DocumentTemplate dt, object Data, DiscoDataContext Database, User User, System.DateTime TimeStamp, DocumentState State) public static Stream GeneratePdfPackageBulk(this DocumentTemplatePackage package, DiscoDataContext database, User creatorUser, DateTime timestamp, bool? insertBlankPages, List<IAttachmentTarget> dataObjects)
{ {
if (!string.IsNullOrEmpty(dt.FilterExpression)) return Interop.Pdf.PdfGenerator.GenerateBulkFromPackage(package, database, creatorUser, timestamp, insertBlankPages, dataObjects);
{
Expression compiledExpression = dt.FilterExpressionFromCache();
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State);
try
{
object er = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
if (er is bool)
{
return (bool)er;
}
bool erBool;
if (bool.TryParse(er.ToString(), out erBool))
{
return erBool;
}
}
catch
{
return false;
}
}
return true;
} }
public static Expression OnImportAttachmentExpressionFromCache(this DocumentTemplate dt) public static List<bool> PdfPageHasAttachmentId(this DocumentTemplate dt, DiscoDataContext database)
{ {
return ExpressionCache.GetValue("DocumentTemplate_OnImportExpression", dt.Id, () => { return Expression.TokenizeSingleDynamic(null, dt.OnImportAttachmentExpression, 0); }); string templateFilename = dt.RepositoryFilename(database);
if (!File.Exists(templateFilename))
throw new FileNotFoundException("PDF template not found", templateFilename);
PdfReader pdfReader = new PdfReader(templateFilename);
var result = new bool[pdfReader.NumberOfPages];
var fieldNames = pdfReader.AcroFields.Fields.Keys.Where(key => key.Equals("DiscoAttachmentId", StringComparison.OrdinalIgnoreCase)).ToList();
var fieldPositions = fieldNames.SelectMany(name => pdfReader.AcroFields.GetFieldPositions(name));
foreach (var fieldPosition in fieldPositions)
{
result[fieldPosition.page - 1] = true;
} }
public static void OnImportAttachmentExpressionInvalidateCache(this DocumentTemplate dt) pdfReader.Close();
{ return result.ToList();
ExpressionCache.InvalidateKey("DocumentTemplate_OnImportExpression", dt.Id);
}
public static string EvaluateOnAttachmentImportExpression(this DocumentTemplate dt, object Data, DiscoDataContext Database, User User, System.DateTime TimeStamp)
{
if (!string.IsNullOrEmpty(dt.OnImportAttachmentExpression))
{
Expression compiledExpression = dt.OnImportAttachmentExpressionFromCache();
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, null);
try
{
object result = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
if (result == null)
return null;
else
return result.ToString();
}
catch
{
throw;
}
}
return null;
} }
public static Expression OnGenerateExpressionFromCache(this DocumentTemplate dt)
{
return ExpressionCache.GetValue("DocumentTemplate_OnGenerateExpression", dt.Id, () => { return Expression.TokenizeSingleDynamic(null, dt.OnGenerateExpression, 0); });
}
public static void OnGenerateExpressionInvalidateCache(this DocumentTemplate dt)
{
ExpressionCache.InvalidateKey("DocumentTemplate_OnGenerateExpression", dt.Id);
}
public static string EvaluateOnGenerateExpression(this DocumentTemplate dt, object Data, DiscoDataContext Database, User User, System.DateTime TimeStamp, DocumentState State)
{
if (!string.IsNullOrEmpty(dt.OnGenerateExpression))
{
Expression compiledExpression = dt.OnGenerateExpressionFromCache();
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State);
try
{
object result = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
return result.ToString();
}
catch
{
throw;
}
}
return null;
}
public static string GetDataId(this DocumentTemplate dt, object Data)
{
if (Data is string)
{
return (string)Data;
}
else
{
switch (dt.Scope)
{
case Models.Repository.DocumentTemplate.DocumentTemplateScopes.Device:
if (!(Data is Device))
throw new ArgumentException("This Document Template is configured for Devices only", "Data");
Device d = (Device)Data;
return d.SerialNumber;
case Models.Repository.DocumentTemplate.DocumentTemplateScopes.Job:
if (!(Data is Job))
throw new ArgumentException("This Document Template is configured for Jobs only", "Data");
Job d2 = (Job)Data;
return d2.Id.ToString();
case Models.Repository.DocumentTemplate.DocumentTemplateScopes.User:
if (!(Data is User))
throw new ArgumentException("This Document Template is configured for Users only", "Data");
User d3 = (User)Data;
return d3.UserId;
default:
throw new InvalidOperationException("Invalid Document Template Scope");
}
}
}
public static string UniqueIdentifier(string DocumentTemplateId, string DataId, string CreatorId, System.DateTime Timestamp)
{
return string.Format("Disco|1|{0}|{1}|{2}|{3:s}",
DocumentTemplateId,
DataId,
CreatorId,
Timestamp
);
}
public static string UniqueIdentifier(this DocumentTemplate dt, object Data, string CreatorId, System.DateTime Timestamp)
{
return string.Format("Disco|1|{0}|{1}|{2}|{3:s}",
dt.Id,
dt.GetDataId(System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(Data)),
CreatorId,
Timestamp
);
}
public static string UniquePageIdentifier(this DocumentTemplate dt, object Data, string CreatorId, System.DateTime Timestamp, int Page)
{
return string.Format("Disco|1|{0}|{1}|{2}|{3:s}|{4}",
dt.Id,
dt.GetDataId(System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(Data)),
CreatorId,
Timestamp,
Page
);
}
public static List<RectangleF> QRCodeLocations(this DocumentTemplate dt, DiscoDataContext Database)
{
return DocumentTemplateBI.DocumentTemplateQRCodeLocationCache.GetLocations(dt, Database);
}
public static void Delete(this DocumentTemplate dt, DiscoDataContext Database) public static void Delete(this DocumentTemplate dt, DiscoDataContext Database)
{ {
// Find & Rename all references // Find & Rename all references
foreach (DeviceAttachment a in Database.DeviceAttachments.Where(a => a.DocumentTemplateId == dt.Id)) void updateAttachment(IAttachment a)
{ {
a.Comments = string.Format("{0} - {1}", dt.Description, a.Comments); var comments = $"{dt.Description} - {a.Comments}";
if (a.Comments.Length > 500) if (comments.Length > 500)
a.Comments = a.Comments.Substring(0, 500); comments = comments.Substring(0, 500);
a.Comments = comments;
a.DocumentTemplateId = null; a.DocumentTemplateId = null;
a.DocumentTemplate = null;
}
foreach (JobAttachment a in Database.JobAttachments.Where(a => a.DocumentTemplateId == dt.Id))
{
a.Comments = string.Format("{0} - {1}", dt.Description, a.Comments);
if (a.Comments.Length > 500)
a.Comments = a.Comments.Substring(0, 500);
a.DocumentTemplateId = null;
a.DocumentTemplate = null;
} }
foreach (var a in Database.DeviceAttachments.Where(a => a.DocumentTemplateId == dt.Id))
updateAttachment(a);
foreach (var a in Database.JobAttachments.Where(a => a.DocumentTemplateId == dt.Id))
updateAttachment(a);
foreach (UserAttachment a in Database.UserAttachments.Where(a => a.DocumentTemplateId == dt.Id)) foreach (UserAttachment a in Database.UserAttachments.Where(a => a.DocumentTemplateId == dt.Id))
{ updateAttachment(a);
a.Comments = string.Format("{0} - {1}", dt.Description, a.Comments);
if (a.Comments.Length > 500) // Remove Linked Group
a.Comments = a.Comments.Substring(0, 500); ActiveDirectory.Context.ManagedGroups.Remove(DocumentTemplateDevicesManagedGroup.GetKey(dt));
a.DocumentTemplateId = null; ActiveDirectory.Context.ManagedGroups.Remove(DocumentTemplateUsersManagedGroup.GetKey(dt));
a.DocumentTemplate = null;
}
// Delete SubTypes // Delete SubTypes
dt.JobSubTypes.Clear(); dt.JobSubTypes.Clear();
// Delete Template // Delete Template
string templateRepositoryFilename = dt.RepositoryFilename(Database); string templateRepositoryFilename = dt.RepositoryFilename(Database);
if (System.IO.File.Exists(templateRepositoryFilename)) if (File.Exists(templateRepositoryFilename))
System.IO.File.Delete(templateRepositoryFilename); File.Delete(templateRepositoryFilename);
// Remove from Cache // Remove from Cache
dt.FilterExpressionInvalidateCache(); dt.FilterExpressionInvalidateCache();
-232
View File
@@ -1,232 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Models.Repository;
using Disco.Data.Repository;
using System.IO;
using Disco.Models.BI.DocumentTemplates;
using Disco.Services.Plugins;
using Disco.Models.BI.Job;
using Disco.Services.Authorization;
namespace Disco.BI.Extensions
{
public static class JobExtensions
{
public static JobAttachment CreateAttachment(this Job Job, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, byte[] PdfThumbnail = null)
{
if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase))
MimeType = Interop.MimeTypes.ResolveMimeType(Filename);
JobAttachment ja = new JobAttachment()
{
JobId = Job.Id,
TechUserId = CreatorUser.UserId,
Filename = Filename,
MimeType = MimeType,
Timestamp = DateTime.Now,
Comments = Comments
};
if (DocumentTemplate != null)
ja.DocumentTemplateId = DocumentTemplate.Id;
Database.JobAttachments.Add(ja);
Database.SaveChanges();
ja.SaveAttachment(Database, Content);
Content.Position = 0;
if (PdfThumbnail == null)
ja.GenerateThumbnail(Database, Content);
else
ja.SaveThumbnailAttachment(Database, PdfThumbnail);
return ja;
}
public static List<DocumentTemplate> AvailableDocumentTemplates(this Job j, DiscoDataContext Database, User User, DateTime TimeStamp)
{
var dts = Database.DocumentTemplates.Include("JobSubTypes")
.Where(dt => dt.Scope == DocumentTemplate.DocumentTemplateScopes.Job)
.ToList();
foreach (var dt in dts.ToArray())
{
if (dt.JobSubTypes.Count != 0)
{ // Filter Applied
bool match = false;
foreach (var st in j.JobSubTypes)
{
if (dt.JobSubTypes.Contains(st))
{
match = true;
break;
}
}
if (!match)
dts.Remove(dt);
}
}
// Evaluate Filters
dts = dts.Where(dt => dt.FilterExpressionMatches(j, Database, User, TimeStamp, DocumentState.DefaultState())).ToList();
return dts;
}
public static DateTime ValidateDateAfterOpened(this Job j, DateTime d)
{
if (d < j.OpenedDate)
{
if (d > j.OpenedDate.AddMinutes(-1))
return j.OpenedDate;
else
throw new ArgumentException("The Date must be >= the Open Date.", "d");
}
return d;
}
public static string GenerateFaultDescription(this Job j, DiscoDataContext Database)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Faulty Components:");
foreach (var jst in j.JobSubTypes)
sb.Append("- ").AppendLine(jst.Description).AppendLine(" - ");
return sb.ToString();
}
public static string GenerateFaultDescriptionFooter(this Job j, DiscoDataContext Database, PluginFeatureManifest WarrantyProviderDefinition)
{
var versionDisco = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
return string.Format("Automation by Disco v{0}.{1}.{2:0000}.{3:0000} (Provider: {4} v{5})",
versionDisco.Major, versionDisco.Minor, versionDisco.Build, versionDisco.Revision, WarrantyProviderDefinition.Id, WarrantyProviderDefinition.PluginManifest.Version.ToString(4));
}
public static void UpdateSubTypes(this Job j, DiscoDataContext Database, List<JobSubType> SubTypes, bool AddComponents, User TechUser)
{
if (SubTypes == null || SubTypes.Count == 0)
throw new ArgumentException("The Job must contain at least one Sub Type");
List<JobSubType> addedSubTypes = new List<JobSubType>();
List<JobSubType> removedSubTypes = new List<JobSubType>();
// Removed Sub Types
foreach (var t in j.JobSubTypes.ToArray())
if (!SubTypes.Contains(t))
{
removedSubTypes.Add(t);
j.JobSubTypes.Remove(t);
}
// Added Sub Types
foreach (var t in SubTypes)
if (!j.JobSubTypes.Contains(t))
{
addedSubTypes.Add(t);
j.JobSubTypes.Add(t);
}
// Write Log
if (addedSubTypes.Count > 0 || removedSubTypes.Count > 0)
{
StringBuilder logBuilder = new StringBuilder();
logBuilder.AppendLine("Updated Job Sub Types");
if (removedSubTypes.Count > 0)
{
logBuilder.AppendLine("Removed:");
foreach (var t in removedSubTypes)
logBuilder.Append("- ").AppendLine(t.ToString());
}
if (addedSubTypes.Count > 0)
{
logBuilder.AppendLine("Added:");
foreach (var t in addedSubTypes)
logBuilder.Append("- ").AppendLine(t.ToString());
}
Database.JobLogs.Add(new JobLog()
{
JobId = j.Id,
TechUserId = TechUser.UserId,
Timestamp = DateTime.Now,
Comments = logBuilder.ToString()
});
}
// Add Components
if (AddComponents && addedSubTypes.Count > 0 && j.DeviceSerialNumber != null)
{
var components = Database.DeviceComponents.Include("JobSubTypes").Where(c => !c.DeviceModelId.HasValue || c.DeviceModelId == j.Device.DeviceModelId);
var addedComponents = new List<DeviceComponent>();
foreach (var c in components)
{
foreach (var st in c.JobSubTypes)
{
foreach (var jst in addedSubTypes)
{
if (st.JobTypeId == jst.JobTypeId && st.Id == jst.Id)
{
addedComponents.Add(c);
break;
}
}
if (addedComponents.Contains(c))
break;
}
}
foreach (var c in addedComponents)
{
if (!j.JobComponents.Any(jc => jc.Description.Equals(c.Description, StringComparison.OrdinalIgnoreCase)))
{ // Job Component with matching Description doesn't exist.
Database.JobComponents.Add(new JobComponent()
{
Job = j,
TechUserId = TechUser.UserId,
Cost = c.Cost,
Description = c.Description
});
}
}
}
}
private static List<string> FilterCreatableTypePermissions(AuthorizationToken Authorization)
{
if (!Authorization.HasAll(Claims.Job.Types.CreateHMisc, Claims.Job.Types.CreateHNWar, Claims.Job.Types.CreateHWar, Claims.Job.Types.CreateSApp, Claims.Job.Types.CreateSImg, Claims.Job.Types.CreateSOS, Claims.Job.Types.CreateUMgmt))
{
// Must Filter
List<string> allowedTypes = new List<string>(6);
if (Authorization.Has(Claims.Job.Types.CreateHMisc))
allowedTypes.Add(JobType.JobTypeIds.HMisc);
if (Authorization.Has(Claims.Job.Types.CreateHNWar))
allowedTypes.Add(JobType.JobTypeIds.HNWar);
if (Authorization.Has(Claims.Job.Types.CreateHWar))
allowedTypes.Add(JobType.JobTypeIds.HWar);
if (Authorization.Has(Claims.Job.Types.CreateSApp))
allowedTypes.Add(JobType.JobTypeIds.SApp);
if (Authorization.Has(Claims.Job.Types.CreateSImg))
allowedTypes.Add(JobType.JobTypeIds.SImg);
if (Authorization.Has(Claims.Job.Types.CreateSOS))
allowedTypes.Add(JobType.JobTypeIds.SOS);
if (Authorization.Has(Claims.Job.Types.CreateUMgmt))
allowedTypes.Add(JobType.JobTypeIds.UMgmt);
return allowedTypes;
}
return null;
}
public static IQueryable<JobType> FilterCreatableTypePermissions(this IQueryable<JobType> JobTypes, AuthorizationToken Authorization)
{
var allowedTypes = FilterCreatableTypePermissions(Authorization);
if (allowedTypes != null)
{
return JobTypes.Where(jt => allowedTypes.Contains(jt.Id));
}
return JobTypes;
}
}
}
@@ -1,80 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Models.Repository;
using System.ComponentModel.DataAnnotations;
namespace Disco.BI.Extensions
{
public static class JobFlagExtensions
{
private static Dictionary<string, Dictionary<long, string>> allFlags;
private static void CacheAllFlags()
{
if (allFlags == null)
{
var fType = typeof(Job.UserManagementFlags);
var fMembers = fType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
var flags = new Dictionary<string, Dictionary<long, string>>();
foreach (var f in fMembers)
{
DisplayAttribute display = (DisplayAttribute)(f.GetCustomAttributes(typeof(DisplayAttribute), false)[0]);
string gn = display.GroupName;
Dictionary<long, string> g;
if (!flags.TryGetValue(gn, out g))
{
g = new Dictionary<long, string>();
flags.Add(gn, g);
}
g[(long)f.GetRawConstantValue()] = display.Name;
}
allFlags = flags;
}
}
public static Dictionary<string, List<Tuple<long, string, bool>>> ValidFlagsGrouped(this Job j)
{
Dictionary<string, List<Tuple<long, string, bool>>> validFlags = new Dictionary<string, List<Tuple<long, string, bool>>>();
CacheAllFlags();
var currentFlags = (long)(j.Flags ?? 0);
foreach (var jt in j.JobSubTypes)
{
Dictionary<long, string> g;
if (allFlags.TryGetValue(jt.Id, out g))
{
validFlags[jt.Id] = g.Select(f => new Tuple<long, string, bool>(f.Key, f.Value, ((currentFlags & f.Key) == f.Key))).ToList();
}
else
{
validFlags[jt.Id] = null;
}
}
return validFlags;
}
public static Dictionary<long, Tuple<string, bool>> ValidFlags(this Job j)
{
Dictionary<long, Tuple<string, bool>> validFlags = new Dictionary<long, Tuple<string, bool>>();
CacheAllFlags();
var currentFlags = (long)(j.Flags ?? 0);
foreach (var jt in j.JobSubTypes)
{
Dictionary<long, string> g;
if (allFlags.TryGetValue(jt.Id, out g))
{
foreach (var f in g)
validFlags[f.Key] = new Tuple<string, bool>(string.Format("{0}: {1}", jt.Description, f.Value), ((currentFlags & f.Key) == f.Key));
}
}
return validFlags;
}
}
}
-73
View File
@@ -1,73 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Models.Repository;
using Disco.Data.Repository;
using System.IO;
using Disco.Models.BI.DocumentTemplates;
using Disco.Services.Interop.ActiveDirectory;
namespace Disco.BI.Extensions
{
public static class UserExtensions
{
public static UserAttachment CreateAttachment(this User User, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, byte[] PdfThumbnail = null)
{
if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase))
MimeType = Interop.MimeTypes.ResolveMimeType(Filename);
UserAttachment ua = new UserAttachment()
{
UserId = User.UserId,
TechUserId = CreatorUser.UserId,
Filename = Filename,
MimeType = MimeType,
Timestamp = DateTime.Now,
Comments = Comments
};
if (DocumentTemplate != null)
ua.DocumentTemplateId = DocumentTemplate.Id;
Database.UserAttachments.Add(ua);
Database.SaveChanges();
ua.SaveAttachment(Database, Content);
Content.Position = 0;
if (PdfThumbnail == null)
ua.GenerateThumbnail(Database, Content);
else
ua.SaveThumbnailAttachment(Database, PdfThumbnail);
return ua;
}
public static List<DocumentTemplate> AvailableDocumentTemplates(this User u, DiscoDataContext Database, User User, DateTime TimeStamp)
{
var dts = Database.DocumentTemplates.Include("JobSubTypes")
.Where(dt => dt.Scope == DocumentTemplate.DocumentTemplateScopes.User)
.ToArray()
.Where(dt => dt.FilterExpressionMatches(u, Database, User, TimeStamp, DocumentState.DefaultState())).ToList();
return dts;
}
public static List<DeviceUserAssignment> CurrentDeviceUserAssignments(this User u)
{
return u.DeviceUserAssignments.Where(dua => !dua.UnassignedDate.HasValue).ToList();
}
public static ADUserAccount ActiveDirectoryAccount(this User User, params string[] AdditionalProperties)
{
return ActiveDirectory.RetrieveADUserAccount(User.UserId, AdditionalProperties);
}
public static bool CanCreateJob(this User u)
{
if (!JobActionExtensions.CanCreate())
return false;
return true;
}
}
}
@@ -1,85 +0,0 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Authorization;
using Disco.Services.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.BI.Extensions
{
public static class UserFlagActionExtensions
{
#region Edit Comments
public static bool CanEditComments(this UserFlagAssignment fa)
{
return UserService.CurrentAuthorization.Has(Claims.User.Actions.EditFlags);
}
public static void OnEditComments(this UserFlagAssignment fa, string Comments)
{
if (!fa.CanEditComments())
throw new InvalidOperationException("Editing comments for user flags is denied");
fa.Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim();
}
#endregion
#region Remove
public static bool CanRemove(this UserFlagAssignment fa)
{
if (fa.RemovedDate.HasValue)
return false;
return UserService.CurrentAuthorization.Has(Claims.User.Actions.RemoveFlags);
}
public static void OnRemove(this UserFlagAssignment fa, User Technician)
{
if (!fa.CanRemove())
throw new InvalidOperationException("Removing user flags is denied");
fa.RemovedDate = DateTime.Now;
fa.RemovedUserId = Technician.UserId;
}
#endregion
#region Add
public static bool CanAddUserFlags(this User u)
{
return UserService.CurrentAuthorization.Has(Claims.User.Actions.AddFlags);
}
public static bool CanAddUserFlag(this User u, UserFlag flag)
{
// Shortcut
if (!u.CanAddUserFlags())
return false;
// Already has User Flag?
if (u.UserFlagAssignments.Any(fa => !fa.RemovedDate.HasValue && fa.UserFlagId == flag.Id))
return false;
return true;
}
public static UserFlagAssignment OnAddUserFlag(this User u, DiscoDataContext Database, UserFlag flag, User Technician, string Comments)
{
if (!u.CanAddUserFlag(flag))
throw new InvalidOperationException("Adding user flag is denied");
var fa = new UserFlagAssignment()
{
UserFlagId = flag.Id,
UserId = u.UserId,
AddedDate = DateTime.Now,
AddedUserId = Technician.UserId,
Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim()
};
Database.UserFlagAssignments.Add(fa);
return fa;
}
#endregion
}
}
@@ -1,18 +0,0 @@
using Disco.Models.Repository;
using System;
using System.Security.Cryptography.X509Certificates;
namespace Disco.BI.Extensions
{
public static class WirelessCertificateExtensions
{
public static System.DateTime? CertificateExpirationDate(this DeviceCertificate wc)
{
if (wc.Content == null || wc.Content.Length == 0)
{
return null;
}
X509Certificate2 c = new X509Certificate2(wc.Content, "password");
return c.NotAfter;
}
}
}
+201 -47
View File
@@ -1,15 +1,18 @@
using Disco.BI.Expressions; using Disco.BI.Extensions;
using Disco.BI.Extensions;
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.BI.DocumentTemplates;
using Disco.Models.BI.Expressions;
using Disco.Models.Repository; using Disco.Models.Repository;
using Disco.Models.Services.Documents;
using Disco.Models.Services.Expressions.Extensions;
using Disco.Services;
using Disco.Services.Documents;
using Disco.Services.Expressions;
using Disco.Services.Interop.ActiveDirectory; using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Tasks;
using Disco.Services.Users; using Disco.Services.Users;
using iTextSharp.text.pdf; using iTextSharp.text.pdf;
using iTextSharp.text.pdf.codec;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -18,17 +21,16 @@ namespace Disco.BI.Interop.Pdf
{ {
public static class PdfGenerator public static class PdfGenerator
{ {
public static Stream GenerateBulkFromPackage(DocumentTemplatePackage package, DiscoDataContext database, User creatorUser, DateTime timestamp, bool? insertBlankPages, List<IAttachmentTarget> dataObjects)
public static System.IO.Stream GenerateBulkFromTemplate(DocumentTemplate dt, DiscoDataContext Database, User CreatorUser, System.DateTime Timestamp, params object[] DataObjects)
{ {
if (DataObjects.Length > 0) if (dataObjects.Count > 0)
{ {
List<Stream> generatedPdfs = new List<Stream>(DataObjects.Length); List<Stream> generatedPdfs = new List<Stream>(dataObjects.Count);
using (Models.BI.DocumentTemplates.DocumentState state = Models.BI.DocumentTemplates.DocumentState.DefaultState()) using (var state = DocumentState.DefaultState())
{ {
foreach (object d in DataObjects) foreach (var d in dataObjects)
{ {
generatedPdfs.Add(dt.GeneratePdf(Database, d, CreatorUser, Timestamp, state, true)); generatedPdfs.Add(package.GeneratePdfPackage(database, d, creatorUser, timestamp, state));
state.SequenceNumber++; state.SequenceNumber++;
state.FlushScopeCache(); state.FlushScopeCache();
} }
@@ -39,7 +41,7 @@ namespace Disco.BI.Interop.Pdf
} }
else else
{ {
Stream bulkPdf = DocumentTemplateBI.Utilities.JoinPdfs(generatedPdfs.ToArray()); Stream bulkPdf = Utilities.JoinPdfs(insertBlankPages.GetValueOrDefault(package.InsertBlankPages), generatedPdfs);
foreach (Stream singlePdf in generatedPdfs) foreach (Stream singlePdf in generatedPdfs)
singlePdf.Dispose(); singlePdf.Dispose();
return bulkPdf; return bulkPdf;
@@ -48,38 +50,164 @@ namespace Disco.BI.Interop.Pdf
return null; return null;
} }
public static System.IO.Stream GenerateBulkFromTemplate(DocumentTemplate dt, DiscoDataContext Database, User CreatorUser, System.DateTime Timestamp, params string[] DataObjectsIds) public static Stream GenerateBulkFromPackage(DocumentTemplatePackage package, DiscoDataContext database, User creatorUser, DateTime timestamp, bool? insertBlankPages, List<string> dataObjectsIds)
{ {
object[] DataObjects; List<IAttachmentTarget> DataObjects;
switch (dt.Scope) switch (package.Scope)
{ {
case DocumentTemplate.DocumentTemplateScopes.Device: case AttachmentTypes.Device:
DataObjects = Database.Devices.Where(d => DataObjectsIds.Contains(d.SerialNumber)).ToArray(); DataObjects = database.Devices.Where(d => dataObjectsIds.Contains(d.SerialNumber)).ToList<IAttachmentTarget>();
break; break;
case DocumentTemplate.DocumentTemplateScopes.Job: case AttachmentTypes.Job:
int[] intDataObjectsIds = DataObjectsIds.Select(i => int.Parse(i)).ToArray(); int[] intDataObjectsIds = dataObjectsIds.Select(i => int.Parse(i)).ToArray();
DataObjects = Database.Jobs.Where(j => intDataObjectsIds.Contains(j.Id)).ToArray(); DataObjects = database.Jobs.Where(j => intDataObjectsIds.Contains(j.Id)).ToList<IAttachmentTarget>();
break; break;
case DocumentTemplate.DocumentTemplateScopes.User: case AttachmentTypes.User:
DataObjects = new object[DataObjectsIds.Length]; DataObjects = new List<IAttachmentTarget>(dataObjectsIds.Count);
for (int idIndex = 0; idIndex < DataObjectsIds.Length; idIndex++) for (int idIndex = 0; idIndex < dataObjectsIds.Count; idIndex++)
{ {
string dataObjectId = DataObjectsIds[idIndex]; string dataObjectId = dataObjectsIds[idIndex];
var user = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(dataObjectId), database, true);
DataObjects[idIndex] = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(dataObjectId), Database, true); if (user == null)
if (DataObjects[idIndex] == null) throw new Exception($"Unknown Username specified: {dataObjectId}");
throw new Exception(string.Format("Unknown Username specified: {0}", dataObjectId)); DataObjects.Add(user);
} }
break; break;
default: default:
throw new InvalidOperationException("Invalid DocumentType Scope"); throw new InvalidOperationException("Invalid DocumentType Scope");
} }
return GenerateBulkFromTemplate(dt, Database, CreatorUser, Timestamp, DataObjects); return GenerateBulkFromPackage(package, database, creatorUser, timestamp, insertBlankPages, DataObjects);
} }
public static System.IO.Stream GenerateFromTemplate(DocumentTemplate dt, DiscoDataContext Database, object Data, User CreatorUser, System.DateTime TimeStamp, DocumentState State, bool FlattenFields = false) public static Stream GenerateFromPackage(DocumentTemplatePackage package, DiscoDataContext database, IAttachmentTarget data, User creatorUser, DateTime timestamp, DocumentState state)
{
var templates = package.GetDocumentTemplates(database);
if (templates.Count == 0)
return null;
bool generateExpression = !string.IsNullOrEmpty(package.OnGenerateExpression);
string generateExpressionResult = null;
if (generateExpression)
generateExpressionResult = package.EvaluateOnGenerateExpression(data, database, creatorUser, timestamp, state);
List<Stream> generatedPdfs = new List<Stream>(templates.Count);
foreach (var template in templates)
{
generatedPdfs.Add(template.GeneratePdf(database, data, creatorUser, timestamp, state, true));
state.SequenceNumber++;
state.FlushScopeCache();
}
if (generateExpression)
DocumentsLog.LogDocumentPackageGenerated(package, data, creatorUser, generateExpressionResult);
else
DocumentsLog.LogDocumentPackageGenerated(package, data, creatorUser);
if (generatedPdfs.Count == 1)
{
return generatedPdfs[0];
}
else
{
Stream bulkPdf = Utilities.JoinPdfs(package.InsertBlankPages, generatedPdfs);
foreach (Stream singlePdf in generatedPdfs)
singlePdf.Dispose();
return bulkPdf;
}
}
public static Stream GenerateBulkFromTemplate(DocumentTemplate dt, DiscoDataContext database, User creatorUser, DateTime timestamp, bool insertBlankPages, List<IAttachmentTarget> dataObjects, IScheduledTaskStatus taskStatus)
{
if (dataObjects.Count > 0)
{
List<Stream> generatedPdfs = new List<Stream>(dataObjects.Count);
var progressPerDoc = 80d / dataObjects.Count;
var progressDoc = 10d;
using (var state = DocumentState.DefaultState())
{
taskStatus.UpdateStatus(10, "Rendering", "Starting");
foreach (var d in dataObjects)
{
taskStatus.UpdateStatus(progressDoc += progressPerDoc, $"Rendering {d.AttachmentReferenceId}");
generatedPdfs.Add(dt.GeneratePdf(database, d, creatorUser, timestamp, state, true));
state.SequenceNumber++;
state.FlushScopeCache();
}
}
if (generatedPdfs.Count == 1)
{
return generatedPdfs[0];
}
else
{
taskStatus.UpdateStatus(90, "Merging", "Merging documents");
Stream bulkPdf = Utilities.JoinPdfs(insertBlankPages, generatedPdfs);
foreach (Stream singlePdf in generatedPdfs)
singlePdf.Dispose();
return bulkPdf;
}
}
return null;
}
public static Stream GenerateBulkFromTemplate(DocumentTemplate dt, DiscoDataContext database, User creatorUser, DateTime timestamp, bool insertBlankPages, List<string> dataObjectsIds, IScheduledTaskStatus taskStatus)
{
Dictionary<string, IAttachmentTarget> dataObjectLookup;
List<string> dataObjectIds = dataObjectsIds;
taskStatus.UpdateStatus(0, "Resolving targets", "Resolving render targets");
switch (dt.Scope)
{
case DocumentTemplate.DocumentTemplateScopes.Device:
dataObjectLookup = database.Devices.Where(d => dataObjectsIds.Contains(d.SerialNumber)).AsEnumerable().Cast<IAttachmentTarget>().ToDictionary(i => i.AttachmentReferenceId, StringComparer.OrdinalIgnoreCase);
break;
case DocumentTemplate.DocumentTemplateScopes.Job:
var intDataObjectsIds = dataObjectsIds.Select(i => int.Parse(i)).ToList();
dataObjectLookup = database.Jobs.Where(j => intDataObjectsIds.Contains(j.Id)).AsEnumerable().Cast<IAttachmentTarget>().ToDictionary(i => i.AttachmentReferenceId, StringComparer.OrdinalIgnoreCase);
break;
case DocumentTemplate.DocumentTemplateScopes.User:
dataObjectLookup = new Dictionary<string, IAttachmentTarget>(dataObjectsIds.Count, StringComparer.OrdinalIgnoreCase);
dataObjectIds = new List<string>(dataObjectsIds.Count);
foreach (var userId in dataObjectsIds)
{
var user = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(userId), database, true);
if (user == null)
{
dataObjectIds.Add(userId);
continue;
}
dataObjectIds.Add(user.UserId);
dataObjectLookup.Add(user.UserId, user);
}
break;
default:
throw new InvalidOperationException("Invalid DocumentType Scope");
}
// recreate list to honor the sort-order provided in DataObjectsIds
var dataObjects = new List<IAttachmentTarget>(dataObjectsIds.Count);
var missingIds = new List<string>();
foreach (var id in dataObjectIds)
{
if (dataObjectLookup.TryGetValue(id, out var dataObject))
dataObjects.Add(dataObject);
else
missingIds.Add(id);
}
if (missingIds.Any())
{
throw new Exception($"Unknown id specified: {string.Join("; ", missingIds)}");
}
return GenerateBulkFromTemplate(dt, database, creatorUser, timestamp, insertBlankPages, dataObjects, taskStatus);
}
public static Stream GenerateFromTemplate(DocumentTemplate dt, DiscoDataContext Database, IAttachmentTarget Data, User CreatorUser, DateTime TimeStamp, DocumentState State, bool flattenFields = false)
{ {
// Validate Data // Validate Data
switch (dt.Scope) switch (dt.Scope)
@@ -104,9 +232,9 @@ namespace Disco.BI.Interop.Pdf
// Override FlattenFields if Document Template instructs. // Override FlattenFields if Document Template instructs.
if (dt.FlattenForm) if (dt.FlattenForm)
FlattenFields = true; flattenFields = true;
ConcurrentDictionary<string, Expression> expressionCache = dt.PdfExpressionsFromCache(Database); var expressionCache = dt.PdfExpressionsFromCache(Database);
string templateFilename = dt.RepositoryFilename(Database); string templateFilename = dt.RepositoryFilename(Database);
PdfReader pdfReader = new PdfReader(templateFilename); PdfReader pdfReader = new PdfReader(templateFilename);
@@ -114,19 +242,19 @@ namespace Disco.BI.Interop.Pdf
MemoryStream pdfGeneratedStream = new MemoryStream(); MemoryStream pdfGeneratedStream = new MemoryStream();
PdfStamper pdfStamper = new PdfStamper(pdfReader, pdfGeneratedStream); PdfStamper pdfStamper = new PdfStamper(pdfReader, pdfGeneratedStream);
pdfStamper.FormFlattening = FlattenFields; pdfStamper.FormFlattening = flattenFields;
pdfStamper.Writer.CloseStream = false; pdfStamper.Writer.CloseStream = false;
IDictionary expressionVariables = Expression.StandardVariables(dt, Database, CreatorUser, TimeStamp, State); IDictionary expressionVariables = Expression.StandardVariables(dt, Database, CreatorUser, TimeStamp, State, Data);
foreach (string pdfFieldKey in pdfStamper.AcroFields.Fields.Keys) foreach (string pdfFieldKey in pdfStamper.AcroFields.Fields.Keys)
{ {
if (pdfFieldKey.Equals("DiscoAttachmentId", StringComparison.OrdinalIgnoreCase)) if (pdfFieldKey.Equals("DiscoAttachmentId", StringComparison.OrdinalIgnoreCase))
{ {
AcroFields.Item fields = pdfStamper.AcroFields.Fields[pdfFieldKey]; AcroFields.Item fields = pdfStamper.AcroFields.Fields[pdfFieldKey];
string fieldValue = dt.UniqueIdentifier(Data, CreatorUser.UserId, TimeStamp); string fieldValue = dt.CreateUniqueIdentifier(Database, Data, CreatorUser, TimeStamp, 0).ToJson();
if (FlattenFields) if (flattenFields)
pdfStamper.AcroFields.SetField(pdfFieldKey, String.Empty); pdfStamper.AcroFields.SetField(pdfFieldKey, string.Empty);
else else
pdfStamper.AcroFields.SetField(pdfFieldKey, fieldValue); pdfStamper.AcroFields.SetField(pdfFieldKey, fieldValue);
@@ -134,11 +262,22 @@ namespace Disco.BI.Interop.Pdf
for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++) for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++)
{ {
AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal]; AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal];
string pdfBarcodeContent = dt.UniquePageIdentifier(Data, CreatorUser.UserId, TimeStamp, pdfFieldPosition.page);
BarcodeQRCode pdfBarcode = new BarcodeQRCode(pdfBarcodeContent, (int)pdfFieldPosition.position.Width, (int)pdfFieldPosition.position.Height, null); // Create Binary Unique Identifier
iTextSharp.text.Image pdfBarcodeImage = pdfBarcode.GetImage(); var pageUniqueId = dt.CreateUniqueIdentifier(Database, Data, CreatorUser, TimeStamp, pdfFieldPosition.page);
pdfBarcodeImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom); var pageUniqueIdBytes = pageUniqueId.ToQRCodeBytes();
pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfBarcodeImage);
// Encode to QRCode byte array
var pageUniqueIdEncoded = QRCodeBinaryEncoder.Encode(pageUniqueIdBytes, out var qrWidth, out var qrHeight);
// Encode byte array to Image
var pageUniqueIdImageData = CCITTG4Encoder.Compress(pageUniqueIdEncoded, qrWidth, qrHeight);
var pageUniqueIdImage = iTextSharp.text.Image.GetInstance(qrWidth, qrHeight, false, 256, 1, pageUniqueIdImageData, null);
// Add to the pdf page
pageUniqueIdImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom);
pageUniqueIdImage.ScaleToFit(pdfFieldPosition.position.Width, pdfFieldPosition.position.Height);
pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pageUniqueIdImage);
} }
// Hide Fields // Hide Fields
PdfDictionary field = fields.GetValue(0); PdfDictionary field = fields.GetValue(0);
@@ -157,8 +296,7 @@ namespace Disco.BI.Interop.Pdf
} }
else else
{ {
Expression fieldExpression = null; if (expressionCache.TryGetValue(pdfFieldKey, out var fieldExpression))
if (expressionCache.TryGetValue(pdfFieldKey, out fieldExpression))
{ {
if (fieldExpression.IsDynamic) if (fieldExpression.IsDynamic)
{ {
@@ -175,8 +313,24 @@ namespace Disco.BI.Interop.Pdf
for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++) for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++)
{ {
AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal]; AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal];
iTextSharp.text.Image pdfImage = iTextSharp.text.Image.GetInstance(imageResult.GetImage((int)pdfFieldPosition.position.Width, (int)pdfFieldPosition.position.Height));
iTextSharp.text.Image pdfImage;
var imageWidth = (int)(pdfFieldPosition.position.Width * 1.6);
var imageHeight = (int)(pdfFieldPosition.position.Height * 1.6);
if (imageResult.Format == ImageExpressionFormat.Jpeg || imageResult.Format == ImageExpressionFormat.Png)
{
pdfImage = iTextSharp.text.Image.GetInstance(imageResult.GetImage(imageWidth, imageHeight));
}
else if (imageResult.Format == ImageExpressionFormat.CcittG4)
{
var imageData = imageResult.GetImage(out imageWidth, out imageHeight);
pdfImage = iTextSharp.text.Image.GetInstance(imageWidth, imageHeight, false, 256, 1, imageData.GetBuffer(), null);
}
else
throw new NotSupportedException($"Unexpected image format {imageResult.Format}");
pdfImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom); pdfImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom);
pdfImage.ScaleToFit(pdfFieldPosition.position.Width, pdfFieldPosition.position.Height);
pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfImage); pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfImage);
} }
if (!fieldExpressionResult.Item2 && !imageResult.ShowField) if (!fieldExpressionResult.Item2 && !imageResult.ShowField)
@@ -242,7 +396,7 @@ namespace Disco.BI.Interop.Pdf
TechUserId = CreatorUser.UserId, TechUserId = CreatorUser.UserId,
Timestamp = DateTime.Now Timestamp = DateTime.Now
}; };
jl.Comments = string.Format("Document Generated{0}{1} [{2}]", Environment.NewLine, dt.Description, dt.Id); jl.Comments = $"# Document Generated\r\n**{dt.Description}** [{dt.Id}]";
Database.JobLogs.Add(jl); Database.JobLogs.Add(jl);
} }
-977
View File
@@ -1,977 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTextSharp.text.pdf;
using System.IO;
using System.Drawing;
using Disco.BI.DocumentTemplateBI.Importer;
using Disco.BI.DocumentTemplateBI;
using System.Drawing.Drawing2D;
using com.google.zxing;
using com.google.zxing.multi.qrcode;
using Disco.Data.Repository;
using System.Web.Caching;
using Disco.BI.Extensions;
using Disco.Models.Repository;
using System.Collections;
using com.google.zxing.common;
using BitMiracle.LibTiff.Classic;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace Disco.BI.Interop.Pdf
{
public static class PdfImporter
{
public static RectangleF CalculateLocationRatio(this Result zxingResult, int ImageWidth, int ImageHeight)
{
var orderedPoints = zxingResult.ResultPoints.OrderBy(p => p.X * p.Y).ToArray();
var topLeftPoint = orderedPoints.First();
var bottomRightPoint = orderedPoints.Last();
var x = topLeftPoint.X;
var y = topLeftPoint.Y;
var width = bottomRightPoint.X - x;
var height = bottomRightPoint.Y - y;
return new RectangleF(
(float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(x / ImageWidth) - 0.05)),
(float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(y / ImageHeight) - 0.05)),
(float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(width / ImageWidth) + 0.1)),
(float)System.Math.Min(1.0, System.Math.Max(0.0, (double)(height / ImageHeight) + 0.1))
);
}
private class DetectImageResult : IDisposable
{
public Result Result { get; set; }
public Point ResultOffset { get; set; }
public double ResultScale { get; set; }
public float CalculateRotation()
{
var p1 = this.Result.ResultPoints[0];
var p2 = this.Result.ResultPoints[1];
double rotOpposite = p1.X - p2.X;
double rotAdjacent = p1.Y - p2.Y;
float rotation = 0;
if (rotOpposite != 0 || rotAdjacent != 0)
{
rotation = (float)(Math.Atan2(rotOpposite, rotAdjacent) * (180 / Math.PI)); // Degrees
}
return rotation;
}
public void Dispose()
{
// Do Nothing; yet...
}
}
private class DetectStateHints
{
public List<Tuple<RectangleF, Rotation>> PriorDetections { get; set; }
public DetectStateHints()
{
this.PriorDetections = new List<Tuple<RectangleF, Rotation>>();
}
}
private enum Rotation
{
None = 0,
Degrees90 = 1,
DegreesNeg90 = 2,
Degrees180 = 3
}
private class DetectPageResult : IDisposable
{
public int PageNumber { get; set; }
public DocumentUniqueIdentifier DetectedIdentifier { get; set; }
public Disco.BI.Extensions.UtilityExtensions.ImageMontage ThumbnailImage { get; set; }
public MemoryStream AttachmentThumbnailImage { get; set; }
public Disco.BI.Extensions.UtilityExtensions.ImageMontage UndetectedPageImage { get; set; }
public void DrawThumbnailImageResult(DetectImageResult Result, Image DetectedImage)
{
if (Result.Result.ResultPoints.Length == 4)
{ // Draw Square on Thumbnail
using (Graphics thumbnailGraphics = Graphics.FromImage(ThumbnailImage.Montage))
{
var thumbnailOffset = ThumbnailImage.MontageSourceImageOffsets[DetectedImage];
var linePoints = Result.Result.ResultPoints.Select(p => new Point((int)(thumbnailOffset + ((Result.ResultOffset.X + p.X) * Result.ResultScale * ThumbnailImage.MontageScale)), (int)((p.Y + Result.ResultOffset.Y) * Result.ResultScale * ThumbnailImage.MontageScale))).ToArray();
using (GraphicsPath graphicsPath = new GraphicsPath())
{
for (int linePointIndex = 0; linePointIndex < (linePoints.Length - 1); linePointIndex++)
graphicsPath.AddLine(linePoints[linePointIndex], linePoints[linePointIndex + 1]);
graphicsPath.AddLine(linePoints[linePoints.Length - 1], linePoints[0]);
using (SolidBrush graphicsBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 0)))
thumbnailGraphics.FillPath(graphicsBrush, graphicsPath);
using (Pen graphicsPen = new Pen(Color.FromArgb(200, 255, 0, 0), 2))
thumbnailGraphics.DrawPath(graphicsPen, graphicsPath);
}
}
}
}
public void Dispose()
{
if (ThumbnailImage != null)
{
ThumbnailImage.Dispose();
ThumbnailImage = null;
}
if (AttachmentThumbnailImage != null)
{
AttachmentThumbnailImage.Dispose();
AttachmentThumbnailImage = null;
}
if (UndetectedPageImage != null)
{
UndetectedPageImage.Dispose();
UndetectedPageImage = null;
}
}
}
private static Tuple<Result, Rectangle, Rotation> DetectImageFromSegment(Bitmap pageImage, QRCodeMultiReader zxingReader, Hashtable zxingReaderHints, RectangleF LocationPercentage, Rotation Rotation)
{
System.Drawing.Rectangle region;
switch (Rotation)
{
case Rotation.None: // Original Position
region = new Rectangle(
(int)(pageImage.Width * LocationPercentage.Left),
(int)(pageImage.Height * LocationPercentage.Top),
(int)(pageImage.Width * LocationPercentage.Width),
(int)(pageImage.Height * LocationPercentage.Height));
break;
case Rotation.Degrees90: // Clockwise 90 degrees
region = new Rectangle(
(int)(pageImage.Width - (pageImage.Width * (LocationPercentage.Top + LocationPercentage.Height))),
(int)(pageImage.Height * LocationPercentage.Left),
(int)(pageImage.Width * LocationPercentage.Height),
(int)(pageImage.Height * LocationPercentage.Width));
break;
case Rotation.DegreesNeg90: // Anti-clockwise 90 degrees
region = new Rectangle(
(int)(pageImage.Width * LocationPercentage.Top),
(int)(pageImage.Height - (pageImage.Height * (LocationPercentage.Left + LocationPercentage.Width))),
(int)(pageImage.Width * LocationPercentage.Height),
(int)(pageImage.Height * LocationPercentage.Width));
break;
case Rotation.Degrees180: // 180 degrees
region = new Rectangle(
(int)(pageImage.Width - (pageImage.Width * (LocationPercentage.Left + LocationPercentage.Width))),
(int)(pageImage.Height - (pageImage.Height * (LocationPercentage.Top + LocationPercentage.Height))),
(int)(pageImage.Width * LocationPercentage.Width),
(int)(pageImage.Height * LocationPercentage.Height));
break;
default:
throw new InvalidOperationException("Unknown Rotation");
}
LuminanceSource zxingSource;
using (Bitmap pageImageRegion = new Bitmap(region.Width, region.Height))
{
pageImageRegion.SetResolution(pageImage.HorizontalResolution, pageImage.VerticalResolution);
using (Graphics pageImageRegionGraphics = Graphics.FromImage(pageImageRegion))
{
pageImageRegionGraphics.DrawImage(pageImage, 0, 0, region, GraphicsUnit.Pixel);
}
zxingSource = new BitmapLuminanceSource(pageImageRegion);
}
var zxingHB = new HybridBinarizer(zxingSource);
var zxingBB = new BinaryBitmap(zxingHB);
try
{
var zxingResult = zxingReader.decode(zxingBB, zxingReaderHints);
if (zxingResult != null)
return new Tuple<Result, Rectangle, Rotation>(zxingResult, region, Rotation);
}
catch (ReaderException)
{
// Ignore Location Errors
}
return null;
}
private static DetectImageResult DetectImage(DiscoDataContext Database, Bitmap pageImageOriginal, string SessionId, IEnumerable<DocumentTemplate> detectDocumentTemplates, DetectStateHints StateHints)
{
Bitmap pageImage = pageImageOriginal;
double pageImageModifiedScale = 1;
try
{
// Resize if Resolution > 80; Set to 72 Dpi
if (pageImage.HorizontalResolution > 80 || pageImage.VerticalResolution > 80)
{
pageImageModifiedScale = pageImage.HorizontalResolution / 72;
int newWidth = (int)((72 / pageImage.HorizontalResolution) * pageImage.Width);
int newHeight = (int)((72 / pageImage.VerticalResolution) * pageImage.Height);
pageImage = pageImage.ResizeImage(newWidth, newHeight);
}
Tuple<Result, Rectangle, Rotation> result = default(Tuple<Result, Rectangle, Rotation>);
QRCodeMultiReader zxingMfr = new QRCodeMultiReader();
Hashtable zxingMfrHints = new Hashtable();
zxingMfrHints.Add(DecodeHintType.TRY_HARDER, true);
// Look in previously found locations
if (StateHints.PriorDetections.Count > 0)
{
foreach (var previousLocation in StateHints.PriorDetections)
{
result = DetectImageFromSegment(pageImage, zxingMfr, zxingMfrHints,
previousLocation.Item1, previousLocation.Item2);
if (result != null)
break;
}
}
if (result == null)
{
// Try the whole image
var zxingSource = new BitmapLuminanceSource(pageImage);
var zxingHB = new HybridBinarizer(zxingSource);
var zxingBB = new BinaryBitmap(zxingHB);
try
{
var zxingResult = zxingMfr.decode(zxingBB, zxingMfrHints);
if (zxingResult != null)
{
result = new Tuple<Result, Rectangle, Rotation>(zxingResult, new Rectangle(0, 0, pageImage.Width, pageImage.Height), Rotation.None);
StateHints.PriorDetections.Insert(0, new Tuple<RectangleF, Rotation>(
result.Item1.CalculateLocationRatio(pageImage.Width, pageImage.Height)
, Rotation.None));
}
}
catch (ReaderException)
{
// Ignore Errors
}
}
if (result == null)
{
// Look in 'Known' locations
for (int rotationIndex = 0; rotationIndex < 4; rotationIndex++)
{
foreach (DocumentTemplate dt in detectDocumentTemplates)
{
var locationBag = dt.QRCodeLocations(Database);
foreach (var location in locationBag)
{
result = DetectImageFromSegment(pageImage, zxingMfr, zxingMfrHints,
location, (Rotation)rotationIndex);
StateHints.PriorDetections.Insert(0, new Tuple<RectangleF, Rotation>(location, (Rotation)rotationIndex));
}
if (result != null)
break;
}
if (result != null)
break;
}
}
if (result != null)
return new DetectImageResult() { Result = result.Item1, ResultOffset = result.Item2.Location, ResultScale = pageImageModifiedScale };
else
return null;
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (pageImageOriginal != pageImage)
pageImage.Dispose();
}
}
private static DetectPageResult DetectPage(DiscoDataContext Database, PdfReader pdfReader, int PageNumber, string SessionId, string DataStoreSessionCacheLocation, IEnumerable<DocumentTemplate> detectDocumentTemplates, DetectStateHints StateHints)
{
DetectPageResult result = new DetectPageResult() { PageNumber = PageNumber };
DocumentsLog.LogImportPageProgress(SessionId, PageNumber, 10, "Loading Page Images");
using (DisposableImageCollection pageImages = pdfReader.PdfPageImages(PageNumber))
{
if (pageImages.Count > 0)
{
result.ThumbnailImage = pageImages.BuildImageMontage(256, 256);
var pageThumbnailFilename = Path.Combine(DataStoreSessionCacheLocation, string.Format("{0}-{1}", SessionId, PageNumber));
result.ThumbnailImage.Montage.SavePng(pageThumbnailFilename);
DocumentsLog.LogImportPageImageUpdate(SessionId, PageNumber);
double pageProgressInterval = 90 / pageImages.Count;
foreach (var pageImageOriginal in pageImages)
{
DocumentsLog.LogImportPageProgress(SessionId, PageNumber, (int)(10 + (pageProgressInterval * pageImages.IndexOf(pageImageOriginal))), String.Format("Processing Page Image {0} of {1}", pageImages.IndexOf(pageImageOriginal) + 1, pageImages.Count));
using (var zxingResult = DetectImage(Database, pageImageOriginal, SessionId, detectDocumentTemplates, StateHints))
{
if (zxingResult != null)
{
if (DocumentUniqueIdentifier.IsDocumentUniqueIdentifier(zxingResult.Result.Text))
{
float imageRotation = zxingResult.CalculateRotation();
result.DrawThumbnailImageResult(zxingResult, pageImageOriginal);
if (imageRotation != 0)
{
var preImageRotation = result.ThumbnailImage.Montage;
result.ThumbnailImage.Montage = result.ThumbnailImage.Montage.RotateImage(imageRotation);
preImageRotation.Dispose();
}
result.ThumbnailImage.Montage.SavePng(pageThumbnailFilename);
DocumentsLog.LogImportPageImageUpdate(SessionId, PageNumber);
result.AttachmentThumbnailImage = new MemoryStream();
using (var attachmentThumbImage = pageImages.BuildImageMontage(48, 48, true))
{
using (Image mimeTypeIcon = Disco.Properties.Resources.MimeType_pdf16)
attachmentThumbImage.Montage.EmbedIconOverlay(mimeTypeIcon);
if (imageRotation != 0)
{
var preImageRotation = attachmentThumbImage.Montage;
attachmentThumbImage.Montage = attachmentThumbImage.Montage.RotateImage(imageRotation, Brushes.White);
preImageRotation.Dispose();
}
attachmentThumbImage.Montage.SaveJpg(95, result.AttachmentThumbnailImage);
}
result.DetectedIdentifier = new DocumentUniqueIdentifier(zxingResult.Result.Text, PageNumber.ToString());
return result;
}
}
}
}
// Page Unassigned
result.UndetectedPageImage = pageImages.BuildImageMontage(700, 700);
}
return result;
}
}
public static bool ProcessPdfAttachment(string Filename, DiscoDataContext Database, string SessionId, Cache HttpCache)
{
var dataStoreUnassignedLocation = DataStore.CreateLocation(Database, "DocumentDropBox_Unassigned");
DocumentsLog.LogImportProgress(SessionId, 0, "Reading File");
using (FileStream fs = new FileStream(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
var pdfReader = new PdfReader(fs);
var pdfPagesAssigned = new Dictionary<int, Tuple<DocumentUniqueIdentifier, byte[]>>();
var dataStoreSessionPagesCacheLocation = DataStore.CreateLocation(Database, "Cache\\DocumentDropBox_SessionPages");
var detectDocumentTemplates = Database.DocumentTemplates.ToArray();
double progressInterval = 70 / pdfReader.NumberOfPages;
DetectStateHints detectStateHints = new DetectStateHints();
for (int PageNumber = 1; PageNumber <= pdfReader.NumberOfPages; PageNumber++)
{
DocumentsLog.LogImportProgress(SessionId, (int)(PageNumber * progressInterval), string.Format("Processing Page {0} of {1}", PageNumber, pdfReader.NumberOfPages));
DocumentsLog.LogImportPageStarting(SessionId, PageNumber);
using (var pageResult = DetectPage(Database, pdfReader, PageNumber, SessionId, dataStoreSessionPagesCacheLocation, detectDocumentTemplates, detectStateHints))
{
if (pageResult.DetectedIdentifier != null)
{
var docId = pageResult.DetectedIdentifier;
pdfPagesAssigned.Add(PageNumber, new Tuple<DocumentUniqueIdentifier, byte[]>(docId, pageResult.AttachmentThumbnailImage.ToArray()));
docId.LoadComponents(Database);
DocumentsLog.LogImportPageDetected(SessionId, PageNumber, docId.TemplateTypeId, docId.DocumentTemplate.Description, docId.DocumentTemplate.Scope, docId.DataId, docId.DataDescription);
}
else
{
// Undetected Page - Write Preview-Images while still in Memory
DocumentsLog.LogImportPageUndetected(SessionId, PageNumber);
// Thumbnail:
string unassignedImageThumbnailFilename = Path.Combine(dataStoreUnassignedLocation, string.Format("{0}_{1}_thumbnail.png", SessionId, PageNumber));
if (pageResult.ThumbnailImage != null)
pageResult.ThumbnailImage.Montage.SavePng(unassignedImageThumbnailFilename);
else
Disco.Properties.Resources.MimeType_pdf48.SavePng(unassignedImageThumbnailFilename);
// Large Preview
string unassignedImageFilename = Path.Combine(dataStoreUnassignedLocation, string.Format("{0}_{1}.jpg", SessionId, PageNumber));
if (pageResult.UndetectedPageImage != null)
pageResult.UndetectedPageImage.Montage.SaveJpg(90, unassignedImageFilename);
else
Disco.Properties.Resources.MimeType_pdf48.SaveJpg(90, unassignedImageFilename);
}
}
}
// Write out Assigned Documents
var assignedDocuments = pdfPagesAssigned.GroupBy(u => u.Value.Item1.DocumentUniqueId).ToList();
if (assignedDocuments.Count > 0)
{
progressInterval = 20 / assignedDocuments.Count;
foreach (var documentPortion in assignedDocuments)
{
DocumentsLog.LogImportProgress(SessionId, (int)(70 + (assignedDocuments.IndexOf(documentPortion) * progressInterval)), string.Format("Importing Documents {0} of {1}", assignedDocuments.IndexOf(documentPortion) + 1, assignedDocuments.Count));
var documentPortionInfo = documentPortion.First().Value;
var documentPortionIdentifier = documentPortionInfo.Item1;
var documentPortionThumbnail = documentPortionInfo.Item2;
if (!documentPortionIdentifier.LoadComponents(Database))
{
// Unknown Document Unique Id
foreach (var dp in documentPortion)
{
var tag = int.Parse(dp.Value.Item1.Tag);
if (pdfPagesAssigned.ContainsKey(tag))
pdfPagesAssigned.Remove(tag);
}
}
else
{
using (MemoryStream msBuilder = new MemoryStream())
{
var pdfDoc = new iTextSharp.text.Document();
var pdfCopy = new PdfCopy(pdfDoc, msBuilder);
pdfDoc.Open();
pdfCopy.CloseStream = false;
foreach (var dp in documentPortion.OrderBy(dg => dg.Value.Item1.Page))
{
var pageSize = pdfReader.GetPageSizeWithRotation(dp.Key);
var page = pdfCopy.GetImportedPage(pdfReader, dp.Key);
pdfDoc.SetPageSize(pageSize);
pdfDoc.NewPage();
pdfCopy.AddPage(page);
}
pdfDoc.Close();
pdfCopy.Close();
msBuilder.Position = 0;
var attachmentSuccess = documentPortionIdentifier.ImportPdfAttachment(Database, msBuilder, documentPortionThumbnail);
if (!attachmentSuccess)
{ // Unable to add Attachment
foreach (var dp in documentPortion)
{
var tag = int.Parse(dp.Value.Item1.Tag);
if (pdfPagesAssigned.ContainsKey(tag))
pdfPagesAssigned.Remove(tag);
}
}
}
}
}
}
// Write out Unassigned Pages
List<int> pdfPagesUnassigned = new List<int>();
for (int PageNumber = 1; PageNumber <= pdfReader.NumberOfPages; PageNumber++)
if (!pdfPagesAssigned.ContainsKey(PageNumber))
pdfPagesUnassigned.Add(PageNumber);
if (pdfPagesUnassigned.Count > 0)
{
progressInterval = 10 / pdfPagesUnassigned.Count;
//dataStoreUnassignedLocation
foreach (var PageNumber in pdfPagesUnassigned)
{
DocumentsLog.LogImportProgress(SessionId, (int)(90 + (pdfPagesUnassigned.IndexOf(PageNumber) * progressInterval)), string.Format("Processing Undetected Documents {0} of {1}", pdfPagesUnassigned.IndexOf(PageNumber) + 1, pdfPagesUnassigned.Count));
using (MemoryStream msBuilder = new MemoryStream())
{
var pdfDoc = new iTextSharp.text.Document();
var pdfCopy = new PdfCopy(pdfDoc, msBuilder);
pdfDoc.Open();
pdfCopy.CloseStream = false;
var pageSize = pdfReader.GetPageSizeWithRotation(PageNumber);
var page = pdfCopy.GetImportedPage(pdfReader, PageNumber);
pdfDoc.SetPageSize(pageSize);
pdfDoc.NewPage();
pdfCopy.AddPage(page);
pdfDoc.Close();
pdfCopy.Close();
File.WriteAllBytes(Path.Combine(dataStoreUnassignedLocation, string.Format("{0}_{1}.pdf", SessionId, PageNumber)), msBuilder.ToArray());
DocumentsLog.LogImportPageUndetectedStored(SessionId, PageNumber);
}
}
}
}
DocumentsLog.LogImportProgress(SessionId, 100, "Finished Importing Document");
return true;
}
public static bool ProcessPdfAttachment(string Filename, DiscoDataContext Database, string DocumentTemplateId, string DataId, string UserId, DateTime Timestamp)
{
using (FileStream fs = new FileStream(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
DocumentUniqueIdentifier identifier = new DocumentUniqueIdentifier(DocumentTemplateId, DataId, UserId, Timestamp);
identifier.LoadComponents(Database);
return identifier.ImportPdfAttachment(Database, fs, null);
}
}
public static DisposableImageCollection GetPageImages(PdfReader pdfReader, int PageNumber)
{
var pageImages = new DisposableImageCollection();
var pdfPage = pdfReader.GetPageN(PageNumber);
PdfDictionary pdfPageResouces = (PdfDictionary)((PdfDictionary)pdfPage.GetDirectObject(PdfName.RESOURCES)).GetDirectObject(PdfName.XOBJECT);
foreach (var pdfResKey in pdfPageResouces.Keys)
{
var pdfRes = pdfPageResouces.GetDirectObject(pdfResKey);
if (pdfRes.IsStream())
{
var pdfResStream = (PdfStream)pdfRes;
var pdfResSubType = pdfResStream.Get(PdfName.SUBTYPE);
if (pdfResSubType != null && pdfResSubType == PdfName.IMAGE)
{
if (pdfResStream.Get(PdfName.FILTER) == PdfName.CCITTFAXDECODE)
{ // TIFF
// Try Using GDI+ for TIFF...
var width = ((PdfNumber)(pdfResStream.Get(PdfName.WIDTH))).IntValue;
var height = ((PdfNumber)(pdfResStream.Get(PdfName.HEIGHT))).IntValue;
var bpc = ((PdfNumber)(pdfResStream.Get(PdfName.BITSPERCOMPONENT))).IntValue;
var compressionMethod = Compression.CCITTFAX3;
var decodeParams = pdfResStream.GetAsDict(PdfName.DECODEPARMS);
if (decodeParams != null && decodeParams.Contains(PdfName.K) && decodeParams.GetAsNumber(PdfName.K).IntValue < 0)
compressionMethod = Compression.CCITTFAX4;
using (MemoryStream tiffStream = PdfToTiffStream(PdfReader.GetStreamBytesRaw((PRStream)pdfResStream), width, height, bpc, compressionMethod))
{
pageImages.Add((Bitmap)Bitmap.FromStream(tiffStream));
}
continue;
}
if (pdfResStream.Get(PdfName.FILTER) == PdfName.DCTDECODE)
{ // JPG
using (MemoryStream jpgStream = new MemoryStream(PdfReader.GetStreamBytesRaw((PRStream)pdfResStream)))
{
pageImages.Add((Bitmap)Bitmap.FromStream(jpgStream, true, true));
}
continue;
}
}
}
}
return pageImages;
}
private static MemoryStream PdfToTiffStream(byte[] PdfStream, int Width, int Height, int BitsPerComponent, Compression CompressionMethod)
{
var ms = new MemoryStream();
Tiff tif = Tiff.ClientOpen("in-memory", "w", ms, new TiffStream());
tif.SetField(TiffTag.IMAGEWIDTH, Width);
tif.SetField(TiffTag.IMAGELENGTH, Height);
tif.SetField(TiffTag.COMPRESSION, CompressionMethod);
tif.SetField(TiffTag.BITSPERSAMPLE, BitsPerComponent);
tif.SetField(TiffTag.SAMPLESPERPIXEL, 1);
tif.WriteRawStrip(0, PdfStream, PdfStream.Length);
tif.Flush();
return ms;
}
}
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <summary>
/// The base class for luminance sources which supports
/// cropping and rotating based upon the luminance values.
/// </summary>
public abstract class BaseLuminanceSource : LuminanceSource
{
/// <summary>
///
/// </summary>
protected sbyte[] luminances;
/// <summary>
/// Initializes a new instance of the <see cref="BaseLuminanceSource"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected BaseLuminanceSource(int width, int height)
: base(width, height)
{
luminances = new sbyte[width * height];
}
/// <summary>
/// Initializes a new instance of the <see cref="BaseLuminanceSource"/> class.
/// </summary>
/// <param name="luminanceArray">The luminance array.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected BaseLuminanceSource(sbyte[] luminanceArray, int width, int height)
: base(width, height)
{
luminances = luminanceArray;
//Buffer.BlockCopy(luminanceArray, 0, luminances, 0, width * height);
}
/// <summary>
/// Fetches one row of luminance data from the underlying platform's bitmap. Values range from
/// 0 (black) to 255 (white). It is preferable for implementations of this method
/// to only fetch this row rather than the whole image, since no 2D Readers may be installed and
/// getMatrix() may never be called.
/// </summary>
/// <param name="y">The row to fetch, 0 &lt;= y &lt; Height.</param>
/// <param name="row">An optional preallocated array. If null or too small, it will be ignored.
/// Always use the returned object, and ignore the .length of the array.</param>
/// <returns>
/// An array containing the luminance data.
/// </returns>
override public sbyte[] getRow(int y, sbyte[] row)
{
int width = Width;
if (row == null || row.Length < width)
{
row = new sbyte[width];
}
//for (int i = 0; i < width; i++)
// row[i] = luminances[y * width + i];
Buffer.BlockCopy(luminances, y * width, row, 0, width);
return row;
}
public override sbyte[] Matrix
{
get { return luminances; }
}
/// <summary>
/// Returns a new object with rotated image data by 90 degrees counterclockwise.
/// Only callable if {@link #isRotateSupported()} is true.
/// </summary>
/// <returns>
/// A rotated version of this object.
/// </returns>
public override LuminanceSource rotateCounterClockwise()
{
var rotatedLuminances = new sbyte[Width * Height];
var newWidth = Height;
var newHeight = Width;
var localLuminances = Matrix;
for (var yold = 0; yold < Height; yold++)
{
for (var xold = 0; xold < Width; xold++)
{
var ynew = xold;
var xnew = newWidth - yold - 1;
rotatedLuminances[ynew * newWidth + xnew] = localLuminances[yold * Width + xold];
}
}
return CreateLuminanceSource(rotatedLuminances, newWidth, newHeight);
}
/// <summary>
/// </summary>
/// <returns> Whether this subclass supports counter-clockwise rotation.</returns>
public override bool RotateSupported
{
get
{
return true;
}
}
/// <summary>
/// Returns a new object with cropped image data. Implementations may keep a reference to the
/// original data rather than a copy. Only callable if CropSupported is true.
/// </summary>
/// <param name="left">The left coordinate, 0 &lt;= left &lt; Width.</param>
/// <param name="top">The top coordinate, 0 &lt;= top &lt;= Height.</param>
/// <param name="width">The width of the rectangle to crop.</param>
/// <param name="height">The height of the rectangle to crop.</param>
/// <returns>
/// A cropped version of this object.
/// </returns>
public override LuminanceSource crop(int left, int top, int width, int height)
{
if (left + width > Width || top + height > Height)
{
throw new ArgumentException("Crop rectangle does not fit within image data.");
}
var croppedLuminances = new sbyte[width * height];
for (int yold = top, ynew = 0; yold < height; yold++, ynew++)
{
for (int xold = left, xnew = 0; xold < width; xold++, xnew++)
{
croppedLuminances[ynew * width + xnew] = luminances[yold * Width + xold];
}
}
return CreateLuminanceSource(croppedLuminances, width, height);
}
/// <summary>
/// </summary>
/// <returns> Whether this subclass supports cropping.</returns>
public override bool CropSupported
{
get
{
return true;
}
}
/// <summary>
/// Should create a new luminance source with the right class type.
/// The method is used in methods crop and rotate.
/// </summary>
/// <param name="newLuminances">The new luminances.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns></returns>
protected abstract LuminanceSource CreateLuminanceSource(sbyte[] newLuminances, int width, int height);
}
public partial class BitmapLuminanceSource : BaseLuminanceSource
{
/// <summary>
/// Initializes a new instance of the <see cref="BitmapLuminanceSource"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected BitmapLuminanceSource(int width, int height)
: base(width, height)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BitmapLuminanceSource"/> class
/// with the image of a Bitmap instance
/// </summary>
/// <param name="bitmap">The bitmap.</param>
public BitmapLuminanceSource(Bitmap bitmap)
: base(bitmap.Width, bitmap.Height)
{
var height = bitmap.Height;
var width = bitmap.Width;
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
luminances = new sbyte[width * height];
// The underlying raster of image consists of bytes with the luminance values
#if WindowsCE
var data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
#else
var data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
#endif
try
{
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
if (pixelWidth > 4)
{
// old slow way for unsupported bit depth
Color c;
for (int y = 0; y < height; y++)
{
int offset = y * width;
for (int x = 0; x < width; x++)
{
c = bitmap.GetPixel(x, y);
luminances[offset + x] = (sbyte)(0.3 * c.R + 0.59 * c.G + 0.11 * c.B + 0.01);
}
}
}
else
{
var strideStep = data.Stride;
var buffer = new byte[stride];
var ptrInBitmap = data.Scan0;
#if !WindowsCE
// prepare palette for 1 and 8 bit indexed bitmaps
var luminancePalette = new sbyte[bitmap.Palette.Entries.Length];
for (var index = 0; index < bitmap.Palette.Entries.Length; index++)
{
var color = bitmap.Palette.Entries[index];
luminancePalette[index] = (sbyte)(0.3 * color.R +
0.59 * color.G +
0.11 * color.B + 0.01);
}
if (bitmap.PixelFormat == PixelFormat.Format32bppArgb ||
bitmap.PixelFormat == PixelFormat.Format32bppPArgb)
{
pixelWidth = 40;
}
#endif
for (int y = 0; y < height; y++)
{
// copy a scanline not the whole bitmap because of memory usage
Marshal.Copy(ptrInBitmap, buffer, 0, stride);
#if NET40
ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep);
#else
ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep);
#endif
var offset = y * width;
switch (pixelWidth)
{
#if !WindowsCE
case 0:
for (int x = 0; x * 8 < width; x++)
{
for (int subX = 0; subX < 8 && 8 * x + subX < width; subX++)
{
var index = (buffer[x] >> (7 - subX)) & 1;
luminances[offset + 8 * x + subX] = luminancePalette[index];
}
}
break;
case 1:
for (int x = 0; x < width; x++)
{
luminances[offset + x] = luminancePalette[buffer[x]];
}
break;
#endif
case 2:
// should be RGB565 or RGB555, assume RGB565
{
for (int index = 0, x = 0; index < 2 * width; index += 2, x++)
{
var byte1 = buffer[index];
var byte2 = buffer[index + 1];
var b5 = byte1 & 0x1F;
var g5 = (((byte1 & 0xE0) >> 5) | ((byte2 & 0x03) << 3)) & 0x1F;
var r5 = (byte2 >> 2) & 0x1F;
var r8 = (r5 * 527 + 23) >> 6;
var g8 = (g5 * 527 + 23) >> 6;
var b8 = (b5 * 527 + 23) >> 6;
luminances[offset + x] = (sbyte)(0.3 * r8 + 0.59 * g8 + 0.11 * b8 + 0.01);
}
}
break;
case 3:
for (int x = 0; x < width; x++)
{
var luminance = (sbyte)(0.3 * buffer[x * 3] +
0.59 * buffer[x * 3 + 1] +
0.11 * buffer[x * 3 + 2] + 0.01);
luminances[offset + x] = luminance;
}
break;
case 4:
// 4 bytes without alpha channel value
for (int x = 0; x < width; x++)
{
var luminance = (sbyte)(0.30 * buffer[x * 4] +
0.59 * buffer[x * 4 + 1] +
0.11 * buffer[x * 4 + 2] + 0.01);
luminances[offset + x] = luminance;
}
break;
case 40:
// with alpha channel; some barcodes are completely black if you
// only look at the r, g and b channel but the alpha channel controls
// the view
for (int x = 0; x < width; x++)
{
var luminance = (sbyte)(0.30 * buffer[x * 4] +
0.59 * buffer[x * 4 + 1] +
0.11 * buffer[x * 4 + 2] + 0.01);
// calculating the resulting luminance based upon a white background
// var alpha = buffer[x * pixelWidth + 3] / 255.0;
// luminance = (byte)(luminance * alpha + 255 * (1 - alpha));
var alpha = buffer[x * 4 + 3];
luminance = (sbyte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8));
luminances[offset + x] = luminance;
}
break;
default:
throw new NotSupportedException();
}
}
}
}
finally
{
bitmap.UnlockBits(data);
}
}
/// <summary>
/// Should create a new luminance source with the right class type.
/// The method is used in methods crop and rotate.
/// </summary>
/// <param name="newLuminances">The new luminances.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns></returns>
protected override LuminanceSource CreateLuminanceSource(sbyte[] newLuminances, int width, int height)
{
return new BitmapLuminanceSource(width, height) { luminances = newLuminances };
}
}
}
+63
View File
@@ -0,0 +1,63 @@
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.codec;
using System;
using System.Collections.Generic;
using System.IO;
namespace Disco.BI.Interop.Pdf
{
public static class Utilities
{
public static Func<byte[], int, int, byte[]> GetCCITTG4EncoderCompressDelegate()
{
return CCITTG4Encoder.Compress;
}
public static Stream JoinPdfs(bool InsertBlankPages, List<Stream> Pdfs)
{
if (Pdfs.Count == 0)
throw new ArgumentNullException(nameof(Pdfs));
// Only One PDF - Possible Reference Bug v's Memory/Speed (Returning Param Memory Stream)
if (Pdfs.Count == 1)
return Pdfs[0];
// Join Pdfs
var msBuilder = new MemoryStream();
var pdfLastPageSize = PageSize.A4;
var pdfDoc = new Document();
var pdfCopy = new PdfSmartCopy(pdfDoc, msBuilder);
pdfDoc.Open();
pdfCopy.CloseStream = false;
for (int i = 0; i < Pdfs.Count; i++)
{
var pdf = Pdfs[i];
var pdfReader = new PdfReader(pdf);
if (InsertBlankPages && (pdfCopy.CurrentPageNumber % 2) == 0)
{
pdfCopy.AddPage(pdfLastPageSize, 0);
}
for (int indexPage = 1; indexPage <= pdfReader.NumberOfPages; indexPage++)
{
pdfLastPageSize = pdfReader.GetPageSizeWithRotation(indexPage);
var page = pdfCopy.GetImportedPage(pdfReader, indexPage);
pdfDoc.SetPageSize(pdfLastPageSize);
pdfCopy.AddPage(page);
}
pdfReader.Close();
}
pdfDoc.Close();
pdfCopy.Close();
msBuilder.Position = 0;
return msBuilder;
}
}
}
+6 -123
View File
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Disco</RootNamespace> <RootNamespace>Disco</RootNamespace>
<AssemblyName>Disco.BI</AssemblyName> <AssemblyName>Disco.BI</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
@@ -37,29 +37,15 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="BitMiracle.LibTiff.NET">
<HintPath>..\Resources\Libraries\LibTiff.NET\BitMiracle.LibTiff.NET.dll</HintPath>
</Reference>
<Reference Include="EntityFramework"> <Reference Include="EntityFramework">
<HintPath>..\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll</HintPath> <HintPath>..\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll</HintPath>
</Reference> </Reference>
<Reference Include="Exceptionless, Version=1.5.2092.0, Culture=neutral, PublicKeyToken=fc181f0a46f65747, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Exceptionless.1.5.2092\lib\net45\Exceptionless.dll</HintPath>
</Reference>
<Reference Include="Exceptionless.Models, Version=1.5.2092.0, Culture=neutral, PublicKeyToken=fc181f0a46f65747, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Exceptionless.1.5.2092\lib\net45\Exceptionless.Models.dll</HintPath>
</Reference>
<Reference Include="itextsharp"> <Reference Include="itextsharp">
<HintPath>..\Resources\Libraries\iTextSharp\itextsharp.dll</HintPath> <HintPath>..\Resources\Libraries\iTextSharp\itextsharp.dll</HintPath>
</Reference> </Reference>
<Reference Include="Quartz"> <Reference Include="Quartz">
<HintPath>..\Resources\Libraries\Quartz\Quartz.dll</HintPath> <HintPath>..\Resources\Libraries\Quartz\Quartz.dll</HintPath>
</Reference> </Reference>
<Reference Include="Spring.Core">
<HintPath>..\Resources\Libraries\Spring.NET\Spring.Core.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
@@ -67,29 +53,13 @@
<Reference Include="System.DirectoryServices" /> <Reference Include="System.DirectoryServices" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Management" /> <Reference Include="System.Management" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http.Extensions, Version=2.2.22.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Reference Include="System.Net.Http.Extensions">
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Extensions.dll</HintPath> <HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Extensions.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="System.Net.Http.Primitives"> <Reference Include="System.Net.Http.Primitives, Version=4.2.22.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Primitives.dll</HintPath> <HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Primitives.dll</HintPath>
</Reference> <Private>True</Private>
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Linq, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.PlatformServices, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" /> <Reference Include="System.ServiceModel" />
@@ -99,75 +69,11 @@
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="Tamir.SharpSSH">
<HintPath>..\Resources\Libraries\SharpSSH\Tamir.SharpSSH.dll</HintPath>
</Reference>
<Reference Include="zxing">
<HintPath>..\Resources\Libraries\ZXing\zxing.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="BI\DataStore.cs" />
<Compile Include="BI\AttachmentBI\Utilities.cs" />
<Compile Include="BI\DeviceBI\BatchUtilities.cs" />
<Compile Include="BI\DeviceBI\DeviceModelBI.cs" />
<Compile Include="BI\DeviceBI\Migration\LogMacAddressImporting.cs" />
<Compile Include="BI\DisposableImageCollection.cs" />
<Compile Include="BI\DocumentTemplateBI\DocumentTemplateQRCodeLocationCache.cs" />
<Compile Include="BI\DocumentTemplateBI\ManagedGroups\DocumentTemplateUsersManagedGroup.cs" />
<Compile Include="BI\DocumentTemplateBI\ManagedGroups\DocumentTemplateDevicesManagedGroup.cs" />
<Compile Include="BI\DocumentTemplateBI\ManagedGroups\DocumentTemplateManagedGroups.cs" />
<Compile Include="BI\Expressions\EvaluateExpressionParseException.cs" />
<Compile Include="BI\Expressions\ExpressionCachePreloadTask.cs" />
<Compile Include="BI\Expressions\Extensions\DataExt.cs" />
<Compile Include="BI\Expressions\Extensions\DeviceExt.cs" />
<Compile Include="BI\Expressions\Extensions\ImageExt.cs" />
<Compile Include="BI\Expressions\Extensions\ImageResultImplementations\BaseImageExpressionResult.cs" />
<Compile Include="BI\Expressions\Extensions\ImageResultImplementations\FileMontageImageExpressionResult.cs" />
<Compile Include="BI\Expressions\Extensions\ImageResultImplementations\FileImageExpressionResult.cs" />
<Compile Include="BI\Expressions\Extensions\ImageResultImplementations\BitmapImageExpressionResult.cs" />
<Compile Include="BI\Expressions\Extensions\UserExt.cs" />
<Compile Include="BI\Extensions\AttachmentActionExtensions.cs" />
<Compile Include="BI\Extensions\AttachmentExtensions.cs" />
<Compile Include="BI\Extensions\AuthorizationRoleExtensions.cs" />
<Compile Include="BI\Extensions\ClientServicesExtensions.cs" />
<Compile Include="BI\Extensions\UserFlagActionExtensions.cs" />
<Compile Include="BI\Extensions\DeviceActionExtensions.cs" />
<Compile Include="BI\Extensions\DeviceBatchExtensions.cs" />
<Compile Include="BI\Extensions\DeviceCertificateExtensions.cs" />
<Compile Include="BI\Extensions\DeviceDetailExtensions.cs" />
<Compile Include="BI\Extensions\DeviceModelExtensions.cs" />
<Compile Include="BI\Extensions\DeviceProfileExtensions.cs" />
<Compile Include="BI\Extensions\JobActionExtensions.cs" />
<Compile Include="BI\Extensions\JobExtensions.cs" />
<Compile Include="BI\Extensions\JobFlagExtensions.cs" />
<Compile Include="BI\Extensions\JobQueueActionExtensions.cs" />
<Compile Include="BI\Extensions\UserExtensions.cs" />
<Compile Include="BI\Extensions\WirelessCertificateExtensions.cs" />
<Compile Include="BI\Extensions\DeviceExtensions.cs" />
<Compile Include="BI\DeviceBI\EnrolSafeException.cs" />
<Compile Include="BI\DeviceBI\Enrol.cs" />
<Compile Include="BI\DeviceBI\EnrolmentLog.cs" />
<Compile Include="BI\Extensions\DocumentTemplateExtensions.cs" /> <Compile Include="BI\Extensions\DocumentTemplateExtensions.cs" />
<Compile Include="BI\DocumentTemplateBI\Utilities.cs" /> <Compile Include="BI\Interop\Pdf\Utilities.cs" />
<Compile Include="BI\DocumentTemplateBI\DocumentUniqueIdentifier.cs" />
<Compile Include="BI\Expressions\EvaluateExpressionPart.cs" />
<Compile Include="BI\Expressions\Expression.cs" />
<Compile Include="BI\Expressions\ExpressionTypeDescriptor.cs" />
<Compile Include="BI\Expressions\ExpressionTypeMemberDescriptor.cs" />
<Compile Include="BI\Expressions\IExpressionPart.cs" />
<Compile Include="BI\Expressions\TextExpressionPart.cs" />
<Compile Include="BI\DocumentTemplateBI\Importer\DocumentDropBoxMonitor.cs" />
<Compile Include="BI\DocumentTemplateBI\Importer\DocumentImporterJob.cs" />
<Compile Include="BI\DocumentTemplateBI\Importer\DocumentImporterCleanCacheJob.cs" />
<Compile Include="BI\DocumentTemplateBI\DocumentsLog.cs" />
<Compile Include="BI\Expressions\ExpressionCache.cs" />
<Compile Include="BI\Interop\MimeTypes.cs" />
<Compile Include="BI\Interop\Pdf\PdfGenerator.cs" /> <Compile Include="BI\Interop\Pdf\PdfGenerator.cs" />
<Compile Include="BI\Interop\Pdf\PdfImporter.cs" />
<Compile Include="BI\JobBI\Statistics\DailyOpenedClosed.cs" />
<Compile Include="BI\JobBI\Utilities.cs" />
<Compile Include="BI\Extensions\UtilityExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs"> <Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
@@ -200,31 +106,8 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="Resources\MimeType-img16.png" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\MimeType-pdf16.png" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\EmptyLogo.png" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\MimeType-doc48.png" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\MimeType-pdf48.png" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\MimeType-unknown48.png" />
</ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_DetectChanges="False" BuildVersion_BuildAction="Both" BuildVersion_StartDate="2014/6/1" />
</VisualStudio>
</ProjectExtensions>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" /> <Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''"> <Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" /> <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
+6 -7
View File
@@ -1,15 +1,14 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("Disco - Business Intelligence")] [assembly: AssemblyTitle("Disco ICT - Business Intelligence")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("https://discoict.com.au")]
[assembly: AssemblyProduct("Disco")] [assembly: AssemblyProduct("Disco ICT")]
[assembly: AssemblyCopyright("")] [assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -32,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.0731.1600")] [assembly: AssemblyVersion("2.5.25262.0000")]
[assembly: AssemblyFileVersion("2.0.0731.1600")] [assembly: AssemblyFileVersion("2.5.25262.0000")]
+2 -52
View File
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // This code was generated by a tool.
// Runtime Version:4.0.30319.17929 // Runtime Version:4.0.30319.42000
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
@@ -19,7 +19,7 @@ namespace Disco.Properties {
// class via a tool like ResGen or Visual Studio. // class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen // To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project. // with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources { internal class Resources {
@@ -59,55 +59,5 @@ namespace Disco.Properties {
resourceCulture = value; resourceCulture = value;
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_doc48 {
get {
object obj = ResourceManager.GetObject("MimeType_doc48", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_img16 {
get {
object obj = ResourceManager.GetObject("MimeType_img16", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_pdf16 {
get {
object obj = ResourceManager.GetObject("MimeType_pdf16", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_pdf48 {
get {
object obj = ResourceManager.GetObject("MimeType_pdf48", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_unknown48 {
get {
object obj = ResourceManager.GetObject("MimeType_unknown48", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
} }
} }
-16
View File
@@ -117,20 +117,4 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="MimeType_doc48" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MimeType-doc48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MimeType_img16" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MimeType-img16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MimeType_pdf16" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MimeType-pdf16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MimeType_pdf48" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MimeType-pdf48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MimeType_unknown48" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MimeType-unknown48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root> </root>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

+16 -4
View File
@@ -16,7 +16,7 @@
</defaultConnectionFactory> </defaultConnectionFactory>
</entityFramework> </entityFramework>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
</startup> </startup>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
@@ -30,7 +30,7 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Web.Razor" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="System.Web.Razor" publicKeyToken="31bf3856ad364e35" culture="neutral" />
@@ -46,11 +46,23 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.3" newVersion="4.1.1.3" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
-6
View File
@@ -1,13 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="EntityFramework" version="5.0.0" targetFramework="net45" /> <package id="EntityFramework" version="5.0.0" targetFramework="net45" />
<package id="Exceptionless" version="1.5.2092" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.9" targetFramework="net45" /> <package id="Microsoft.Bcl" version="1.1.9" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" /> <package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.22" targetFramework="net45" /> <package id="Microsoft.Net.Http" version="2.2.22" targetFramework="net45" />
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
<package id="Rx-Linq" version="2.2.5" targetFramework="net45" />
<package id="Rx-Main" version="2.2.5" targetFramework="net45" />
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="net45" />
</packages> </packages>
+70 -11
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -9,9 +9,8 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Disco.Client</RootNamespace> <RootNamespace>Disco.Client</RootNamespace>
<AssemblyName>Disco.Client</AssemblyName> <AssemblyName>Disco.Client</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages> <RestorePackages>true</RestorePackages>
</PropertyGroup> </PropertyGroup>
@@ -24,6 +23,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@@ -33,6 +33,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<StartupObject>Disco.Client.Program</StartupObject> <StartupObject>Disco.Client.Program</StartupObject>
@@ -48,8 +49,8 @@
<ApplicationManifest>Properties\app.manifest</ApplicationManifest> <ApplicationManifest>Properties\app.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Newtonsoft.Json"> <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.6.0.3\lib\net40\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.13.0.2\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
@@ -65,6 +66,54 @@
<Compile Include="..\Disco.Models\ClientServices\Enrol.cs"> <Compile Include="..\Disco.Models\ClientServices\Enrol.cs">
<Link>Models\ClientServices\Enrol.cs</Link> <Link>Models\ClientServices\Enrol.cs</Link>
</Compile> </Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\BaseBoard.cs">
<Link>Models\ClientServices\EnrolmentInformation\BaseBoard.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\Battery.cs">
<Link>Models\ClientServices\EnrolmentInformation\Battery.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\Bios.cs">
<Link>Models\ClientServices\EnrolmentInformation\Bios.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\Certificate.cs">
<Link>Models\ClientServices\EnrolmentInformation\Certificate.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\CertificateStore.cs">
<Link>Models\ClientServices\EnrolmentInformation\CertificateStore.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\ComputerSystem.cs">
<Link>Models\ClientServices\EnrolmentInformation\ComputerSystem.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\DeviceHardware.cs">
<Link>Models\ClientServices\EnrolmentInformation\DeviceHardware.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\DiskDrive.cs">
<Link>Models\ClientServices\EnrolmentInformation\DiskDrive.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\DiskDrivePartition.cs">
<Link>Models\ClientServices\EnrolmentInformation\DiskDrivePartition.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\DiskLogical.cs">
<Link>Models\ClientServices\EnrolmentInformation\DiskLogical.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\NetworkAdapter.cs">
<Link>Models\ClientServices\EnrolmentInformation\NetworkAdapter.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\PhysicalMemory.cs">
<Link>Models\ClientServices\EnrolmentInformation\PhysicalMemory.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\Processor.cs">
<Link>Models\ClientServices\EnrolmentInformation\Processor.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\WirelessProfile.cs">
<Link>Models\ClientServices\EnrolmentInformation\WirelessProfile.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\WirelessProfileStore.cs">
<Link>Models\ClientServices\EnrolmentInformation\WirelessProfileStore.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolmentInformation\WirelessProfileTransformation.cs">
<Link>Models\ClientServices\EnrolmentInformation\WirelessProfileTransformation.cs</Link>
</Compile>
<Compile Include="..\Disco.Models\ClientServices\EnrolResponse.cs"> <Compile Include="..\Disco.Models\ClientServices\EnrolResponse.cs">
<Link>Models\ClientServices\EnrolResponse.cs</Link> <Link>Models\ClientServices\EnrolResponse.cs</Link>
</Compile> </Compile>
@@ -92,10 +141,20 @@
<Compile Include="Extensions\EnrolExtensions.cs" /> <Compile Include="Extensions\EnrolExtensions.cs" />
<Compile Include="Extensions\WhoAmIExtensions.cs" /> <Compile Include="Extensions\WhoAmIExtensions.cs" />
<Compile Include="Interop\Certificates.cs" /> <Compile Include="Interop\Certificates.cs" />
<Compile Include="Interop\EndpointDiscovery.cs" />
<Compile Include="Interop\Hardware.cs" />
<Compile Include="Interop\LocalAuthentication.cs" /> <Compile Include="Interop\LocalAuthentication.cs" />
<Compile Include="Interop\Native\NetworkConnectionStatuses.cs" />
<Compile Include="Interop\Native\ProfileInfoFlags.cs" />
<Compile Include="Interop\Native\WlanApi.cs" />
<Compile Include="Interop\Native\WLAN_INTERFACE_INFO.cs" />
<Compile Include="Interop\Native\WLAN_INTERFACE_INFO_LIST.cs" />
<Compile Include="Interop\Native\WLAN_INTERFACE_STATE.cs" />
<Compile Include="Interop\Native\WLAN_PROFILE_INFO.cs" />
<Compile Include="Interop\Native\WLAN_PROFILE_INFO_LIST.cs" />
<Compile Include="Interop\Network.cs" /> <Compile Include="Interop\Network.cs" />
<Compile Include="Interop\WirelessNetwork.cs" />
<Compile Include="Presentation.cs" /> <Compile Include="Presentation.cs" />
<Compile Include="Interop\SystemAudit.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
@@ -119,16 +178,16 @@
<None Include="Package Creation\7z.exe" /> <None Include="Package Creation\7z.exe" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties BuildVersion_UseGlobalSettings="False" BuildVersion_DetectChanges="False" BuildVersion_StartDate="2014/6/1" BuildVersion_BuildAction="Both" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" />
</VisualStudio>
</ProjectExtensions>
<PropertyGroup> <PropertyGroup>
<PostBuildEvent>DEL "$(ProjectDir)Package Creation\PreparationClient.zip" <PostBuildEvent>DEL "$(ProjectDir)Package Creation\PreparationClient.zip"
"$(ProjectDir)Package Creation\7z.exe" a -tzip "$(ProjectDir)Package Creation\PreparationClient.zip" "$(TargetDir)*.*" -x!*.tmp -x!*.vshost.* -x!Newtonsoft.Json.xml -x!*.pdb "$(ProjectDir)Package Creation\7z.exe" a -tzip "$(ProjectDir)Package Creation\PreparationClient.zip" "$(TargetDir)*.*" -x!*.tmp -x!*.vshost.* -x!Newtonsoft.Json.xml -x!*.pdb
COPY "$(ProjectDir)Package Creation\PreparationClient.zip" "$(ProjectDir)..\Disco.Web\ClientBin"</PostBuildEvent> COPY "$(ProjectDir)Package Creation\PreparationClient.zip" "$(ProjectDir)..\Disco.Web\ClientBin"</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties BuildVersion_StartDate="2000/1/1" />
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
+13 -15
View File
@@ -1,31 +1,27 @@
using System; using Disco.Client.Extensions;
using System.Collections.Generic; using Newtonsoft.Json;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
using System.Text;
using Disco.Client.Extensions;
using Newtonsoft.Json;
namespace Disco.Client namespace Disco.Client
{ {
public static class ErrorReporting public static class ErrorReporting
{ {
private const string ServicePathTemplate = "http://DISCO:9292/Services/Client/ClientError";
public static string DeviceIdentifier { get; set; } public static string DeviceIdentifier { get; set; }
public static string EnrolmentSessionId { get; set; } public static string EnrolmentSessionId { get; set; }
public static void ReportError(Exception Ex, bool ReportToServer) public static void ReportError(Exception exception, bool reportToServer)
{ {
bool isClientServiceException = Ex is ClientServiceException; bool isClientServiceException = exception is ClientServiceException;
ErrorReport report = new ErrorReport() ErrorReport report = new ErrorReport()
{ {
DeviceIdentifier = DeviceIdentifier, DeviceIdentifier = DeviceIdentifier,
SessionId = EnrolmentSessionId, SessionId = EnrolmentSessionId,
JsonException = Ex.IntenseExceptionSerialization() JsonException = exception.IntenseExceptionSerialization()
}; };
try try
@@ -41,7 +37,7 @@ namespace Disco.Client
catch (Exception) { } catch (Exception) { }
// Don't log server errors back to the server // Don't log server errors back to the server
if (!isClientServiceException && ReportToServer) if (!isClientServiceException && reportToServer)
{ {
try try
{ {
@@ -52,7 +48,7 @@ namespace Disco.Client
try try
{ {
Presentation.WriteFatalError(Ex); Presentation.WriteFatalError(exception);
} }
catch (Exception) { } catch (Exception) { }
} }
@@ -88,8 +84,10 @@ namespace Disco.Client
string reportJson = JsonConvert.SerializeObject(report); string reportJson = JsonConvert.SerializeObject(report);
string reportResponse; string reportResponse;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(ServicePathTemplate); var serverUri = new Uri(Program.ServerUrl ?? new Uri("http://disco:9292"), "/Services/Client/ClientError");
request.UserAgent = string.Format("Disco-Client/{0}", Assembly.GetExecutingAssembly().GetName().Version.ToString(3));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serverUri);
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
request.ContentType = "application/json"; request.ContentType = "application/json";
request.Method = WebRequestMethods.Http.Post; request.Method = WebRequestMethods.Http.Post;
request.UseDefaultCredentials = true; request.UseDefaultCredentials = true;
@@ -108,7 +106,7 @@ namespace Disco.Client
} }
} }
System.Diagnostics.Debug.WriteLine("Error Report Logged to Server; Response: {0}", reportResponse); Debug.WriteLine($"Error Report Logged to Server; Response: {reportResponse}");
} }
#endregion #endregion
@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Client.Extensions namespace Disco.Client.Extensions
{ {
@@ -1,55 +1,53 @@
using System; using Disco.Models.ClientServices;
using System.Collections.Generic; using Newtonsoft.Json;
using System;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Disco.Models.ClientServices;
using Newtonsoft.Json;
namespace Disco.Client.Extensions namespace Disco.Client.Extensions
{ {
public static class ClientServicesExtensions internal static class ClientServicesExtensions
{ {
public const string ServicePathAuthenticatedTemplate = "http://DISCO:9292/Services/Client/Authenticated/{0}"; public static ResponseType Post<ResponseType>(this ServiceBase<ResponseType> service, bool authenticated)
public const string ServicePathUnauthenticatedTemplate = "http://DISCO:9292/Services/Client/Unauthenticated/{0}"; {
ResponseType serviceResponse;
Uri serviceUrl;
public static ResponseType Post<ResponseType>(this ServiceBase<ResponseType> Service, bool Authenticated) if (authenticated)
{ serviceUrl = new Uri(Program.ServerUrl, $"/Services/Client/Authenticated/{service.Feature}");
string jsonResponse;
string serviceUrl;
if (Authenticated)
serviceUrl = string.Format(ServicePathAuthenticatedTemplate, Service.Feature);
else else
serviceUrl = string.Format(ServicePathUnauthenticatedTemplate, Service.Feature); serviceUrl = new Uri(Program.ServerUrl, $"/Services/Client/Unauthenticated/{service.Feature}");
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceUrl); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceUrl);
request.UserAgent = string.Format("Disco-Client/{0}", Assembly.GetExecutingAssembly().GetName().Version.ToString(3)); request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
request.ContentType = "application/json"; request.ContentType = "application/json";
request.Method = WebRequestMethods.Http.Post; request.Method = WebRequestMethods.Http.Post;
request.UseDefaultCredentials = true; request.UseDefaultCredentials = true;
request.Timeout = 300000; // 5 Minutes request.Timeout = 300000; // 5 Minutes
string jsonRequest = JsonConvert.SerializeObject(Service);
using (StreamWriter requestWriter = new StreamWriter(request.GetRequestStream())) var jsonSerializer = new JsonSerializer();
using (var requestWriter = new StreamWriter(request.GetRequestStream()))
{ {
requestWriter.Write(jsonRequest); using (var jsonWriter = new JsonTextWriter(requestWriter))
{
jsonSerializer.Serialize(jsonWriter, service);
}
} }
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{ {
using (StreamReader responseReader = new StreamReader(response.GetResponseStream())) using (var responseReader = new StreamReader(response.GetResponseStream()))
{ {
jsonResponse = responseReader.ReadToEnd(); using (var jsonReader = new JsonTextReader(responseReader))
{
serviceResponse = jsonSerializer.Deserialize<ResponseType>(jsonReader);
}
} }
} }
if (string.IsNullOrEmpty(jsonResponse)) return serviceResponse;
return default(ResponseType);
else
return JsonConvert.DeserializeObject<ResponseType>(jsonResponse);
} }
} }
+93 -89
View File
@@ -1,41 +1,32 @@
using System; using Disco.Client.Interop;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Models.ClientServices; using Disco.Models.ClientServices;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Diagnostics;
using Microsoft.Win32; using Microsoft.Win32;
using System.Text.RegularExpressions; using System;
using System.Runtime.InteropServices;
namespace Disco.Client.Extensions namespace Disco.Client.Extensions
{ {
public static class EnrolExtensions internal static class EnrolExtensions
{ {
public static void Build(this Enrol enrol) public static void Build(this Enrol enrol)
{ {
enrol.DeviceUUID = Interop.SystemAudit.DeviceUUID; enrol.ComputerName = Environment.MachineName;
enrol.DeviceSerialNumber = Interop.SystemAudit.DeviceSerialNumber; enrol.RunningUserDomain = Environment.UserDomainName;
enrol.RunningUserName = Environment.UserName;
enrol.RunningInteractively = Environment.UserInteractive;
enrol.DeviceComputerName = Interop.LocalAuthentication.ComputerName; // Hardware Audit
enrol.Hardware = Hardware.Information;
enrol.SerialNumber = enrol.Hardware.SerialNumber;
enrol.DeviceManufacturer = Interop.SystemAudit.DeviceManufacturer; // Apply System Information
enrol.DeviceModel = Interop.SystemAudit.DeviceModel; enrol.ApplySystemInformation();
enrol.DeviceModelType = Interop.SystemAudit.DeviceType;
enrol.DeviceIsPartOfDomain = Interop.SystemAudit.DeviceIsPartOfDomain;
enrol.DeviceDNSDomainName = Interop.SystemAudit.DeviceDNSDomainName;
// LAN
enrol.DeviceLanMacAddress = Interop.Network.PrimaryLanMacAddress;
// WAN
enrol.DeviceWlanMacAddress = Interop.Network.PrimaryWlanMacAddress;
// Certificates // Certificates
enrol.DeviceCertificates = Interop.Certificates.GetCertificateSubjects(StoreName.My, StoreLocation.LocalMachine); enrol.Certificates = Certificates.GetAllCertificates();
// Wireless Profiles
enrol.WirelessProfiles = WirelessNetwork.GetWirelessProfiles();
} }
public static void Process(this EnrolResponse enrolResponse) public static void Process(this EnrolResponse enrolResponse)
@@ -48,15 +39,20 @@ namespace Disco.Client.Extensions
if (!string.IsNullOrEmpty(enrolResponse.ErrorMessage)) if (!string.IsNullOrEmpty(enrolResponse.ErrorMessage))
throw new ClientServiceException("Enrolment", enrolResponse.ErrorMessage); throw new ClientServiceException("Enrolment", enrolResponse.ErrorMessage);
if (enrolResponse.IsPending)
return;
// Offline Domain Join // Offline Domain Join
bool requireReboot = enrolResponse.ApplyOfflineDomainJoin(); bool requireReboot = enrolResponse.ApplyOfflineDomainJoin();
// Certificates
enrolResponse.ApplyDeviceCertificates();
// Device Owner // Device Owner
enrolResponse.ApplyDeviceAssignedUser(); enrolResponse.ApplyDeviceAssignedUser();
// Certificates
enrolResponse.ApplyDeviceCertificates();
// Wireless Profiles
enrolResponse.ApplyWirelessProfiles();
Presentation.UpdateStatus("Enrolling Device", "Device Enrolment Successfully Completed", false, 0, 1500); Presentation.UpdateStatus("Enrolling Device", "Device Enrolment Successfully Completed", false, 0, 1500);
@@ -64,6 +60,28 @@ namespace Disco.Client.Extensions
Program.AllowUninstall = enrolResponse.AllowBootstrapperUninstall; Program.AllowUninstall = enrolResponse.AllowBootstrapperUninstall;
} }
[Flags]
private enum NETSETUP_PROVISION_FLAGS : int
{
NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT = 0x00000001,
NETSETUP_PROVISION_REUSE_ACCOUNT = 0x00000002,
NETSETUP_PROVISION_USE_DEFAULT_PASSWORD = 0x00000004,
NETSETUP_PROVISION_SKIP_ACCOUNT_SEARCH = 0x00000008,
NETSETUP_PROVISION_ROOT_CA_CERTS = 0x00000010,
NETSETUP_PROVISION_PERSISTENTSITE = 0x00000020,
NETSETUP_PROVISION_ONLINE_CALLER = 0x40000000,
NETSETUP_PROVISION_CHECK_PWD_ONLY = unchecked((int)0x80000000),
}
[DllImport("Netapi32.dll", CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.I4)]
private static extern int NetRequestOfflineDomainJoin(
[In] IntPtr pProvisionBinData,
[In, MarshalAs(UnmanagedType.I4)] int cbProvisionBinDataSize,
[In, MarshalAs(UnmanagedType.I4)] NETSETUP_PROVISION_FLAGS dwOptions,
[In, MarshalAs(UnmanagedType.LPWStr)] string lpWindowsPath
);
/// <summary> /// <summary>
/// Processes a Client Service Enrol Response for Offline Domain Join Actions /// Processes a Client Service Enrol Response for Offline Domain Join Actions
/// </summary> /// </summary>
@@ -71,42 +89,43 @@ namespace Disco.Client.Extensions
/// <returns>Boolean indicating whether a reboot is required.</returns> /// <returns>Boolean indicating whether a reboot is required.</returns>
private static bool ApplyOfflineDomainJoin(this EnrolResponse enrolResponse) private static bool ApplyOfflineDomainJoin(this EnrolResponse enrolResponse)
{ {
if (!string.IsNullOrWhiteSpace(enrolResponse.OfflineDomainJoin)) if (!string.IsNullOrWhiteSpace(enrolResponse.OfflineDomainJoinManifest))
{ {
Presentation.UpdateStatus("Enrolling Device", string.Format("Performing Offline Domain Join:{0}Renaming Computer: {1} -> {2}", Environment.NewLine, Interop.LocalAuthentication.ComputerName, enrolResponse.DeviceComputerName), true, -1, 1500); Presentation.UpdateStatus("Enrolling Device", $"Performing Offline Domain Join:\r\nRenaming Computer: {Environment.MachineName} -> {enrolResponse.ComputerName}", true, -1, 1500);
string odjFile = Path.GetTempFileName(); var provisionData = Convert.FromBase64String(enrolResponse.OfflineDomainJoinManifest);
File.WriteAllBytes(odjFile, Convert.FromBase64String(enrolResponse.OfflineDomainJoin)); string systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
string odjWindowsPath = Environment.GetEnvironmentVariable("SystemRoot"); var provisionDataPointer = Marshal.AllocCoTaskMem(provisionData.Length);
string odjProcessArguments = string.Format("/REQUESTODJ /LOADFILE \"{0}\" /WINDOWSPATH \"{1}\" /LOCALOS", odjFile, odjWindowsPath); Marshal.Copy(provisionData, 0, provisionDataPointer, provisionData.Length);
var joinResult = default(int);
ProcessStartInfo odjProcessStartInfo = new ProcessStartInfo("DJOIN.EXE", odjProcessArguments) try
{ {
CreateNoWindow = true, joinResult = NetRequestOfflineDomainJoin(provisionDataPointer, provisionData.Length, NETSETUP_PROVISION_FLAGS.NETSETUP_PROVISION_ONLINE_CALLER, systemRoot);
ErrorDialog = false, }
LoadUserProfile = true, finally
RedirectStandardOutput = true, {
UseShellExecute = false Marshal.FreeCoTaskMem(provisionDataPointer);
};
string odjResult;
using (Process odjProcess = System.Diagnostics.Process.Start(odjProcessStartInfo))
{
odjResult = odjProcess.StandardOutput.ReadToEnd();
odjProcess.WaitForExit(20000); // 20 Seconds
} }
Presentation.UpdateStatus("Enrolling Device", string.Format("Offline Domain Join Result:{0}{1}", Environment.NewLine, odjResult), true, -1, 3000);
if (File.Exists(odjFile)) if (joinResult != 0)
File.Delete(odjFile); {
var win32Exception = new System.ComponentModel.Win32Exception(joinResult);
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Failed:\r\n{win32Exception.Message} [{joinResult}]", true, -1, 3000);
throw new InvalidOperationException($"Offline Domain Join Failed:\r\n{win32Exception.Message} [{joinResult}]");
}
else
{
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Succeeded", true, -1, 2000);
}
// Flush Logged-On History // Flush Logged-On History
if (!string.IsNullOrEmpty(enrolResponse.DeviceDomainName)) if (enrolResponse.SetAssignedUserForLogon && !string.IsNullOrEmpty(enrolResponse.DomainName))
{ {
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true)) using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
{ {
regWinlogon.SetValue("DefaultDomainName", enrolResponse.DeviceDomainName, RegistryValueKind.String); regWinlogon.SetValue("DefaultDomainName", enrolResponse.DomainName, RegistryValueKind.String);
regWinlogon.SetValue("DefaultUserName", String.Empty, RegistryValueKind.String); regWinlogon.SetValue("DefaultUserName", string.Empty, RegistryValueKind.String);
} }
using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true)) using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true))
{ {
@@ -130,62 +149,47 @@ namespace Disco.Client.Extensions
private static void ApplyDeviceAssignedUser(this EnrolResponse enrolResponse) private static void ApplyDeviceAssignedUser(this EnrolResponse enrolResponse)
{ {
// Only run task if Assigned User was specified // Only run task if Assigned User was specified
if (!string.IsNullOrWhiteSpace(enrolResponse.DeviceAssignedUserSID)) if (!string.IsNullOrWhiteSpace(enrolResponse.AssignedUserSID))
{ {
Presentation.UpdateStatus("Enrolling Device", string.Format(@"Configuring the device owner:{0}{1} ({2}\{3})", Environment.NewLine, enrolResponse.DeviceAssignedUserName, enrolResponse.DeviceAssignedUserDomain, enrolResponse.DeviceAssignedUserUsername), true, -1, 3000); Presentation.UpdateStatus("Enrolling Device", $"Configuring the device owner:\r\n{enrolResponse.AssignedUserDescription} ({enrolResponse.AssignedUserDomain}\\{enrolResponse.AssignedUserUsername})", true, -1, 3000);
if (enrolResponse.DeviceAssignedUserIsLocalAdmin) if (enrolResponse.AssignedUserIsLocalAdmin)
Interop.LocalAuthentication.AddLocalGroupMembership("Administrators", enrolResponse.DeviceAssignedUserSID, enrolResponse.DeviceAssignedUserUsername, enrolResponse.DeviceAssignedUserDomain); LocalAuthentication.AddLocalGroupMembership("Administrators", enrolResponse.AssignedUserSID, enrolResponse.AssignedUserUsername, enrolResponse.AssignedUserDomain);
}
if (enrolResponse.SetAssignedUserForLogon && !string.IsNullOrEmpty(enrolResponse.AssignedUserDomain) && !string.IsNullOrEmpty(enrolResponse.AssignedUserUsername))
{
// Make Windows think this user was the last to logon // Make Windows think this user was the last to logon
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true)) using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
{ {
regWinlogon.SetValue("DefaultDomainName", enrolResponse.DeviceAssignedUserDomain, RegistryValueKind.String); regWinlogon.SetValue("DefaultDomainName", enrolResponse.AssignedUserDomain, RegistryValueKind.String);
regWinlogon.SetValue("DefaultUserName", enrolResponse.DeviceAssignedUserUsername, RegistryValueKind.String); regWinlogon.SetValue("DefaultUserName", enrolResponse.AssignedUserUsername, RegistryValueKind.String);
} }
using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true)) using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true))
{ {
regLogonUI.SetValue("LastLoggedOnUser", string.Format(@"{0}\{1}", enrolResponse.DeviceAssignedUserDomain, enrolResponse.DeviceAssignedUserUsername), RegistryValueKind.String); regLogonUI.SetValue("LastLoggedOnUser", $@"{enrolResponse.AssignedUserDomain}\{enrolResponse.AssignedUserUsername}", RegistryValueKind.String);
} }
} }
} }
/// <summary>
/// Processes a Client Service Enrol Response for Device Certificate Actions
/// </summary>
/// <param name="enrolResponse"></param>
private static void ApplyDeviceCertificates(this EnrolResponse enrolResponse) private static void ApplyDeviceCertificates(this EnrolResponse enrolResponse)
{ {
// Only run if a Certificate was supplied if (enrolResponse.Certificates != null)
if (!string.IsNullOrEmpty(enrolResponse.DeviceCertificate))
{ {
Presentation.UpdateStatus("Enrolling Device", "Configuring Wireless Certificates", true, -1, 1000); Presentation.UpdateStatus("Enrolling Device", "Configuring Certificates", true, -1, 1000);
var certPersonalBytes = Convert.FromBase64String(enrolResponse.DeviceCertificate); enrolResponse.Certificates.Apply();
var certPersonal = new X509Certificate2(certPersonalBytes, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); }
if (certPersonal == null) }
throw new ClientServiceException("Enrolment > Device Certificate", "Unable to Import Device Certificate Provided, possibly check password.");
// Certificate Removal private static void ApplyWirelessProfiles(this EnrolResponse enrolResponse)
if (enrolResponse.DeviceCertificateRemoveExisting != null && enrolResponse.DeviceCertificateRemoveExisting.Count > 0)
{ {
List<Regex> regExMatchesSubject = new List<Regex>(); if (enrolResponse.WirelessProfiles != null)
foreach (var subjectRegEx in enrolResponse.DeviceCertificateRemoveExisting) {
regExMatchesSubject.Add(new Regex(subjectRegEx, RegexOptions.IgnoreCase)); Presentation.UpdateStatus("Enrolling Device", "Configuring Wireless Profiles", true, -1, 1000);
// Remove from 'My' Store enrolResponse.WirelessProfiles.Apply();
Interop.Certificates.RemoveCertificates(StoreName.My, StoreLocation.LocalMachine, regExMatchesSubject, certPersonal);
// Remove from 'Root' Store
Interop.Certificates.RemoveCertificates(StoreName.Root, StoreLocation.LocalMachine, regExMatchesSubject, certPersonal);
// Remove from 'CertificateAuthority' Store
Interop.Certificates.RemoveCertificates(StoreName.CertificateAuthority, StoreLocation.LocalMachine, regExMatchesSubject, certPersonal);
}
// Add Certificate
Presentation.UpdateStatus("Enrolling Device", string.Format("Configuring Wireless Certificates{0}Installing Certificate: {1}", Environment.NewLine, Interop.Certificates.GetCertificateFriendlyName(certPersonal)), true, -1);
Interop.Certificates.AddCertificate(StoreName.My, StoreLocation.LocalMachine, certPersonal);
} }
} }
} }
} }
+1 -5
View File
@@ -1,9 +1,5 @@
using System; using Disco.Models.ClientServices;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Disco.Models.ClientServices;
namespace Disco.Client.Extensions namespace Disco.Client.Extensions
{ {
+83 -57
View File
@@ -1,103 +1,129 @@
using System; using Disco.Models.ClientServices.EnrolmentInformation;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
namespace Disco.Client.Interop namespace Disco.Client.Interop
{ {
public static class Certificates public static class Certificates
{ {
public static List<Certificate> GetAllCertificates()
public static string GetCertificateFriendlyName(X509Certificate2 Certificate)
{ {
string subject = Certificate.Subject; var certificates = new List<Certificate>();
return subject.Substring(subject.IndexOf("=") + 1, subject.IndexOf(",") - subject.IndexOf("=") - 1);
// Trusted Root Certificates
certificates.AddRange(GetCertificates(StoreName.Root, "TrustedRoot"));
// Intermediate Certificates
certificates.AddRange(GetCertificates(StoreName.CertificateAuthority, "Intermediate"));
// Personal Certificates
certificates.AddRange(GetCertificates(StoreName.My, "Personal"));
return certificates;
} }
public static List<string> GetCertificateSubjects(StoreName StoreName, StoreLocation StoreLocation) private static IEnumerable<Certificate> GetCertificates(StoreName StoreName, string StoreDescription)
{ {
X509Store certStore = new X509Store(StoreName, StoreLocation); var store = new X509Store(StoreName, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly); store.Open(OpenFlags.ReadOnly);
var certSubjects = certStore.Certificates.Cast<X509Certificate2>().Select(c => c.Subject).ToList();
certStore.Close();
return certSubjects;
}
public static bool AddCertificate(StoreName StoreName, StoreLocation StoreLocation, X509Certificate2 Certificate)
{
X509Store certStore = new X509Store(StoreName, StoreLocation);
bool certAlreadyAdded = false;
certStore.Open(OpenFlags.ReadWrite);
try try
{ {
foreach (X509Certificate2 cert in certStore.Certificates) foreach (var certificate in store.Certificates)
{ {
if (cert.SerialNumber.Equals(Certificate.SerialNumber)) yield return new Certificate()
{ {
certAlreadyAdded = true; Store = StoreDescription,
break; SubjectName = certificate.SubjectName.Name,
Thumbprint = certificate.Thumbprint,
FriendlyName = certificate.FriendlyName,
DnsName = certificate.GetNameInfo(X509NameType.DnsName, false),
Version = certificate.Version,
SignatureAlgorithm = certificate.SignatureAlgorithm.FriendlyName,
Issuer = certificate.IssuerName.Name,
NotAfter = certificate.NotAfter,
NotBefore = certificate.NotBefore,
HasPrivateKey = certificate.HasPrivateKey
};
} }
} }
if (!certAlreadyAdded)
{
Presentation.UpdateStatus("Enrolling Device", string.Format("Configuring Wireless Certificates{0}Adding Certificate: '{1}' from {2}@{3}", Environment.NewLine, GetCertificateFriendlyName(Certificate), StoreName.ToString(), StoreLocation.ToString()), true, -1, 3000);
certStore.Add(Certificate);
}
}
catch (Exception) { throw; }
finally finally
{ {
certStore.Close(); store.Close();
} }
return !certAlreadyAdded;
} }
public static List<string> RemoveCertificates(StoreName StoreName, StoreLocation StoreLocation, List<Regex> RegExMatchesSubject, X509Certificate2 CertificateException) public static void Apply(this CertificateStore EnrolStore)
{ {
X509Store certStore = new X509Store(StoreName, StoreLocation); if (EnrolStore != null)
List<string> results = new List<string>(); {
List<X509Certificate2> certStoreRemove = new List<X509Certificate2>(); // Apply Trusted Root
ApplyToStore(StoreName.Root, EnrolStore.TrustedRootCertificates, EnrolStore.TrustedRootRemoveThumbprints);
certStore.Open(OpenFlags.ReadWrite); // Apply Intermediate
ApplyToStore(StoreName.CertificateAuthority, EnrolStore.IntermediateCertificates, EnrolStore.IntermediateRemoveThumbprints);
// Apply Personal
ApplyToStore(StoreName.My, EnrolStore.PersonalCertificates, EnrolStore.PersonalRemoveThumbprints);
}
}
private static void ApplyToStore(StoreName StoreName, List<byte[]> Certificates, List<string> RemoveThumbprints)
{
if ((Certificates != null && Certificates.Count > 0) ||
(RemoveThumbprints != null && RemoveThumbprints.Count > 0))
{
var store = new X509Store(StoreName, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
try try
{ {
foreach (X509Certificate2 cert in certStore.Certificates) var addedThumbprints = new List<string>();
var existingThumbprints = store.Certificates.Cast<X509Certificate2>().GroupBy(c => c.Thumbprint).ToDictionary(c => c.Key, c => c.ToList(), StringComparer.OrdinalIgnoreCase);
// Add
if (Certificates != null && Certificates.Count > 0)
{ {
if (!cert.SerialNumber.Equals(CertificateException.SerialNumber)) foreach (var certificateBytes in Certificates)
{ {
foreach (var subjectRegEx in RegExMatchesSubject) var certificate = new X509Certificate2(certificateBytes, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
// check if it already exists
if (!existingThumbprints.ContainsKey(certificate.Thumbprint) && !addedThumbprints.Contains(certificate.Thumbprint))
{ {
if (subjectRegEx.IsMatch(cert.Subject)) Presentation.UpdateStatus("Enrolling Device", $"Configuring Certificates\r\nAdding Certificate: '{certificate.GetNameInfo(X509NameType.DnsName, false)}' from {store.Name}@{store.Location}", true, -1, 1000);
{ store.Add(certificate);
certStoreRemove.Add(cert); addedThumbprints.Add(certificate.Thumbprint);
break;
}
} }
} }
} }
foreach (var cert in certStoreRemove) // Remove
if (RemoveThumbprints != null && RemoveThumbprints.Count > 0)
{ {
results.Add(cert.Subject); foreach (var thumbprint in RemoveThumbprints)
{
Presentation.UpdateStatus("Enrolling Device", string.Format("Configuring Wireless Certificates{0}Removing Certificate: '{1}' from {2}@{3}", Environment.NewLine, GetCertificateFriendlyName(cert), StoreName.ToString(), StoreLocation.ToString()), true, -1, 1500); if (existingThumbprints.TryGetValue(thumbprint, out var certificates) && !addedThumbprints.Contains(thumbprint))
certStore.Remove(cert); {
foreach (var certificate in certificates)
{
Presentation.UpdateStatus("Enrolling Device", $"Configuring Certificates\r\nRemoving Certificate: '{certificate.GetNameInfo(X509NameType.DnsName, false)}' from {store.Name}@{store.Location}", true, -1, 1000);
store.Remove(certificate);
existingThumbprints.Remove(thumbprint);
}
}
}
} }
} }
catch (Exception) { throw; }
finally finally
{ {
certStore.Close(); store.Close();
}
} }
return results;
} }
} }
} }
+317
View File
@@ -0,0 +1,317 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Threading;
namespace Disco.Client.Interop
{
internal class EndpointDiscovery
{
[DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)] ref string pszName, NativeDnsQueryTypes wType, NativeDnsQueryOptions options, int aipServers, ref IntPtr ppQueryResults, int pReserved);
[DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);
private const int DNS_ERROR_RCODE_NAME_ERROR = 0x232B;
private const int DNS_ERROR_BAD_PACKET = 0x251E;
public static Tuple<Uri, string> DiscoverServer(Uri forcedServerUri)
{
// 1. Check first command line argument for server name
if (forcedServerUri != null)
return Tuple.Create(forcedServerUri, "Manual");
// 2. Check for a DNS SRV record for _discoict._tcp.domain
var domainSuffixes = new List<string>();
var primaryDomain = IPGlobalProperties.GetIPGlobalProperties().DomainName;
if (!string.IsNullOrEmpty(primaryDomain))
domainSuffixes.Add(primaryDomain);
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces()
.Where(ni => ni.OperationalStatus == OperationalStatus.Up);
foreach (var ni in networkInterfaces)
{
var domainSuffix = ni.GetIPProperties().DnsSuffix;
if (!string.IsNullOrWhiteSpace(domainSuffix))
{
if (domainSuffix.Equals("mshome.net", StringComparison.OrdinalIgnoreCase))
continue;
if (!domainSuffixes.Contains(domainSuffix, StringComparer.OrdinalIgnoreCase))
domainSuffixes.Add(domainSuffix);
}
}
foreach (var domain in domainSuffixes)
{
var dnsRecords = GetSRVRecords("_discoict._tcp." + domain);
if (dnsRecords.Count > 0)
{
var firstRecord = dnsRecords.OrderBy(r => r.Priority).ThenByDescending(r => r.Weight).First();
if (firstRecord.Port == 443)
return Tuple.Create(new Uri($"https://{firstRecord.Target}"), "SRV");
else
return Tuple.Create(new Uri($"https://{firstRecord.Target}:{firstRecord.Port}"), "SRV");
}
}
// 3. Detect VicSmart network and try resolving with Disco ICT Online Services
if (TryResolveVicSmartServer(domainSuffixes, out var vicSmartServerUrl))
return Tuple.Create(vicSmartServerUrl, "VicSmart");
// 4. Legacy: Ping 'disco' and assume port 9292
using (Ping p = new Ping())
{
try
{
PingReply pr = p.Send("disco", 2000);
if (pr.Status == IPStatus.Success)
return Tuple.Create(new Uri("http://disco:9292"), "Legacy");
}
catch (Exception)
{
}
}
throw new Exception("Could not locate Disco ICT server on the network.");
}
private static bool TryResolveVicSmartServer(List<string> domainSuffixes, out Uri serverUrl)
{
if (IsVicSmartNetwork(domainSuffixes))
{
var potentialVicSmartAddresses = NetworkInterface.GetAllNetworkInterfaces()
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
.SelectMany(ni => ni.GetIPProperties().UnicastAddresses)
.Where(ua => ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
.Select(ua => ua.Address.GetAddressBytes())
.Where(a => a[0] == 10)
.Select(a => (ushort)((a[1] >> 4) & 0x000F) | ((a[1] << 4) & 0x00F0) | ((a[2] << 12) & 0xF000) | ((a[2] << 4) & 0x0F00))
.Distinct()
.Select(a => $"{a:x4}.vicsmart.discoict.com")
.ToList();
foreach (var potentialAddress in potentialVicSmartAddresses)
{
var records = GetTxtRecords(potentialAddress);
foreach (var record in records)
{
if (!record.Content.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
continue;
if (Uri.TryCreate(record.Content, UriKind.Absolute, out var discoveredUri))
{
serverUrl = discoveredUri;
return true;
}
}
}
}
serverUrl = null;
return false;
}
private static bool IsVicSmartNetwork(List<string> domainSuffixes)
{
if (domainSuffixes.Any(s => string.Equals("services.education.vic.gov.au", s, StringComparison.OrdinalIgnoreCase)) ||
domainSuffixes.Any(s => string.Equals("education.vic.gov.au", s, StringComparison.OrdinalIgnoreCase))
)
return true;
IPHostEntry doeWanDnsEntry;
try
{
doeWanDnsEntry = Dns.GetHostEntry("broadband.doe.wan");
if (doeWanDnsEntry.AddressList.Length > 0)
return true;
}
catch (Exception)
{ }
return false;
}
private static List<DnsTxtRecord> GetTxtRecords(string name)
{
IntPtr resourceRecordsPointer = IntPtr.Zero;
var records = new List<DnsTxtRecord>();
var retry = 5;
retry:
try
{
int queryResult = DnsQuery(ref name, NativeDnsQueryTypes.DNS_TYPE_TEXT, NativeDnsQueryOptions.DNS_QUERY_STANDARD, 0, ref resourceRecordsPointer, 0);
if (queryResult != 0)
{
if (queryResult == DNS_ERROR_RCODE_NAME_ERROR)
return records;
else if (queryResult == DNS_ERROR_BAD_PACKET && retry > 0)
{
// Sometimes a BAD_PACKET error is returned, retry a few times
Thread.Sleep(200);
retry--;
goto retry;
}
else
throw new Win32Exception(queryResult);
}
NativeDnsTxtRecord record;
for (var resourceRecordPointer = resourceRecordsPointer; !resourceRecordPointer.Equals(IntPtr.Zero); resourceRecordPointer = record.pNext)
{
record = Marshal.PtrToStructure<NativeDnsTxtRecord>(resourceRecordPointer);
if (record.wType == (ushort)NativeDnsQueryTypes.DNS_TYPE_TEXT)
records.Add(DnsTxtRecord.FromNativeRecord(record));
}
}
finally
{
if (resourceRecordsPointer != IntPtr.Zero)
DnsRecordListFree(resourceRecordsPointer, 0);
}
return records;
}
private static List<DnsSrvRecord> GetSRVRecords(string name)
{
IntPtr resourceRecordsPointer = IntPtr.Zero;
var records = new List<DnsSrvRecord>();
var retry = 5;
retry:
try
{
int queryResult = DnsQuery(ref name, NativeDnsQueryTypes.DNS_TYPE_SRV, NativeDnsQueryOptions.DNS_QUERY_STANDARD, 0, ref resourceRecordsPointer, 0);
if (queryResult != 0)
{
if (queryResult == DNS_ERROR_RCODE_NAME_ERROR)
return records;
else if (queryResult == DNS_ERROR_BAD_PACKET && retry > 0)
{
// Sometimes a BAD_PACKET error is returned, retry a few times
Thread.Sleep(200);
retry--;
goto retry;
}
else
throw new Win32Exception(queryResult);
}
NativeDnsSrvRecord record;
for (var resourceRecordPointer = resourceRecordsPointer; !resourceRecordPointer.Equals(IntPtr.Zero); resourceRecordPointer = record.pNext)
{
record = Marshal.PtrToStructure<NativeDnsSrvRecord>(resourceRecordPointer);
if (record.wType == (ushort)NativeDnsQueryTypes.DNS_TYPE_SRV)
records.Add(DnsSrvRecord.FromNativeRecord(record));
}
}
finally
{
if (resourceRecordsPointer != IntPtr.Zero)
DnsRecordListFree(resourceRecordsPointer, 0);
}
return records;
}
private enum NativeDnsQueryOptions
{
DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,
DNS_QUERY_BYPASS_CACHE = 8,
DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
DNS_QUERY_NO_HOSTS_FILE = 0x40,
DNS_QUERY_NO_LOCAL_NAME = 0x20,
DNS_QUERY_NO_NETBT = 0x80,
DNS_QUERY_NO_RECURSION = 4,
DNS_QUERY_NO_WIRE_QUERY = 0x10,
DNS_QUERY_RESERVED = -16777216,
DNS_QUERY_RETURN_MESSAGE = 0x200,
DNS_QUERY_STANDARD = 0,
DNS_QUERY_TREAT_AS_FQDN = 0x1000,
DNS_QUERY_USE_TCP_ONLY = 2,
DNS_QUERY_WIRE_ONLY = 0x100
}
private enum NativeDnsQueryTypes
{
DNS_TYPE_TEXT = 0x0010,
DNS_TYPE_SRV = 0x0021
}
[StructLayout(LayoutKind.Sequential)]
private struct NativeDnsSrvRecord
{
public IntPtr pNext;
[MarshalAs(UnmanagedType.LPWStr)]
public string pName;
public ushort wType;
public ushort wDataLength;
public int flags;
public int dwTtl;
public int dwReserved;
[MarshalAs(UnmanagedType.LPWStr)]
public string pNameTarget;
public ushort wPriority;
public ushort wWeight;
public ushort wPort;
public ushort Pad;
}
private class DnsSrvRecord
{
public string Name { get; set; }
public int Type { get; set; }
public int Ttl { get; set; }
public string Target { get; set; }
public int Priority { get; set; }
public int Weight { get; set; }
public int Port { get; set; }
public static DnsSrvRecord FromNativeRecord(NativeDnsSrvRecord nativeRecord)
{
return new DnsSrvRecord
{
Name = nativeRecord.pName,
Type = nativeRecord.wType,
Ttl = nativeRecord.dwTtl,
Target = nativeRecord.pNameTarget,
Priority = nativeRecord.wPriority,
Weight = nativeRecord.wWeight,
Port = nativeRecord.wPort
};
}
}
[StructLayout(LayoutKind.Sequential)]
private struct NativeDnsTxtRecord
{
public IntPtr pNext;
[MarshalAs(UnmanagedType.LPWStr)]
public string pName;
public ushort wType;
public ushort wDataLength;
public int flags;
public int dwTtl;
public int dwReserved;
public uint dwStringLength;
[MarshalAs(UnmanagedType.LPWStr)]
public string pStringArray;
}
private class DnsTxtRecord
{
public string Name { get; set; }
public int Type { get; set; }
public int Ttl { get; set; }
public string Content { get; set; }
public static DnsTxtRecord FromNativeRecord(NativeDnsTxtRecord nativeRecord)
{
return new DnsTxtRecord
{
Name = nativeRecord.pName,
Type = nativeRecord.wType,
Ttl = nativeRecord.dwTtl,
Content = nativeRecord.pStringArray,
};
}
}
}
}
+702
View File
@@ -0,0 +1,702 @@
using Disco.Models.ClientServices;
using Disco.Models.ClientServices.EnrolmentInformation;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management;
namespace Disco.Client.Interop
{
public static class Hardware
{
private static DeviceHardware information;
public static DeviceHardware Information
{
get
{
if (information == null)
{
information = GatherInformation();
}
return information;
}
}
private static DeviceHardware GatherInformation()
{
var audit = new DeviceHardware();
audit.ApplyBIOSInformation();
audit.ApplySystemInformation();
audit.ApplyBaseBoardInformation();
audit.ApplySystemProductInformation();
if (string.IsNullOrWhiteSpace(audit.SerialNumber))
{
throw new Exception("This device has no serial number stored in BIOS or BaseBoard");
}
if (audit.SerialNumber.Length > 60)
{
throw new Exception($"The serial number reported by this device is over 60 characters long:\r\n{audit.SerialNumber}");
}
audit.ApplyProcessorInformation();
audit.ApplyPhysicalMemoryInformation();
audit.ApplyDiskDriveInformation();
audit.ApplyBatteryInformation();
audit.ApplyMobileDeviceManagementInformation();
audit.NetworkAdapters = Network.GetNetworkAdapters();
return audit;
}
private static void ApplyProcessorInformation(this DeviceHardware deviceHardware)
{
try
{
using (var mSearcher = new ManagementObjectSearcher("SELECT DeviceID, Manufacturer, Name, Description, Architecture, Family, MaxClockSpeed, NumberOfCores, NumberOfLogicalProcessors FROM Win32_Processor"))
{
using (var mResults = mSearcher.Get())
{
if (mResults.Count > 0)
{
var processors = new List<Processor>(mResults.Count);
foreach (var mItem in mResults.Cast<ManagementObject>())
{
if (mItem != null)
{
var processor = new Processor();
processor.DeviceID = (string)mItem.GetPropertyValue("DeviceID");
processor.Manufacturer = (string)mItem.GetPropertyValue("Manufacturer");
processor.Name = (string)mItem.GetPropertyValue("Name");
processor.Description = (string)mItem.GetPropertyValue("Description");
processor.Family = (ushort?)mItem.GetPropertyValue("Family");
processor.MaxClockSpeed = (uint?)mItem.GetPropertyValue("MaxClockSpeed");
processor.NumberOfCores = (uint?)mItem.GetPropertyValue("NumberOfCores");
processor.NumberOfLogicalProcessors = (uint?)mItem.GetPropertyValue("NumberOfLogicalProcessors");
processor.Architecture = ((ProcessorArchitectures?)(ushort?)mItem.GetPropertyValue("Architecture"))?.ToString();
processors.Add(processor);
}
}
deviceHardware.Processors = processors;
}
}
}
}
catch (Exception)
{
// ignore errors to ensure backwards compatibility
}
}
private static void ApplyPhysicalMemoryInformation(this DeviceHardware deviceHardware)
{
try
{
using (var mSearcher = new ManagementObjectSearcher("SELECT Tag, SerialNumber, Manufacturer, PartNumber, Capacity, ConfiguredClockSpeed, Speed, DeviceLocator FROM Win32_PhysicalMemory"))
{
using (var mResults = mSearcher.Get())
{
if (mResults.Count > 0)
{
var physicalMemories = new List<PhysicalMemory>(mResults.Count);
foreach (var mItem in mResults.Cast<ManagementObject>())
{
if (mItem != null)
{
var physicalMemory = new PhysicalMemory();
physicalMemory.Tag = (string)mItem.GetPropertyValue("Tag");
physicalMemory.SerialNumber = (string)mItem.GetPropertyValue("SerialNumber");
physicalMemory.Manufacturer = (string)mItem.GetPropertyValue("Manufacturer");
physicalMemory.PartNumber = (string)mItem.GetPropertyValue("PartNumber");
physicalMemory.Capacity = (ulong?)mItem.GetPropertyValue("Capacity");
physicalMemory.ConfiguredClockSpeed = (uint?)mItem.GetPropertyValue("ConfiguredClockSpeed");
physicalMemory.Speed = (uint?)mItem.GetPropertyValue("Speed");
physicalMemory.DeviceLocator = (string)mItem.GetPropertyValue("DeviceLocator");
physicalMemories.Add(physicalMemory);
}
}
deviceHardware.PhysicalMemory = physicalMemories;
}
}
}
}
catch (Exception)
{
// ignore errors to ensure backwards compatibility
}
}
private static void ApplyDiskDriveInformation(this DeviceHardware deviceHardware)
{
try
{
using (var diskSearcher = new ManagementObjectSearcher("SELECT DeviceID, Manufacturer, Model, MediaType, InterfaceType, SerialNumber, FirmwareRevision, Size FROM Win32_DiskDrive"))
{
using (var diskResults = diskSearcher.Get())
{
if (diskResults.Count > 0)
{
var diskDrives = new List<DiskDrive>(diskResults.Count);
foreach (var diskResult in diskResults.Cast<ManagementObject>())
{
if (diskResult != null)
{
var diskDrive = new DiskDrive();
diskDrive.DeviceID = (string)diskResult.GetPropertyValue("DeviceID");
diskDrive.Manufacturer = (string)diskResult.GetPropertyValue("Manufacturer");
diskDrive.Model = (string)diskResult.GetPropertyValue("Model");
diskDrive.MediaType = (string)diskResult.GetPropertyValue("MediaType");
diskDrive.InterfaceType = (string)diskResult.GetPropertyValue("InterfaceType");
diskDrive.SerialNumber = (string)diskResult.GetPropertyValue("SerialNumber");
diskDrive.FirmwareRevision = (string)diskResult.GetPropertyValue("FirmwareRevision");
diskDrive.Size = (ulong?)diskResult.GetPropertyValue("Size");
using (var partitionSearcher = new ManagementObjectSearcher($"ASSOCIATORS OF {{Win32_DiskDrive.DeviceID=\"{diskDrive.DeviceID.Replace(@"\", @"\\")}\"}} WHERE AssocClass = Win32_DiskDriveToDiskPartition"))
{
using (var partitionResults = partitionSearcher.Get())
{
if (partitionResults.Count > 0)
{
var partitions = new List<DiskDrivePartition>(partitionResults.Count);
foreach (var partitionResult in partitionResults.Cast<ManagementObject>())
{
if (partitionResult != null)
{
var partition = new DiskDrivePartition();
partition.DeviceID = (string)partitionResult.GetPropertyValue("DeviceID");
partition.Bootable = (bool?)partitionResult.GetPropertyValue("Bootable");
partition.BootPartition = (bool?)partitionResult.GetPropertyValue("BootPartition");
partition.PrimaryParition = (bool?)partitionResult.GetPropertyValue("PrimaryPartition");
partition.Size = (ulong?)partitionResult.GetPropertyValue("Size");
partition.StartingOffset = (ulong?)partitionResult.GetPropertyValue("StartingOffset");
partition.Type = (string)partitionResult.GetPropertyValue("Type");
using (var logicalSearcher = new ManagementObjectSearcher($"ASSOCIATORS OF {{Win32_DiskPartition.DeviceID=\"{partition.DeviceID}\"}} WHERE AssocClass = Win32_LogicalDiskToPartition"))
{
using (var logicalResults = logicalSearcher.Get())
{
if (logicalResults.Count > 0)
{
foreach (var logicalResult in logicalResults.Cast<ManagementObject>().Take(1))
{
if (logicalResult != null)
{
var logical = new DiskLogical();
logical.DeviceID = (string)logicalResult.GetPropertyValue("DeviceID");
logical.Description = (string)logicalResult.GetPropertyValue("Description");
logical.DriveType = ((DiskLogicalDriveTypes?)(uint?)logicalResult.GetPropertyValue("DriveType")).ToString();
logical.MediaType = ((DiskLogicalMediaTypes?)(uint?)logicalResult.GetPropertyValue("MediaType")).ToString();
logical.FileSystem = (string)logicalResult.GetPropertyValue("FileSystem");
logical.Size = (ulong?)logicalResult.GetPropertyValue("Size");
logical.FreeSpace = (ulong?)logicalResult.GetPropertyValue("FreeSpace");
logical.VolumeName = (string)logicalResult.GetPropertyValue("VolumeName");
logical.VolumeSerialNumber = (string)logicalResult.GetPropertyValue("VolumeSerialNumber");
partition.LogicalDisk = logical;
}
}
}
}
}
partitions.Add(partition);
}
}
diskDrive.Partitions = partitions;
}
}
}
diskDrives.Add(diskDrive);
}
}
deviceHardware.DiskDrives = diskDrives;
}
}
}
}
catch (Exception)
{
// ignore errors to ensure backwards compatibility
}
}
private static void ApplyBatteryInformation(this DeviceHardware deviceHardware)
{
try
{
using (var mSearcher = new ManagementObjectSearcher("SELECT DeviceID, Availability, Chemistry, Description, DesignCapacity, DesignVoltage, FullChargeCapacity, Name FROM Win32_Battery"))
{
using (var mResults = mSearcher.Get())
{
if (mResults.Count > 0)
{
var batteries = new List<Battery>(mResults.Count);
foreach (var mItem in mResults.Cast<ManagementObject>())
{
if (mItem != null)
{
var battery = new Battery();
battery.Availability = ((BatteryAvailability?)(ushort?)mItem.GetPropertyValue("Availability"))?.ToString();
battery.Chemistry = ((BatteryChemistry?)(ushort?)mItem.GetPropertyValue("Chemistry"))?.ToString();
battery.Description = (string)mItem.GetPropertyValue("Description");
battery.DesignCapacity = (uint?)mItem.GetPropertyValue("DesignCapacity");
battery.DeviceID = (string)mItem.GetPropertyValue("DeviceID");
battery.FullChargeCapacity = (uint?)mItem.GetPropertyValue("FullChargeCapacity");
battery.Name = (string)mItem.GetPropertyValue("Name");
batteries.Add(battery);
}
}
deviceHardware.Batteries = batteries;
}
}
}
}
catch (Exception)
{
// ignore errors to ensure backwards compatibility
}
}
private static void ApplyBIOSInformation(this DeviceHardware deviceHardware)
{
try
{
using (var mSearcher = new ManagementObjectSearcher("SELECT BIOSVersion, Manufacturer, ReleaseDate, SerialNumber, SMBIOSBIOSVersion, SMBIOSMajorVersion, SMBIOSMinorVersion, SystemBiosMajorVersion, SystemBiosMinorVersion FROM Win32_BIOS WHERE PrimaryBios=true"))
{
using (var mResults = mSearcher.Get())
{
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
{
if (mItem != null)
{
var serialNumber = (string)mItem.GetPropertyValue("SerialNumber");
if (!string.IsNullOrWhiteSpace(serialNumber))
deviceHardware.SerialNumber = serialNumber.Trim();
var manufacturer = (string)mItem.GetPropertyValue("Manufacturer");
if (deviceHardware.Manufacturer == null && !string.IsNullOrWhiteSpace(manufacturer))
deviceHardware.Manufacturer = manufacturer.Trim();
ErrorReporting.DeviceIdentifier = deviceHardware.SerialNumber;
var biosVersion = (string[])mItem.GetPropertyValue("BIOSVersion");
var releaseDate = default(DateTime?);
var releaseDateString = (string)mItem.GetPropertyValue("ReleaseDate");
if (releaseDateString != null && releaseDateString.Length == 25 && int.TryParse(releaseDateString.Substring(22), out var offsetMinutes))
{
releaseDateString = $"{releaseDateString.Substring(0, 22)}{offsetMinutes / 60:00}:{Math.Abs(offsetMinutes % 60):00}";
if (DateTime.TryParseExact(releaseDateString, "yyyyMMddHHmmss.ffffffzzz", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var releaseDateResult))
releaseDate = releaseDateResult;
}
var sMBIOSBIOSVersion = (string)mItem.GetPropertyValue("SMBIOSBIOSVersion");
var sMBIOSMajorVersion = (ushort?)mItem.GetPropertyValue("SMBIOSMajorVersion");
var sMBIOSMinorVersion = (ushort?)mItem.GetPropertyValue("SMBIOSMinorVersion");
var systemBiosMajorVersion = (byte?)mItem.GetPropertyValue("SystemBiosMajorVersion");
var systemBiosMinorVersion = (byte?)mItem.GetPropertyValue("SystemBiosMinorVersion");
var bios = new Bios()
{
BIOSVersion = biosVersion,
Manufacturer = manufacturer,
ReleaseDate = releaseDate,
SerialNumber = serialNumber,
SMBIOSBIOSVersion = sMBIOSBIOSVersion,
SMBIOSMajorVersion = sMBIOSMajorVersion,
SMBIOSMinorVersion = sMBIOSMinorVersion,
SystemBiosMajorVersion = systemBiosMajorVersion,
SystemBiosMinorVersion = systemBiosMinorVersion,
};
deviceHardware.Bios = new List<Bios>() { bios };
}
else
{
throw new Exception("No Win32_BIOS WHERE PrimaryBios=true was found");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve BIOS information from WMI", ex);
}
}
private static void ApplySystemInformation(this DeviceHardware deviceHardware)
{
try
{
using (var mSearcher = new ManagementObjectSearcher("SELECT ChassisSKUNumber, CurrentTimeZone, Description, Manufacturer, Model, OEMStringArray, PCSystemType, PrimaryOwnerContact, PrimaryOwnerName, Roles, SystemSKUNumber, SystemType FROM Win32_ComputerSystem"))
{
using (var mResults = mSearcher.Get())
{
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
{
if (mItem != null)
{
var manufacturer = (string)mItem.GetPropertyValue("Manufacturer");
if (!string.IsNullOrWhiteSpace(manufacturer))
deviceHardware.Manufacturer = manufacturer.Trim();
var model = (string)mItem.GetPropertyValue("Model");
if (!string.IsNullOrWhiteSpace(model))
deviceHardware.Model = model;
deviceHardware.ModelType = ((PCSystemTypes)mItem.GetPropertyValue("PCSystemType")).Description();
var chassisSKUNumber = (string)mItem.GetPropertyValue("ChassisSKUNumber");
var currentTimeZone = (short?)mItem.GetPropertyValue("CurrentTimeZone");
var description = (string)mItem.GetPropertyValue("Description");
var oemStringArray = (string[])mItem.GetPropertyValue("OEMStringArray");
var pcSystemType = ((PCSystemTypes)mItem.GetPropertyValue("PCSystemType")).ToString();
var primaryOwnerContact = (string)mItem.GetPropertyValue("PrimaryOwnerContact");
var primaryOwnerName = (string)mItem.GetPropertyValue("PrimaryOwnerName");
var roles = (string[])mItem.GetPropertyValue("Roles");
var systemSKUNumber = (string)mItem.GetPropertyValue("SystemSKUNumber");
var systemType = (string)mItem.GetPropertyValue("SystemType");
var computerSystem = new ComputerSystem()
{
ChassisSKUNumber = chassisSKUNumber,
CurrentTimeZone = currentTimeZone,
Description = description,
OEMStringArray = oemStringArray,
PCSystemType = pcSystemType,
PrimaryOwnerContact = primaryOwnerContact,
PrimaryOwnerName = primaryOwnerName,
Roles = roles,
SystemSKUNumber = systemSKUNumber,
SystemType = systemType,
};
deviceHardware.ComputerSystem = new List<ComputerSystem>() { computerSystem };
}
else
{
throw new Exception("No Win32_ComputerSystem was found");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve ComputerSystem information from WMI", ex);
}
}
public static void ApplySystemInformation(this Enrol enrol)
{
try
{
using (var mSearcher = new ManagementObjectSearcher("SELECT PartOfDomain, Domain FROM Win32_ComputerSystem"))
{
using (var mResults = mSearcher.Get())
{
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
{
if (mItem != null)
{
enrol.IsPartOfDomain = (bool)mItem.GetPropertyValue("PartOfDomain");
if (enrol.IsPartOfDomain)
{
enrol.DNSDomainName = (string)mItem.GetPropertyValue("Domain");
}
}
else
{
throw new Exception("No Win32_ComputerSystem was found");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve ComputerSystem information from WMI", ex);
}
}
private static void ApplyBaseBoardInformation(this DeviceHardware deviceHardware)
{
try
{
using (var mSearcher = new ManagementObjectSearcher("SELECT ConfigOptions, Manufacturer, Model, PartNumber, Product, SerialNumber, SKU, Version FROM Win32_BaseBoard"))
{
using (var mResults = mSearcher.Get())
{
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
{
if (mItem != null)
{
// Apply Manufacturer/Model information only if not previously found in Win32_ComputerSystem
var manufacturer = (string)mItem.GetPropertyValue("Manufacturer");
if (deviceHardware.Manufacturer == null && !string.IsNullOrWhiteSpace(manufacturer))
{
deviceHardware.Manufacturer = manufacturer.Trim();
}
var model = (string)mItem.GetPropertyValue("Model");
if (deviceHardware.Model == null && !string.IsNullOrWhiteSpace(model))
{
deviceHardware.Model = model;
}
var product = (string)mItem.GetPropertyValue("Product");
if (deviceHardware.Model == null && !string.IsNullOrWhiteSpace(product))
{
deviceHardware.Model = product;
}
// Added 2012-11-22 G# - Lenovo IdeaPad Serial SHIM
// http://www.discoict.com.au/forum/feature-requests/2012/11/serial-number-detection-on-ideapads.aspx
var serialNumber = (string)mItem.GetPropertyValue("SerialNumber");
if (!string.IsNullOrWhiteSpace(serialNumber) &&
(deviceHardware.SerialNumber == null ||
((deviceHardware.Manufacturer?.Equals("LENOVO", StringComparison.OrdinalIgnoreCase) ?? false) &&
((deviceHardware.Model?.Equals("S10-3", StringComparison.OrdinalIgnoreCase) ?? false) // S10-3
|| (deviceHardware.Model?.Equals("2957", StringComparison.OrdinalIgnoreCase) ?? false)))))
{
deviceHardware.SerialNumber = serialNumber.Trim();
ErrorReporting.DeviceIdentifier = deviceHardware.SerialNumber;
}
var configOptions = (string[])mItem.GetPropertyValue("ConfigOptions");
var partNumber = (string)mItem.GetPropertyValue("PartNumber");
var sku = (string)mItem.GetPropertyValue("SKU");
var version = (string)mItem.GetPropertyValue("Version");
var baseBoard = new BaseBoard()
{
ConfigOptions = configOptions,
Manufacturer = manufacturer,
Model = model,
PartNumber = partNumber,
Product = product,
SerialNumber = serialNumber,
SKU = sku,
Version = version,
};
deviceHardware.BasebBoard = new List<BaseBoard>() { baseBoard };
}
else
{
throw new Exception("No Win32_BaseBoard was found");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve BaseBoard information from WMI", ex);
}
}
private static void ApplySystemProductInformation(this DeviceHardware deviceHardware)
{
try
{
using (var mSearcher = new ManagementObjectSearcher("SELECT IdentifyingNumber, UUID FROM Win32_ComputerSystemProduct"))
{
using (var mResults = mSearcher.Get())
{
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
{
if (mItem != null)
{
if (deviceHardware.SerialNumber == null)
{
var serialNumber = mItem.GetPropertyValue("IdentifyingNumber") as string;
if (!string.IsNullOrWhiteSpace(serialNumber))
{
deviceHardware.SerialNumber = serialNumber.Trim();
ErrorReporting.DeviceIdentifier = deviceHardware.SerialNumber;
}
}
var uUID = (string)mItem.GetPropertyValue("UUID");
if (!string.IsNullOrWhiteSpace(uUID))
{
deviceHardware.UUID = uUID.Trim();
// if serial number is absent attempt using UUID if valid
if (string.IsNullOrWhiteSpace(deviceHardware.SerialNumber))
{
if (Guid.TryParse(deviceHardware.UUID, out var uuidGuid) && uuidGuid != Guid.Empty)
{
deviceHardware.SerialNumber = $"UUID{uuidGuid:N}";
}
}
}
}
else
{
throw new Exception("No Win32_ComputerSystemProduct was found");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve ComputerSystemProduct information from WMI", ex);
}
}
private static void ApplyMobileDeviceManagementInformation(this DeviceHardware deviceHardware)
{
try
{
using (var wmiObject = new ManagementObject(@"\\.\ROOT\CIMV2\mdm\dmmap:MDM_DevDetail_Ext01.InstanceID=""Ext"",ParentID=""./DevDetail"""))
{
deviceHardware.MdmHardwareData = (string)wmiObject.GetPropertyValue("DeviceHardwareData");
}
}
catch (Exception) { }
}
private static string Description(this PCSystemTypes type)
{
switch (type)
{
case PCSystemTypes.Desktop:
return "Desktop";
case PCSystemTypes.Mobile:
return "Mobile";
case PCSystemTypes.Workstation:
return "Workstation";
case PCSystemTypes.EnterpriseServer:
return "Enterprise Server";
case PCSystemTypes.SmallOfficeAndHomeOfficeServer:
return "Small Office And Home Office Server";
case PCSystemTypes.AppliancePC:
return "Appliance PC";
case PCSystemTypes.PerformanceServer:
return "Performance Server";
case PCSystemTypes.Maximum:
return "Maximum";
case PCSystemTypes.Unknown:
default:
return "Unknown";
}
}
private enum PCSystemTypes : ushort
{
Unknown = 0,
Desktop,
Mobile,
Workstation,
EnterpriseServer,
SmallOfficeAndHomeOfficeServer,
AppliancePC,
PerformanceServer,
Maximum
}
private enum ProcessorArchitectures : ushort
{
x86 = 0,
MIPS = 1,
Alpha = 2,
PowerPC = 3,
ia64 = 6,
x64 = 9,
}
private enum DiskLogicalDriveTypes : uint
{
Unknown = 0,
NoRootDirectory,
Removable,
Fixed,
Remote,
CDRom,
RAMDisk,
}
private enum DiskLogicalMediaTypes : uint
{
Unknown = 0,
F5_1Pt2_512,
F3_1Pt44_512,
F3_2Pt88_512,
F3_20Pt8_512,
F3_720_512,
F5_360_512,
F5_320_512,
F5_320_1024,
F5_180_512,
F5_160_512,
RemovableMedia,
FixedMedia,
F3_120M_512,
F3_640_512,
F5_640_512,
F5_720_512,
F3_1Pt2_512,
F3_1Pt23_1024,
F5_1Pt23_1024,
F3_128Mb_512,
F3_230Mb_512,
F8_256_128,
F3_200Mb_512,
F3_240M_512,
F3_32M_512,
}
private enum BatteryAvailability : ushort
{
Other = 1,
Unknown = 2,
RunningFullPower = 3,
Warning = 4,
InTest = 5,
NotApplicable = 6,
PowerOff = 7,
OffLine = 8,
OffDuty = 9,
Degraded = 10,
NotInstalled = 11,
InstallError = 12,
PowerSaveUnknown = 13,
PowerSaveLowPowerMode = 14,
PowerSaveStandby = 15,
PowerCycle = 16,
PowerSaveWarning = 17,
Paused = 18,
NotReady = 19,
NotConfigured = 20,
Quiesced = 21,
}
private enum BatteryChemistry : ushort
{
Other = 1,
Unknown = 2,
LeadAcid = 3,
NickelCadmium = 4,
NickelMetalHydride = 5,
LithiumIon = 6,
ZincAir = 7,
LithiumPolymer = 8,
}
}
}
+4 -31
View File
@@ -1,10 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.DirectoryServices; using System.DirectoryServices;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Client.Interop namespace Disco.Client.Interop
{ {
@@ -14,7 +10,7 @@ namespace Disco.Client.Interop
public static bool AddLocalGroupMembership(string GroupName, string UserSID, string Username, string UserDomain) public static bool AddLocalGroupMembership(string GroupName, string UserSID, string Username, string UserDomain)
{ {
using (DirectoryEntry group = new DirectoryEntry(string.Format("WinNT://./{0},group", GroupName))) using (DirectoryEntry group = new DirectoryEntry($"WinNT://./{GroupName},group"))
{ {
// Check to see if the User is already a member // Check to see if the User is already a member
foreach (object memberRef in (IEnumerable)group.Invoke("Members")) foreach (object memberRef in (IEnumerable)group.Invoke("Members"))
@@ -22,38 +18,15 @@ namespace Disco.Client.Interop
using (DirectoryEntry member = new DirectoryEntry(memberRef)) using (DirectoryEntry member = new DirectoryEntry(memberRef))
{ {
var memberPath = member.Path; var memberPath = member.Path;
if (memberPath.Equals(string.Format("WinNT://{0}/{1}", UserDomain, Username), StringComparison.OrdinalIgnoreCase) || if (memberPath.Equals($"WinNT://{UserDomain}/{Username}", StringComparison.OrdinalIgnoreCase) ||
memberPath.Equals(string.Format("WinNT://{0}", UserSID), StringComparison.OrdinalIgnoreCase)) memberPath.Equals($"WinNT://{UserSID}", StringComparison.OrdinalIgnoreCase))
return false; return false;
} }
} }
group.Invoke("Add", string.Format("WinNT://{0}", UserSID)); group.Invoke("Add", $"WinNT://{UserSID}");
} }
return true; return true;
} }
public static string CurrentUserDomain
{
get
{
return Environment.UserDomainName;
}
}
public static string CurrentUserName
{
get
{
return Environment.UserName;
}
}
public static string ComputerName
{
get
{
return Environment.MachineName;
}
}
} }
} }
@@ -0,0 +1,19 @@
namespace Disco.Client.Interop.Native
{
public enum NetworkConnectionStatuses : ushort
{
Disconnected = 0,
Connecting,
Connected,
Disconnecting,
HardwareNotPresent,
HardwareDisabled,
HardwareMalfunction,
MediaDisconnected,
Authenticating,
AuthenticationSucceeded,
AuthenticationFailed,
InvalidAddress,
CredentialsRequired
}
}
@@ -0,0 +1,11 @@
using System;
namespace Disco.Client.Interop.Native
{
[Flags]
public enum ProfileInfoFlags : uint
{
WLAN_PROFILE_GROUP_POLICY = 1,
WLAN_PROFILE_USER = 2
}
}
@@ -0,0 +1,30 @@
using System;
using System.Runtime.InteropServices;
namespace Disco.Client.Interop.Native
{
/// <summary >
/// The WLAN_INTERFACE_INFO structure contains information about a wireless LAN interface.
/// </summary >
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WLAN_INTERFACE_INFO
{
/// <summary>
/// Contains the GUID of the interface.
/// </summary>
public Guid InterfaceGuid;
/// <summary>
/// Contains the description of the interface.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string strInterfaceDescription;
/// <summary>
/// Contains a WLAN_INTERFACE_STATE value that indicates the current state of the interface.
/// Windows XP with SP3 and Wireless LAN API for Windows XP with SP2:  Only the wlan_interface_state_connected,
/// wlan_interface_state_disconnected, and wlan_interface_state_authenticating values are supported.
/// </summary>
public WLAN_INTERFACE_STATE isState;
}
}
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Disco.Client.Interop.Native
{
/// <summary>
/// The WLAN_INTERFACE_INFO_LIST structure contains an array of NIC interface information.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct WLAN_INTERFACE_INFO_LIST
{
/// <summary>
/// Contains the number of items in the InterfaceInfo member.
/// </summary>
public uint dwNumberOfItems;
/// <summary>
/// The index of the current item. The index of the first item is 0. dwIndex must be less than dwNumberOfItems.
/// This member is not used by the wireless service. Applications can use this member when processing individual
/// interfaces in the WLAN_INTERFACE_INFO_LIST structure. When an application passes this structure from one
/// function to another, it can set the value of dwIndex to the index of the item currently being processed.
/// This can help an application maintain state.
/// dwIndex should always be initialized before use.
/// </summary>
public uint dwIndex;
private IntPtr InterfaceInfoPtr;
/// <summary>
/// An array of WLAN_INTERFACE_INFO structures containing interface information.
/// </summary>
public IEnumerable<WLAN_INTERFACE_INFO> InterfaceInfo
{
get
{
var size = Marshal.SizeOf(typeof(WLAN_INTERFACE_INFO));
for (int i = 0; i < dwNumberOfItems; i++)
{
yield return (WLAN_INTERFACE_INFO)Marshal.PtrToStructure(InterfaceInfoPtr + (i * size), typeof(WLAN_INTERFACE_INFO));
}
}
}
public WLAN_INTERFACE_INFO_LIST(IntPtr Pointer)
{
dwNumberOfItems = (uint)Marshal.ReadInt32(Pointer, 0);
dwIndex = (uint)Marshal.ReadInt32(Pointer, 4);
InterfaceInfoPtr = Pointer + 8;
}
}
}
@@ -0,0 +1,43 @@
namespace Disco.Client.Interop.Native
{
/// <summary>
/// The WLAN_INTERFACE_STATE enumerated type indicates the state of an interface.
/// Windows XP with SP3 and Wireless LAN API for Windows XP with SP2:  Only the wlan_interface_state_connected,
/// wlan_interface_state_disconnected, and wlan_interface_state_authenticating values are supported.
/// </summary>
public enum WLAN_INTERFACE_STATE
{
/// <summary>
/// The interface is not ready to operate.
/// </summary>
wlan_interface_state_not_ready = 0,
/// <summary>
/// The interface is connected to a network.
/// </summary>
wlan_interface_state_connected = 1,
/// <summary>
/// The interface is the first node in an ad hoc network. No peer has connected.
/// </summary>
wlan_interface_state_ad_hoc_network_formed = 2,
/// <summary>
/// The interface is disconnecting from the current network.
/// </summary>
wlan_interface_state_disconnecting = 3,
/// <summary>
/// The interface is not connected to any network.
/// </summary>
wlan_interface_state_disconnected = 4,
/// <summary>
/// The interface is attempting to associate with a network.
/// </summary>
wlan_interface_state_associating = 5,
/// <summary>
/// Auto configuration is discovering the settings for the network.
/// </summary>
wlan_interface_state_discovering = 6,
/// <summary>
/// The interface is in the process of authenticating.
/// </summary>
wlan_interface_state_authenticating = 7,
}
}
@@ -0,0 +1,24 @@
using System.Runtime.InteropServices;
namespace Disco.Client.Interop.Native
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WLAN_PROFILE_INFO
{
/// <summary>
/// The name of the profile. This value may be the name of a domain if the profile is for provisioning. Profile names are case-sensitive.
/// This string must be NULL-terminated.
/// Windows XP with SP3 and Wireless LAN API for Windows XP with SP2:  The name of the profile is derived automatically from
/// the SSID of the wireless network. For infrastructure network profiles, the name of the profile is the SSID of the network.
/// For ad hoc network profiles, the name of the profile is the SSID of the ad hoc network followed by -adhoc.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string strInterfaceDescription;
/// <summary>
/// A set of flags specifying settings for wireless profile. These values are defined in the Wlanapi.h header file.
/// Windows XP with SP3 and Wireless LAN API for Windows XP with SP2:  dwFlags must be 0. Per-user profiles are not supported.
/// </summary>
public ProfileInfoFlags dwFlags;
}
}
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Disco.Client.Interop.Native
{
public struct WLAN_PROFILE_INFO_LIST
{
/// <summary>
/// The number of wireless profile entries in the ProfileInfo member.
/// </summary>
public uint dwNumberOfItems;
/// <summary>
/// The index of the current item. The index of the first item is 0. The dwIndex member must be less than the dwNumberOfItems member.
/// This member is not used by the wireless service. Applications can use this member when processing individual profiles in the
/// WLAN_PROFILE_INFO_LIST structure. When an application passes this structure from one function to another, it can set the value
/// of dwIndex to the index of the item currently being processed. This can help an application maintain state.
/// dwIndex should always be initialized before use.
/// </summary>
public uint dwIndex;
private IntPtr ProfileInfoPointer;
/// <summary>
/// An array of WLAN_PROFILE_INFO structures containing interface information. The number of items in the array is specified in the dwNumberOfItems member.
/// </summary>
public IEnumerable<WLAN_PROFILE_INFO> ProfileInfo
{
get
{
var size = Marshal.SizeOf(typeof(WLAN_PROFILE_INFO));
for (int i = 0; i < dwNumberOfItems; i++)
{
yield return (WLAN_PROFILE_INFO)Marshal.PtrToStructure(ProfileInfoPointer + (i * size), typeof(WLAN_PROFILE_INFO));
}
}
}
public WLAN_PROFILE_INFO_LIST(IntPtr Pointer)
{
dwNumberOfItems = (uint)Marshal.ReadInt32(Pointer, 0);
dwIndex = (uint)Marshal.ReadInt32(Pointer, 4);
ProfileInfoPointer = Pointer + 8;
}
}
}
+208
View File
@@ -0,0 +1,208 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Disco.Client.Interop.Native
{
public static class WlanApi
{
public const uint WLAN_API_VERSION_2_0 = 2; // Windows Vista WiFi API Version
public const int ERROR_SUCCESS = 0;
public const int ERROR_INVALID_PARAMETER = 87;
public const int ERROR_NOT_ENOUGH_MEMORY = 8;
public const int ERROR_SERVICE_NOT_ACTIVE = 1062; // The service has not been started.
/// <summary>
/// The WlanOpenHandle function opens a connection to the server.
/// </summary>
/// <param name="dwClientVersion">The highest version of the WLAN API that the client supports.
/// 1 = Client version for Windows XP with SP3 and Wireless LAN API for Windows XP with SP2.
/// 2 = Client version for Windows Vista and Windows Server 2008</param>
/// <param name="pReserved">Reserved for future use. Must be set to NULL.</param>
/// <param name="pdwNegotiatedVersion">The version of the WLAN API that will be used in this session. This value is usually the highest version supported by both the client and server.</param>
/// <param name="phClientHandle">A handle for the client to use in this session. This handle is used by other functions throughout the session.</param>
/// <returns>
/// If the function succeeds, the return value is ERROR_SUCCESS.
/// If the function fails, the return value may be one of the following return codes.
/// ERROR_INVALID_PARAMETER: pdwNegotiatedVersion is NULL, phClientHandle is NULL, or pReserved is not NULL.
/// ERROR_NOT_ENOUGH_MEMORY: Failed to allocate memory to create the client context.
/// RPC_STATUS: Various error codes.
/// ERROR_REMOTE_SESSION_LIMIT_EXCEEDED: Too many handles have been issued by the server.
/// </returns>
/// <remarks>
/// The version number specified by dwClientVersion and pdwNegotiatedVersion is a composite version number
/// made up of both major and minor versions. The major version is specified by the low-order word, and the
/// minor version is specified by the high-order word. The macros WLAN_API_VERSION_MAJOR(_v) and
/// WLAN_API_VERSION_MINOR(_v) return the major and minor version numbers respectively.
/// You can construct a version number using the macro WLAN_API_MAKE_VERSION(_major, _minor).
/// Windows XP with SP3 and Wireless LAN API for Windows XP with SP2:  WlanOpenHandle will return an
/// error message if the Wireless Zero Configuration (WZC) service has not been started or if the WZC service is not responsive.
/// </remarks>
[DllImport("Wlanapi.dll", SetLastError = true)]
public static extern uint WlanOpenHandle(uint dwClientVersion, IntPtr pReserved, out uint pdwNegotiatedVersion, out IntPtr phClientHandle);
/// <summary>
/// The WlanCloseHandle function closes a connection to the server.
/// </summary>
/// <param name="hClientHandle">The client's session handle, which identifies the connection to be closed. This handle was obtained by a previous call to the WlanOpenHandle function.</param>
/// <param name="pReserved">Reserved for future use. Set this parameter to NULL.</param>
/// <returns>
/// If the function succeeds, the return value is ERROR_SUCCESS.
/// If the function fails, the return value may be one of the following return codes.
/// ERROR_INVALID_PARAMETER: hClientHandle is NULL or invalid, or pReserved is not NULL.
/// ERROR_INVALID_HANDLE: The handle hClientHandle was not found in the handle table.
/// RPC_STATUS: Various error codes.
/// </returns>
/// <remarks>
/// After a connection has been closed, any attempted use of the closed handle can cause unexpected errors.
/// Upon closing, all outstanding notifications are discarded.
/// Do not call WlanCloseHandle from a callback function. If the client is in the middle of a
/// notification callback when WlanCloseHandle is called, the function waits for the callback to
/// finish before returning a value. Calling this function inside a callback function will result in
/// the call never completing. If both the callback function and the thread that closes the handle try
/// to acquire the same lock, a deadlock may occur. In addition, do not call WlanCloseHandle from
/// the DllMain function in an application DLL. This could also cause a deadlock.
/// </remarks>
[DllImport("Wlanapi", SetLastError = true)]
public static extern uint WlanCloseHandle(IntPtr hClientHandle, IntPtr pReserved);
/// <summary>
/// The WlanEnumInterfaces function enumerates all of the wireless LAN interfaces currently enabled on the local computer.
/// </summary>
/// <param name="hClientHandle">The client's session handle, obtained by a previous call to the WlanOpenHandle function.</param>
/// <param name="pReserved">Reserved for future use. This parameter must be set to NULL.</param>
/// <param name="ppInterfaceList">
/// A pointer to storage for a pointer to receive the returned list of wireless LAN interfaces in a WLAN_INTERFACE_INFO_LIST structure.
/// The buffer for the WLAN_INTERFACE_INFO_LIST returned is allocated by the WlanEnumInterfaces function if the call succeeds.
/// </param>
/// <returns>
/// If the function succeeds, the return value is ERROR_SUCCESS.
/// If the function fails, the return value may be one of the following return codes.
/// ERROR_INVALID_PARAMETER: A parameter is incorrect. This error is returned if the hClientHandle or ppInterfaceList parameter is NULL. This error is returned if the pReserved is not NULL.
/// This error is also returned if the hClientHandle parameter is not valid.
/// ERROR_INVALID_HANDLE: The handle hClientHandle was not found in the handle table.
/// RPC_STATUS: Various error codes.
/// ERROR_NOT_ENOUGH_MEMORY: Not enough memory is available to process this request and allocate memory for the query results.
/// </returns>
/// <remarks>
/// The WlanEnumInterfaces function allocates memory for the list of returned interfaces that is returned in the
/// buffer pointed to by the ppInterfaceList parameter when the function succeeds. The memory used for the buffer
/// pointed to by ppInterfaceList parameter should be released by calling the WlanFreeMemory function
/// after the buffer is no longer needed.
/// </remarks>
[DllImport("Wlanapi", SetLastError = true)]
public static extern uint WlanEnumInterfaces(IntPtr hClientHandle, IntPtr pReserved, out IntPtr ppInterfaceList);
/// <summary>
/// The WlanGetProfileList function retrieves the list of profiles in preference order.
/// </summary>
/// <param name="hClientHandle">The client's session handle, obtained by a previous call to the WlanOpenHandle function.</param>
/// <param name="pInterfaceGuid">The GUID of the wireless interface.</param>
/// <param name="pReserved">Reserved for future use. Must be set to NULL.</param>
/// <param name="ppProfileList">A PWLAN_PROFILE_INFO_LIST structure that contains the list of profile information.</param>
/// <returns>
/// If the function succeeds, the return value is ERROR_SUCCESS.
/// If the function fails, the return value may be one of the following return codes.
/// ERROR_INVALID_HANDLE: The handle hClientHandle was not found in the handle table.
/// ERROR_INVALID_PARAMETER: A parameter is incorrect.
/// ERROR_NOT_ENOUGH_MEMORY: Not enough memory is available to process this request and allocate memory for the query results.
/// RPC_STATUS: Various error codes.
/// </returns>
/// <remarks>
/// The WlanGetProfileList function returns only the basic information on the wireless profiles on a wireless interface.
/// The list of wireless profiles on a wireless interface are retrieved in the preference order. The WlanSetProfilePosition
/// can be used to change the preference order for the wireless profiles on a wireless interface.
/// More detailed information for a wireless profile on a wireless interface can be retrieved by using the WlanGetProfile
/// function. The WlanGetProfileCustomUserData function can be used to retrieve custom user data for a wireless profile on
/// a wireless interface. A list of the wireless interfaces and associated GUIDs on the local computer can be retrieved
/// using the WlanEnumInterfaces function.
/// The WlanGetProfileList function allocates memory for the list of profiles returned in the buffer pointed to by the
/// ppProfileList parameter. The caller is responsible for freeing this memory using the WlanFreeMemory function when
/// this buffer is no longer needed.
/// Windows XP with SP3 and Wireless LAN API for Windows XP with SP2:  Guest profiles, profiles with Wireless Provisioning
/// Service (WPS) authentication, and profiles with Wi-Fi Protected Access-None (WPA-None) authentication are not
/// supported. These types of profiles are not returned by WlanGetProfileList, even if a profile of this type appears
/// on the preferred profile list.
/// </remarks>
[DllImport("Wlanapi", SetLastError = true)]
public static extern uint WlanGetProfileList(IntPtr hClientHandle, [MarshalAs(UnmanagedType.LPStruct)] Guid pInterfaceGuid, IntPtr pReserved, out IntPtr ppProfileList);
/// <summary>
/// The WlanGetProfile function retrieves all information about a specified wireless profile.
/// </summary>
/// <param name="hClientHandle">The client's session handle, obtained by a previous call to the WlanOpenHandle function.</param>
/// <param name="pInterfaceGuid">The GUID of the wireless interface. </param>
/// <param name="strProfileName">The name of the profile. Profile names are case-sensitive. This string must be NULL-terminated. The maximum length of the profile name is 255 characters. This means that the maximum length of this string, including the NULL terminator, is 256 characters.</param>
/// <param name="pReserved">Reserved for future use. Must be set to NULL.</param>
/// <param name="pstrProfileXml">A string that is the XML representation of the queried profile. There is no predefined maximum string length.</param>
/// <param name="pdwFlags">On input, a pointer to the address location used to provide additional information about the request. If this parameter is NULL on input, then no information on profile flags will be returned. On output, a pointer to the address location used to receive profile flags.</param>
/// <param name="pdwGrantedAccess">The access mask of the all-user profile.</param>
/// <returns>If the function succeeds, the return value is ERROR_SUCCESS.</returns>
[DllImport("Wlanapi", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint WlanGetProfile(IntPtr hClientHandle, [MarshalAs(UnmanagedType.LPStruct)] Guid pInterfaceGuid, [MarshalAs(UnmanagedType.LPWStr)] string strProfileName, IntPtr pReserved, out IntPtr pstrProfileXml, out uint pdwFlags, out IntPtr pdwGrantedAccess);
/// <summary>
/// The WlanSetProfile function sets the content of a specific profile.
/// </summary>
/// <param name="hClientHandle">The client's session handle, obtained by a previous call to the WlanOpenHandle function.</param>
/// <param name="pInterfaceGuid">The GUID of the interface.</param>
/// <param name="dwFlags">The flags to set on the profile.</param>
/// <param name="strProfileXml">Contains the XML representation of the profile. The WLANProfile element is the root profile element. To view sample profiles, see Wireless Profile Samples. There is no predefined maximum string length.</param>
/// <param name="strAllUserProfileSecurity">Sets the security descriptor string on the all-user profile. For more information about profile permissions, see the Remarks section.</param>
/// <param name="bOverwrite">Specifies whether this profile is overwriting an existing profile. If this parameter is FALSE and the profile already exists, the existing profile will not be overwritten and an error will be returned.</param>
/// <param name="pReserved">Reserved for future use. Must be set to NULL.</param>
/// <param name="pdwReasonCode">A WLAN_REASON_CODE value that indicates why the profile is not valid.</param>
/// <returns>If the function succeeds, the return value is ERROR_SUCCESS.</returns>
[DllImport("Wlanapi", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint WlanSetProfile(IntPtr hClientHandle, [MarshalAs(UnmanagedType.LPStruct)] Guid pInterfaceGuid, uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string strProfileXml, [MarshalAs(UnmanagedType.LPWStr)] string strAllUserProfileSecurity, bool bOverwrite, IntPtr pReserved, out uint pdwReasonCode);
/// <summary>
/// The WlanDeleteProfile function deletes a wireless profile for a wireless interface on the local computer.
/// </summary>
/// <param name="hClientHandle">The client's session handle, obtained by a previous call to the WlanOpenHandle function.</param>
/// <param name="pInterfaceGuid">The GUID of the interface from which to delete the profile. </param>
/// <param name="strProfileName">The name of the profile to be deleted. Profile names are case-sensitive. This string must be NULL-terminated.</param>
/// <param name="pReserved">Reserved for future use. Must be set to NULL.</param>
/// <returns>
/// If the function succeeds, the return value is ERROR_SUCCESS.
/// If the function fails, the return value may be one of the following return codes.
/// ERROR_INVALID_PARAMETER: The hClientHandle parameter is NULL or not valid, the pInterfaceGuid parameter is NULL, the strProfileName parameter is NULL, or pReserved is not NULL.
/// ERROR_INVALID_HANDLE: The handle specified in the hClientHandle parameter was not found in the handle table.
/// ERROR_NOT_FOUND: The wireless profile specified by strProfileName was not found in the profile store.
/// ERROR_ACCESS_DENIED: The caller does not have sufficient permissions to delete the profile.
/// RPC_STATUS: Various error codes.
/// </returns>
/// <remarks>
/// The WlanDeleteProfile function deletes a wireless profile for a wireless interface on the local computer.
/// All wireless LAN functions require an interface GUID for the wireless interface when performing profile operations.
/// When a wireless interface is removed, its state is cleared from Wireless LAN Service (WLANSVC) and no profile operations are possible.
/// The WlanDeleteProfile function can fail with ERROR_INVALID_PARAMETER if the wireless interface specified in the pInterfaceGuid parameter
/// for the wireless LAN profile has been removed from the system (a USB wireless adapter that has been removed, for example).
/// </remarks>
[DllImport("Wlanapi", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint WlanDeleteProfile(IntPtr hClientHandle, [MarshalAs(UnmanagedType.LPStruct)] Guid pInterfaceGuid, [MarshalAs(UnmanagedType.LPWStr)] string strProfileName, IntPtr pReserved);
/// <summary>
/// The WlanReasonCodeToString function retrieves a string that describes a specified reason code.
/// </summary>
/// <param name="dwReasonCode">A WLAN_REASON_CODE value of which the string description is requested.</param>
/// <param name="dwBufferSize">The size of the buffer used to store the string, in WCHAR. If the reason code string is longer than the buffer, it will be truncated and NULL-terminated. If dwBufferSize is larger than the actual amount of memory allocated to pStringBuffer, then an access violation will occur in the calling program.</param>
/// <param name="pStringBuffer">Pointer to a buffer that will receive the string. The caller must allocate memory to pStringBuffer before calling WlanReasonCodeToString.</param>
/// <param name="pReserved">Reserved for future use. Must be set to NULL.</param>
/// <returns>If the function succeeds, the return value is a pointer to a constant string.</returns>
[DllImport("Wlanapi", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint WlanReasonCodeToString(uint dwReasonCode, uint dwBufferSize, ref StringBuilder pStringBuffer, IntPtr pReserved);
/// <summary>
/// The WlanFreeMemory function frees memory. Any memory returned from Native Wifi functions must be freed.
/// </summary>
/// <param name="pMemory">Pointer to the memory to be freed.</param>
/// <remarks>
/// If pMemory points to memory that has already been freed, an access violation or heap corruption may occur.
/// </remarks>
[DllImport("Wlanapi")]
public static extern void WlanFreeMemory(IntPtr pMemory);
}
}
+73 -297
View File
@@ -1,335 +1,111 @@
using System; using Disco.Client.Interop.Native;
using Disco.Models.ClientServices.EnrolmentInformation;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Management; using System.Management;
using System.Runtime.InteropServices;
using System.Text;
namespace Disco.Client.Interop namespace Disco.Client.Interop
{ {
public static class Network public static class Network
{ {
private static List<NetworkAdapterInfo> NetworkAdapters { get; set; } public static List<NetworkAdapter> GetNetworkAdapters()
private static NetworkAdapterInfo PrimaryLanNetworkAdapter { get; set; }
private static NetworkAdapterInfo PrimaryWlanNetworkAdapter { get; set; }
public static void Initialize()
{ {
// Get All Adapters var adapters = GetWmiNetworkAdapters();
RetrieveLanAdapters();
if (NetworkAdapters.Count > 0) if (adapters != null && adapters.Count > 0)
{
// Apply Wlan Information
adapters.ApplyWlanInformation();
}
return adapters;
}
private static List<NetworkAdapter> GetWmiNetworkAdapters()
{ {
// Only Retrieve Wlan Adapters if at least one adapter was found by WMI
try try
{ {
RetrieveWlanAdapters(); // Load Physical Adapters
} using (var wmiSearcher = new ManagementObjectSearcher("SELECT DeviceID, GUID, Manufacturer, ProductName, AdapterType, MACAddress, Speed, NetConnectionID, NetConnectionStatus, NetEnabled FROM Win32_NetworkAdapter WHERE PhysicalAdapter=true AND MACAddress IS NOT NULL AND NetConnectionID IS NOT NULL AND Speed IS NOT NULL"))
catch (DllNotFoundException)
{ {
// Ignore DllNotFoundException using (var wmiResults = wmiSearcher.Get())
// This which indicates 'Wlanapi.dll' isn't present (eg. Windows Servers)
}
// Determine Primary Adapters
// Lan
PrimaryLanNetworkAdapter = NetworkAdapters.Where(n => !n.IsWLanAdapter && n.NetConnectionId.StartsWith("Local Area Connection", StringComparison.OrdinalIgnoreCase)).OrderByDescending(n => n.Speed).FirstOrDefault();
// Might be too restrictive - remove name restriction just in case.
if (PrimaryLanNetworkAdapter == null)
PrimaryLanNetworkAdapter = NetworkAdapters.Where(n => !n.IsWLanAdapter).OrderByDescending(n => n.Speed).FirstOrDefault();
// Wan
PrimaryWlanNetworkAdapter = NetworkAdapters.Where(n => n.IsWLanAdapter).OrderByDescending(n => n.Speed).FirstOrDefault();
}
}
private static void RetrieveLanAdapters()
{ {
// Get NetworkAdapter Information return wmiResults
try .Cast<ManagementObject>()
.Select(wmiResult =>
{ {
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT Index, GUID, MACAddress, Name, NetConnectionID, Speed FROM Win32_NetworkAdapter WHERE PhysicalAdapter=true AND MACAddress IS NOT NULL AND Name IS NOT NULL AND NetConnectionID IS NOT NULL AND Speed IS NOT NULL")) var adapter = new NetworkAdapter()
{ {
using (ManagementObjectCollection mResults = mSearcher.Get()) DeviceID = (string)wmiResult.GetPropertyValue("DeviceID"),
{ ConnectionIdentifier = Guid.Parse((string)wmiResult.GetPropertyValue("GUID")),
NetworkAdapters = new List<NetworkAdapterInfo>(); Manufacturer = (string)wmiResult.GetPropertyValue("Manufacturer"),
foreach (var mResult in mResults.Cast<ManagementObject>()) ProductName = (string)wmiResult.GetPropertyValue("ProductName"),
{ AdapterType = (string)wmiResult.GetPropertyValue("AdapterType"),
NetworkAdapterInfo nic = new NetworkAdapterInfo() MACAddress = (string)wmiResult.GetPropertyValue("MACAddress"),
{ Speed = (ulong)wmiResult.GetPropertyValue("Speed"),
Index = (UInt32)mResult.GetPropertyValue("Index"), NetConnectionID = (string)wmiResult.GetPropertyValue("NetConnectionID"),
Guid = Guid.Parse((string)mResult.GetPropertyValue("GUID")), NetConnectionStatus = ((NetworkConnectionStatuses)wmiResult.GetPropertyValue("NetConnectionStatus")).Description(),
MacAddress = mResult.GetPropertyValue("MACAddress").ToString(), NetEnabled = (bool)wmiResult.GetPropertyValue("NetEnabled")
Name = mResult.GetPropertyValue("Name").ToString(),
NetConnectionId = mResult.GetPropertyValue("NetConnectionID").ToString(),
Speed = Convert.ToUInt64(mResult.GetPropertyValue("Speed")),
IsWLanAdapter = false
}; };
NetworkAdapters.Add(nic);
using (var wmiRelatedResults = wmiResult.GetRelated("Win32_NetworkAdapterConfiguration", "Win32_NetworkAdapterSetting", null, null, null, null, false, null))
{
var wmiConfiguration = wmiRelatedResults.Cast<ManagementObject>().First();
adapter.IPEnabled = (bool)wmiConfiguration.GetPropertyValue("IPEnabled");
if (adapter.IPEnabled)
{
adapter.IPAddresses = ((string[])wmiConfiguration.GetPropertyValue("IPAddress")).ToList();
} }
} }
return adapter;
})
.ToList();
}
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new Exception("Disco Client was unable to retrieve NetworkAdapter information from WMI", ex); throw new Exception("Disco ICT Client was unable to retrieve NetworkAdapter information from WMI", ex);
} }
} }
private static void RetrieveWlanAdapters() public static string Description(this NetworkConnectionStatuses Status)
{ {
WLAN_INTERFACE_INFO_LIST wlanApiInterfaceList; switch (Status)
IntPtr wlanApiHandle = IntPtr.Zero;
uint wlanApiServiceVersion = 0;
if (WlanOpenHandle(WLAN_API_VERSION_2_0, IntPtr.Zero, out wlanApiServiceVersion, ref wlanApiHandle) == ERROR_SUCCESS)
{ {
IntPtr wlanApiInterfaceListPointer = IntPtr.Zero; case NetworkConnectionStatuses.Disconnected:
if (WlanEnumInterfaces(wlanApiHandle, IntPtr.Zero, ref wlanApiInterfaceListPointer) == ERROR_SUCCESS)
{
wlanApiInterfaceList = new WLAN_INTERFACE_INFO_LIST(wlanApiInterfaceListPointer);
WlanFreeMemory(wlanApiInterfaceListPointer);
}
else
{
// Error - No Wlan Adapters Reported
WlanCloseHandle(wlanApiHandle, IntPtr.Zero);
return;
}
WlanCloseHandle(wlanApiHandle, IntPtr.Zero);
}
else
{
// Error - No Wlan Adapters Reported
return;
}
if (wlanApiInterfaceList.InterfaceInfo != null)
{
foreach (var wlanApiAdapter in wlanApiInterfaceList.InterfaceInfo)
{
var wlanApiAdapterInfo = wlanApiAdapter;
var wmiAdapterInfo = NetworkAdapters.FirstOrDefault(n => n.Guid == wlanApiAdapterInfo.InterfaceGuid);
if (wmiAdapterInfo != null)
{
wmiAdapterInfo.IsWLanAdapter = true;
wmiAdapterInfo.WlanState = wlanApiAdapterInfo.isState;
}
}
}
}
public static string PrimaryLanMacAddress
{
get
{
// Return null if no Primary LAN Network Adapter found on this Device
return (PrimaryLanNetworkAdapter == null) ? null : PrimaryLanNetworkAdapter.MacAddress;
}
}
public static string PrimaryWlanMacAddress
{
get
{
// Return null if no Primary WLAN Network Adapter found on this Device
return (PrimaryWlanNetworkAdapter == null) ? null : PrimaryWlanNetworkAdapter.MacAddress;
}
}
private class NetworkAdapterInfo
{
public UInt32 Index { get; set; }
public Guid Guid { get; set; }
public string Name { get; set; }
public string NetConnectionId { get; set; }
public string MacAddress { get; set; }
public UInt64 Speed { get; set; }
public bool IsWLanAdapter { get; set; }
public WLAN_INTERFACE_STATE WlanState { get; set; }
public string WlanStateDescription
{
get
{
switch (WlanState)
{
case WLAN_INTERFACE_STATE.wlan_interface_state_not_ready:
return "Not Ready";
case WLAN_INTERFACE_STATE.wlan_interface_state_connected:
return "Connected";
case WLAN_INTERFACE_STATE.wlan_interface_state_ad_hoc_network_formed:
return "Ad Hoc Network Formed";
case WLAN_INTERFACE_STATE.wlan_interface_state_disconnecting:
return "Disconnecting";
case WLAN_INTERFACE_STATE.wlan_interface_state_disconnected:
return "Disconnected"; return "Disconnected";
case WLAN_INTERFACE_STATE.wlan_interface_state_associating: case NetworkConnectionStatuses.Connecting:
return "Associating"; return "Connecting";
case WLAN_INTERFACE_STATE.wlan_interface_state_discovering: case NetworkConnectionStatuses.Connected:
return "Discovering"; return "Connected";
case WLAN_INTERFACE_STATE.wlan_interface_state_authenticating: case NetworkConnectionStatuses.Disconnecting:
return "Disconnecting";
case NetworkConnectionStatuses.HardwareNotPresent:
return "Hardware Not Present";
case NetworkConnectionStatuses.HardwareDisabled:
return "Hardware Disabled";
case NetworkConnectionStatuses.HardwareMalfunction:
return "Hardware Malfunction";
case NetworkConnectionStatuses.MediaDisconnected:
return "Media Disconnected";
case NetworkConnectionStatuses.Authenticating:
return "Authenticating"; return "Authenticating";
case NetworkConnectionStatuses.AuthenticationSucceeded:
return "Authentication Succeeded";
case NetworkConnectionStatuses.AuthenticationFailed:
return "Authentication Failed";
case NetworkConnectionStatuses.InvalidAddress:
return "Invalid Address";
case NetworkConnectionStatuses.CredentialsRequired:
return "Credentials Required";
default: default:
return "Unknown"; return "Unknown";
} }
} }
}
}
#region Wlan Win32 Interop
private const uint WLAN_API_VERSION_2_0 = 2; // Windows Vista WiFi API Version
private const int ERROR_SUCCESS = 0;
/// <summary >
/// Opens a connection to the server
/// </summary >
[DllImport("Wlanapi.dll")]
private static extern int WlanOpenHandle(
uint dwClientVersion,
IntPtr pReserved, //not in MSDN but required
[Out] out uint pdwNegotiatedVersion,
ref IntPtr ClientHandle);
/// <summary >
/// Closes a connection to the server
/// </summary >
[DllImport("Wlanapi", EntryPoint = "WlanCloseHandle")]
private static extern uint WlanCloseHandle(
[In] IntPtr hClientHandle,
IntPtr pReserved);
/// <summary >
/// Enumerates all wireless interfaces in the laptop
/// </summary >
[DllImport("Wlanapi", EntryPoint = "WlanEnumInterfaces")]
private static extern uint WlanEnumInterfaces(
[In] IntPtr hClientHandle,
IntPtr pReserved,
ref IntPtr ppInterfaceList);
/// <summary >
/// Frees memory returned by native WiFi functions
/// </summary >
[DllImport("Wlanapi", EntryPoint = "WlanFreeMemory")]
private static extern void WlanFreeMemory([In] IntPtr pMemory);
/// <summary>
/// Defines the state of the interface. e.g. connected, disconnected.
/// </summary>
private enum WLAN_INTERFACE_STATE
{
/// <summary>
/// wlan_interface_state_not_ready -> 0
/// </summary>
wlan_interface_state_not_ready = 0,
/// <summary>
/// wlan_interface_state_connected -> 1
/// </summary>
wlan_interface_state_connected = 1,
/// <summary>
/// wlan_interface_state_ad_hoc_network_formed -> 2
/// </summary>
wlan_interface_state_ad_hoc_network_formed = 2,
/// <summary>
/// wlan_interface_state_disconnecting -> 3
/// </summary>
wlan_interface_state_disconnecting = 3,
/// <summary>
/// wlan_interface_state_disconnected -> 4
/// </summary>
wlan_interface_state_disconnected = 4,
/// <summary>
/// wlan_interface_state_associating -> 5
/// </summary>
wlan_interface_state_associating = 5,
/// <summary>
/// wlan_interface_state_discovering -> 6
/// </summary>
wlan_interface_state_discovering = 6,
/// <summary>
/// wlan_interface_state_authenticating -> 7
/// </summary>
wlan_interface_state_authenticating = 7,
}
/// <summary >
/// Stores interface info
/// </summary >
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WLAN_INTERFACE_INFO
{
/// GUID->_GUID
public Guid InterfaceGuid;
/// WCHAR[256]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string strInterfaceDescription;
/// WLAN_INTERFACE_STATE->_WLAN_INTERFACE_STATE
public WLAN_INTERFACE_STATE isState;
}
/// <summary>
/// Contains an array of NIC information
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct WLAN_INTERFACE_INFO_LIST
{
/// <summary>
/// Length of <see cref="InterfaceInfo"/> array
/// </summary>
public Int32 dwNumberOfItems;
/// <summary>
/// This member is not used by the wireless service. Applications can use this member when processing individual interfaces.
/// </summary>
public Int32 dwIndex;
/// <summary>
/// Array of WLAN interfaces.
/// </summary>
public WLAN_INTERFACE_INFO[] InterfaceInfo;
/// <summary>
/// Constructor for WLAN_INTERFACE_INFO_LIST.
/// Constructor is needed because the InterfaceInfo member varies based on how many adapters are in the system.
/// </summary>
/// <param name="pList">the unmanaged pointer containing the list.</param>
public WLAN_INTERFACE_INFO_LIST(IntPtr pList)
{
// The first 4 bytes are the number of WLAN_INTERFACE_INFO structures.
dwNumberOfItems = Marshal.ReadInt32(pList, 0);
// The next 4 bytes are the index of the current item in the unmanaged API.
dwIndex = Marshal.ReadInt32(pList, 4);
// Construct the array of WLAN_INTERFACE_INFO structures.
InterfaceInfo = new WLAN_INTERFACE_INFO[dwNumberOfItems];
for (int i = 0; i <= dwNumberOfItems - 1; i++)
{
// The offset of the array of structures is 8 bytes past the beginning.
// Then, take the index and multiply it by the number of bytes in the
// structure.
// The length of the WLAN_INTERFACE_INFO structure is 532 bytes - this
// was determined by doing a Marshall.SizeOf(WLAN_INTERFACE_INFO)
IntPtr pItemList = new IntPtr(pList.ToInt64() + (i * 532) + 8);
// Construct the WLAN_INTERFACE_INFO structure, marshal the unmanaged
// structure into it, then copy it to the array of structures.
InterfaceInfo[i] = (WLAN_INTERFACE_INFO)Marshal.PtrToStructure(pItemList, typeof(WLAN_INTERFACE_INFO));
}
}
}
#endregion
} }
} }
-201
View File
@@ -1,201 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Client.Interop
{
public static class SystemAudit
{
public static string DeviceSerialNumber { get; private set; }
public static string DeviceSMBIOSVersion { get; private set; }
public static string DeviceManufacturer { get; private set; }
public static string DeviceModel { get; private set; }
public static string DeviceType { get; private set; }
public static string DeviceUUID { get; private set; }
public static bool DeviceIsPartOfDomain { get; private set; }
public static string DeviceDNSDomainName { get; private set; }
public static void Initialize()
{
// Get BIOS Information
try
{
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT SerialNumber, SMBIOSBIOSVersion FROM Win32_BIOS WHERE PrimaryBios=true"))
{
using (ManagementObjectCollection mResults = mSearcher.Get())
{
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
{
if (mItem != null)
{
DeviceSerialNumber = mItem.GetPropertyValue("SerialNumber") as string;
if (!string.IsNullOrEmpty(DeviceSerialNumber))
DeviceSerialNumber = DeviceSerialNumber.Trim();
ErrorReporting.DeviceIdentifier = DeviceSerialNumber;
DeviceSMBIOSVersion = mItem.GetPropertyValue("SMBIOSBIOSVersion") as string;
if (!string.IsNullOrEmpty(DeviceSMBIOSVersion))
DeviceSMBIOSVersion = DeviceSMBIOSVersion.Trim();
}
else
{
throw new Exception("No Win32_BIOS WHERE PrimaryBios=true was found");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("Disco Client was unable to retrieve BIOS information from WMI", ex);
}
// Get System Information
try
{
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT Manufacturer, Model, PartOfDomain, PCSystemType, Domain FROM Win32_ComputerSystem"))
{
using (ManagementObjectCollection mResults = mSearcher.Get())
{
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
{
if (mItem != null)
{
DeviceManufacturer = mItem.GetPropertyValue("Manufacturer") as string;
if (!string.IsNullOrEmpty(DeviceManufacturer))
DeviceManufacturer = DeviceManufacturer.Trim();
DeviceModel = mItem.GetPropertyValue("Model") as string;
if (!string.IsNullOrEmpty(DeviceModel))
DeviceModel = DeviceModel.Trim();
DeviceIsPartOfDomain = (bool)mItem.GetPropertyValue("PartOfDomain");
DeviceType = PCSystemTypeToString((UInt16)mItem.GetPropertyValue("PCSystemType"));
DeviceDNSDomainName = DeviceIsPartOfDomain ? mItem.GetPropertyValue("Domain") as string : null;
}
else
{
throw new Exception("No Win32_ComputerSystem was found");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("Disco Client was unable to retrieve ComputerSystem information from WMI", ex);
}
// Get System Product Information
string ComputerSystemProductSerialNumber;
try
{
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT IdentifyingNumber, UUID FROM Win32_ComputerSystemProduct"))
{
using (ManagementObjectCollection mResults = mSearcher.Get())
{
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
{
if (mItem != null)
{
ComputerSystemProductSerialNumber = mItem.GetPropertyValue("IdentifyingNumber") as string;
if (!string.IsNullOrEmpty(ComputerSystemProductSerialNumber))
ComputerSystemProductSerialNumber = ComputerSystemProductSerialNumber.Trim();
DeviceUUID = mItem.GetPropertyValue("UUID") as string;
if (!string.IsNullOrEmpty(DeviceUUID))
DeviceUUID = DeviceUUID.Trim();
}
else
{
throw new Exception("No Win32_ComputerSystemProduct was found");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("Disco Client was unable to retrieve ComputerSystemProduct information from WMI", ex);
}
// Added 2012-11-22 G# - Lenovo IdeaPad Serial SHIM
// http://www.discoict.com.au/forum/feature-requests/2012/11/serial-number-detection-on-ideapads.aspx
if (string.IsNullOrWhiteSpace(DeviceSerialNumber) ||
(DeviceManufacturer.Equals("LENOVO", StringComparison.OrdinalIgnoreCase) &&
(DeviceModel.Equals("S10-3", StringComparison.OrdinalIgnoreCase) // S10-3
|| DeviceModel.Equals("2957", StringComparison.OrdinalIgnoreCase)))) // S10-2
{
try
{
using (ManagementObjectSearcher mSearcher = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BaseBoard"))
{
using (ManagementObjectCollection mResults = mSearcher.Get())
{
using (var mItem = mResults.Cast<ManagementObject>().FirstOrDefault())
{
if (mItem != null)
{
DeviceSerialNumber = mItem.GetPropertyValue("SerialNumber") as string;
if (!string.IsNullOrEmpty(DeviceSerialNumber))
DeviceSerialNumber = DeviceSerialNumber.Trim();
}
else
{
throw new Exception("No Win32_BaseBoard was found");
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("Disco Client was unable to retrieve BaseBoard information from WMI", ex);
}
if (string.IsNullOrWhiteSpace(DeviceSerialNumber))
DeviceSerialNumber = ComputerSystemProductSerialNumber;
}
// End Added 2012-11-22 G#
ErrorReporting.DeviceIdentifier = DeviceSerialNumber;
// Validate Device 'State'
if (string.IsNullOrWhiteSpace(DeviceSerialNumber))
throw new Exception("This device has no serial number stored in BIOS or BaseBoard");
if (DeviceSerialNumber.Length > 60)
throw new Exception(string.Format("The serial number reported by this device is over 60 characters long:{0}{1}", Environment.NewLine, DeviceSerialNumber));
}
private static string PCSystemTypeToString(UInt16 PCSystemType)
{
switch (PCSystemType)
{
case 0:
return "Unknown";
case 1:
return "Desktop";
case 2:
return "Mobile";
case 3:
return "Workstation";
case 4:
return "EnterpriseServer";
case 5:
return "SmallOfficeAndHomeOfficeServer";
case 6:
return "AppliancePC";
case 7:
return "PerformanceServer";
case 8:
return "Maximum";
default:
return "Unknown";
}
}
}
}
+374
View File
@@ -0,0 +1,374 @@
using Disco.Client.Interop.Native;
using Disco.Models.ClientServices.EnrolmentInformation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
namespace Disco.Client.Interop
{
public static class WirelessNetwork
{
public static void ApplyWlanInformation(this List<NetworkAdapter> Adapters)
{
try
{
if (WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out var wlanServiceVersion, out var wlanHandle) == WlanApi.ERROR_SUCCESS)
{
try
{
if (WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out var wlanInterfacesPtr) == WlanApi.ERROR_SUCCESS)
{
try
{
var wlanInterfaces = new WLAN_INTERFACE_INFO_LIST(wlanInterfacesPtr);
foreach (var wlanInterface in wlanInterfaces.InterfaceInfo)
{
var adapter = Adapters.FirstOrDefault(a => a.ConnectionIdentifier == wlanInterface.InterfaceGuid);
if (adapter != null)
{
adapter.IsWlanAdapter = true;
adapter.WlanStatus = wlanInterface.isState.Description();
}
}
}
finally
{
WlanApi.WlanFreeMemory(wlanInterfacesPtr);
}
}
}
finally
{
WlanApi.WlanCloseHandle(wlanHandle, IntPtr.Zero);
}
}
}
catch (DllNotFoundException)
{
// Ignore
// Indicates 'Wlanapi.dll' isn't present (ie. Servers)
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve Wireless NetworkAdapter information from WlanApi", ex);
}
}
public static List<WirelessProfile> GetWirelessProfiles()
{
try
{
uint interopResult;
// Connect to wireless service
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out var wlanServiceVersion, out var wlanHandle);
if (interopResult == WlanApi.ERROR_SERVICE_NOT_ACTIVE)
{
// Indicates the Wlan service has not been started on the client
// typically as it is not needed (no wireless adapter) or if it
// has been forcibly disabled.
return null;
}
if (interopResult != WlanApi.ERROR_SUCCESS)
{
throw new Exception($"Unable to connect to local wireless service. WlanOpenHandle returned: {interopResult}");
}
try
{
return GetWirelessProfiles(wlanHandle);
}
finally
{
WlanApi.WlanCloseHandle(wlanHandle, IntPtr.Zero);
}
}
catch (DllNotFoundException)
{
// Indicates 'Wlanapi.dll' isn't present (ie. Servers)
return null;
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve Wireless Profiles from WlanApi", ex);
}
}
private static List<WirelessProfile> GetWirelessProfiles(IntPtr wlanHandle)
{
uint interopResult;
// Enumerate wireless interfaces
interopResult = WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out var wlanInterfacesPtr);
if (interopResult != WlanApi.ERROR_SUCCESS)
{
throw new Exception($"Unable to list interfaces with the local wireless service. WlanEnumInterfaces returned: {interopResult}");
}
try
{
var wlanInterfaces = new WLAN_INTERFACE_INFO_LIST(wlanInterfacesPtr);
var profiles = new List<WirelessProfile>();
foreach (var wlanInterface in wlanInterfaces.InterfaceInfo)
{
// Enumerate wireless profiles for interface
interopResult = WlanApi.WlanGetProfileList(wlanHandle, wlanInterface.InterfaceGuid, IntPtr.Zero, out var wlanProfilesPtr);
if (interopResult != WlanApi.ERROR_SUCCESS)
{
throw new Exception($"Unable to list wireless profiles for the {wlanInterface.InterfaceGuid} interface with the local wireless service. WlanGetProfileList returned: {interopResult}");
}
try
{
var wlanProfiles = new WLAN_PROFILE_INFO_LIST(wlanProfilesPtr);
foreach (var wlanProfile in wlanProfiles.ProfileInfo)
{
profiles.Add(new WirelessProfile()
{
Name = wlanProfile.strInterfaceDescription,
InterfaceGuid = wlanInterface.InterfaceGuid,
IsGroupPolicy = wlanProfile.dwFlags.HasFlag(ProfileInfoFlags.WLAN_PROFILE_GROUP_POLICY)
});
}
}
finally
{
WlanApi.WlanFreeMemory(wlanProfilesPtr);
}
}
return profiles;
}
finally
{
WlanApi.WlanFreeMemory(wlanInterfacesPtr);
}
}
public static void Apply(this WirelessProfileStore ProfileStore)
{
var adapters = Network.GetNetworkAdapters().Where(a => a.IsWlanAdapter).ToList();
try
{
uint interopResult;
// Connect to wireless service
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out var wlanServiceVersion, out var wlanHandle);
if (interopResult == WlanApi.ERROR_SERVICE_NOT_ACTIVE)
{
// Indicates the Wlan service has not been started on the client
// typically as it is not needed (no wireless adapter) or if it
// has been forcibly disabled.
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nSkipping, the WLAN service is not enabled", true, -1, 3000);
return;
}
if (interopResult != WlanApi.ERROR_SUCCESS)
{
throw new Exception($"Unable to connect to local wireless service. WlanOpenHandle returned: {interopResult}");
}
try
{
var existingProfiles = GetWirelessProfiles(wlanHandle);
var addedProfiles = new List<string>();
// Remove Profiles
if (ProfileStore.RemoveNames != null && ProfileStore.RemoveNames.Count > 0)
{
var profileRemoved = false;
foreach (var removeName in ProfileStore.RemoveNames)
{
var foundProfiles = existingProfiles.Where(p => p.Name == removeName);
foreach (var profile in foundProfiles)
{
var adapter = adapters.FirstOrDefault(a => a.ConnectionIdentifier == profile.InterfaceGuid);
if (profile.IsGroupPolicy == true)
{
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nUnable to remove Group Policy Wireless Profile '{removeName}' from '{adapter?.NetConnectionID ?? profile.InterfaceGuid.ToString()}'", true, -1, 3000);
}
else
{
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nRemoving Wireless Profile '{removeName}' from '{adapter?.NetConnectionID ?? profile.InterfaceGuid.ToString()}'", true, -1, 1000);
interopResult = WlanApi.WlanDeleteProfile(wlanHandle, profile.InterfaceGuid.Value, profile.Name, IntPtr.Zero);
if (interopResult != WlanApi.ERROR_SUCCESS)
{
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nFailed to remove Wireless Profile '{removeName}' from '{adapter?.NetConnectionID ?? profile.InterfaceGuid.ToString()}'; WlanDeleteProfile returned: {interopResult}", true, -1, 3000);
}
profileRemoved = true;
}
}
}
if (profileRemoved)
{
existingProfiles = GetWirelessProfiles(wlanHandle);
}
}
// Add Profiles
if (ProfileStore.Profiles != null && ProfileStore.Profiles.Count > 0)
{
foreach (var addProfile in ProfileStore.Profiles)
{
foreach (var adapter in adapters)
{
var existingProfile = existingProfiles.FirstOrDefault(p => p.Name == addProfile.Name && p.InterfaceGuid == adapter.ConnectionIdentifier);
if (addProfile.ForceDeployment.Value ||
existingProfile == null)
{
if (existingProfile != null && existingProfile.IsGroupPolicy.Value)
{
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nSkipped Wireless Profile '{addProfile.Name}' on '{adapter.NetConnectionID}' as this profile is managed by Group Policy", true, -1, 3000);
}
else
{
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nAdding Wireless Profile '{addProfile.Name}' on '{adapter.NetConnectionID}'", true, -1, 1000);
interopResult = WlanApi.WlanSetProfile(wlanHandle, adapter.ConnectionIdentifier, 0, addProfile.ProfileXml, null, true, IntPtr.Zero, out var pdwReasonCode);
if (interopResult != WlanApi.ERROR_SUCCESS)
{
// Get Reason Code
var reason = new StringBuilder(256);
WlanApi.WlanReasonCodeToString(pdwReasonCode, (uint)reason.Capacity, ref reason, IntPtr.Zero);
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nFailed to add Wireless Profile '{addProfile.Name}' on '{adapter.NetConnectionID}'; WlanSetProfile returned: {interopResult}; {reason.ToString()}", true, -1, 3000);
}
}
addedProfiles.Add(addProfile.Name);
}
}
}
}
// Transform Profiles
if (ProfileStore.Transformations != null && ProfileStore.Transformations.Count > 0)
{
foreach (var transformGroup in ProfileStore.Transformations.GroupBy(t => t.Name))
{
var profileName = transformGroup.Key;
// Don't transform if just added
if (!addedProfiles.Contains(transformGroup.Key))
{
foreach (var adapter in adapters)
{
var existingProfile = existingProfiles.FirstOrDefault(p => p.Name == profileName && p.InterfaceGuid == adapter.ConnectionIdentifier);
if (existingProfile != null)
{
if (existingProfile.IsGroupPolicy.Value)
{
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nSkipped Wireless Profile '{profileName}' on '{adapter.NetConnectionID}' as this profile is managed by Group Policy", true, -1, 3000);
}
else
{
// Load profile
interopResult = WlanApi.WlanGetProfile(wlanHandle, adapter.ConnectionIdentifier, profileName, IntPtr.Zero, out var pstrProfileXml, out var pdwFlags, out var pdwGrantAccess);
if (interopResult == WlanApi.ERROR_SUCCESS)
{
try
{
var profileXml = Marshal.PtrToStringUni(pstrProfileXml);
var originalProfileXml = XElement.Parse(profileXml);
var transformProfileXml = originalProfileXml.ToString(SaveOptions.DisableFormatting);
// Apply Transforms
foreach (var transform in transformGroup)
{
var regex = new Regex(transform.RegularExpression, RegexOptions.Singleline);
transformProfileXml = regex.Replace(transformProfileXml, transform.RegularExpressionReplacement);
}
// Compare XML
var transformedProfileXml = XElement.Parse(transformProfileXml);
if (!XNode.DeepEquals(originalProfileXml, transformedProfileXml))
{
// Set Profile
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nModifying Wireless Profile '{profileName}' on '{adapter.NetConnectionID}'", true, -1, 1000);
transformProfileXml = transformedProfileXml.ToString(SaveOptions.None);
interopResult = WlanApi.WlanSetProfile(wlanHandle, adapter.ConnectionIdentifier, 0, transformProfileXml, null, true, IntPtr.Zero, out var pdwReasonCode);
if (interopResult != WlanApi.ERROR_SUCCESS)
{
// Get Reason Code
var reason = new StringBuilder(256);
WlanApi.WlanReasonCodeToString(pdwReasonCode, (uint)reason.Capacity, ref reason, IntPtr.Zero);
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nFailed to modify Wireless Profile '{profileName}' to '{adapter.NetConnectionID}'; WlanSetProfile returned: {interopResult}; {reason.ToString()}", true, -1, 3000);
}
}
}
finally
{
WlanApi.WlanFreeMemory(pstrProfileXml);
}
}
else
{
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nFailed to transform Wireless Profile '{profileName}' on '{adapter.NetConnectionID}'; WlanGetProfile returned: {interopResult}", true, -1, 3000);
}
}
}
}
}
}
}
}
finally
{
WlanApi.WlanCloseHandle(wlanHandle, IntPtr.Zero);
}
}
catch (DllNotFoundException)
{
// Indicates 'Wlanapi.dll' isn't present (ie. Servers)
// Ignore policies
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to apply Wireless Profile Changes using WlanApi", ex);
}
}
public static string Description(this WLAN_INTERFACE_STATE State)
{
switch (State)
{
case WLAN_INTERFACE_STATE.wlan_interface_state_not_ready:
return "Not Ready";
case WLAN_INTERFACE_STATE.wlan_interface_state_connected:
return "Connected";
case WLAN_INTERFACE_STATE.wlan_interface_state_ad_hoc_network_formed:
return "Ad Hoc Network";
case WLAN_INTERFACE_STATE.wlan_interface_state_disconnecting:
return "Disconnecting";
case WLAN_INTERFACE_STATE.wlan_interface_state_disconnected:
return "Disconnected";
case WLAN_INTERFACE_STATE.wlan_interface_state_associating:
return "Associating";
case WLAN_INTERFACE_STATE.wlan_interface_state_discovering:
return "Discovering";
case WLAN_INTERFACE_STATE.wlan_interface_state_authenticating:
return "Authenticating";
default:
return "Unknown";
}
}
}
}
+23 -12
View File
@@ -1,10 +1,10 @@
using System; using Disco.Client.Extensions;
using System.Collections.Generic; using Disco.Client.Interop;
using System.Linq; using System;
using System.Net;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using Disco.Client.Extensions;
namespace Disco.Client namespace Disco.Client
{ {
@@ -21,13 +21,13 @@ namespace Disco.Client
} }
public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress, int TryDelay) public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress, int TryDelay)
{ {
Presentation.UpdateStatus(SubHeading, Message, ShowProgress, Progress); UpdateStatus(SubHeading, Message, ShowProgress, Progress);
if (TryDelay > 0) if (TryDelay > 0)
Presentation.TryDelay(TryDelay); Presentation.TryDelay(TryDelay);
} }
public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress) public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress)
{ {
Console.WriteLine("#{0},{1},{2},{3}", SubHeading.EscapeMessage(), Message.EscapeMessage(), ShowProgress.ToString(), Progress.ToString()); Console.WriteLine($"#{SubHeading.EscapeMessage()},{Message.EscapeMessage()},{ShowProgress},{Progress}");
} }
public static void TryDelay(int Milliseconds) public static void TryDelay(int Milliseconds)
{ {
@@ -38,8 +38,13 @@ namespace Disco.Client
public static void WriteBanner() public static void WriteBanner()
{ {
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
message.Append("Version: ").AppendLine(Assembly.GetExecutingAssembly().GetName().Version.ToString(3)); message.AppendLine($"Version: {Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}");
message.Append("Device: ").Append(Interop.SystemAudit.DeviceSerialNumber).Append(" (").Append(Interop.SystemAudit.DeviceManufacturer).Append(" ").Append(Interop.SystemAudit.DeviceModel).AppendLine(")"); message.Append($"Server: {Program.ServerUrl})");
if (Program.ServerUrl.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
message.AppendLine(" [Secure]");
else
message.AppendLine(" [Insecure]");
message.AppendLine($"Device: {Hardware.Information.SerialNumber} ({Hardware.Information.Manufacturer} {Hardware.Information.Model})");
Console.ForegroundColor = ConsoleColor.Yellow; Console.ForegroundColor = ConsoleColor.Yellow;
UpdateStatus("Preparation Client Started", message.ToString(), false, 0); UpdateStatus("Preparation Client Started", message.ToString(), false, 0);
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
@@ -49,12 +54,18 @@ namespace Disco.Client
{ {
Console.ForegroundColor = ConsoleColor.Magenta; Console.ForegroundColor = ConsoleColor.Magenta;
ClientServiceException clientServiceException = ex as ClientServiceException; if (ex is ClientServiceException clientServiceException)
if (clientServiceException != null)
{ {
UpdateStatus(string.Format("An error occurred during {0}", clientServiceException.ServiceFeature), UpdateStatus($"An error occurred during {clientServiceException.ServiceFeature}",
clientServiceException.Message, false, 0); clientServiceException.Message, false, 0);
} }
else if (ex is WebException exWeb &&
exWeb.Response is HttpWebResponse webResponse &&
webResponse.StatusCode == HttpStatusCode.InternalServerError)
{
UpdateStatus("Something went wrong on the server",
"Review logs for more information (Configuration > Logging)", false, 0);
}
else else
{ {
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
@@ -91,7 +102,7 @@ namespace Disco.Client
public static void RegisterBootstrapperPostActions(ShutdownActions ShutdownAction, bool Uninstall) public static void RegisterBootstrapperPostActions(ShutdownActions ShutdownAction, bool Uninstall)
{ {
Console.WriteLine("!{0},{1}", Enum.GetName(typeof(ShutdownActions), ShutdownAction), Uninstall ? "UninstallBootstrapper" : "DontUninstallBootstrapper"); Console.WriteLine($"!{Enum.GetName(typeof(ShutdownActions), ShutdownAction)},{(Uninstall ? "UninstallBootstrapper" : "DontUninstallBootstrapper")}");
} }
public enum ShutdownActions public enum ShutdownActions
{ {
+97 -17
View File
@@ -1,11 +1,10 @@
using System; using Disco.Client.Extensions;
using System.Collections.Generic; using Disco.Client.Interop;
using Disco.Models.ClientServices;
using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text;
using System.Threading.Tasks;
using Disco.Models.ClientServices;
using Disco.Client.Extensions;
namespace Disco.Client namespace Disco.Client
{ {
@@ -14,30 +13,47 @@ namespace Disco.Client
public static bool IsAuthenticated { get; set; } public static bool IsAuthenticated { get; set; }
public static bool RebootRequired { get; set; } public static bool RebootRequired { get; set; }
public static bool AllowUninstall { get; set; } public static bool AllowUninstall { get; set; }
public static int BootstrapperVersion { get; private set; } = 1;
public static int BootstrapperProcessId { get; private set; } = -1;
public static Uri ServerUrl { get; private set; }
[STAThread] [STAThread]
public static void Main(string[] args) public static void Main(string[] args)
{ {
bool keepProcessing; bool keepProcessing;
#if DEBUG
if (args != null && args.Any(a => a.Equals("debug", StringComparison.OrdinalIgnoreCase)))
{
do
{
Console.WriteLine("Waiting for Debugger to Attach");
System.Threading.Thread.Sleep(1000);
} while (!Debugger.IsAttached);
}
#endif
// Initialize Environment Settings // Initialize Environment Settings
SetupEnvironment(); SetupEnvironment(args);
if (ServerUrl == null)
keepProcessing = DiscoverDiscoIct();
// Report to Bootstrapper // Report to Bootstrapper
Presentation.WriteBanner(); Presentation.WriteBanner();
// WhoAmI Phase // WhoAmI Phase
keepProcessing = Program.WhoAmI(); keepProcessing = WhoAmI();
// Enrol Phase // Enrol Phase
if (keepProcessing) if (keepProcessing)
keepProcessing = Program.Enrol(); keepProcessing = Enrol();
// End conversation with Bootstrapper // End conversation with Bootstrapper
Presentation.WriteFooter(Program.RebootRequired, Program.AllowUninstall, !keepProcessing); Presentation.WriteFooter(RebootRequired, AllowUninstall, !keepProcessing);
} }
public static void SetupEnvironment() public static void SetupEnvironment(string[] args)
{ {
// Hookup Unhandled Error Handling // Hookup Unhandled Error Handling
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.CurrentDomain_UnhandledException; AppDomain.CurrentDomain.UnhandledException += ErrorReporting.CurrentDomain_UnhandledException;
@@ -46,23 +62,64 @@ namespace Disco.Client
WebRequest.DefaultWebProxy = new WebProxy(); WebRequest.DefaultWebProxy = new WebProxy();
// Override Http 100 Continue Behaviour // Override Http 100 Continue Behaviour
ServicePointManager.Expect100Continue = false; ServicePointManager.Expect100Continue = false;
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
// Assume success unless otherwise notified // Assume success unless otherwise notified
AllowUninstall = true; AllowUninstall = true;
if (args != null && args.Length == 3)
{
// Parse Bootstrapper Version
int parsedVersion;
if (int.TryParse(args[0], out parsedVersion))
BootstrapperVersion = parsedVersion;
// Parse Bootstrapper Process ID
int parsedProcessId;
if (int.TryParse(args[1], out parsedProcessId))
BootstrapperProcessId = parsedProcessId;
// Parse Server URL
Uri parsedUri;
if (Uri.TryCreate(args[2], UriKind.Absolute, out parsedUri))
ServerUrl = parsedUri;
}
else
{
BootstrapperVersion = 1;
BootstrapperProcessId = -1;
ServerUrl = null;
}
// Detect Disco.Bootstrapper - Create Enable UI Delay if Running // Detect Disco.Bootstrapper - Create Enable UI Delay if Running
Presentation.DelayUI = false;
try try
{ {
Presentation.DelayUI = (System.Diagnostics.Process.GetProcessesByName("Disco.ClientBootstrapper").Length > 0); if (BootstrapperProcessId != -1)
{
var parentProcess = Process.GetProcessById(BootstrapperProcessId);
Presentation.DelayUI = !parentProcess.HasExited;
}
} }
catch (Exception) catch (Exception)
{ {
Presentation.DelayUI = true; // Add Delays on Error }
} }
// Initialize Interop public static bool DiscoverDiscoIct()
Interop.SystemAudit.Initialize(); {
Interop.Network.Initialize(); try
{
Presentation.UpdateStatus("Detecting Disco ICT", "Locating Disco ICT Server, Please wait...", true, -1);
Presentation.TryDelay(3000);
ServerUrl = EndpointDiscovery.DiscoverServer(null).Item1;
// Complete
return true;
}
catch (Exception ex)
{
ErrorReporting.ReportError(ex, false);
}
return false;
} }
public static bool WhoAmI() public static bool WhoAmI()
@@ -119,14 +176,37 @@ namespace Disco.Client
request = new Enrol(); request = new Enrol();
request.Build(); request.Build();
var startTime = DateTimeOffset.Now;
do
{
// Send Request // Send Request
Presentation.UpdateStatus("Enrolling Device", "Sending the enrolment request to the server.", true, -1); Presentation.UpdateStatus("Enrolling Device", "Sending the enrolment request to the server.", true, -1);
response = request.Post(Program.IsAuthenticated); response = request.Post(IsAuthenticated);
// Process Response // Process Response
Presentation.UpdateStatus("Enrolling Device", "Processing the enrolment response from the server.", true, -1); Presentation.UpdateStatus("Enrolling Device", "Processing the enrolment response from the server.", true, -1);
response.Process(); response.Process();
if (response.IsPending)
{
request.PendingSessionId = response.SessionId;
request.PendingAuthorization = response.PendingAuthorization;
// Session Pending
var totalSeconds = (response.PendingTimeout - startTime).TotalSeconds;
var secondsConsumed = (DateTimeOffset.Now - startTime).TotalSeconds;
var progress = (int)((secondsConsumed / totalSeconds) * 100);
Presentation.UpdateStatus($"Pending Device Enrolment Approval: {response.PendingIdentifier}", $"Server: {Program.ServerUrl}{Environment.NewLine}Reason: {response.PendingReason}", true, progress);
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(10));
}
else
{
// Session Complete
break;
}
} while (true);
// Complete // Complete
return true; return true;
} }
+6 -7
View File
@@ -1,15 +1,14 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("Disco - Client")] [assembly: AssemblyTitle("Disco ICT - Client")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("https://discoict.com.au")]
[assembly: AssemblyProduct("Disco")] [assembly: AssemblyProduct("Disco ICT")]
[assembly: AssemblyCopyright("")] [assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -32,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.0731.1600")] [assembly: AssemblyVersion("2.5.25262.0000")]
[assembly: AssemblyFileVersion("2.0.0731.1600")] [assembly: AssemblyFileVersion("2.5.25262.0000")]
+2 -2
View File
@@ -1,9 +1,9 @@
@ECHO OFF @ECHO OFF
IF /I "%USERDOMAIN%"=="NT AUTHORITY" GOTO RunAsNetworkService IF /I "%USERDOMAIN%"=="NT AUTHORITY" GOTO RunAsNetworkService
Disco.Client.exe Disco.Client.exe %1 %2 %3
EXIT /B 0 EXIT /B 0
:RunAsNetworkService :RunAsNetworkService
ECHO #Running,Launching Preparation Client, Please wait...{newline}Starting client as 'NT AUTHORITY\Network Service',true,-1 ECHO #Running,Launching Preparation Client, Please wait...{newline}Starting client as 'NT AUTHORITY\Network Service',true,-1
PsExec -acceptula -i -u "NT AUTHORITY\Network Service" -w "%CD%" "%CD%\Start.bat" PsExec -acceptula -i -u "NT AUTHORITY\Network Service" -w "%CD%" "%CD%\Start.bat %1 %2 %3"
EXIT /B 0 EXIT /B 0
+1 -1
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Newtonsoft.Json" version="6.0.3" targetFramework="net40-Client" /> <package id="Newtonsoft.Json" version="13.0.2" targetFramework="net40-client" />
</packages> </packages>
+94 -99
View File
@@ -1,140 +1,122 @@
//#define Debug using Disco.Client.Interop;
using Disco.ClientBootstrapper.Interop;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Net;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Net; using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
namespace Disco.ClientBootstrapper namespace Disco.ClientBootstrapper
{ {
class BootstrapperLoop internal class BootstrapperLoop
{ {
private readonly Func<CancellationToken, Task> completeCallback;
public Thread LoopThread; private readonly CancellationToken cancellationToken;
public delegate void LoopCompleteCallback(); private readonly IStatus statusUI;
private LoopCompleteCallback mLoopCompleteCallback; private readonly Uri forcedServerUrl;
private IStatus statusUI;
private string tempWorkingDirectory; private string tempWorkingDirectory;
private StringBuilder errorMessage;
private Process clientProcess; private Process clientProcess;
public BootstrapperLoop(IStatus StatusUI, LoopCompleteCallback Callback) public BootstrapperLoop(IStatus statusUI, Uri forcedServerUrl, Func<CancellationToken, Task> callback, CancellationToken cancellationToken)
{ {
this.statusUI = StatusUI; this.statusUI = statusUI;
this.mLoopCompleteCallback = Callback; this.forcedServerUrl = forcedServerUrl;
this.errorMessage = new StringBuilder(); completeCallback = callback;
this.cancellationToken = cancellationToken;
} }
public void Start() public void Start()
{ {
this.LoopThread = new Thread(new ThreadStart(loopHost)); Task.Factory.StartNew(async () =>
this.LoopThread.Start(); {
await Loop(forcedServerUrl, cancellationToken);
}, cancellationToken);
} }
private void loopHost() private async Task Loop(Uri forcedServerUrl, CancellationToken cancellationToken)
{ {
try try
{ {
loop();
}
catch (Exception ex)
{
if (ex.GetType() == typeof(ThreadAbortException))
return;
if (ex.GetType() == typeof(ThreadInterruptedException))
return;
Program.WriteAppError(ex);
throw;
}
}
private void loop()
{
#if Debug
statusUI.UpdateStatus("Waiting for Debugger", "Please wait...", true, -1);
try
{
do
{
System.Threading.Thread.Sleep(10);
} while (!System.Diagnostics.Debugger.IsAttached);
}
catch (Exception ex)
{
statusUI.UpdateStatus("Error", ex.Message, true, -1);
return;
}
#else
statusUI.UpdateStatus("System Preparation (Bootstrapper)", "Starting", "Please wait...", true, -1); statusUI.UpdateStatus("System Preparation (Bootstrapper)", "Starting", "Please wait...", true, -1);
#endif
tempWorkingDirectory = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco\\Temp"); tempWorkingDirectory = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), @"Disco\Temp");
if (!Directory.Exists(tempWorkingDirectory)) if (!Directory.Exists(tempWorkingDirectory))
Directory.CreateDirectory(tempWorkingDirectory); Directory.CreateDirectory(tempWorkingDirectory);
// Check for Network Connectivity // Check for Network Connectivity
statusUI.UpdateStatus(null, "Detecting Network", "Checking network connectivity, Please wait...", true, -1); statusUI.UpdateStatus(null, "Detecting Network", "Checking network connectivity, Please wait...", true, -1);
if (!Interop.NetworkInterop.PingDisco()) if (!NetworkInterop.HasNetworkConnectivity())
{ {
statusUI.UpdateStatus(null, "Detecting Network", "No network connectivity detected, Diagnosing...", true, -1); statusUI.UpdateStatus(null, "Detecting Network", "No network connectivity detected, Diagnosing...", true, -1);
statusUI_WriteAdapterInfo(); statusUI_WriteAdapterInfo();
if (!Interop.NetworkInterop.PingDisco()) if (!NetworkInterop.HasNetworkConnectivity())
{ {
// Check for Wireless // Check for Wireless
var hasWireless = (Interop.NetworkInterop.NetworkAdapters.Count(na => na.IsWireless) > 0); var hasWireless = (NetworkInterop.NetworkAdapters.Count(na => na.IsWireless) > 0);
if (hasWireless) if (hasWireless)
{ {
// True: Do wireless loop // True: Do wireless loop
statusUI.UpdateStatus(null, "Configuring Wireless Network", "Wireless adapter detected, Configuring...", true, -1); statusUI.UpdateStatus(null, "Configuring Wireless Network", "Wireless adapter detected, Configuring...", true, -1);
Interop.NetworkInterop.ConfigureWireless(); await NetworkInterop.ConfigureWireless(cancellationToken);
statusUI.UpdateStatus(null, "Waiting for Wireless Network", null, true, 0); statusUI.UpdateStatus(null, "Waiting for Wireless Network", null, true, 0);
for (int i = 0; i < 100; i++) for (int i = 0; i < 30; i++)
{ {
statusUI_WriteAdapterInfo(); statusUI_WriteAdapterInfo();
statusUI.UpdateStatus(null, null, null, true, i); statusUI.UpdateStatus(null, null, null, true, i);
Program.SleepThread(500, false); await Program.SleepThread(2000, false, cancellationToken);
if (Interop.NetworkInterop.PingDisco()) if (NetworkInterop.HasNetworkConnectivity())
break; break;
} }
if (!Interop.NetworkInterop.PingDisco()) if (!NetworkInterop.HasNetworkConnectivity())
{ {
statusUI.UpdateStatus(null, "Wireless Network Failed", "Unable to connect to the wireless network, please connect the network cable...", false); statusUI.UpdateStatus(null, "Wireless Network Failed", "Unable to connect to the wireless network, please connect the network cable...", false);
Program.SleepThread(3000, false); await Program.SleepThread(3000, false, cancellationToken);
} }
} }
if (!Interop.NetworkInterop.PingDisco()) if (!NetworkInterop.HasNetworkConnectivity())
{ {
// Instruct user to connect network cable // Instruct user to connect network cable
statusUI.UpdateStatus(null, "Please connect the network cable", null); statusUI.UpdateStatus(null, "Please connect the network cable", null);
for (int i = 0; i < 100; i++) for (int i = 0; i < 30; i++)
{ {
statusUI_WriteAdapterInfo(); statusUI_WriteAdapterInfo();
statusUI.UpdateStatus(null, null, null, true, i); statusUI.UpdateStatus(null, null, null, true, i);
Program.SleepThread(500, false); await Program.SleepThread(2000, false, cancellationToken);
if (Interop.NetworkInterop.PingDisco()) if (NetworkInterop.HasNetworkConnectivity())
break; break;
} }
} }
} }
if (!Interop.NetworkInterop.PingDisco()) if (!NetworkInterop.HasNetworkConnectivity())
{ {
// Client Failed // Client Failed
if (this.mLoopCompleteCallback != null) if (completeCallback != null)
{ await completeCallback(cancellationToken);
this.mLoopCompleteCallback.BeginInvoke(null, null);
}
return; return;
} }
} }
Tuple<Uri, string> serverDiscovery;
statusUI.UpdateStatus(null, "Detecting Disco ICT", "Locating Disco ICT Server, Please wait...", true, -1);
try
{
serverDiscovery = EndpointDiscovery.DiscoverServer(forcedServerUrl);
statusUI.UpdateStatus(null, null, $"{serverDiscovery.Item1} ({serverDiscovery.Item2})", true, -1);
}
catch (Exception)
{
statusUI.UpdateStatus(null, null, "Failed to locate Disco ICT Server, exiting...", true, -1);
await Program.SleepThread(2000, false, cancellationToken);
throw;
}
// Download Client // Download Client
statusUI.UpdateStatus(null, "Downloading", "Retrieving Preparation Client, Please wait...", true, -1); statusUI.UpdateStatus(null, "Downloading", "Retrieving Preparation Client, Please wait...", true, -1);
string clientSourceLocation = Path.Combine(tempWorkingDirectory, "PreparationClient.zip"); string clientSourceLocation = Path.Combine(tempWorkingDirectory, "PreparationClient.zip");
@@ -142,8 +124,29 @@ namespace Disco.ClientBootstrapper
{ {
// Don't use a proxy when downloading the Client // Don't use a proxy when downloading the Client
webClient.Proxy = new WebProxy(); webClient.Proxy = new WebProxy();
webClient.Headers.Add("X-DiscoICT-Discovery", serverDiscovery.Item2);
webClient.DownloadFile("http://disco:9292/Services/Client/PreparationClient", clientSourceLocation); try
{
webClient.DownloadFile(new Uri(serverDiscovery.Item1, "/Services/Client/PreparationClient"), clientSourceLocation);
}
catch (WebException ex)
{
if (ex.Response != null &&
ex.Response is HttpWebResponse response)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
{
statusUI.UpdateStatus(null, "Download failed: Bad Request", response.StatusDescription, true, -1);
await Program.SleepThread(5000, false, cancellationToken);
}
else if (response.StatusCode == HttpStatusCode.InternalServerError)
{
statusUI.UpdateStatus(null, "Download failed: Something went wrong on the server", "Review logs for more information (Configuration > Logging)", true, -1);
await Program.SleepThread(5000, false, cancellationToken);
}
}
throw;
}
} }
// Unzip Client // Unzip Client
@@ -160,7 +163,7 @@ namespace Disco.ClientBootstrapper
// Launch Client // Launch Client
statusUI.UpdateStatus("System Preparation (Client)", "Running", "Launching Preparation Client, Please wait...", true, -1); statusUI.UpdateStatus("System Preparation (Client)", "Running", "Launching Preparation Client, Please wait...", true, -1);
ProcessStartInfo clientProcessStart = new ProcessStartInfo(Path.Combine(clientLocation, "Start.bat")) ProcessStartInfo clientProcessStart = new ProcessStartInfo(Path.Combine(clientLocation, "Start.bat"), $"2 {Process.GetCurrentProcess().Id} {serverDiscovery.Item1}")
{ {
WorkingDirectory = clientLocation, WorkingDirectory = clientLocation,
CreateNoWindow = true, CreateNoWindow = true,
@@ -188,45 +191,47 @@ namespace Disco.ClientBootstrapper
// Cleanup // Cleanup
if (Directory.Exists(tempWorkingDirectory)) if (Directory.Exists(tempWorkingDirectory))
Directory.Delete(tempWorkingDirectory, true); Directory.Delete(tempWorkingDirectory, true);
Interop.CertificateInterop.RemoveTempCerts(); CertificateInterop.RemoveTempCerts();
// Pause if Error
if (this.errorMessage.Length > 0)
{
Program.SleepThread(10000, true);
} }
catch (Exception ex)
{
if (ex.GetType() == typeof(ThreadAbortException))
return;
if (ex.GetType() == typeof(ThreadInterruptedException))
return;
if (ex.GetType() == typeof(OperationCanceledException))
return;
Program.WriteAppError(ex);
}
// End Of Loop // End Of Loop
if (this.mLoopCompleteCallback != null) if (completeCallback != null)
{ await completeCallback(cancellationToken);
this.mLoopCompleteCallback.BeginInvoke(null, null);
}
} }
void statusUI_WriteAdapterInfo() private void statusUI_WriteAdapterInfo()
{ {
var info = new StringBuilder(); var info = new StringBuilder();
foreach (var na in Interop.NetworkInterop.NetworkAdapters) foreach (var na in NetworkInterop.NetworkAdapters)
{ {
if (na.IsWireless) if (na.IsWireless)
{ {
info.AppendLine(string.Format("{0}: {1}", na.NetConnectionID, na.WirelessConnectionStatusMeaning(na.WirelessConnectionStatus))); info.AppendLine($"{na.NetConnectionID}: {na.WirelessConnectionStatusMeaning(na.WirelessConnectionStatus)}");
} }
else else
{ {
info.AppendLine(string.Format("{0}: {1}", na.NetConnectionID, na.ConnectionStatusMeaning(na.ConnectionStatus))); info.AppendLine($"{na.NetConnectionID}: {na.ConnectionStatusMeaning(na.ConnectionStatus)}");
} }
} }
statusUI.UpdateStatus(null, null, info.ToString()); statusUI.UpdateStatus(null, null, info.ToString());
} }
void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) private void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{ {
if (!string.IsNullOrWhiteSpace(e.Data)) if (!string.IsNullOrWhiteSpace(e.Data))
{ {
System.Diagnostics.Debug.WriteLine(string.Format("OUTPUT: {0}", e.Data));
var data = e.Data.Substring(1).Split(new char[] { ',' }); var data = e.Data.Substring(1).Split(new char[] { ',' });
switch (e.Data[0]) switch (e.Data[0])
{ {
@@ -243,15 +248,5 @@ namespace Disco.ClientBootstrapper
} }
} }
//void clientProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
//{
// if (!string.IsNullOrEmpty(e.Data))
// {
// System.Diagnostics.Debug.WriteLine(string.Format("ERROR: {0}", e.Data));
// this.errorMessage.AppendLine(e.Data);
// statusUI.UpdateStatus(null, "An Error Occurred", this.errorMessage.ToString(), false);
// }
//}
} }
} }
@@ -8,4 +8,4 @@ BootstrapperLocation = Mid(WScript.ScriptFullName, 1, InStrRev(WScript.ScriptFul
Call objShell.Run("""" & BootstrapperLocation & """ /Install", , True) Call objShell.Run("""" & BootstrapperLocation & """ /Install", , True)
WScript.Echo "Disco Client Bootstrapper Installed" WScript.Echo "Disco ICT Client Bootstrapper Installed"
@@ -10,8 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Disco.ClientBootstrapper</RootNamespace> <RootNamespace>Disco.ClientBootstrapper</RootNamespace>
<AssemblyName>Disco.ClientBootstrapper</AssemblyName> <AssemblyName>Disco.ClientBootstrapper</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl> <PublishUrl>publish\</PublishUrl>
<Install>true</Install> <Install>true</Install>
@@ -91,6 +90,9 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Disco.Client\Interop\EndpointDiscovery.cs">
<Link>Interop\EndpointDiscovery.cs</Link>
</Compile>
<Compile Include="..\Resources\Libraries\DotNetZip\Source\BZip2\BitWriter.cs"> <Compile Include="..\Resources\Libraries\DotNetZip\Source\BZip2\BitWriter.cs">
<Link>DotNetZip\BZip2\BitWriter.cs</Link> <Link>DotNetZip\BZip2\BitWriter.cs</Link>
</Compile> </Compile>
@@ -317,11 +319,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties BuildVersion_StartDate="2014/6/1" BuildVersion_DetectChanges="False" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildAction="Both" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" />
</VisualStudio>
</ProjectExtensions>
<PropertyGroup> <PropertyGroup>
<PostBuildEvent>COPY "$(TargetPath)" "$(ProjectDir)..\Disco.Web\ClientBin"</PostBuildEvent> <PostBuildEvent>COPY "$(TargetPath)" "$(ProjectDir)..\Disco.Web\ClientBin"</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
+2 -2
View File
@@ -85,7 +85,7 @@
this.labelVersion.Name = "labelVersion"; this.labelVersion.Name = "labelVersion";
this.labelVersion.Size = new System.Drawing.Size(167, 20); this.labelVersion.Size = new System.Drawing.Size(167, 20);
this.labelVersion.TabIndex = 0; this.labelVersion.TabIndex = 0;
this.labelVersion.Text = "Disco Bootstrapper v"; this.labelVersion.Text = "Disco ICT Bootstrapper v";
this.labelVersion.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelVersion.TextAlign = System.Drawing.ContentAlignment.TopRight;
// //
// FormStatus // FormStatus
@@ -106,7 +106,7 @@
this.ShowIcon = false; this.ShowIcon = false;
this.ShowInTaskbar = false; this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Disco - Client Bootstrapper"; this.Text = "Disco ICT - Client Bootstrapper";
this.TopMost = true; this.TopMost = true;
this.TransparencyKey = System.Drawing.Color.Magenta; this.TransparencyKey = System.Drawing.Color.Magenta;
this.ResumeLayout(false); this.ResumeLayout(false);
+20 -26
View File
@@ -1,10 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
namespace Disco.ClientBootstrapper namespace Disco.ClientBootstrapper
@@ -12,69 +6,69 @@ namespace Disco.ClientBootstrapper
public partial class FormStatus : Form, IStatus public partial class FormStatus : Form, IStatus
{ {
private delegate void dUpdateStatus(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress, Nullable<int> Progress); private delegate void dUpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress);
private dUpdateStatus mUpdateStatus; private readonly dUpdateStatus mUpdateStatus;
public FormStatus() public FormStatus()
{ {
InitializeComponent(); InitializeComponent();
var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
this.labelVersion.Text = string.Format("v{0}", version.ToString(3)); labelVersion.Text = $"v{version.ToString(3)}";
this.FormClosed += new FormClosedEventHandler(FormStatus_FormClosed); FormClosed += new FormClosedEventHandler(FormStatus_FormClosed);
mUpdateStatus = new dUpdateStatus(UpdateStatusDo); mUpdateStatus = new dUpdateStatus(UpdateStatusDo);
Cursor.Hide(); Cursor.Hide();
} }
void FormStatus_FormClosed(object sender, FormClosedEventArgs e) private void FormStatus_FormClosed(object sender, FormClosedEventArgs e)
{ {
Cursor.Show(); Cursor.Show();
Program.ExitApplication(); Program.ExitApplication();
} }
public void UpdateStatus(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress = null, Nullable<int> Progress = null) public void UpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress = null, int? Progress = null)
{ {
try try
{ {
this.Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress); Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress);
} }
catch (Exception) { } catch (Exception) { }
} }
private void UpdateStatusDo(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress, Nullable<int> Progress) private void UpdateStatusDo(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress)
{ {
if (Heading != null) if (Heading != null)
if (this.labelHeading.Text != Heading) if (labelHeading.Text != Heading)
this.labelHeading.Text = Heading; labelHeading.Text = Heading;
if (SubHeading != null) if (SubHeading != null)
if (this.labelSubHeading.Text != SubHeading) if (labelSubHeading.Text != SubHeading)
this.labelSubHeading.Text = SubHeading; labelSubHeading.Text = SubHeading;
if (Message != null) if (Message != null)
if (this.labelMessage.Text != Message) if (labelMessage.Text != Message)
this.labelMessage.Text = Message; labelMessage.Text = Message;
if (ShowProgress.HasValue) if (ShowProgress.HasValue)
{ {
if (ShowProgress.Value) if (ShowProgress.Value)
{ {
this.progressBar.Visible = true; progressBar.Visible = true;
if (Progress.HasValue) if (Progress.HasValue)
{ {
if (Progress.Value > 0) if (Progress.Value >= 0)
{ {
this.progressBar.Value = Math.Min(Progress.Value, 100); progressBar.Value = Math.Min(Progress.Value, 100);
this.progressBar.Style = ProgressBarStyle.Continuous; progressBar.Style = ProgressBarStyle.Continuous;
} }
else else
{ {
this.progressBar.Style = ProgressBarStyle.Marquee; progressBar.Style = ProgressBarStyle.Marquee;
} }
} }
} }
else else
{ {
this.progressBar.Visible = false; progressBar.Visible = false;
} }
} }
} }
+2 -7
View File
@@ -1,12 +1,7 @@
using System; namespace Disco.ClientBootstrapper
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.ClientBootstrapper
{ {
interface IStatus interface IStatus
{ {
void UpdateStatus(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress = null, Nullable<int> Progress = null); void UpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress = null, int? Progress = null);
} }
} }
+22 -28
View File
@@ -1,44 +1,36 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace Disco.ClientBootstrapper namespace Disco.ClientBootstrapper
{ {
class InstallLoop internal class InstallLoop
{ {
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private readonly string installLocation;
private readonly string wimImageId;
private readonly string tempPath;
private readonly Action completeCallback;
private readonly Uri forcedServerUrl;
public Thread LoopThread; public InstallLoop(string installLocation, string wimImageId, string tempPath, Action completeCallback, Uri forcedServerUrl)
public delegate void CompleteCallback();
private CompleteCallback mCompleteCallback;
private string InstallLocation;
private string WimImageId;
public InstallLoop(string InstallLocation, string WimImageId = null)
{ {
this.InstallLocation = InstallLocation; this.installLocation = installLocation;
this.WimImageId = WimImageId; this.wimImageId = wimImageId;
this.tempPath = tempPath;
this.completeCallback = completeCallback;
this.forcedServerUrl = forcedServerUrl;
} }
public void Start(CompleteCallback Callback) public void Start()
{ {
this.mCompleteCallback = Callback; var cancellationToken = cancellationTokenSource.Token;
this.LoopThread = new Thread(new ThreadStart(loopHost)); Task.Run(async () =>
this.LoopThread.Start();
}
private void loopHost()
{ {
try try
{ {
await Interop.InstallInterop.Install(installLocation, wimImageId, tempPath, forcedServerUrl, cancellationToken);
//Program.Status.UpdateStatus(null, null, "Testing UI"); completeCallback?.BeginInvoke(null, null);
//Program.SleepThread(5000, false);
Interop.InstallInterop.Install(this.InstallLocation, this.WimImageId);
if (this.mCompleteCallback != null)
{
this.mCompleteCallback.BeginInvoke(null, null);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -46,10 +38,12 @@ namespace Disco.ClientBootstrapper
return; return;
if (ex.GetType() == typeof(ThreadInterruptedException)) if (ex.GetType() == typeof(ThreadInterruptedException))
return; return;
if (ex.GetType() == typeof(OperationCanceledException))
return;
Program.WriteAppError(ex); Program.WriteAppError(ex);
throw; throw;
} }
}, cancellationToken);
} }
} }
} }
@@ -1,10 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.IO; using System.Threading;
using System.Threading.Tasks;
namespace Disco.ClientBootstrapper.Interop namespace Disco.ClientBootstrapper.Interop
{ {
@@ -16,16 +17,17 @@ namespace Disco.ClientBootstrapper.Interop
if (_tempCerts != null && _tempCerts.Count > 0) if (_tempCerts != null && _tempCerts.Count > 0)
{ {
Remove(StoreName.My, StoreLocation.LocalMachine, _tempCerts); Remove(StoreName.My, StoreLocation.LocalMachine, _tempCerts);
Remove(StoreName.CertificateAuthority, StoreLocation.LocalMachine, _tempCerts); // dont remove root/intermediate certs as they may be have installed by client
Remove(StoreName.Root, StoreLocation.LocalMachine, _tempCerts); //Remove(StoreName.CertificateAuthority, StoreLocation.LocalMachine, _tempCerts);
//Remove(StoreName.Root, StoreLocation.LocalMachine, _tempCerts);
} }
} }
public static void AddTempCerts() public static async Task AddTempCerts(CancellationToken cancellationToken)
{ {
if (_tempCerts == null) if (_tempCerts == null)
_tempCerts = new List<string>(); _tempCerts = new List<string>();
var inlineCertificateLocation = Program.InlinePath.Value; var inlineCertificateLocation = Path.GetDirectoryName(typeof(Program).Assembly.Location);
// Root Certificates // Root Certificates
try try
@@ -35,14 +37,15 @@ namespace Disco.ClientBootstrapper.Interop
{ {
foreach (var certFile in CertFiles) foreach (var certFile in CertFiles)
{ {
cancellationToken.ThrowIfCancellationRequested();
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password"); var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
var result = Add(StoreName.Root, StoreLocation.LocalMachine, cert); var result = Add(StoreName.Root, StoreLocation.LocalMachine, cert);
if (result) if (result)
{ {
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp")) if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
_tempCerts.Add(cert.SerialNumber); _tempCerts.Add(cert.SerialNumber);
Program.Status.UpdateStatus(null, null, string.Format("Added Root Certificate: {0}", cert.ShortSubjectName())); Program.Status.UpdateStatus(null, null, $"Added Root Certificate: {cert.ShortSubjectName()}");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
} }
} }
} }
@@ -60,14 +63,15 @@ namespace Disco.ClientBootstrapper.Interop
{ {
foreach (var certFile in CertFiles) foreach (var certFile in CertFiles)
{ {
cancellationToken.ThrowIfCancellationRequested();
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password"); var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
var result = Add(StoreName.CertificateAuthority, StoreLocation.LocalMachine, cert); var result = Add(StoreName.CertificateAuthority, StoreLocation.LocalMachine, cert);
if (result) if (result)
{ {
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp")) if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
_tempCerts.Add(cert.SerialNumber); _tempCerts.Add(cert.SerialNumber);
Program.Status.UpdateStatus(null, null, string.Format("Added Intermediate Certificate: {0}", cert.ShortSubjectName())); Program.Status.UpdateStatus(null, null, $"Added Intermediate Certificate: {cert.ShortSubjectName()}");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
} }
} }
} }
@@ -85,14 +89,15 @@ namespace Disco.ClientBootstrapper.Interop
{ {
foreach (var certFile in CertFiles) foreach (var certFile in CertFiles)
{ {
cancellationToken.ThrowIfCancellationRequested();
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
var result = Add(StoreName.My, StoreLocation.LocalMachine, cert); var result = Add(StoreName.My, StoreLocation.LocalMachine, cert);
if (result) if (result)
{ {
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp")) if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
_tempCerts.Add(cert.SerialNumber); _tempCerts.Add(cert.SerialNumber);
Program.Status.UpdateStatus(null, null, string.Format("Added Host Certificate: {0}", cert.ShortSubjectName())); Program.Status.UpdateStatus(null, null, $"Added Host Certificate: {cert.ShortSubjectName()}");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
} }
} }
} }
@@ -106,7 +111,26 @@ namespace Disco.ClientBootstrapper.Interop
public static string ShortSubjectName(this X509Certificate2 Certificate) public static string ShortSubjectName(this X509Certificate2 Certificate)
{ {
string s = Certificate.Subject; string s = Certificate.Subject;
return s.Substring(s.IndexOf("=") + 1, s.IndexOf(",") - s.IndexOf("=") - 1); if (string.IsNullOrWhiteSpace(s))
{
return $"Unknown Certificate: {Certificate.Thumbprint}";
}
else
{
if (s.Length > 3 && s.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
{
var nameLength = s.IndexOf(',') - 3;
if (nameLength > 0)
{
return s.Substring(3, nameLength);
}
else
{
return s.Substring(3);
}
}
return s;
}
} }
public static bool Add(StoreName StoreName, StoreLocation StoreLocation, X509Certificate2 Certificate) public static bool Add(StoreName StoreName, StoreLocation StoreLocation, X509Certificate2 Certificate)
+129 -106
View File
@@ -1,22 +1,20 @@
using System; using Microsoft.Win32;
using System.Collections.Generic; using System;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace Disco.ClientBootstrapper.Interop namespace Disco.ClientBootstrapper.Interop
{ {
public static class InstallInterop public static class InstallInterop
{ {
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags); private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
[Flags] [Flags]
enum MoveFileFlags private enum MoveFileFlags
{ {
MOVEFILE_REPLACE_EXISTING = 0x00000001, MOVEFILE_REPLACE_EXISTING = 0x00000001,
MOVEFILE_COPY_ALLOWED = 0x00000002, MOVEFILE_COPY_ALLOWED = 0x00000002,
@@ -26,19 +24,19 @@ namespace Disco.ClientBootstrapper.Interop
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020 MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
} }
private static void Install(string RootFilesystemLocation, RegistryKey RootRegistryLocation, string FilesystemInstallLocation, string VirtualRootFilesystemLocation) private static async Task Install(string rootFilesystemLocation, RegistryKey rootRegistryLocation, string filesystemInstallLocation, string virtualRootFilesystemLocation, Uri forcedServerUrl, CancellationToken cancellationToken)
{ {
var SourceLocation = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); var SourceLocation = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var InstallLocation = Path.Combine(RootFilesystemLocation, FilesystemInstallLocation); var InstallLocation = Path.Combine(rootFilesystemLocation, filesystemInstallLocation);
var BootstrapperCmdLinePath = Path.Combine(VirtualRootFilesystemLocation, FilesystemInstallLocation, "Disco.ClientBootstrapper.exe"); var BootstrapperCmdLinePath = Path.Combine(virtualRootFilesystemLocation, filesystemInstallLocation, "Disco.ClientBootstrapper.exe");
var GroupPolicyScriptsIniLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\scripts.ini"); var GroupPolicyScriptsIniLocation = Path.Combine(rootFilesystemLocation, @"Windows\System32\GroupPolicy\Machine\Scripts\scripts.ini");
var GroupPolicyScriptsIniBackupLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\disco_scripts.ini"); var GroupPolicyScriptsIniBackupLocation = Path.Combine(rootFilesystemLocation, @"Windows\System32\GroupPolicy\Machine\Scripts\disco_scripts.ini");
// Create file system Location // Create file system Location
#region "Create File System Location" #region "Create File System Location"
Program.Status.UpdateStatus(null, null, "Creating Installation Location"); Program.Status.UpdateStatus(null, null, "Creating Installation Location");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
if (Directory.Exists(InstallLocation)) if (Directory.Exists(InstallLocation))
{ {
// Try and Delete Directory // Try and Delete Directory
@@ -48,7 +46,7 @@ namespace Disco.ClientBootstrapper.Interop
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new IOException(string.Format("Unable to delete folder: ", InstallLocation), ex); throw new IOException($"Unable to delete folder: {InstallLocation}", ex);
} }
} }
if (!Directory.Exists(InstallLocation)) if (!Directory.Exists(InstallLocation))
@@ -56,19 +54,23 @@ namespace Disco.ClientBootstrapper.Interop
var installDir = Directory.CreateDirectory(InstallLocation); var installDir = Directory.CreateDirectory(InstallLocation);
installDir.Attributes = installDir.Attributes | FileAttributes.Hidden; installDir.Attributes = installDir.Attributes | FileAttributes.Hidden;
} }
cancellationToken.ThrowIfCancellationRequested();
#endregion #endregion
// Copy files to file system location // Copy files to file system location
#region "Copy to File System" #region "Copy to File System"
Program.Status.UpdateStatus(null, null, "Copying Files"); Program.Status.UpdateStatus(null, null, "Copying Files");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
// Copy Bootstrapper // Copy Bootstrapper
// ie: Executing Assembly // ie: Executing Assembly
File.Copy(System.Reflection.Assembly.GetExecutingAssembly().Location, Path.Combine(InstallLocation, "Disco.ClientBootstrapper.exe")); File.Copy(System.Reflection.Assembly.GetExecutingAssembly().Location, Path.Combine(InstallLocation, "Disco.ClientBootstrapper.exe"));
cancellationToken.ThrowIfCancellationRequested();
foreach (var file in Directory.EnumerateFiles(SourceLocation)) foreach (var file in Directory.EnumerateFiles(SourceLocation))
{ {
cancellationToken.ThrowIfCancellationRequested();
var fileName = Path.GetFileName(file); var fileName = Path.GetFileName(file);
// Only Copy Certain Files // Only Copy Certain Files
@@ -90,7 +92,7 @@ namespace Disco.ClientBootstrapper.Interop
// Backup & Create Group Policy Scripts.ini // Backup & Create Group Policy Scripts.ini
#region "Group Policy Scripts.ini" #region "Group Policy Scripts.ini"
Program.Status.UpdateStatus(null, null, "Creating Group Policy Script Entry"); Program.Status.UpdateStatus(null, null, "Creating Group Policy Script Entry");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
// Backup // Backup
if (!File.Exists(GroupPolicyScriptsIniBackupLocation)) if (!File.Exists(GroupPolicyScriptsIniBackupLocation))
{ {
@@ -99,6 +101,7 @@ namespace Disco.ClientBootstrapper.Interop
File.Move(GroupPolicyScriptsIniLocation, GroupPolicyScriptsIniBackupLocation); File.Move(GroupPolicyScriptsIniLocation, GroupPolicyScriptsIniBackupLocation);
} }
} }
cancellationToken.ThrowIfCancellationRequested();
// Create // Create
if (File.Exists(GroupPolicyScriptsIniLocation)) if (File.Exists(GroupPolicyScriptsIniLocation))
@@ -109,56 +112,67 @@ namespace Disco.ClientBootstrapper.Interop
{ {
using (var scriptsIniStreamWriter = new StreamWriter(scriptsIniStream, Encoding.Unicode)) using (var scriptsIniStreamWriter = new StreamWriter(scriptsIniStream, Encoding.Unicode))
{ {
scriptsIniStreamWriter.Write(string.Format("[Startup]{0}0CmdLine={1}{0}0Parameters=/AllowUninstall", Environment.NewLine, BootstrapperCmdLinePath)); scriptsIniStreamWriter.WriteLine("[Startup]");
scriptsIniStreamWriter.Flush(); scriptsIniStreamWriter.WriteLine($"0CmdLine={BootstrapperCmdLinePath}");
if (forcedServerUrl == null)
scriptsIniStreamWriter.WriteLine("0Parameters=/AllowUninstall");
else
scriptsIniStreamWriter.WriteLine($"0Parameters=/AllowUninstall {forcedServerUrl}");
} }
} }
cancellationToken.ThrowIfCancellationRequested();
#endregion #endregion
// Backup & Create Group Policy Registry // Backup & Create Group Policy Registry
#region "Group Policy Registry" #region "Group Policy Registry"
Program.Status.UpdateStatus(null, null, "Creating Group Policy Registry Entries"); Program.Status.UpdateStatus(null, null, "Creating Group Policy Registry Entries");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
// Backup Scripts // Backup Scripts
using (var regGroupPolicy = RootRegistryLocation.OpenSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy", true)) using (var regGroupPolicy = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy", true))
{ {
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts")) if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
{ {
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts"); RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
} }
} }
cancellationToken.ThrowIfCancellationRequested();
// Create Scripts // Create Scripts
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Shutdown").Dispose(); rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown").Dispose();
using (var regScriptsStartup = RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup\\0")) using (var regScriptsStartup = rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0"))
{ {
regScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String); regScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
regScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String); regScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
regScriptsStartup.SetValue("FileSysPath", Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine"), RegistryValueKind.String); regScriptsStartup.SetValue("FileSysPath", Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine"), RegistryValueKind.String);
regScriptsStartup.SetValue("DisplayName", "Local Group Policy", RegistryValueKind.String); regScriptsStartup.SetValue("DisplayName", "Local Group Policy", RegistryValueKind.String);
regScriptsStartup.SetValue("GPOName", "Local Group Policy", RegistryValueKind.String); regScriptsStartup.SetValue("GPOName", "Local Group Policy", RegistryValueKind.String);
regScriptsStartup.SetValue("PSScriptOrder", 1, RegistryValueKind.DWord); regScriptsStartup.SetValue("PSScriptOrder", 1, RegistryValueKind.DWord);
using (var regScriptsStartup0 = regScriptsStartup.CreateSubKey("0")) using (var regScriptsStartup0 = regScriptsStartup.CreateSubKey("0"))
{ {
regScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String); regScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
if (forcedServerUrl == null)
regScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String); regScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
else
regScriptsStartup0.SetValue("Parameters", $"/AllowUninstall {forcedServerUrl}", RegistryValueKind.String);
regScriptsStartup0.SetValue("IsPowershell", 0, RegistryValueKind.DWord); regScriptsStartup0.SetValue("IsPowershell", 0, RegistryValueKind.DWord);
regScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary); regScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary);
} }
} }
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Shutdown").Dispose(); rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown").Dispose();
cancellationToken.ThrowIfCancellationRequested();
// Backup Scripts State // Backup Scripts State
using (var regGroupPolicy = RootRegistryLocation.OpenSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine", true)) using (var regGroupPolicy = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine", true))
{ {
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts")) if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
{ {
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts"); RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
} }
} }
cancellationToken.ThrowIfCancellationRequested();
// Create Scripts State // Create Scripts State
using (var regStateScriptsStartup = RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Startup\\0")) using (var regStateScriptsStartup = rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0"))
{ {
regStateScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String); regStateScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
regStateScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String); regStateScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
@@ -169,17 +183,21 @@ namespace Disco.ClientBootstrapper.Interop
using (var regStateScriptsStartup0 = regStateScriptsStartup.CreateSubKey("0")) using (var regStateScriptsStartup0 = regStateScriptsStartup.CreateSubKey("0"))
{ {
regStateScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String); regStateScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
if (forcedServerUrl == null)
regStateScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String); regStateScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
else
regStateScriptsStartup0.SetValue("Parameters", $"/AllowUninstall {forcedServerUrl}", RegistryValueKind.String);
regStateScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary); regStateScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary);
} }
} }
cancellationToken.ThrowIfCancellationRequested();
#endregion #endregion
// Set Registry Startup Environment Policies // Set Registry Startup Environment Policies
#region "Registry Startup Policies" #region "Registry Startup Policies"
Program.Status.UpdateStatus(null, null, "Creating Startup Policy Registry Entries"); Program.Status.UpdateStatus(null, null, "Creating Startup Policy Registry Entries");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
using (var regWinlogon = RootRegistryLocation.OpenSubKey("Microsoft\\Windows NT\\CurrentVersion\\Winlogon", true)) using (var regWinlogon = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows NT\CurrentVersion\Winlogon", true))
{ {
regWinlogon.SetValue("HideStartupScripts", 0, RegistryValueKind.DWord); regWinlogon.SetValue("HideStartupScripts", 0, RegistryValueKind.DWord);
regWinlogon.SetValue("RunStartupScriptSync", 1, RegistryValueKind.DWord); regWinlogon.SetValue("RunStartupScriptSync", 1, RegistryValueKind.DWord);
@@ -187,96 +205,112 @@ namespace Disco.ClientBootstrapper.Interop
#endregion #endregion
} }
public static void Install(string InstallLocation, string WimImageId = null) public static async Task Install(string installLocation, string wimImageId, string tempPath, Uri forcedServerUrl, CancellationToken cancellationToken)
{ {
Program.Status.UpdateStatus("Installing Bootstrapper", "Starting", "Please wait...", false); Program.Status.UpdateStatus("Installing Bootstrapper", "Starting", "Please wait...", false);
if (string.IsNullOrWhiteSpace(InstallLocation)) if (string.IsNullOrWhiteSpace(installLocation))
InstallLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco"); installLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco");
if (InstallLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase)) cancellationToken.ThrowIfCancellationRequested();
if (installLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase))
{ {
// Offline File System (WIM) // Offline File System (WIM)
Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", string.Format("Install Location: {0}", InstallLocation)); Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", $"Install Location: {installLocation}");
Program.SleepThread(1000, false); await Program.SleepThread(1000, false, cancellationToken);
// Mount WIM // Mount WIM
int wimImageIndex = 0; int wimImageIndex = 0;
using (var wim = new Interop.WIMInterop.WindowsImageContainer(InstallLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write)) using (var wim = new WIMInterop.WindowsImageContainer(installLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write))
{ {
if (WimImageId == null) cancellationToken.ThrowIfCancellationRequested();
WimImageId = "1"; if (wimImageId == null)
if (!int.TryParse(WimImageId, out wimImageIndex)) wimImageId = "1";
if (!int.TryParse(wimImageId, out wimImageIndex))
{ {
Program.Status.UpdateStatus(null, "Analysing WIM", string.Format("Looking for Image Name: {0}", WimImageId)); Program.Status.UpdateStatus(null, "Analysing WIM", $"Looking for Image Name: {wimImageId}");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
for (int i = 0; i < wim.ImageCount; i++) for (int i = 0; i < wim.ImageCount; i++)
{ {
var wimImageInfo = new System.Xml.XmlDocument(); var wimImageInfo = new System.Xml.XmlDocument();
using (var wimImage = wim[i]) using (var wimImage = wim[i])
wimImageInfo.LoadXml(wimImage.ImageInformation); wimImageInfo.LoadXml(wimImage.ImageInformation);
var wimImageInfoName = wimImageInfo.SelectSingleNode("//IMAGE/NAME"); var wimImageInfoName = wimImageInfo.SelectSingleNode("//IMAGE/NAME");
if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(WimImageId, StringComparison.OrdinalIgnoreCase)) if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(wimImageId, StringComparison.OrdinalIgnoreCase))
{ {
wimImageIndex = i + 1; wimImageIndex = i + 1;
Program.Status.UpdateStatus(null, "Analysing WIM", string.Format("Found Image Id '{0}' at Index {1}", WimImageId, wimImageIndex)); Program.Status.UpdateStatus(null, "Analysing WIM", $"Found Image Id '{wimImageId}' at Index {wimImageIndex}");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
break; break;
} }
} }
} }
} }
cancellationToken.ThrowIfCancellationRequested();
if (wimImageIndex == 0) if (wimImageIndex == 0)
{ {
Program.Status.UpdateStatus(null, "Error", string.Format("Unable to load WIM Image Id: {0}", WimImageId)); Program.Status.UpdateStatus(null, "Error", $"Unable to load WIM Image Id: {wimImageId}");
Program.SleepThread(5000, false); await Program.SleepThread(5000, false, cancellationToken);
return; return;
} }
// Get Temp Path // Get Temp Path
var wimMountPath = Path.Combine(Path.GetTempPath(), "DiscoClientBootstrapperWimMount"); var wimMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimMount");
if (Directory.Exists(wimMountPath)) if (Directory.Exists(wimMountPath))
Directory.Delete(wimMountPath, true); Directory.Delete(wimMountPath, true);
Directory.CreateDirectory(wimMountPath); Directory.CreateDirectory(wimMountPath);
var wimTempMountPath = Path.Combine(Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount"); cancellationToken.ThrowIfCancellationRequested();
var wimTempMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount");
if (Directory.Exists(wimTempMountPath)) if (Directory.Exists(wimTempMountPath))
Directory.Delete(wimTempMountPath, true); Directory.Delete(wimTempMountPath, true);
Directory.CreateDirectory(wimTempMountPath); Directory.CreateDirectory(wimTempMountPath);
cancellationToken.ThrowIfCancellationRequested();
bool wimCommitChanges = true; bool wimCommitChanges = true;
Interop.WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback m_MessageCallback = null; WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback m_MessageCallback = null;
try try
{ {
// Mount WIM // Mount WIM
Program.Status.UpdateStatus(null, "Mounting WIM", string.Format("Mounting WIM Image to '{0}'", wimMountPath)); Program.Status.UpdateStatus(null, "Mounting WIM", $"Mounting WIM Image to '{wimMountPath}'");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
m_MessageCallback = new Interop.WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback(WimImageEventMessagePump); m_MessageCallback = new WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback(WimImageEventMessagePump);
Interop.WIMInterop.WindowsImageContainer.NativeMethods.RegisterCallback(m_MessageCallback); WIMInterop.WindowsImageContainer.NativeMethods.RegisterCallback(m_MessageCallback);
Interop.WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, InstallLocation, wimImageIndex, wimTempMountPath); WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, installLocation, wimImageIndex, wimTempMountPath);
// Load Local Machine Registry // Load Local Machine Registry
var wimHivePath = Path.Combine(wimMountPath, "Windows\\System32\\config\\SOFTWARE"); var wimHivePath = Path.Combine(wimMountPath, "Windows\\System32\\config\\SOFTWARE");
Program.Status.UpdateStatus(null, "Mounting Offline Registry Hive", string.Format("Mounting Offline Registry Hive at '{0}'", wimHivePath)); Program.Status.UpdateStatus(null, "Mounting Offline Registry Hive", $"Mounting Offline Registry Hive at '{wimHivePath}'");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
using (var wimReg = new Interop.RegistryInterop(RegistryInterop.RegistryHives.HKEY_LOCAL_MACHINE, "DiscoClientBootstrapperWimHive", wimHivePath)) using (var wimReg = new RegistryInterop(RegistryInterop.RegistryHives.HKEY_LOCAL_MACHINE, "DiscoClientBootstrapperWimHive", wimHivePath))
{ {
try
{
cancellationToken.ThrowIfCancellationRequested();
using (RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("DiscoClientBootstrapperWimHive", true)) using (RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("DiscoClientBootstrapperWimHive", true))
{ {
string rootFileSystemLocation = wimMountPath; string rootFileSystemLocation = wimMountPath;
string fileSystemInstallLocation = "Disco"; string fileSystemInstallLocation = "Disco";
string virtualRootFileSystemLocation = "C:\\"; string virtualRootFileSystemLocation = "C:\\";
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation); cancellationToken.ThrowIfCancellationRequested();
await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation, forcedServerUrl, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
} }
}
finally
{
// Unload Local Machine Registry // Unload Local Machine Registry
Program.Status.UpdateStatus(null, "Unmounting Offline Registry Hive", string.Format("Unmounting Offline Registry Hive at '{0}'", wimHivePath)); Program.Status.UpdateStatus(null, "Unmounting Offline Registry Hive", $"Unmounting Offline Registry Hive at '{wimHivePath}'");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
wimReg.Unload(); wimReg.Unload();
} }
} }
}
catch (Exception) catch (Exception)
{ {
wimCommitChanges = false; wimCommitChanges = false;
@@ -285,13 +319,13 @@ namespace Disco.ClientBootstrapper.Interop
finally finally
{ {
// Unmount WIM // Unmount WIM
Program.Status.UpdateStatus(null, "Unmounting WIM", string.Format("Unmounting WIM Image at '{0}'", wimMountPath)); Program.Status.UpdateStatus(null, "Unmounting WIM", $"Unmounting WIM Image at '{wimMountPath}'");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
Interop.WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, InstallLocation, wimImageIndex, wimCommitChanges); WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, installLocation, wimImageIndex, wimCommitChanges);
if (m_MessageCallback != null) if (m_MessageCallback != null)
{ {
Interop.WIMInterop.WindowsImageContainer.NativeMethods.UnregisterMessageCallback(m_MessageCallback); WIMInterop.WindowsImageContainer.NativeMethods.UnregisterMessageCallback(m_MessageCallback);
m_MessageCallback = null; m_MessageCallback = null;
} }
@@ -299,23 +333,25 @@ namespace Disco.ClientBootstrapper.Interop
Directory.Delete(wimMountPath, true); Directory.Delete(wimMountPath, true);
if (Directory.Exists(wimTempMountPath)) if (Directory.Exists(wimTempMountPath))
Directory.Delete(wimTempMountPath, true); Directory.Delete(wimTempMountPath, true);
cancellationToken.ThrowIfCancellationRequested();
} }
} }
else else
{ {
// Online File System // Online File System
Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", string.Format("Install Location: {0}", InstallLocation), true, -1); Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", $"Install Location: {installLocation}", true, -1);
Program.SleepThread(1000, false); await Program.SleepThread(1000, false, cancellationToken);
string rootFileSystemLocation = Path.GetPathRoot(InstallLocation); string rootFileSystemLocation = Path.GetPathRoot(installLocation);
RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("SOFTWARE", true); RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("SOFTWARE", true);
string fileSystemInstallLocation = InstallLocation.Substring(rootFileSystemLocation.Length); string fileSystemInstallLocation = installLocation.Substring(rootFileSystemLocation.Length);
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation); await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation, forcedServerUrl, cancellationToken);
Program.Status.UpdateStatus(null, "Online File System Installation Complete", string.Empty, true, -1); Program.Status.UpdateStatus(null, "Online File System Installation Complete", string.Empty, true, -1);
Program.SleepThread(1000, false); await Program.SleepThread(1000, false, cancellationToken);
} }
Program.Status.UpdateStatus(null, "Complete", "Finished Installing Bootstrapper"); Program.Status.UpdateStatus(null, "Complete", "Finished Installing Bootstrapper");
Program.SleepThread(1500, false); await Program.SleepThread(1500, false, cancellationToken);
} }
private static uint WimImageEventMessagePump( private static uint WimImageEventMessagePump(
@@ -325,16 +361,16 @@ namespace Disco.ClientBootstrapper.Interop
IntPtr UserData IntPtr UserData
) )
{ {
uint status = (uint)Interop.WIMInterop.WindowsImageContainer.NativeMethods.WIMMessage.WIM_MSG_SUCCESS; uint status = (uint)WIMInterop.WindowsImageContainer.NativeMethods.WIMMessage.WIM_MSG_SUCCESS;
Interop.WIMInterop.DefaultImageEventArgs eventArgs = new Interop.WIMInterop.DefaultImageEventArgs(wParam, lParam, UserData); WIMInterop.DefaultImageEventArgs eventArgs = new WIMInterop.DefaultImageEventArgs(wParam, lParam, UserData);
//System.Diagnostics.Debug.WriteLine(MessageId); //System.Diagnostics.Debug.WriteLine(MessageId);
switch ((Interop.WIMInterop.WindowsImageContainer.ImageEventMessage)MessageId) switch ((WIMInterop.WindowsImageContainer.ImageEventMessage)MessageId)
{ {
case Interop.WIMInterop.WindowsImageContainer.ImageEventMessage.Progress: case WIMInterop.WindowsImageContainer.ImageEventMessage.Progress:
case Interop.WIMInterop.WindowsImageContainer.ImageEventMessage.MountCleanupProgress: case WIMInterop.WindowsImageContainer.ImageEventMessage.MountCleanupProgress:
var timeRemainingMil = eventArgs.LeftParameter.ToInt32(); var timeRemainingMil = eventArgs.LeftParameter.ToInt32();
string timeRemainingMessage; string timeRemainingMessage;
if (timeRemainingMil > 0) if (timeRemainingMil > 0)
@@ -343,7 +379,7 @@ namespace Disco.ClientBootstrapper.Interop
timeRemainingMessage = "Calculating, please wait..."; timeRemainingMessage = "Calculating, please wait...";
var progress = eventArgs.WideParameter.ToInt32(); var progress = eventArgs.WideParameter.ToInt32();
Program.Status.UpdateStatus(null, null, string.Format("Time remaining: {0}", timeRemainingMessage), true, progress); Program.Status.UpdateStatus(null, null, $"Time remaining: {timeRemainingMessage}", true, progress);
break; break;
default: default:
@@ -353,41 +389,28 @@ namespace Disco.ClientBootstrapper.Interop
return status; return status;
} }
public static void Uninstall() public static async Task Uninstall(CancellationToken cancellationToken)
{ {
// Application Directory // Application Directory
var appDirectory = Program.InlinePath.Value; var appDirectory = Path.GetDirectoryName(typeof(Program).Assembly.Location);
if (Program.AllowUninstall && !appDirectory.StartsWith("\\\\")) if (Program.AllowUninstall && !appDirectory.StartsWith(@"\\"))
{ {
Program.Status.UpdateStatus("System Preparation (Bootstrapper)", "Uninstalling Bootstrapper...", string.Empty, false, 0); Program.Status.UpdateStatus("System Preparation (Bootstrapper)", "Uninstalling Bootstrapper...", string.Empty, false, 0);
Program.SleepThread(1000, true); await Program.SleepThread(1000, true, cancellationToken);
//var uninstallScriptLocation = System.IO.Path.Combine(appDirectory, "UninstallBootstrapper.vbs");
//if (System.IO.File.Exists(uninstallScriptLocation))
//{
// var bootstrapperPID = System.Diagnostics.Process.GetCurrentProcess().Id;
// var cscriptPath = System.IO.Path.Combine(Environment.SystemDirectory, "cscript.exe");
// var cscriptArgs = string.Format("\"{0}\" /WaitForProcessID:{1}", uninstallScriptLocation, bootstrapperPID);
// var startProc = new ProcessStartInfo(cscriptPath, cscriptArgs);
// startProc.WorkingDirectory = Environment.SystemDirectory;
// startProc.WindowStyle = ProcessWindowStyle.Hidden;
// Process.Start(startProc);
//}
// Remove Registry Entries // Remove Registry Entries
using (var regWinlogon = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", true)) using (var regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
{ {
regWinlogon.DeleteValue("HideStartupScripts", false); regWinlogon.DeleteValue("HideStartupScripts", false);
regWinlogon.DeleteValue("RunStartupScriptSync", false); regWinlogon.DeleteValue("RunStartupScriptSync", false);
} }
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Shutdown", false); Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown", false);
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup", false); Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup", false);
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Shutdown", false); Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown", false);
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Startup", false); Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup", false);
// Restore Registry Backups // Restore Registry Backups
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy", true)) using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy", true))
{ {
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts")) if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
{ {
@@ -395,7 +418,7 @@ namespace Disco.ClientBootstrapper.Interop
RegistryUtilities.RenameSubKey(regGroupPolicy, "Disco_Scripts", "Scripts"); RegistryUtilities.RenameSubKey(regGroupPolicy, "Disco_Scripts", "Scripts");
} }
} }
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine", true)) using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine", true))
{ {
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts")) if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
{ {
@@ -405,10 +428,10 @@ namespace Disco.ClientBootstrapper.Interop
} }
// Delete Group Policy Script File // Delete Group Policy Script File
var groupPolicyScriptsPath = Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine\\Scripts\\scripts.ini"); var groupPolicyScriptsPath = Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine\Scripts\scripts.ini");
if (File.Exists(groupPolicyScriptsPath)) if (File.Exists(groupPolicyScriptsPath))
File.Delete(groupPolicyScriptsPath); File.Delete(groupPolicyScriptsPath);
var groupPolicyScriptsBackupPath = Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine\\Scripts\\disco_scripts.ini"); var groupPolicyScriptsBackupPath = Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine\Scripts\disco_scripts.ini");
if (File.Exists(groupPolicyScriptsBackupPath)) if (File.Exists(groupPolicyScriptsBackupPath))
File.Move(groupPolicyScriptsBackupPath, groupPolicyScriptsPath); File.Move(groupPolicyScriptsBackupPath, groupPolicyScriptsPath);
@@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management; using System.Management;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -17,8 +14,8 @@ namespace Disco.ClientBootstrapper.Interop
public string Name { get; set; } public string Name { get; set; }
public string NetConnectionID { get; set; } public string NetConnectionID { get; set; }
public string MACAddress { get; set; } public string MACAddress { get; set; }
public UInt64 Speed { get; set; } public ulong Speed { get; set; }
public UInt16 LastConnectionStatus { get; set; } public ushort LastConnectionStatus { get; set; }
public bool IsWireless { get; set; } public bool IsWireless { get; set; }
public string WirelessInterfaceDescription { get; set; } public string WirelessInterfaceDescription { get; set; }
public int LastWirelessConnectionStatus { get; set; } public int LastWirelessConnectionStatus { get; set; }
@@ -30,51 +27,51 @@ namespace Disco.ClientBootstrapper.Interop
private void UpdateFromWmi(ManagementObject wmiObject) private void UpdateFromWmi(ManagementObject wmiObject)
{ {
this.WmiPath = (string)wmiObject.GetPropertyValue("__PATH"); WmiPath = (string)wmiObject.GetPropertyValue("__PATH");
this.Index = (UInt32)wmiObject.GetPropertyValue("Index"); Index = (uint)wmiObject.GetPropertyValue("Index");
this.Guid = Guid.Parse((string)wmiObject.GetPropertyValue("GUID")); Guid = Guid.Parse((string)wmiObject.GetPropertyValue("GUID"));
this.MACAddress = (string)wmiObject.GetPropertyValue("MACAddress"); MACAddress = (string)wmiObject.GetPropertyValue("MACAddress");
this.Name = (string)wmiObject.GetPropertyValue("Name"); Name = (string)wmiObject.GetPropertyValue("Name");
this.NetConnectionID = (string)wmiObject.GetPropertyValue("NetConnectionID"); NetConnectionID = (string)wmiObject.GetPropertyValue("NetConnectionID");
this.Speed = (UInt64)wmiObject.GetPropertyValue("Speed"); Speed = (ulong)wmiObject.GetPropertyValue("Speed");
var connectionStatus = ConnectionStatus; _ = ConnectionStatus;
this.IsWireless = true; IsWireless = true;
try try
{ {
var wirelessConnectionStatus = WirelessConnectionStatus; var wirelessConnectionStatus = WirelessConnectionStatus;
} }
catch (Exception) { catch (Exception)
this.IsWireless = false; {
}; IsWireless = false;
}
} }
public int WirelessConnectionStatus public int WirelessConnectionStatus
{ {
get { get
if (this.IsWireless) {
if (IsWireless)
{ {
IntPtr handle = IntPtr.Zero; IntPtr handle = IntPtr.Zero;
uint negotiatedVersion;
try try
{ {
if (NetworkInterop.WlanOpenHandle(1, IntPtr.Zero, out negotiatedVersion, ref handle) != 0) if (NetworkInterop.WlanOpenHandle(1, IntPtr.Zero, out var negotiatedVersion, ref handle) != 0)
throw new NotSupportedException("This network adapter does not support Wireless"); throw new NotSupportedException("This network adapter does not support Wireless");
IntPtr ptr = new IntPtr(); IntPtr ptr = new IntPtr();
uint dataSize;
var interfaceGuid = this.Guid; var interfaceGuid = Guid;
if (NetworkInterop.WlanQueryInterface(handle, ref interfaceGuid, NetworkInterop.WLAN_INTF_OPCODE.wlan_intf_opcode_interface_state, IntPtr.Zero, out dataSize, ref ptr, IntPtr.Zero) != 0) if (NetworkInterop.WlanQueryInterface(handle, ref interfaceGuid, NetworkInterop.WLAN_INTF_OPCODE.wlan_intf_opcode_interface_state, IntPtr.Zero, out var dataSize, ref ptr, IntPtr.Zero) != 0)
throw new NotSupportedException("This network adapter does not support Wireless"); throw new NotSupportedException("This network adapter does not support Wireless");
this.LastWirelessConnectionStatus = Marshal.ReadInt32(ptr); LastWirelessConnectionStatus = Marshal.ReadInt32(ptr);
NetworkInterop.WlanFreeMemory(ptr); NetworkInterop.WlanFreeMemory(ptr);
return this.LastWirelessConnectionStatus; return LastWirelessConnectionStatus;
} }
finally finally
{ {
@@ -114,46 +111,46 @@ namespace Disco.ClientBootstrapper.Interop
} }
} }
public UInt16 ConnectionStatus public ushort ConnectionStatus
{ {
get get
{ {
using (var wmiObject = new ManagementObject(this.WmiPath)) using (var wmiObject = new ManagementObject(WmiPath))
{ {
this.LastConnectionStatus = (UInt16)wmiObject.GetPropertyValue("NetConnectionStatus"); LastConnectionStatus = (ushort)wmiObject.GetPropertyValue("NetConnectionStatus");
} }
return this.LastConnectionStatus; return LastConnectionStatus;
} }
} }
public string ConnectionStatusMeaning(UInt16 status) public string ConnectionStatusMeaning(ushort status)
{ {
switch (status) switch (status)
{ {
case (UInt16)0: case (ushort)0:
return "Disconnected"; return "Disconnected";
case (UInt16)1: case (ushort)1:
return "Connecting"; return "Connecting";
case (UInt16)2: case (ushort)2:
return "Connected"; return "Connected";
case (UInt16)3: case (ushort)3:
return "Disconnecting"; return "Disconnecting";
case (UInt16)4: case (ushort)4:
return "Hardware not present"; return "Hardware not present";
case (UInt16)5: case (ushort)5:
return "Hardware disabled"; return "Hardware disabled";
case (UInt16)6: case (ushort)6:
return "Hardware malfunction"; return "Hardware malfunction";
case (UInt16)7: case (ushort)7:
return "Media disconnected"; return "Media disconnected";
case (UInt16)8: case (ushort)8:
return "Authenticating"; return "Authenticating";
case (UInt16)9: case (ushort)9:
return "Authentication succeeded"; return "Authentication succeeded";
case (UInt16)10: case (ushort)10:
return "Authentication failed"; return "Authentication failed";
case (UInt16)11: case (ushort)11:
return "Invalid address"; return "Invalid address";
case (UInt16)12: case (ushort)12:
return "Credentials required"; return "Credentials required";
default: default:
return "Unknown"; return "Unknown";
@@ -1,15 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Net.NetworkInformation;
using System.Management; using System.Management;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Xml; using System.Xml;
namespace Disco.ClientBootstrapper.Interop namespace Disco.ClientBootstrapper.Interop
{ {
static class NetworkInterop internal static class NetworkInterop
{ {
#region PInvoke #region PInvoke
@@ -165,30 +167,35 @@ namespace Disco.ClientBootstrapper.Interop
} }
} }
public static bool PingDisco() public static bool HasNetworkConnectivity()
{ {
using (Ping p = new Ping()) var nics = NetworkInterface.GetAllNetworkInterfaces()
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
.ToList();
foreach (var nic in nics)
{ {
try if (nic.Supports(NetworkInterfaceComponent.IPv4))
{ {
PingReply pr = p.Send("disco", 2000); var ipProps = nic.GetIPProperties();
if (pr.Status == IPStatus.Success) var ipv4Props = ipProps.GetIPv4Properties();
return true; if (ipv4Props.IsAutomaticPrivateAddressingActive)
else continue;
return false;
} return ipProps.UnicastAddresses
catch (Exception) .Where(ua => ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{ .Any();
return false;
}
} }
} }
public static void ConfigureWireless() return false;
}
public static async Task ConfigureWireless(CancellationToken cancellationToken)
{ {
// Add Certificates // Add Certificates
Program.Status.UpdateStatus(null, null, "Configuring Wireless Certificates"); Program.Status.UpdateStatus(null, null, "Configuring Wireless Certificates");
CertificateInterop.AddTempCerts(); await CertificateInterop.AddTempCerts(cancellationToken);
// Add Wireless Profiles // Add Wireless Profiles
Program.Status.UpdateStatus(null, null, "Configuring Wireless Profiles"); Program.Status.UpdateStatus(null, null, "Configuring Wireless Profiles");
@@ -197,11 +204,10 @@ namespace Disco.ClientBootstrapper.Interop
{ {
IntPtr wlanHandle = IntPtr.Zero; IntPtr wlanHandle = IntPtr.Zero;
uint negotiatedVersion;
try try
{ {
if (WlanOpenHandle(1, IntPtr.Zero, out negotiatedVersion, ref wlanHandle) != 0) if (WlanOpenHandle(1, IntPtr.Zero, out var negotiatedVersion, ref wlanHandle) != 0)
throw new NotSupportedException("This device does not support Wireless"); throw new NotSupportedException("This device does not support Wireless");
// Add Profile to Each Wireless Adapter // Add Profile to Each Wireless Adapter
@@ -210,15 +216,16 @@ namespace Disco.ClientBootstrapper.Interop
{ {
foreach (var inlineWirelessProfile in wirelessInlineProfiles) foreach (var inlineWirelessProfile in wirelessInlineProfiles)
{ {
cancellationToken.ThrowIfCancellationRequested();
if (inlineWirelessProfile.AddProfile(wlanHandle, na.Guid)) if (inlineWirelessProfile.AddProfile(wlanHandle, na.Guid))
{ {
Program.Status.UpdateStatus(null, null, string.Format("Added Wireless Profile: {0}", inlineWirelessProfile.ProfileName)); Program.Status.UpdateStatus(null, null, $"Added Wireless Profile: {inlineWirelessProfile.ProfileName}");
Program.SleepThread(500, false); await Program.SleepThread(500, false, cancellationToken);
} }
else else
{ {
Program.Status.UpdateStatus(null, null, string.Format("Unable to add Wireless Profile: {0}", inlineWirelessProfile.ProfileName)); Program.Status.UpdateStatus(null, null, $"Unable to add Wireless Profile: {inlineWirelessProfile.ProfileName}");
Program.SleepThread(5000, false); await Program.SleepThread(5000, false, cancellationToken);
} }
} }
} }
@@ -226,7 +233,7 @@ namespace Disco.ClientBootstrapper.Interop
finally finally
{ {
if (wlanHandle != IntPtr.Zero) if (wlanHandle != IntPtr.Zero)
NetworkInterop.WlanCloseHandle(wlanHandle, IntPtr.Zero); WlanCloseHandle(wlanHandle, IntPtr.Zero);
} }
} }
@@ -240,23 +247,23 @@ namespace Disco.ClientBootstrapper.Interop
public bool AddProfile(IntPtr WlanHandle, Guid interfaceGuid) public bool AddProfile(IntPtr WlanHandle, Guid interfaceGuid)
{ {
var pInterfaceGuid = interfaceGuid; var pInterfaceGuid = interfaceGuid;
var pProfileXml = this.ProfileXml; var pProfileXml = ProfileXml;
uint pFlag = 0; uint pFlag = 0;
uint failReason; return WlanSetProfile(WlanHandle, ref pInterfaceGuid, pFlag, pProfileXml, null, true, IntPtr.Zero, out _) == 0;
return (WlanSetProfile(WlanHandle, ref pInterfaceGuid, pFlag, pProfileXml, null, true, IntPtr.Zero, out failReason) == 0);
} }
} }
private static List<WirelessProfile> GetInlineWirelessProfiles() private static List<WirelessProfile> GetInlineWirelessProfiles()
{ {
var inlineProfileFiles = System.IO.Directory.EnumerateFiles(Program.InlinePath.Value, "WLAN_Profile_*.xml").ToList(); var directoryPath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
var inlineProfileFiles = Directory.EnumerateFiles(directoryPath, "WLAN_Profile_*.xml").ToList();
var inlineProfiles = new List<WirelessProfile>(inlineProfileFiles.Count); var inlineProfiles = new List<WirelessProfile>(inlineProfileFiles.Count);
foreach (var filename in inlineProfileFiles) foreach (var filename in inlineProfileFiles)
{ {
var profile = new WirelessProfile() var profile = new WirelessProfile()
{ {
Filename = filename, Filename = filename,
ProfileXml = System.IO.File.ReadAllText(filename) ProfileXml = File.ReadAllText(filename)
}; };
var profileXml = new XmlDocument(); var profileXml = new XmlDocument();
profileXml.LoadXml(profile.ProfileXml); profileXml.LoadXml(profile.ProfileXml);
@@ -280,7 +287,7 @@ namespace Disco.ClientBootstrapper.Interop
WlanGetProfileList(WlanHandle, ref pInterfaceGuid, new IntPtr(), ref ppProfileList); WlanGetProfileList(WlanHandle, ref pInterfaceGuid, new IntPtr(), ref ppProfileList);
WLAN_PROFILE_INFO_LIST wlanProfileInfoList = new WLAN_PROFILE_INFO_LIST(ppProfileList); WLAN_PROFILE_INFO_LIST wlanProfileInfoList = new WLAN_PROFILE_INFO_LIST(ppProfileList);
NetworkInterop.WlanFreeMemory(ppProfileList); WlanFreeMemory(ppProfileList);
return wlanProfileInfoList; return wlanProfileInfoList;
} }
@@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Disco.ClientBootstrapper.Interop namespace Disco.ClientBootstrapper.Interop
@@ -33,7 +30,7 @@ namespace Disco.ClientBootstrapper.Interop
private static extern int LookupPrivilegeValue(string lpsystemname, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid); private static extern int LookupPrivilegeValue(string lpsystemname, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)] [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
private static extern int AdjustTokenPrivileges(int tokenhandle, int disableprivs, [MarshalAs(UnmanagedType.Struct)]ref TOKEN_PRIVILEGES Newstate, int bufferlength, int PreivousState, int Returnlength); private static extern int AdjustTokenPrivileges(int tokenhandle, int disableprivs, [MarshalAs(UnmanagedType.Struct)] ref TOKEN_PRIVILEGES Newstate, int bufferlength, int PreivousState, int Returnlength);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegLoadKey(uint hKey, string lpSubKey, string lpFile); private static extern int RegLoadKey(uint hKey, string lpSubKey, string lpFile);
@@ -61,16 +58,15 @@ namespace Disco.ClientBootstrapper.Interop
public RegistryInterop(RegistryHives hive, string subKey, string filePath) public RegistryInterop(RegistryHives hive, string subKey, string filePath)
{ {
int token = 0; int token = 0;
int retval = 0;
TOKEN_PRIVILEGES TP = new TOKEN_PRIVILEGES(); TOKEN_PRIVILEGES TP = new TOKEN_PRIVILEGES();
TOKEN_PRIVILEGES TP2 = new TOKEN_PRIVILEGES(); TOKEN_PRIVILEGES TP2 = new TOKEN_PRIVILEGES();
LUID RestoreLuid = new LUID(); LUID RestoreLuid = new LUID();
LUID BackupLuid = new LUID(); LUID BackupLuid = new LUID();
retval = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token); OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token);
retval = LookupPrivilegeValue(null, SE_RESTORE_NAME, ref RestoreLuid); LookupPrivilegeValue(null, SE_RESTORE_NAME, ref RestoreLuid);
retval = LookupPrivilegeValue(null, SE_BACKUP_NAME, ref BackupLuid); LookupPrivilegeValue(null, SE_BACKUP_NAME, ref BackupLuid);
TP.PrivilegeCount = 1; TP.PrivilegeCount = 1;
TP.Attributes = SE_PRIVILEGE_ENABLED; TP.Attributes = SE_PRIVILEGE_ENABLED;
TP.Luid = RestoreLuid; TP.Luid = RestoreLuid;
@@ -78,25 +74,25 @@ namespace Disco.ClientBootstrapper.Interop
TP2.Attributes = SE_PRIVILEGE_ENABLED; TP2.Attributes = SE_PRIVILEGE_ENABLED;
TP2.Luid = BackupLuid; TP2.Luid = BackupLuid;
retval = AdjustTokenPrivileges(token, 0, ref TP, 1024, 0, 0); AdjustTokenPrivileges(token, 0, ref TP, 1024, 0, 0);
retval = AdjustTokenPrivileges(token, 0, ref TP2, 1024, 0, 0); AdjustTokenPrivileges(token, 0, ref TP2, 1024, 0, 0);
uint regHive = (uint)hive; uint regHive = (uint)hive;
this.Hive = hive; Hive = hive;
this.SubKey = subKey; SubKey = subKey;
RegLoadKey(regHive, subKey, filePath); RegLoadKey(regHive, subKey, filePath);
this.IsUnloaded = false; IsUnloaded = false;
} }
public void Unload() public void Unload()
{ {
if (!IsUnloaded) if (!IsUnloaded)
{ {
uint regHive = (uint)this.Hive; uint regHive = (uint)Hive;
string subKey = this.SubKey; string subKey = SubKey;
RegUnLoadKey(regHive, subKey); RegUnLoadKey(regHive, subKey);
this.IsUnloaded = true; IsUnloaded = true;
} }
} }
@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Disco.ClientBootstrapper.Interop namespace Disco.ClientBootstrapper.Interop
@@ -43,21 +39,20 @@ namespace Disco.ClientBootstrapper.Interop
// End Removed 2012-11-23 G# // End Removed 2012-11-23 G#
// Added 2012-11-23 G# - Migrate to Win32 PInvoke Shutdown // Added 2012-11-23 G# - Migrate to Win32 PInvoke Shutdown
bool result;
TokPriv1Luid tp; TokPriv1Luid tp;
IntPtr hproc = GetCurrentProcess(); IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero; IntPtr htok = IntPtr.Zero;
result = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1; tp.Count = 1;
tp.Luid = 0; tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED; tp.Attr = SE_PRIVILEGE_ENABLED;
result = LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid); LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
result = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
result = ExitWindowsEx(flag, 0); ExitWindowsEx(flag, 0);
// End Added 2012-11-23 G# // End Added 2012-11-23 G#
} }
+43 -64
View File
@@ -1,10 +1,8 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Globalization;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Xml;
namespace Disco.ClientBootstrapper.Interop.WIMInterop namespace Disco.ClientBootstrapper.Interop.WIMInterop
{ {
@@ -140,8 +138,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
CreateFileAccessPrivate fileAccess = GetMappedFileAccess(access); CreateFileAccessPrivate fileAccess = GetMappedFileAccess(access);
if (fileAccess == CreateFileAccessPrivate.Read && (!File.Exists(imageFilePath) || (CreateFileMode.OpenExisting != mode))) if (fileAccess == CreateFileAccessPrivate.Read && (!File.Exists(imageFilePath) || (CreateFileMode.OpenExisting != mode)))
{ {
throw new System.UnauthorizedAccessException(string.Format(CultureInfo.CurrentCulture, throw new UnauthorizedAccessException("Read access can be specified only with OpenExisting mode or OpenAlways mode when the .wim file does not exist.");
"Read access can be specified only with OpenExisting mode or OpenAlways mode when the .wim file does not exist."));
} }
// //
@@ -152,9 +149,9 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
m_ImageContainerHandle = NativeMethods.CreateFile(imageFilePath, (uint)fileAccess, (uint)mode); m_ImageContainerHandle = NativeMethods.CreateFile(imageFilePath, (uint)fileAccess, (uint)mode);
m_WindowsImageFilePath = imageFilePath; m_WindowsImageFilePath = imageFilePath;
} }
catch (System.DllNotFoundException ex) catch (DllNotFoundException ex)
{ {
throw new System.DllNotFoundException(string.Format(CultureInfo.CurrentCulture, throw new DllNotFoundException(string.Format(
"Unable to load WIM libraries. Make sure the correct DLLs are present (Wimgapi.dll and Xmlrw.dll)."), ex.InnerException); "Unable to load WIM libraries. Make sure the correct DLLs are present (Wimgapi.dll and Xmlrw.dll)."), ex.InnerException);
} }
@@ -164,7 +161,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//Set the temporary path so that we can write to an image. This //Set the temporary path so that we can write to an image. This
//cannot be %TEMP% as it does not exist on Windows PE //cannot be %TEMP% as it does not exist on Windows PE
// //
string tempDirectory = System.Environment.GetEnvironmentVariable("systemdrive"); string tempDirectory = Environment.GetEnvironmentVariable("systemdrive");
NativeMethods.SetTemporaryPath(m_ImageContainerHandle, tempDirectory); NativeMethods.SetTemporaryPath(m_ImageContainerHandle, tempDirectory);
} }
@@ -173,8 +170,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Unable to open the .wim file {imageFilePath}.");
"Unable to open the .wim file {0}.", imageFilePath));
} }
// //
@@ -481,7 +477,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
// //
//Never commit changes when destroying this object. //Never commit changes when destroying this object.
// //
this.DismountImage(false); DismountImage(false);
} }
GC.KeepAlive(this); GC.KeepAlive(this);
} }
@@ -576,12 +572,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//1. Leading 0xFEFF //1. Leading 0xFEFF
//2. Be in <IMAGE></IMAGE> //2. Be in <IMAGE></IMAGE>
// //
string formattedXml = String.Format(CultureInfo.InvariantCulture, string formattedXml = $"{UNICODE_FILE_MARKER}{"<IMAGE>"}{imageInformation}{"</IMAGE>"}";
"{0}{1}{2}{3}",
UNICODE_FILE_MARKER,
"<IMAGE>",
imageInformation,
"</IMAGE>");
NativeMethods.SetImageInformation(m_ImageHandle, formattedXml); NativeMethods.SetImageInformation(m_ImageHandle, formattedXml);
GC.KeepAlive(this); GC.KeepAlive(this);
@@ -597,7 +588,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//Mount the image //Mount the image
// //
m_MountedPath = pathToMountTo; m_MountedPath = pathToMountTo;
NativeMethods.MountImage(pathToMountTo, m_ParentWindowsImageFilePath, m_Index, System.IO.Path.GetTempPath()); NativeMethods.MountImage(pathToMountTo, m_ParentWindowsImageFilePath, m_Index, Path.GetTempPath());
m_Mounted = true; m_Mounted = true;
} }
@@ -650,7 +641,8 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
///Private null constructor ///Private null constructor
///</summary> ///</summary>
private private
NativeMethods() { } NativeMethods()
{ }
[DllImport("Wimgapi.dll", ExactSpelling = true, [DllImport("Wimgapi.dll", ExactSpelling = true,
EntryPoint = "WIMCreateFile", EntryPoint = "WIMCreateFile",
@@ -684,16 +676,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
IntPtr windowsImageHandle = IntPtr.Zero; IntPtr windowsImageHandle = IntPtr.Zero;
int rc = -1; int rc = -1;
windowsImageHandle = NativeMethods.WimCreateFile(imageFile, access, mode, 0, 0, out creationResult); windowsImageHandle = WimCreateFile(imageFile, access, mode, 0, 0, out _);
rc = Marshal.GetLastWin32Error(); rc = Marshal.GetLastWin32Error();
if (windowsImageHandle == IntPtr.Zero) if (windowsImageHandle == IntPtr.Zero)
{ {
// //
//Everything failed; throw an exception //Everything failed; throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Unable to open/create .wim file {imageFile}. Error = {rc}");
"Unable to open/create .wim file {0}. Error = {1}",
imageFile, rc));
} }
return windowsImageHandle; return windowsImageHandle;
@@ -721,15 +711,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
void void
CloseHandle(IntPtr handle) CloseHandle(IntPtr handle)
{ {
bool status = NativeMethods.WimCloseHandle(handle); bool status = WimCloseHandle(handle);
int rc = Marshal.GetLastWin32Error(); int rc = Marshal.GetLastWin32Error();
if (status == false) if (status == false)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Unable to close image handle. Error = {rc}");
"Unable to close image handle. Error = {0}", rc));
} }
} }
@@ -757,14 +746,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
void void
SetTemporaryPath(IntPtr handle, string temporaryPath) SetTemporaryPath(IntPtr handle, string temporaryPath)
{ {
bool status = NativeMethods.WimSetTemporaryPath(handle, temporaryPath); bool status = WimSetTemporaryPath(handle, temporaryPath);
int rc = Marshal.GetLastWin32Error(); int rc = Marshal.GetLastWin32Error();
if (status == false) if (status == false)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to set temporary path. Error = {0}", rc)); throw new InvalidOperationException($"Unable to set temporary path. Error = {rc}");
} }
} }
@@ -794,14 +783,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
{ {
//Load the image data based on the .wim handle //Load the image data based on the .wim handle
// //
IntPtr hWim = NativeMethods.WimLoadImage(handle, (uint)imageIndex); IntPtr hWim = WimLoadImage(handle, (uint)imageIndex);
int rc = Marshal.GetLastWin32Error(); int rc = Marshal.GetLastWin32Error();
if (hWim == IntPtr.Zero) if (hWim == IntPtr.Zero)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to load image. Error = {0}", rc)); throw new InvalidOperationException($"Unable to load image. Error = {rc}");
} }
return hWim; return hWim;
@@ -832,15 +821,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
IntPtr IntPtr
CaptureImage(IntPtr handle, string path) CaptureImage(IntPtr handle, string path)
{ {
IntPtr hImage = NativeMethods.WimCaptureImage(handle, path, 0); IntPtr hImage = WimCaptureImage(handle, path, 0);
int rc = Marshal.GetLastWin32Error(); int rc = Marshal.GetLastWin32Error();
if (hImage == IntPtr.Zero) if (hImage == IntPtr.Zero)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Failed to capture image from {path}. Error = {rc}");
"Failed to capture image from {0}. Error = {1}", path, rc));
} }
return hImage; return hImage;
} }
@@ -873,14 +861,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
int int
GetImageCount(IntPtr windowsImageHandle) GetImageCount(IntPtr windowsImageHandle)
{ {
int count = NativeMethods.WimGetImageCount(windowsImageHandle); int count = WimGetImageCount(windowsImageHandle);
int rc = Marshal.GetLastWin32Error(); int rc = Marshal.GetLastWin32Error();
if (count == -1) if (count == -1)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to get image count. Error = {0}", rc)); throw new InvalidOperationException($"Unable to get image count. Error = {rc}");
} }
return count; return count;
@@ -971,28 +959,25 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
try try
{ {
status = NativeMethods.WimMountImage(mountPath, status = WimMountImage(mountPath,
windowsImageFileName, windowsImageFileName,
(uint)imageIndex, (uint)imageIndex,
temporaryPath); temporaryPath);
rc = Marshal.GetLastWin32Error(); rc = Marshal.GetLastWin32Error();
} }
catch (System.StackOverflowException) catch (StackOverflowException)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Unable to mount image {windowsImageFileName} to {mountPath}.");
"Unable to mount image {0} to {1}.", windowsImageFileName, mountPath));
} }
if (status == false) if (status == false)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Unable to mount image {windowsImageFileName} to {mountPath}. Error = {rc}");
"Unable to mount image {0} to {1}. Error = {2}",
windowsImageFileName, mountPath, rc));
} }
} }
@@ -1022,15 +1007,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
// //
//Call WimApplyImage always with the Index flag for performance reasons. //Call WimApplyImage always with the Index flag for performance reasons.
// //
bool status = NativeMethods.WimApplyImage(imageHandle, applicationPath, NativeMethods.WIM_FLAG_INDEX); bool status = WimApplyImage(imageHandle, applicationPath, WIM_FLAG_INDEX);
int rc = Marshal.GetLastWin32Error(); int rc = Marshal.GetLastWin32Error();
if (status == false) if (status == false)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Unable to apply image to {applicationPath}. Error = {rc}");
"Unable to apply image to {0}. Error = {1}", applicationPath, rc));
} }
} }
@@ -1062,7 +1046,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
IntPtr info = IntPtr.Zero, sizeOfInfo = IntPtr.Zero; IntPtr info = IntPtr.Zero, sizeOfInfo = IntPtr.Zero;
bool status; bool status;
status = NativeMethods.WimGetImageInformation(handle, out info, out sizeOfInfo); status = WimGetImageInformation(handle, out info, out _);
int rc = Marshal.GetLastWin32Error(); int rc = Marshal.GetLastWin32Error();
if (status == false) if (status == false)
@@ -1070,8 +1054,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Unable to get image information. Error = {rc}");
"Unable to get image information. Error = {0}", rc));
} }
string s = Marshal.PtrToStringUni(info); string s = Marshal.PtrToStringUni(info);
@@ -1112,15 +1095,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
IntPtr xmlBuffer = Marshal.AllocHGlobal(byteBufferSize); IntPtr xmlBuffer = Marshal.AllocHGlobal(byteBufferSize);
Marshal.Copy(byteBuffer, 0, xmlBuffer, byteBufferSize); Marshal.Copy(byteBuffer, 0, xmlBuffer, byteBufferSize);
bool status = NativeMethods.WimSetImageInformation(handle, xmlBuffer, (uint)byteBufferSize); bool status = WimSetImageInformation(handle, xmlBuffer, (uint)byteBufferSize);
int rc = Marshal.GetLastWin32Error(); int rc = Marshal.GetLastWin32Error();
if (status == false) if (status == false)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Unable to set image information. Error = {rc}");
"Unable to set image information. Error = {0}", rc));
} }
} }
@@ -1153,16 +1135,15 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
try try
{ {
status = NativeMethods.WimUnmountImage(mountPath, wimdowsImageFileName, (uint)imageIndex, commitChanges); status = WimUnmountImage(mountPath, wimdowsImageFileName, (uint)imageIndex, commitChanges);
rc = Marshal.GetLastWin32Error(); rc = Marshal.GetLastWin32Error();
} }
catch (System.StackOverflowException ex) catch (StackOverflowException ex)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.StackOverflowException(string.Format(CultureInfo.CurrentCulture, throw new StackOverflowException($"Unable to unmount image {wimdowsImageFileName} from {mountPath}.",
"Unable to unmount image {0} from {1}.", wimdowsImageFileName, mountPath),
ex.InnerException); ex.InnerException);
} }
if (status == false) if (status == false)
@@ -1170,9 +1151,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, throw new InvalidOperationException($"Unable to unmount image {wimdowsImageFileName} from {mountPath}. Error = {rc}");
"Unable to unmount image {0} from {1}. Error = {2}",
wimdowsImageFileName, mountPath, rc));
} }
} }
@@ -1221,14 +1200,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
void void
RegisterCallback(MessageCallback callback) RegisterCallback(MessageCallback callback)
{ {
uint callbackZeroBasedIndex = NativeMethods.WimRegisterMessageCallback(IntPtr.Zero, callback, IntPtr.Zero); WimRegisterMessageCallback(IntPtr.Zero, callback, IntPtr.Zero);
int rc = Marshal.GetLastWin32Error(); int rc = Marshal.GetLastWin32Error();
if (rc != 0) if (rc != 0)
{ {
// //
//Throw an exception //Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to register message callback.")); throw new InvalidOperationException("Unable to register message callback.");
} }
} }
@@ -1253,14 +1232,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
void void
UnregisterMessageCallback(MessageCallback registeredCallback) UnregisterMessageCallback(MessageCallback registeredCallback)
{ {
bool status = NativeMethods.WimUnregisterMessageCallback(IntPtr.Zero, registeredCallback); bool status = WimUnregisterMessageCallback(IntPtr.Zero, registeredCallback);
int rc = Marshal.GetLastWin32Error(); _ = Marshal.GetLastWin32Error();
if (status != true) if (status != true)
{ {
// //
// Throw an exception // Throw an exception
// //
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to unregister message callback.")); throw new InvalidOperationException("Unable to unregister message callback.");
} }
} }
@@ -1358,13 +1337,13 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
fileAccess = CreateFileAccessPrivate.Write; fileAccess = CreateFileAccessPrivate.Write;
break; break;
default: default:
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "No file access level specified.")); throw new ArgumentException("No file access level specified.");
} }
return fileAccess; return fileAccess;
} }
//[CLSCompliant(false)] //[CLSCompliant(false)]
[FlagsAttribute] [Flags]
private private
enum enum
CreateFileAccessPrivate : uint CreateFileAccessPrivate : uint
+1 -6
View File
@@ -1,9 +1,4 @@
using System; namespace Disco.ClientBootstrapper
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.ClientBootstrapper
{ {
class NullStatus : IStatus class NullStatus : IStatus
{ {
+62 -57
View File
@@ -1,37 +1,58 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Disco.ClientBootstrapper namespace Disco.ClientBootstrapper
{ {
static class Program internal static class Program
{ {
public static IStatus Status { get; set; } private static readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
public static BootstrapperLoop BootstrapperLoop { get; set; } public static IStatus Status { get; private set; }
public static InstallLoop InstallLoop { get; set; }
public static List<string> PostBootstrapperActions { get; set; } public static List<string> PostBootstrapperActions { get; set; }
public static bool AllowUninstall { get; set; } public static bool AllowUninstall { get; private set; }
public static bool ApplicationExiting { get; set; } public static Uri ForcedServerUrl { get; private set; } = null;
public static Lazy<string> InlinePath = new Lazy<string>(() =>
{
return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
});
/// <summary> /// <summary>
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
static void Main(string[] args) private static void Main(string[] args)
{ {
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException); Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
if (args.Length > 0) if (args.Length > 0)
{ {
#if DEBUG
if (args.Any(a => a.Equals("debug", StringComparison.OrdinalIgnoreCase)))
{
do
{
Console.WriteLine("Waiting for Debugger to Attach");
Thread.Sleep(1000);
} while (!System.Diagnostics.Debugger.IsAttached);
}
#endif
if (args.Any(a => a.StartsWith("http://", StringComparison.OrdinalIgnoreCase)))
throw new ArgumentException("Only HTTPS URLs are supported for a forced server URL.");
var forcedServerArg = args.FirstOrDefault(a => a.StartsWith("https://", StringComparison.OrdinalIgnoreCase));
if (forcedServerArg != null)
{
if (Uri.TryCreate(forcedServerArg, UriKind.Absolute, out var forcedUri))
ForcedServerUrl = forcedUri;
else
throw new ArgumentException("The provided forced server URL is not valid.");
}
switch (args[0].ToLower()) switch (args[0].ToLower())
{ {
case "/install": case "/install":
@@ -40,18 +61,24 @@ namespace Disco.ClientBootstrapper
statusForm.Show(); statusForm.Show();
string installLocation = null; string installLocation = null;
string wimImage = null; string wimImage = null;
string tempPath = null;
if (args.Length > 1) if (args.Length > 1)
installLocation = args[1]; installLocation = args[1];
if (args.Length > 2) if (args.Length > 2)
wimImage = args[2]; wimImage = args[2];
InstallLoop = new InstallLoop(installLocation, wimImage); if (args.Length > 3)
InstallLoop.Start(new InstallLoop.CompleteCallback(InstallComplete)); tempPath = args[3];
var installLoop = new InstallLoop(installLocation, wimImage, tempPath, InstallComplete, ForcedServerUrl);
installLoop.Start();
Application.Run(); Application.Run();
return; return;
case "/uninstall": case "/uninstall":
AllowUninstall = true; AllowUninstall = true;
Status = new NullStatus(); Status = new NullStatus();
Interop.InstallInterop.Uninstall(); Task.Run(async () =>
{
await Interop.InstallInterop.Uninstall(cancellationTokenSource.Token);
}).Wait(cancellationTokenSource.Token);
return; return;
case "/allowuninstall": case "/allowuninstall":
AllowUninstall = true; AllowUninstall = true;
@@ -69,13 +96,13 @@ namespace Disco.ClientBootstrapper
statusForm.Show(); statusForm.Show();
} }
BootstrapperLoop = new BootstrapperLoop(Status, new BootstrapperLoop.LoopCompleteCallback(LoopComplete)); var bootstrapperLoop = new BootstrapperLoop(Status, ForcedServerUrl, LoopComplete, cancellationTokenSource.Token);
BootstrapperLoop.Start(); bootstrapperLoop.Start();
Application.Run(); Application.Run();
} }
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{ {
WriteAppError(e.Exception); WriteAppError(e.Exception);
} }
@@ -84,21 +111,21 @@ namespace Disco.ClientBootstrapper
{ {
try try
{ {
string AppErrorPath = string.Format("{0}{1}", System.Reflection.Assembly.GetExecutingAssembly().Location, ".errors.txt"); string AppErrorPath = $"{System.Reflection.Assembly.GetExecutingAssembly().Location}.errors.txt";
System.Text.StringBuilder ErrorMessage = new System.Text.StringBuilder(); System.Text.StringBuilder ErrorMessage = new System.Text.StringBuilder();
ErrorMessage.AppendLine(); ErrorMessage.AppendLine();
ErrorMessage.AppendLine(DateTime.Now.ToLongDateString()); ErrorMessage.AppendLine(DateTime.Now.ToLongDateString());
ErrorMessage.AppendLine(DateTime.Now.ToLongTimeString()); ErrorMessage.AppendLine(DateTime.Now.ToLongTimeString());
ErrorMessage.AppendLine(string.Format("Type: {0}", ex.GetType().FullName)); ErrorMessage.AppendLine($"Type: {ex.GetType().FullName}");
ErrorMessage.AppendLine(string.Format("Message: {0}", ex.Message)); ErrorMessage.AppendLine($"Message: {ex.Message}");
ErrorMessage.AppendLine(string.Format("Source: {0}", ex.Source)); ErrorMessage.AppendLine($"Source: {ex.Source}");
ErrorMessage.AppendLine(string.Format("Stack: {0}", ex.StackTrace)); ErrorMessage.AppendLine($"Stack: {ex.StackTrace}");
System.IO.File.AppendAllText(AppErrorPath, ErrorMessage.ToString()); System.IO.File.AppendAllText(AppErrorPath, ErrorMessage.ToString());
} }
catch (Exception) { } catch (Exception) { }
} }
public static void LoopComplete() public static async Task LoopComplete(CancellationToken cancellationToken)
{ {
// Run Post Actions // Run Post Actions
if (PostBootstrapperActions != null) if (PostBootstrapperActions != null)
@@ -106,33 +133,32 @@ namespace Disco.ClientBootstrapper
// Check Uninstall // Check Uninstall
if (AllowUninstall && PostBootstrapperActions.Contains("UninstallBootstrapper")) if (AllowUninstall && PostBootstrapperActions.Contains("UninstallBootstrapper"))
{ {
Interop.InstallInterop.Uninstall(); await Interop.InstallInterop.Uninstall(cancellationToken);
} }
// Check ShutdownActions // Check ShutdownActions
if (PostBootstrapperActions.Contains("Shutdown")) if (PostBootstrapperActions.Contains("Shutdown"))
{ {
Status.UpdateStatus("System Preparation (Bootstrapper)", "Shutting Down; Finished...", string.Empty, false, 0); Status.UpdateStatus("System Preparation (Bootstrapper)", "Shutting Down; Finished...", string.Empty, false, 0);
SleepThread(4000, true); await SleepThread(4000, true, cancellationToken);
Interop.ShutdownInterop.Shutdown(); Interop.ShutdownInterop.Shutdown();
} }
else else if (PostBootstrapperActions.Contains("Reboot"))
if (PostBootstrapperActions.Contains("Reboot"))
{ {
Status.UpdateStatus("System Preparation (Bootstrapper)", "Rebooting; Finished...", string.Empty, false, 0); Status.UpdateStatus("System Preparation (Bootstrapper)", "Rebooting; Finished...", string.Empty, false, 0);
SleepThread(4000, true); await SleepThread(4000, true, cancellationToken);
Interop.ShutdownInterop.Reboot(); Interop.ShutdownInterop.Reboot();
} }
else else
{ {
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0); Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
SleepThread(2000, true); await SleepThread(2000, true, cancellationToken);
} }
} }
else else
{ {
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0); Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
SleepThread(2000, true); await SleepThread(2000, true, cancellationToken);
} }
ExitApplication(); ExitApplication();
@@ -145,33 +171,12 @@ namespace Disco.ClientBootstrapper
public static void ExitApplication() public static void ExitApplication()
{ {
if (!ApplicationExiting) if (!cancellationTokenSource.IsCancellationRequested)
{ cancellationTokenSource.Cancel();
ApplicationExiting = true;
if (BootstrapperLoop != null)
{
if (BootstrapperLoop.LoopThread != null)
{
if (BootstrapperLoop.LoopThread.ThreadState == System.Threading.ThreadState.WaitSleepJoin)
{
BootstrapperLoop.LoopThread.Interrupt();
}
if (BootstrapperLoop.LoopThread.ThreadState == System.Threading.ThreadState.Running)
{
BootstrapperLoop.LoopThread.Abort();
}
}
}
Application.Exit(); Application.Exit();
} }
}
public static void Trace(string Format, params string[] args) public static async Task SleepThread(int millisecondsTimeout, bool updateUI, CancellationToken cancellationToken)
{
System.Diagnostics.Debug.WriteLine(Format, args);
}
public static void SleepThread(int millisecondsTimeout, bool updateUI)
{ {
if (updateUI) if (updateUI)
{ {
@@ -179,12 +184,12 @@ namespace Disco.ClientBootstrapper
{ {
int progress = Convert.ToInt32(((Convert.ToDouble(i) / Convert.ToDouble(millisecondsTimeout)) * 100)); int progress = Convert.ToInt32(((Convert.ToDouble(i) / Convert.ToDouble(millisecondsTimeout)) * 100));
Status.UpdateStatus(null, null, null, true, progress); Status.UpdateStatus(null, null, null, true, progress);
Thread.Sleep(500); await Task.Delay(500, cancellationToken);
} }
} }
else else
{ {
Thread.Sleep(millisecondsTimeout); await Task.Delay(millisecondsTimeout, cancellationToken);
} }
} }
} }
@@ -1,15 +1,14 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("Disco - Client Bootstrapper")] [assembly: AssemblyTitle("Disco ICT - Client Bootstrapper")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("https://discoict.com.au")]
[assembly: AssemblyProduct("Disco")] [assembly: AssemblyProduct("Disco ICT")]
[assembly: AssemblyCopyright("")] [assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -32,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.0731.1600")] [assembly: AssemblyVersion("2.5.25262.0000")]
[assembly: AssemblyFileVersion("2.0.0731.1600")] [assembly: AssemblyFileVersion("2.5.25262.0000")]
+1 -1
View File
@@ -15,6 +15,6 @@
</defaultConnectionFactory> </defaultConnectionFactory>
</entityFramework> </entityFramework>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
</startup> </startup>
</configuration> </configuration>
+13 -13
View File
@@ -28,17 +28,17 @@ namespace Disco.Data.Configuration
{ {
get get
{ {
return ConfigurationCache.GetScopeKeys(Database, this.Scope); return ConfigurationCache.GetScopeKeys(Database, Scope);
} }
} }
private void SetValue<T>(string Key, T Value) private void SetValue<T>(string Key, T Value)
{ {
ConfigurationCache.SetValue(Database, this.Scope, Key, Value); ConfigurationCache.Helpers<T>.SetValue(Database, Scope, Key, Value);
} }
private T GetValue<T>(string Key, T Default) private T GetValue<T>(string Key, T Default)
{ {
return ConfigurationCache.GetValue(Database, this.Scope, Key, Default); return ConfigurationCache.Helpers<T>.GetValue(Database, Scope, Key, Default);
} }
protected void Set<T>(T Value, [CallerMemberName] string Key = null) protected void Set<T>(T Value, [CallerMemberName] string Key = null)
@@ -46,7 +46,7 @@ namespace Disco.Data.Configuration
if (Key == null) if (Key == null)
throw new ArgumentNullException("Key"); throw new ArgumentNullException("Key");
this.SetValue(Key, Value); SetValue(Key, Value);
} }
protected T Get<T>(T Default, [CallerMemberName] string Key = null) protected T Get<T>(T Default, [CallerMemberName] string Key = null)
@@ -54,17 +54,17 @@ namespace Disco.Data.Configuration
if (Key == null) if (Key == null)
throw new ArgumentNullException("Key"); throw new ArgumentNullException("Key");
return this.GetValue(Key, Default); return GetValue(Key, Default);
} }
protected void SetObsfucated(string Value, [CallerMemberName] string Key = null) protected void SetObsfucated(string Value, [CallerMemberName] string Key = null)
{ {
this.Set(Value.Obsfucate(), Key); Set(Value.Obsfucate(), Key);
} }
protected string GetDeobsfucated(string Default, [CallerMemberName] string Key = null) protected string GetDeobsfucated(string Default, [CallerMemberName] string Key = null)
{ {
var obsfucatedValue = this.Get<string>(null, Key); var obsfucatedValue = Get<string>(null, Key);
if (obsfucatedValue == null) if (obsfucatedValue == null)
return Default; return Default;
@@ -74,35 +74,35 @@ namespace Disco.Data.Configuration
protected void RemoveScope() protected void RemoveScope()
{ {
ConfigurationCache.RemoveScope(Database, this.Scope); ConfigurationCache.RemoveScope(Database, Scope);
} }
protected void RemoveItem(string Key) protected void RemoveItem(string Key)
{ {
ConfigurationCache.RemoveScopeKey(Database, this.Scope, Key); ConfigurationCache.RemoveScopeKey(Database, Scope, Key);
} }
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")] [Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
protected void SetAsJson<T>(T Value, [CallerMemberName] string Key = null) protected void SetAsJson<T>(T Value, [CallerMemberName] string Key = null)
{ {
this.Set(Value, Key); Set(Value, Key);
} }
[Obsolete("Types are automatically serialized/deserialized if required, use Get<T>(T Default) instead.")] [Obsolete("Types are automatically serialized/deserialized if required, use Get<T>(T Default) instead.")]
protected T GetFromJson<T>(T Default, [CallerMemberName] string Key = null) protected T GetFromJson<T>(T Default, [CallerMemberName] string Key = null)
{ {
return this.Get(Default, Key); return Get(Default, Key);
} }
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")] [Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
protected void SetAsEnum<T>(T Value, [CallerMemberName] string Key = null) protected void SetAsEnum<T>(T Value, [CallerMemberName] string Key = null)
{ {
this.Set(Value, Key); Set(Value, Key);
} }
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")] [Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
protected T GetFromEnum<T>(T Default, [CallerMemberName] string Key = null) protected T GetFromEnum<T>(T Default, [CallerMemberName] string Key = null)
{ {
return this.Get(Default, Key); return Get(Default, Key);
} }
} }
} }
+79 -41
View File
@@ -22,11 +22,11 @@ namespace Disco.Data.Configuration
private static ConfigurationCacheType Cache(DiscoDataContext Database) private static ConfigurationCacheType Cache(DiscoDataContext Database)
{ {
if (ConfigurationCache.cacheStore == null) if (cacheStore == null)
{ {
lock (configChangeLock) lock (configChangeLock)
{ {
if (ConfigurationCache.cacheStore == null) if (cacheStore == null)
{ {
if (Database == null) if (Database == null)
throw new InvalidOperationException("The Configuration must be loaded at least once before a Cache-Only Configuration Context is used"); throw new InvalidOperationException("The Configuration must be loaded at least once before a Cache-Only Configuration Context is used");
@@ -46,18 +46,16 @@ namespace Disco.Data.Configuration
} }
} }
return ConfigurationCache.cacheStore; return cacheStore;
} }
private static ConfigurationCacheItemType CacheGetItem(DiscoDataContext Database, string Scope, string Key) private static ConfigurationCacheItemType CacheGetItem(DiscoDataContext Database, string Scope, string Key)
{ {
var cache = Cache(Database); var cache = Cache(Database);
ConfigurationCacheScopeType scopeCache; if (cache.TryGetValue(Scope, out var scopeCache))
if (cache.TryGetValue(Scope, out scopeCache))
{ {
ConfigurationCacheItemType item = default(ConfigurationCacheItemType); if (scopeCache.TryGetValue(Key, out var item))
if (scopeCache.TryGetValue(Key, out item))
return item; return item;
} }
@@ -93,8 +91,7 @@ namespace Disco.Data.Configuration
Database.ConfigurationItems.Add(configItem); Database.ConfigurationItems.Add(configItem);
// Add Item to Cache // Add Item to Cache
ConfigurationCacheScopeType scopeCache; if (!cacheStore.TryGetValue(Scope, out var scopeCache))
if (!cacheStore.TryGetValue(Scope, out scopeCache))
{ {
scopeCache = new ConfigurationCacheScopeType(); scopeCache = new ConfigurationCacheScopeType();
cacheStore.TryAdd(Scope, scopeCache); cacheStore.TryAdd(Scope, scopeCache);
@@ -134,10 +131,9 @@ namespace Disco.Data.Configuration
Database.ConfigurationItems.Remove(configItem); Database.ConfigurationItems.Remove(configItem);
// Remove item from Cache // Remove item from Cache
ConfigurationCacheScopeType scopeCache; if (cacheStore.TryGetValue(Scope, out var scopeCache))
if (cacheStore.TryGetValue(Scope, out scopeCache))
{ {
scopeCache.TryRemove(Key, out item); scopeCache.TryRemove(Key, out _);
} }
return null; return null;
@@ -148,8 +144,7 @@ namespace Disco.Data.Configuration
configItem.Value = Value; configItem.Value = Value;
// Update Cache // Update Cache
ConfigurationCacheScopeType scopeCache; if (cacheStore.TryGetValue(Scope, out var scopeCache))
if (cacheStore.TryGetValue(Scope, out scopeCache))
{ {
scopeCache.TryRemove(Key, out item); scopeCache.TryRemove(Key, out item);
item = new ConfigurationCacheItemType(configItem, ObjectValue); item = new ConfigurationCacheItemType(configItem, ObjectValue);
@@ -165,10 +160,9 @@ namespace Disco.Data.Configuration
} }
private static ConfigurationCacheItemType SetItemTypeValue(ConfigurationCacheItemType ExistingItem, object Value) private static ConfigurationCacheItemType SetItemTypeValue(ConfigurationCacheItemType ExistingItem, object Value)
{ {
var cache = ConfigurationCache.cacheStore; var cache = cacheStore;
ConfigurationCacheScopeType scopeCache; if (cache.TryGetValue(ExistingItem.Item1.Scope, out var scopeCache))
if (cache.TryGetValue(ExistingItem.Item1.Scope, out scopeCache))
{ {
ConfigurationCacheItemType newItem = new ConfigurationCacheItemType(ExistingItem.Item1, Value); ConfigurationCacheItemType newItem = new ConfigurationCacheItemType(ExistingItem.Item1, Value);
scopeCache.TryUpdate(ExistingItem.Item1.Key, newItem, ExistingItem); scopeCache.TryUpdate(ExistingItem.Item1.Key, newItem, ExistingItem);
@@ -183,18 +177,18 @@ namespace Disco.Data.Configuration
#region Helpers #region Helpers
private static bool IsConvertableFromString(Type t) private static bool IsConvertableFromString(Type t)
{ {
if (t == typeof(Boolean) || if (t == typeof(bool) ||
t == typeof(Char) || t == typeof(char) ||
t == typeof(SByte) || t == typeof(sbyte) ||
t == typeof(Byte) || t == typeof(byte) ||
t == typeof(Int16) || t == typeof(UInt16) || t == typeof(short) || t == typeof(ushort) ||
t == typeof(Int32) || t == typeof(UInt32) || t == typeof(int) || t == typeof(uint) ||
t == typeof(Int64) || t == typeof(UInt64) || t == typeof(long) || t == typeof(ulong) ||
t == typeof(Single) || t == typeof(float) ||
t == typeof(Double) || t == typeof(double) ||
t == typeof(Decimal) || t == typeof(decimal) ||
t == typeof(DateTime) || t == typeof(DateTime) ||
t == typeof(String)) t == typeof(string))
return true; return true;
else else
return false; return false;
@@ -202,7 +196,11 @@ namespace Disco.Data.Configuration
#endregion #endregion
#region Cache Getters/Setters #region Cache Getters/Setters
internal static T GetValue<T>(DiscoDataContext Database, string Scope, string Key, T Default) internal static class Helpers<T>
{
private static readonly IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
internal static T GetValue(DiscoDataContext Database, string Scope, string Key, T Default)
{ {
var item = CacheGetItem(Database, Scope, Key); var item = CacheGetItem(Database, Scope, Key);
@@ -238,9 +236,34 @@ namespace Disco.Data.Configuration
} }
else if (itemType.BaseType != null && itemType.BaseType == typeof(Enum)) else if (itemType.BaseType != null && itemType.BaseType == typeof(Enum))
{ {
// Enum // enum
itemValue = Enum.Parse(typeof(T), item.Item1.Value); itemValue = Enum.Parse(typeof(T), item.Item1.Value);
} }
else if (itemType.IsGenericType &&
itemType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
IsConvertableFromString(Nullable.GetUnderlyingType(itemType)))
{
// nullable
itemValue = (T)Convert.ChangeType(item.Item1.Value, Nullable.GetUnderlyingType(itemType));
}
else if (itemType == typeof(Guid))
{
// guid
itemValue = new Guid(item.Item1.Value);
}
else if (itemType == typeof(Guid?))
{
// guid
if (string.IsNullOrEmpty(item.Item1.Value))
itemValue = null;
else
itemValue = new Guid(item.Item1.Value);
}
else if (itemType == typeof(byte[]))
{
// byte[]
itemValue = Convert.FromBase64String(item.Item1.Value);
}
else else
{ {
// JSON Deserialize // JSON Deserialize
@@ -254,18 +277,18 @@ namespace Disco.Data.Configuration
} }
} }
} }
internal static void SetValue<T>(DiscoDataContext Database, string Scope, string Key, T Value) internal static void SetValue(DiscoDataContext Database, string Scope, string Key, T Value)
{ {
Type valueType = typeof(T); Type valueType = typeof(T);
string stringValue; string stringValue;
if (Value == null) if (comparer.Equals(Value, default))
{ {
stringValue = null; stringValue = null;
} }
else if (valueType == typeof(object)) else if (valueType == typeof(object))
{ {
throw new ArgumentException(string.Format("Cannot serialize the configuration item [{0}].[{1}] which defines a type of [System.Object]", Scope, Key), "Value"); throw new ArgumentException($"Cannot serialize the configuration item [{Scope}].[{Key}] which has the type [System.Object]", "Value");
} }
else if (IsConvertableFromString(valueType)) else if (IsConvertableFromString(valueType))
{ {
@@ -274,9 +297,25 @@ namespace Disco.Data.Configuration
} }
else if (valueType.BaseType != null && valueType.BaseType == typeof(Enum)) else if (valueType.BaseType != null && valueType.BaseType == typeof(Enum))
{ {
// Enum // enum
stringValue = Value.ToString(); stringValue = Value.ToString();
} }
else if (valueType.IsGenericType &&
valueType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
IsConvertableFromString(Nullable.GetUnderlyingType(valueType)))
{
// nullable
stringValue = Value.ToString();
}
else if (valueType == typeof(Guid) || valueType == typeof(Guid?))
{
stringValue = Value.ToString();
}
else if (Value is byte[] valueBytes)
{
// byte[]
stringValue = Convert.ToBase64String(valueBytes);
}
else else
{ {
// JSON Serialize // JSON Serialize
@@ -285,6 +324,8 @@ namespace Disco.Data.Configuration
CacheSetItem(Database, Scope, Key, stringValue, Value); CacheSetItem(Database, Scope, Key, stringValue, Value);
} }
}
#endregion #endregion
#region Cache Helpers #region Cache Helpers
@@ -293,8 +334,7 @@ namespace Disco.Data.Configuration
{ {
var cache = Cache(Database); var cache = Cache(Database);
ConfigurationCacheScopeType scopeCache; if (cache.TryGetValue(Scope, out var scopeCache))
if (cache.TryGetValue(Scope, out scopeCache))
{ {
return scopeCache.Keys.ToList(); return scopeCache.Keys.ToList();
} }
@@ -318,8 +358,7 @@ namespace Disco.Data.Configuration
// Remove item from Cache // Remove item from Cache
if (cacheStore != null) if (cacheStore != null)
{ {
ConfigurationCacheScopeType scopeCache; cacheStore.TryRemove(Scope, out var scopeCache);
cacheStore.TryRemove(Scope, out scopeCache);
} }
} }
} }
@@ -359,10 +398,9 @@ namespace Disco.Data.Configuration
// Remove item from Cache // Remove item from Cache
if (cacheItem != null) if (cacheItem != null)
{ {
ConfigurationCacheScopeType scopeCache; if (cacheStore.TryGetValue(Scope, out var scopeCache))
if (cacheStore.TryGetValue(Scope, out scopeCache))
{ {
scopeCache.TryRemove(Key, out cacheItem); scopeCache.TryRemove(Key, out _);
} }
} }
} }
@@ -1,5 +1,7 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
namespace Disco.Data.Configuration.Modules namespace Disco.Data.Configuration.Modules
{ {
@@ -24,10 +26,45 @@ namespace Disco.Data.Configuration.Modules
} }
} }
[Obsolete("User SearchAllServers instead"), EditorBrowsable(EditorBrowsableState.Never)]
public bool? SearchAllForestServers public bool? SearchAllForestServers
{ {
get { return Get<bool?>(null); } get { return Get<bool?>(null); }
set { Set(value); } set { Set(value); }
} }
public bool? SearchAllServers
{
get
{
var value = Get<bool?>(null);
/// migrate <see cref="SearchAllForestServers"/>
#pragma warning disable CS0618 // Type or member is obsolete
if (value == null)
{
value = SearchAllForestServers;
if (value != null)
{
SearchAllForestServers = null;
SearchAllServers = value;
}
}
#pragma warning restore CS0618 // Type or member is obsolete
return value;
}
set { Set(value); }
}
/// <summary>
/// If true LDAP filters contain wildcards only at the end of the search term.
/// This greatly improves performance in very large AD environments (ie: EDU001/EDU002)
/// </summary>
public bool SearchWildcardSuffixOnly
{
get { return Get(true); }
set { Set(value); }
}
} }
} }
@@ -1,4 +1,5 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using System;
namespace Disco.Data.Configuration.Modules namespace Disco.Data.Configuration.Modules
{ {
@@ -6,33 +7,24 @@ namespace Disco.Data.Configuration.Modules
{ {
public BootstrapperConfiguration(DiscoDataContext Database) : base(Database) { } public BootstrapperConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope public override string Scope { get; } = "Bootstrapper";
{
get { return "Bootstrapper"; }
}
public string MacSshUsername public string MacSshUsername
{ {
get get => Get("root");
{ set => Set(value);
return this.Get("root");
}
set
{
this.Set(value);
}
} }
public string MacSshPassword public string MacSshPassword
{ {
get get => GetDeobsfucated(string.Empty);
{ set => SetObsfucated(value);
return this.GetDeobsfucated(string.Empty);
} }
set
public TimeSpan PendingTimeout
{ {
this.SetObsfucated(value); get => TimeSpan.FromSeconds(Get(30 * 60)); // 30 minutes default
} set => Set((int)value.TotalSeconds);
} }
} }
} }
@@ -0,0 +1,18 @@
using Disco.Data.Repository;
using Disco.Models.Services.Devices.DeviceFlag;
namespace Disco.Data.Configuration.Modules
{
public class DeviceFlagsConfiguration : ConfigurationBase
{
public DeviceFlagsConfiguration(DiscoDataContext database) : base(database) { }
public override string Scope { get; } = "DeviceFlags";
public DeviceFlagExportOptions LastExportOptions
{
get => Get(DeviceFlagExportOptions.DefaultOptions());
set => Set(value);
}
}
}
@@ -7,13 +7,13 @@ namespace Disco.Data.Configuration.Modules
{ {
public DeviceProfilesConfiguration(DiscoDataContext Database) : base(Database) { } public DeviceProfilesConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get { return "DeviceProfiles"; } } public override string Scope { get; } = "DeviceProfiles";
public int DefaultDeviceProfileId public int DefaultDeviceProfileId
{ {
get get
{ {
var v = this.Get(1); var v = Get(1);
if (v > 0) if (v > 0)
return v; return v;
else else
@@ -24,21 +24,21 @@ namespace Disco.Data.Configuration.Modules
if (value < 1) if (value < 1)
throw new ArgumentOutOfRangeException("value", "Expected >= 1"); throw new ArgumentOutOfRangeException("value", "Expected >= 1");
this.Set(value); Set(value);
} }
} }
public int DefaultAddDeviceOfflineDeviceProfileId public int DefaultAddDeviceOfflineDeviceProfileId
{ {
get get
{ {
return this.Get(0); return Get(0);
} }
set set
{ {
if (value < 0) if (value < 0)
throw new ArgumentOutOfRangeException("value", "Expected >= 0"); throw new ArgumentOutOfRangeException("value", "Expected >= 0");
this.Set(value); Set(value);
} }
} }
@@ -1,5 +1,5 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.Services.Devices.Exporting; using Disco.Models.Services.Devices;
namespace Disco.Data.Configuration.Modules namespace Disco.Data.Configuration.Modules
{ {
@@ -7,12 +7,18 @@ namespace Disco.Data.Configuration.Modules
{ {
public DevicesConfiguration(DiscoDataContext Database) : base(Database) { } public DevicesConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get { return "Devices"; } } public override string Scope { get; } = "Devices";
public DeviceExportOptions LastExportOptions public DeviceExportOptions LastExportOptions
{ {
get { return this.Get<DeviceExportOptions>(DeviceExportOptions.DefaultOptions()); } get => Get(DeviceExportOptions.DefaultOptions());
set { this.Set(value); } set => Set(value);
}
public bool EnrollmentLegacyDiscoveryDisabled
{
get => Get(false);
set => Set(value);
} }
} }
} }
@@ -0,0 +1,25 @@
using Disco.Data.Repository;
using Disco.Models.Services.Documents;
using System.Collections.Generic;
namespace Disco.Data.Configuration.Modules
{
public class DocumentsConfiguration : ConfigurationBase
{
public DocumentsConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get; } = "Documents";
public List<DocumentTemplatePackage> Packages
{
get { return Get<List<DocumentTemplatePackage>>(null); }
set { Set(value); }
}
public DocumentExportOptions LastExportOptions
{
get => Get(DocumentExportOptions.DefaultOptions());
set => Set(value);
}
}
}
@@ -1,5 +1,5 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.BI.Job; using Disco.Models.Services.Jobs;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -9,18 +9,27 @@ namespace Disco.Data.Configuration.Modules
{ {
public JobPreferencesConfiguration(DiscoDataContext Database) : base(Database) { } public JobPreferencesConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get { return "JobPreferences"; } } public override string Scope { get; } = "JobPreferences";
/// <summary>
/// Initial comments template for new jobs
/// </summary>
public string InitialCommentsTemplate
{
get => Get<string>(null);
set => Set(value);
}
/// <summary> /// <summary>
/// Number of days a job is open before it is considered 'Long Running' /// Number of days a job is open before it is considered 'Long Running'
/// </summary> /// </summary>
public int LongRunningJobDaysThreshold public int LongRunningJobDaysThreshold
{ {
get { return Get<int>(7); } get => Get(7);
set set
{ {
if (value < 0) if (value < 0)
throw new ArgumentOutOfRangeException("value", "The Long Running Job Days Threshold cannot be less than zero"); throw new ArgumentOutOfRangeException(nameof(value), "The Long Running Job Days Threshold cannot be less than zero");
Set(value); Set(value);
} }
@@ -31,11 +40,33 @@ namespace Disco.Data.Configuration.Modules
/// </summary> /// </summary>
public int StaleJobMinutesThreshold public int StaleJobMinutesThreshold
{ {
get { return Get<int>(60 * 24 * 2); } // Default to 48 Hours (2 days) get => Get(60 * 24 * 2); // Default to 48 Hours (2 days)
set set
{ {
if (value < 0) if (value < 0)
throw new ArgumentOutOfRangeException("value", "The Stale Job Minutes Threshold cannot be less than zero"); throw new ArgumentOutOfRangeException(nameof(value), "The Stale Job Minutes Threshold cannot be less than zero");
Set(value);
}
}
public bool LodgmentIncludeAllAttachmentsByDefault
{
get => Get(false);
set => Set(value);
}
/// <summary>
/// Theme used in noticeboards by default.
/// <see cref="Disco.Services.Extensions.UIHelpers.NoticeboardThemes"/>
/// </summary>
public string DefaultNoticeboardTheme
{
get => Get("default");
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(DefaultNoticeboardTheme));
Set(value); Set(value);
} }
@@ -43,14 +74,48 @@ namespace Disco.Data.Configuration.Modules
public LocationModes LocationMode public LocationModes LocationMode
{ {
get { return Get<LocationModes>(LocationModes.Unrestricted); } get => Get(LocationModes.Unrestricted);
set { Set(value); } set => Set(value);
} }
public List<string> LocationList public List<string> LocationList
{ {
get { return Get<List<string>>(new List<string>()); } get => Get(new List<string>());
set { Set(value); } set => Set(value);
}
public string OnCreateExpression
{
get => Get<string>(null);
set => Set(value);
}
public string OnDeviceReadyForReturnExpression
{
get => Get<string>(null);
set => Set(value);
}
public string OnCloseExpression
{
get => Get<string>(null);
set => Set(value);
}
public JobExportOptions LastExportOptions
{
get => Get(JobExportOptions.DefaultOptions());
set
{
Set(value);
LastExportDate = DateTime.Now;
}
}
public DateTime? LastExportDate
{
get => Get<DateTime?>(null);
set => Set(value);
} }
} }
} }
@@ -1,6 +1,5 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.BI.Config; using Disco.Models.BI.Config;
using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -8,15 +7,13 @@ namespace Disco.Data.Configuration.Modules
{ {
public class OrganisationAddressesConfiguration : ConfigurationBase public class OrganisationAddressesConfiguration : ConfigurationBase
{ {
private const string scope = "OrganisationAddresses";
public OrganisationAddressesConfiguration(DiscoDataContext Database) : base(Database) { } public OrganisationAddressesConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get { return scope; } } public override string Scope { get; } = "OrganisationAddresses";
public OrganisationAddress GetAddress(int Id) public OrganisationAddress GetAddress(int Id)
{ {
return this.Get<OrganisationAddress>(null, Id.ToString()); return Get<OrganisationAddress>(null, Id.ToString());
} }
public OrganisationAddress SetAddress(OrganisationAddress Address) public OrganisationAddress SetAddress(OrganisationAddress Address)
{ {
@@ -25,21 +22,21 @@ namespace Disco.Data.Configuration.Modules
Address.Id = NextOrganisationAddressId; Address.Id = NextOrganisationAddressId;
} }
this.Set(Address, Address.Id.ToString()); Set(Address, Address.Id.ToString());
return Address; return Address;
} }
public void RemoveAddress(int Id) public void RemoveAddress(int Id)
{ {
// Remove Configuration Item // Remove Configuration Item
this.RemoveItem(Id.ToString()); RemoveItem(Id.ToString());
} }
public List<OrganisationAddress> Addresses public List<OrganisationAddress> Addresses
{ {
get get
{ {
return this.ItemKeys.Select(key => this.Get<OrganisationAddress>(null, key)).ToList(); return ItemKeys.Select(key => Get<OrganisationAddress>(null, key)).ToList();
} }
} }
@@ -50,7 +47,7 @@ namespace Disco.Data.Configuration.Modules
int nextId = 0; int nextId = 0;
while (true) while (true)
{ {
if (this.Get<string>(null, nextId.ToString()) == null) if (Get<string>(null, nextId.ToString()) == null)
break; break;
nextId++; nextId++;
} }
@@ -58,19 +55,5 @@ namespace Disco.Data.Configuration.Modules
} }
} }
internal static void MigrateDatabase(DiscoDataContext Database)
{
// Migrate all organisation addresses to JSON
if (Database.ConfigurationItems.Count(i => i.Scope == scope && !i.Value.StartsWith("{")) > 0)
{
var items = Database.ConfigurationItems.Where(i => i.Scope == scope && !i.Value.StartsWith("{")).ToList();
items.ForEach(i =>
{
i.Value = JsonConvert.SerializeObject(OrganisationAddress.FromConfigurationEntry(int.Parse(i.Key), i.Value));
});
Database.SaveChanges();
}
}
} }
} }
@@ -0,0 +1,18 @@
using Disco.Data.Repository;
using Disco.Models.Services.Users.UserFlags;
namespace Disco.Data.Configuration.Modules
{
public class UserFlagsConfiguration : ConfigurationBase
{
public UserFlagsConfiguration(DiscoDataContext database) : base(database) { }
public override string Scope { get; } = "UserFlags";
public UserFlagExportOptions LastExportOptions
{
get => Get(UserFlagExportOptions.DefaultOptions());
set => Set(value);
}
}
}
+171 -128
View File
@@ -1,6 +1,8 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.Services.Exporting;
using Disco.Models.Services.Interop.DiscoServices; using Disco.Models.Services.Interop.DiscoServices;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
namespace Disco.Data.Configuration namespace Disco.Data.Configuration
@@ -11,12 +13,15 @@ namespace Disco.Data.Configuration
: base(Database) : base(Database)
{ {
// Init Modules // Init Modules
this.moduleBootstrapperConfiguration = new Lazy<Modules.BootstrapperConfiguration>(() => new Modules.BootstrapperConfiguration(Database)); moduleBootstrapperConfiguration = new Lazy<Modules.BootstrapperConfiguration>(() => new Modules.BootstrapperConfiguration(Database));
this.moduleDeviceProfilesConfiguration = new Lazy<Modules.DeviceProfilesConfiguration>(() => new Modules.DeviceProfilesConfiguration(Database)); moduleDeviceProfilesConfiguration = new Lazy<Modules.DeviceProfilesConfiguration>(() => new Modules.DeviceProfilesConfiguration(Database));
this.moduleOrganisationAddressesConfiguration = new Lazy<Modules.OrganisationAddressesConfiguration>(() => new Modules.OrganisationAddressesConfiguration(Database)); moduleOrganisationAddressesConfiguration = new Lazy<Modules.OrganisationAddressesConfiguration>(() => new Modules.OrganisationAddressesConfiguration(Database));
this.moduleJobPreferencesConfiguration = new Lazy<Modules.JobPreferencesConfiguration>(() => new Modules.JobPreferencesConfiguration(Database)); moduleJobPreferencesConfiguration = new Lazy<Modules.JobPreferencesConfiguration>(() => new Modules.JobPreferencesConfiguration(Database));
this.moduleActiveDirectoryConfiguration = new Lazy<Modules.ActiveDirectoryConfiguration>(() => new Modules.ActiveDirectoryConfiguration(Database)); moduleActiveDirectoryConfiguration = new Lazy<Modules.ActiveDirectoryConfiguration>(() => new Modules.ActiveDirectoryConfiguration(Database));
this.moduleDevicesConfiguration = new Lazy<Modules.DevicesConfiguration>(() => new Modules.DevicesConfiguration(Database)); moduleDevicesConfiguration = new Lazy<Modules.DevicesConfiguration>(() => new Modules.DevicesConfiguration(Database));
moduleDocumentsConfiguration = new Lazy<Modules.DocumentsConfiguration>(() => new Modules.DocumentsConfiguration(Database));
moduleUserFlagsConfiguration = new Lazy<Modules.UserFlagsConfiguration>(() => new Modules.UserFlagsConfiguration(Database));
moduleDeviceFlagsConfiguration = new Lazy<Modules.DeviceFlagsConfiguration>(() => new Modules.DeviceFlagsConfiguration(Database));
} }
#region Configuration Modules #region Configuration Modules
@@ -27,82 +32,53 @@ namespace Disco.Data.Configuration
private Lazy<Modules.JobPreferencesConfiguration> moduleJobPreferencesConfiguration; private Lazy<Modules.JobPreferencesConfiguration> moduleJobPreferencesConfiguration;
private Lazy<Modules.ActiveDirectoryConfiguration> moduleActiveDirectoryConfiguration; private Lazy<Modules.ActiveDirectoryConfiguration> moduleActiveDirectoryConfiguration;
private Lazy<Modules.DevicesConfiguration> moduleDevicesConfiguration; private Lazy<Modules.DevicesConfiguration> moduleDevicesConfiguration;
private Lazy<Modules.DocumentsConfiguration> moduleDocumentsConfiguration;
private Lazy<Modules.UserFlagsConfiguration> moduleUserFlagsConfiguration;
private Lazy<Modules.DeviceFlagsConfiguration> moduleDeviceFlagsConfiguration;
public Modules.BootstrapperConfiguration Bootstrapper public Modules.BootstrapperConfiguration Bootstrapper => moduleBootstrapperConfiguration.Value;
{ public Modules.DeviceProfilesConfiguration DeviceProfiles => moduleDeviceProfilesConfiguration.Value;
get public Modules.OrganisationAddressesConfiguration OrganisationAddresses => moduleOrganisationAddressesConfiguration.Value;
{ public Modules.JobPreferencesConfiguration JobPreferences => moduleJobPreferencesConfiguration.Value;
return moduleBootstrapperConfiguration.Value; public Modules.ActiveDirectoryConfiguration ActiveDirectory => moduleActiveDirectoryConfiguration.Value;
} public Modules.DevicesConfiguration Devices => moduleDevicesConfiguration.Value;
} public Modules.DocumentsConfiguration Documents => moduleDocumentsConfiguration.Value;
public Modules.DeviceProfilesConfiguration DeviceProfiles public Modules.UserFlagsConfiguration UserFlags => moduleUserFlagsConfiguration.Value;
{ public Modules.DeviceFlagsConfiguration DeviceFlags => moduleDeviceFlagsConfiguration.Value;
get
{
return moduleDeviceProfilesConfiguration.Value;
}
}
public Modules.OrganisationAddressesConfiguration OrganisationAddresses
{
get
{
return moduleOrganisationAddressesConfiguration.Value;
}
}
public Modules.JobPreferencesConfiguration JobPreferences
{
get
{
return moduleJobPreferencesConfiguration.Value;
}
}
public Modules.ActiveDirectoryConfiguration ActiveDirectory
{
get
{
return moduleActiveDirectoryConfiguration.Value;
}
}
public Modules.DevicesConfiguration Devices
{
get
{
return moduleDevicesConfiguration.Value;
}
}
#endregion #endregion
public override string Scope { get { return "System"; } } public override string Scope { get; } = "System";
public string DataStoreLocation public string DataStoreLocation
{ {
get get
{ {
var result = this.Get<string>(null); var result = Get<string>(null);
if (result == null) if (result == null)
{ {
var appDataPath = System.Web.HttpContext.Current.Server.MapPath("~/App_Data"); var appDataPath = System.Web.HttpContext.Current.Server.MapPath("~/App_Data");
if (appDataPath.EndsWith("\\"))
if (!appDataPath.EndsWith(@"\"))
appDataPath += @"\";
return appDataPath; return appDataPath;
else
return string.Concat(appDataPath, '\\');
} }
else else
return result; return result;
} }
set set
{ {
if (value == null) if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException("value"); throw new ArgumentNullException(nameof(value));
if (!System.IO.Directory.Exists(value))
throw new System.IO.DirectoryNotFoundException(string.Format("DataStoreLocation: '{0}' could not be found", value)); if (!Directory.Exists(value))
string storePath; throw new DirectoryNotFoundException($"DataStoreLocation: '{value}' could not be found");
if (value.EndsWith("\\"))
storePath = value; if (!value.EndsWith(@"\"))
else value += @"\";
storePath = string.Concat(value, '\\');
this.Set(storePath); Set(value);
} }
} }
@@ -110,7 +86,7 @@ namespace Disco.Data.Configuration
{ {
get get
{ {
return this.Get<string>("Domain Admins,Disco Admins"); return Get("Domain Admins,Disco Admins");
} }
set set
{ {
@@ -119,26 +95,15 @@ namespace Disco.Data.Configuration
} }
#region Plugin Locations #region Plugin Locations
public string PluginsLocation public string PluginsLocation => Path.Combine(DataStoreLocation, @"Plugins\");
public string PluginStorageLocation => Path.Combine(DataStoreLocation, @"PluginStorage\");
public string PluginPackagesLocation => Path.Combine(DataStoreLocation, @"PluginPackages\");
public string PluginUserPhotosLocation => Path.Combine(DataStoreLocation, @"PluginUserPhotos\");
public DateTime PluginDetailsCacheExpiration
{ {
get get => Get(DateTime.MinValue);
{ set => Set(value);
return System.IO.Path.Combine(this.DataStoreLocation, @"Plugins\");
}
}
public string PluginStorageLocation
{
get
{
return System.IO.Path.Combine(this.DataStoreLocation, @"PluginStorage\");
}
}
public string PluginPackagesLocation
{
get
{
return System.IO.Path.Combine(this.DataStoreLocation, @"PluginPackages\");
}
} }
#endregion #endregion
@@ -148,14 +113,14 @@ namespace Disco.Data.Configuration
{ {
get get
{ {
return System.IO.Path.Combine(DataStoreLocation, "OrganisationLogo.png"); return Path.Combine(DataStoreLocation, "OrganisationLogo.png");
} }
} }
public string OrganisationLogoHash public string OrganisationLogoHash
{ {
get get
{ {
var path = this.OrganisationLogoPath; var path = OrganisationLogoPath;
if (File.Exists(path)) if (File.Exists(path))
return File.GetLastWriteTimeUtc(path).ToBinary().ToString(); return File.GetLastWriteTimeUtc(path).ToBinary().ToString();
else else
@@ -166,19 +131,19 @@ namespace Disco.Data.Configuration
{ {
get get
{ {
var path = this.OrganisationLogoPath; var path = OrganisationLogoPath;
if (File.Exists(path)) if (File.Exists(path))
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
else else
return new MemoryStream(Disco.Data.Properties.Resources.EmptyLogo); return new MemoryStream(Properties.Resources.EmptyLogo);
} }
set set
{ {
string organisationLogoPath = this.OrganisationLogoPath; string organisationLogoPath = OrganisationLogoPath;
if (value == null) if (value == null)
{ {
if (System.IO.File.Exists(organisationLogoPath)) if (File.Exists(organisationLogoPath))
System.IO.File.Delete(organisationLogoPath); File.Delete(organisationLogoPath);
} }
else else
{ {
@@ -194,22 +159,22 @@ namespace Disco.Data.Configuration
{ {
get get
{ {
return this.Get<string>(null); return Get<string>(null);
} }
set set
{ {
this.Set(value); Set(value);
} }
} }
public bool MultiSiteMode public bool MultiSiteMode
{ {
get get
{ {
return this.Get(false); return Get(false);
} }
set set
{ {
this.Set(value); Set(value);
} }
} }
#endregion #endregion
@@ -219,86 +184,158 @@ namespace Disco.Data.Configuration
{ {
get get
{ {
return this.Get<string>(null); return Get<string>(null);
} }
set set
{ {
this.Set(value); Set(value);
} }
} }
public int ProxyPort public int ProxyPort
{ {
get get
{ {
return this.Get(8080); return Get(8080);
} }
set set
{ {
this.Set(value); Set(value);
} }
} }
public string ProxyUsername public string ProxyUsername
{ {
get get
{ {
return this.Get<string>(null); return Get<string>(null);
} }
set set
{ {
this.Set(value); Set(value);
} }
} }
public string ProxyPassword public string ProxyPassword
{ {
get get
{ {
return this.GetDeobsfucated(null); return GetDeobsfucated(null);
} }
set set
{ {
this.SetObsfucated(value); SetObsfucated(value);
} }
} }
#endregion #endregion
#region Email Configuration
public string EmailSmtpServer
{
get => Get<string>(null);
set => Set(value);
}
public int EmailSmtpPort
{
get => Get(25);
set => Set(value);
}
public bool EmailEnableSsl
{
get => Get(false);
set => Set(value);
}
public string EmailFromAddress
{
get => Get<string>(null);
set => Set(value);
}
public string EmailReplyToAddress
{
get => Get<string>(null);
set => Set(value);
}
public string EmailUsername
{
get => Get<string>(null);
set => Set(value);
}
public string EmailPassword
{
get => GetDeobsfucated(null);
set => SetObsfucated(value);
}
#endregion
#region UpdateCheck #region UpdateCheck
public string DeploymentId public bool IsActivated => ActivationId.HasValue;
public DateTime? ActivatedOn
{
get => Get((DateTime?)null);
set => Set(value);
}
public string ActivatedBy
{
get => Get((string)null);
set => Set(value);
}
public Guid? ActivationId
{
get => Get((Guid?)null);
set => Set(value);
}
public byte[] ActivationKey
{
get => Get((byte[])null);
set => Set(value);
}
public bool IsLicensed
{
get => LicenseKey != null && LicenseExpiresOn != null && LicenseExpiresOn > DateTime.UtcNow && LicenseError == null;
}
public string LicenseKey
{
get => Get<string>(null);
set => Set(value);
}
public DateTime? LicenseExpiresOn
{
get => Get<DateTime?>(null);
set => Set(value);
}
public string LicenseError
{
get => Get<string>(null);
set => Set(value);
}
public string DeploymentId => Get<string>(null);
public string DeploymentSecret => Get<string>(null);
public short DeploymentChecksum
{ {
get get
{ {
return this.Get<string>(null); var deploymentIdBytes = Guid.Parse(DeploymentId).ToByteArray();
} return
} (short)(BitConverter.ToInt16(deploymentIdBytes, 0) ^
public string DeploymentSecret BitConverter.ToInt16(deploymentIdBytes, 2) ^
{ BitConverter.ToInt16(deploymentIdBytes, 4) ^
get BitConverter.ToInt16(deploymentIdBytes, 6) ^
{ BitConverter.ToInt16(deploymentIdBytes, 8) ^
return this.Get<string>(null); BitConverter.ToInt16(deploymentIdBytes, 10) ^
BitConverter.ToInt16(deploymentIdBytes, 12) ^
BitConverter.ToInt16(deploymentIdBytes, 14));
} }
} }
public UpdateResponseV2 UpdateLastCheckResponse public UpdateResponseV2 UpdateLastCheckResponse
{ {
get get => Get<UpdateResponseV2>(null);
{ set => Set(value);
return this.Get<UpdateResponseV2>(null);
}
set
{
this.Set(value);
}
}
public bool UpdateBetaDeployment
{
get
{
return this.Get<bool>(false);
}
} }
public bool UpdateBetaDeployment => Get(false);
public Version InstalledDatabaseVersion public Version InstalledDatabaseVersion
{ {
get get
{ {
var versionString = this.Get<string>(null); var versionString = Get<string>(null);
if (string.IsNullOrEmpty(versionString)) if (string.IsNullOrEmpty(versionString))
return null; return null;
else else
@@ -306,10 +343,16 @@ namespace Disco.Data.Configuration
} }
set set
{ {
this.Set<string>(value.ToString(4)); Set(value.ToString(4));
} }
} }
#endregion #endregion
public List<SavedExport> SavedExports
{
get => Get(new List<SavedExport>());
set => Set(value);
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More