1 Commits

Author SHA1 Message Date
dependabot[bot] 83bd81b127 build(deps): bump jQuery.Validation from 1.12.0 to 1.19.4 in /Disco.Web
Bumps jQuery.Validation from 1.12.0 to 1.19.4.

---
updated-dependencies:
- dependency-name: jQuery.Validation
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-30 19:44:12 +00:00
1161 changed files with 82099 additions and 148157 deletions
-4
View File
@@ -1,4 +0,0 @@
[*.cs]
# VSSpell001: Spell Check
dotnet_diagnostic.VSSpell001.severity = suggestion
@@ -9,6 +9,7 @@ using Disco.Services.Interop.ActiveDirectory;
using iTextSharp.text.pdf;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Drawing;
using System.IO;
using System.Linq;
@@ -17,12 +18,13 @@ namespace Disco.BI.Extensions
{
public static class DocumentTemplateExtensions
{
public static Tuple<Dictionary<string, Expression>, List<DocumentField>> CreateExpressions(string templateFileName, DiscoDataContext database)
private static Tuple<Dictionary<string, Expression>, List<DocumentField>> CreateExpressions(DocumentTemplate dt, DiscoDataContext database)
{
Dictionary<string, Expression> expressions = new Dictionary<string, Expression>();
List<DocumentField> fields = new List<DocumentField>();
PdfReader pdfReader = new PdfReader(templateFileName);
string templateFilename = dt.RepositoryFilename(database);
PdfReader pdfReader = new PdfReader(templateFilename);
int pdfFieldOrdinal = 0;
foreach (string pdfFieldKey in pdfReader.AcroFields.Fields.Keys)
{
@@ -60,60 +62,54 @@ namespace Disco.BI.Extensions
return Tuple.Create(expressions, fields);
}
private static Tuple<Dictionary<string, Expression>, List<DocumentField>> CreateExpressions(DocumentTemplate dt, DiscoDataContext database)
public static Dictionary<string, Expression> PdfExpressionsFromCache(this DocumentTemplate dt, DiscoDataContext Database)
{
string templateFileName = dt.RepositoryFilename(database);
return CreateExpressions(templateFileName, database);
return ExpressionCache.GetOrCreateExpressions(dt, () => CreateExpressions(dt, Database));
}
public static Dictionary<string, Expression> PdfExpressionsFromCache(this DocumentTemplate dt, DiscoDataContext database)
public static List<DocumentField> PdfFieldsFromCache(this DocumentTemplate dt, DiscoDataContext Database)
{
return ExpressionCache.GetOrCreateExpressions(dt, () => CreateExpressions(dt, database));
return ExpressionCache.GetOrCreateFields(dt, () => CreateExpressions(dt, Database));
}
public static List<DocumentField> PdfFieldsFromCache(this DocumentTemplate dt, DiscoDataContext database)
public static List<Expression> ExtractPdfExpressions(this DocumentTemplate dt, DiscoDataContext Database)
{
return ExpressionCache.GetOrCreateFields(dt, () => CreateExpressions(dt, database));
return dt.PdfExpressionsFromCache(Database).Values.OrderBy(e => e.Ordinal).ToList();
}
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)
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);
string generateExpressionResult = null;
if (generateExpression)
generateExpressionResult = dt.EvaluateOnGenerateExpression(target, database, creatorUser, timeStamp, state);
generateExpressionResult = dt.EvaluateOnGenerateExpression(Target, Database, CreatorUser, TimeStamp, State);
var pdfStream = Interop.Pdf.PdfGenerator.GenerateFromTemplate(dt, database, target, creatorUser, timeStamp, state, flattenFields);
var pdfStream = Interop.Pdf.PdfGenerator.GenerateFromTemplate(dt, Database, Target, CreatorUser, TimeStamp, State, FlattenFields);
if (generateExpression)
DocumentsLog.LogDocumentGenerated(dt, target, creatorUser, generateExpressionResult);
DocumentsLog.LogDocumentGenerated(dt, Target, CreatorUser, generateExpressionResult);
else
DocumentsLog.LogDocumentGenerated(dt, target, creatorUser);
DocumentsLog.LogDocumentGenerated(dt, Target, CreatorUser);
return pdfStream;
}
public static Stream GeneratePdfPackage(this DocumentTemplatePackage package, DiscoDataContext database, IAttachmentTarget target, User creatorUser, DateTime timeStamp, DocumentState state)
public static Stream GeneratePdfPackage(this DocumentTemplatePackage package, DiscoDataContext Database, IAttachmentTarget Target, User CreatorUser, DateTime TimeStamp, DocumentState State)
{
return Interop.Pdf.PdfGenerator.GenerateFromPackage(package, database, target, creatorUser, timeStamp, state);
return Interop.Pdf.PdfGenerator.GenerateFromPackage(package, Database, Target, CreatorUser, TimeStamp, State);
}
public static Stream GeneratePdfPackageBulk(this DocumentTemplatePackage package, DiscoDataContext database, User creatorUser, DateTime timestamp, bool? insertBlankPages, List<string> dataObjectsIds)
public static Stream GeneratePdfPackageBulk(this DocumentTemplatePackage package, DiscoDataContext Database, User CreatorUser, DateTime Timestamp, bool InsertBlankPages, List<string> DataObjectsIds)
{
return Interop.Pdf.PdfGenerator.GenerateBulkFromPackage(package, database, creatorUser, timestamp, insertBlankPages, dataObjectsIds);
return Interop.Pdf.PdfGenerator.GenerateBulkFromPackage(package, Database, CreatorUser, Timestamp, InsertBlankPages, DataObjectsIds);
}
public static Stream GeneratePdfPackageBulk(this DocumentTemplatePackage package, DiscoDataContext database, User creatorUser, DateTime timestamp, bool? insertBlankPages, List<IAttachmentTarget> dataObjects)
public static Stream GeneratePdfPackageBulk(this DocumentTemplatePackage package, DiscoDataContext Database, User CreatorUser, DateTime Timestamp, bool InsertBlankPages, List<IAttachmentTarget> DataObjects)
{
return Interop.Pdf.PdfGenerator.GenerateBulkFromPackage(package, database, creatorUser, timestamp, insertBlankPages, dataObjects);
return Interop.Pdf.PdfGenerator.GenerateBulkFromPackage(package, Database, CreatorUser, Timestamp, InsertBlankPages, DataObjects);
}
public static List<bool> PdfPageHasAttachmentId(this DocumentTemplate dt, DiscoDataContext database)
public static List<bool> PdfPageHasAttachmentId(this DocumentTemplate dt, DiscoDataContext Database)
{
string templateFilename = dt.RepositoryFilename(database);
string templateFilename = dt.RepositoryFilename(Database);
if (!File.Exists(templateFilename))
throw new FileNotFoundException("PDF template not found", templateFilename);
@@ -132,20 +128,30 @@ namespace Disco.BI.Extensions
public static void Delete(this DocumentTemplate dt, DiscoDataContext Database)
{
// Find & Rename all references
void updateAttachment(IAttachment a)
foreach (DeviceAttachment a in Database.DeviceAttachments.Where(a => a.DocumentTemplateId == dt.Id))
{
var comments = $"{dt.Description} - {a.Comments}";
if (comments.Length > 500)
comments = comments.Substring(0, 500);
a.Comments = comments;
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 (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))
updateAttachment(a);
{
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;
}
// Remove Linked Group
ActiveDirectory.Context.ManagedGroups.Remove(DocumentTemplateDevicesManagedGroup.GetKey(dt));
@@ -156,8 +162,8 @@ namespace Disco.BI.Extensions
// Delete Template
string templateRepositoryFilename = dt.RepositoryFilename(Database);
if (File.Exists(templateRepositoryFilename))
File.Delete(templateRepositoryFilename);
if (System.IO.File.Exists(templateRepositoryFilename))
System.IO.File.Delete(templateRepositoryFilename);
// Remove from Cache
dt.FilterExpressionInvalidateCache();
+55 -68
View File
@@ -1,8 +1,8 @@
using Disco.BI.Extensions;
using Disco.Data.Repository;
using Disco.Models.BI.Expressions;
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;
@@ -21,16 +21,16 @@ namespace Disco.BI.Interop.Pdf
{
public static class PdfGenerator
{
public static Stream GenerateBulkFromPackage(DocumentTemplatePackage package, DiscoDataContext database, User creatorUser, DateTime timestamp, bool? insertBlankPages, List<IAttachmentTarget> dataObjects)
public static Stream GenerateBulkFromPackage(DocumentTemplatePackage package, DiscoDataContext Database, User CreatorUser, DateTime Timestamp, bool InsertBlankPages, List<IAttachmentTarget> DataObjects)
{
if (dataObjects.Count > 0)
if (DataObjects.Count > 0)
{
List<Stream> generatedPdfs = new List<Stream>(dataObjects.Count);
List<Stream> generatedPdfs = new List<Stream>(DataObjects.Count);
using (var state = DocumentState.DefaultState())
{
foreach (var d in dataObjects)
foreach (var d in DataObjects)
{
generatedPdfs.Add(package.GeneratePdfPackage(database, d, creatorUser, timestamp, state));
generatedPdfs.Add(package.GeneratePdfPackage(Database, d, CreatorUser, Timestamp, state));
state.SequenceNumber++;
state.FlushScopeCache();
}
@@ -41,7 +41,7 @@ namespace Disco.BI.Interop.Pdf
}
else
{
Stream bulkPdf = Utilities.JoinPdfs(insertBlankPages.GetValueOrDefault(package.InsertBlankPages), generatedPdfs);
Stream bulkPdf = Utilities.JoinPdfs(package.InsertBlankPages || InsertBlankPages, generatedPdfs);
foreach (Stream singlePdf in generatedPdfs)
singlePdf.Dispose();
return bulkPdf;
@@ -50,25 +50,25 @@ namespace Disco.BI.Interop.Pdf
return null;
}
public static Stream GenerateBulkFromPackage(DocumentTemplatePackage package, DiscoDataContext database, User creatorUser, DateTime timestamp, bool? insertBlankPages, List<string> dataObjectsIds)
public static Stream GenerateBulkFromPackage(DocumentTemplatePackage package, DiscoDataContext Database, User CreatorUser, DateTime Timestamp, bool InsertBlankPages, List<string> DataObjectsIds)
{
List<IAttachmentTarget> DataObjects;
switch (package.Scope)
{
case AttachmentTypes.Device:
DataObjects = database.Devices.Where(d => dataObjectsIds.Contains(d.SerialNumber)).ToList<IAttachmentTarget>();
DataObjects = Database.Devices.Where(d => DataObjectsIds.Contains(d.SerialNumber)).ToList<IAttachmentTarget>();
break;
case AttachmentTypes.Job:
int[] intDataObjectsIds = dataObjectsIds.Select(i => int.Parse(i)).ToArray();
DataObjects = database.Jobs.Where(j => intDataObjectsIds.Contains(j.Id)).ToList<IAttachmentTarget>();
int[] intDataObjectsIds = DataObjectsIds.Select(i => int.Parse(i)).ToArray();
DataObjects = Database.Jobs.Where(j => intDataObjectsIds.Contains(j.Id)).ToList<IAttachmentTarget>();
break;
case AttachmentTypes.User:
DataObjects = new List<IAttachmentTarget>(dataObjectsIds.Count);
for (int idIndex = 0; idIndex < dataObjectsIds.Count; idIndex++)
DataObjects = new List<IAttachmentTarget>(DataObjectsIds.Count);
for (int idIndex = 0; idIndex < DataObjectsIds.Count; idIndex++)
{
string dataObjectId = dataObjectsIds[idIndex];
var user = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(dataObjectId), database, true);
string dataObjectId = DataObjectsIds[idIndex];
var user = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(dataObjectId), Database, true);
if (user == null)
throw new Exception($"Unknown Username specified: {dataObjectId}");
DataObjects.Add(user);
@@ -78,12 +78,12 @@ namespace Disco.BI.Interop.Pdf
throw new InvalidOperationException("Invalid DocumentType Scope");
}
return GenerateBulkFromPackage(package, database, creatorUser, timestamp, insertBlankPages, DataObjects);
return GenerateBulkFromPackage(package, Database, CreatorUser, Timestamp, InsertBlankPages, DataObjects);
}
public static Stream GenerateFromPackage(DocumentTemplatePackage package, DiscoDataContext database, IAttachmentTarget data, User creatorUser, DateTime timestamp, DocumentState state)
public static Stream GenerateFromPackage(DocumentTemplatePackage package, DiscoDataContext Database, IAttachmentTarget Data, User CreatorUser, DateTime Timestamp, DocumentState State)
{
var templates = package.GetDocumentTemplates(database);
var templates = package.GetDocumentTemplates(Database);
if (templates.Count == 0)
return null;
@@ -92,21 +92,21 @@ namespace Disco.BI.Interop.Pdf
string generateExpressionResult = null;
if (generateExpression)
generateExpressionResult = package.EvaluateOnGenerateExpression(data, database, creatorUser, timestamp, state);
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));
generatedPdfs.Add(template.GeneratePdf(Database, Data, CreatorUser, Timestamp, State, true));
state.SequenceNumber++;
state.FlushScopeCache();
State.SequenceNumber++;
State.FlushScopeCache();
}
if (generateExpression)
DocumentsLog.LogDocumentPackageGenerated(package, data, creatorUser, generateExpressionResult);
DocumentsLog.LogDocumentPackageGenerated(package, Data, CreatorUser, generateExpressionResult);
else
DocumentsLog.LogDocumentPackageGenerated(package, data, creatorUser);
DocumentsLog.LogDocumentPackageGenerated(package, Data, CreatorUser);
if (generatedPdfs.Count == 1)
{
@@ -120,20 +120,20 @@ namespace Disco.BI.Interop.Pdf
return bulkPdf;
}
}
public static Stream GenerateBulkFromTemplate(DocumentTemplate dt, DiscoDataContext database, User creatorUser, DateTime timestamp, bool insertBlankPages, List<IAttachmentTarget> dataObjects, IScheduledTaskStatus taskStatus)
public static Stream GenerateBulkFromTemplate(DocumentTemplate dt, DiscoDataContext Database, User CreatorUser, DateTime Timestamp, bool InsertBlankPages, List<IAttachmentTarget> DataObjects, IScheduledTaskStatus taskStatus)
{
if (dataObjects.Count > 0)
if (DataObjects.Count > 0)
{
List<Stream> generatedPdfs = new List<Stream>(dataObjects.Count);
var progressPerDoc = 80d / dataObjects.Count;
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)
foreach (var d in DataObjects)
{
taskStatus.UpdateStatus(progressDoc += progressPerDoc, $"Rendering {d.AttachmentReferenceId}");
generatedPdfs.Add(dt.GeneratePdf(database, d, creatorUser, timestamp, state, true));
generatedPdfs.Add(dt.GeneratePdf(Database, d, CreatorUser, Timestamp, state, true));
state.SequenceNumber++;
state.FlushScopeCache();
}
@@ -145,7 +145,7 @@ namespace Disco.BI.Interop.Pdf
else
{
taskStatus.UpdateStatus(90, "Merging", "Merging documents");
Stream bulkPdf = Utilities.JoinPdfs(insertBlankPages, generatedPdfs);
Stream bulkPdf = Utilities.JoinPdfs(InsertBlankPages, generatedPdfs);
foreach (Stream singlePdf in generatedPdfs)
singlePdf.Dispose();
return bulkPdf;
@@ -154,28 +154,28 @@ namespace Disco.BI.Interop.Pdf
return null;
}
public static Stream GenerateBulkFromTemplate(DocumentTemplate dt, DiscoDataContext database, User creatorUser, DateTime timestamp, bool insertBlankPages, List<string> dataObjectsIds, IScheduledTaskStatus taskStatus)
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;
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);
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);
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)
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);
var user = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(userId), Database, true);
if (user == null)
{
dataObjectIds.Add(userId);
@@ -190,7 +190,7 @@ namespace Disco.BI.Interop.Pdf
}
// recreate list to honor the sort-order provided in DataObjectsIds
var dataObjects = new List<IAttachmentTarget>(dataObjectsIds.Count);
var dataObjects = new List<IAttachmentTarget>(DataObjectsIds.Count);
var missingIds = new List<string>();
foreach (var id in dataObjectIds)
{
@@ -204,10 +204,10 @@ namespace Disco.BI.Interop.Pdf
throw new Exception($"Unknown id specified: {string.Join("; ", missingIds)}");
}
return GenerateBulkFromTemplate(dt, database, creatorUser, timestamp, insertBlankPages, dataObjects, taskStatus);
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)
public static Stream GenerateFromTemplate(DocumentTemplate dt, DiscoDataContext Database, IAttachmentTarget Data, User CreatorUser, DateTime TimeStamp, DocumentState State, bool FlattenFields = false)
{
// Validate Data
switch (dt.Scope)
@@ -232,7 +232,7 @@ namespace Disco.BI.Interop.Pdf
// Override FlattenFields if Document Template instructs.
if (dt.FlattenForm)
flattenFields = true;
FlattenFields = true;
var expressionCache = dt.PdfExpressionsFromCache(Database);
@@ -242,7 +242,7 @@ namespace Disco.BI.Interop.Pdf
MemoryStream pdfGeneratedStream = new MemoryStream();
PdfStamper pdfStamper = new PdfStamper(pdfReader, pdfGeneratedStream);
pdfStamper.FormFlattening = flattenFields;
pdfStamper.FormFlattening = FlattenFields;
pdfStamper.Writer.CloseStream = false;
IDictionary expressionVariables = Expression.StandardVariables(dt, Database, CreatorUser, TimeStamp, State, Data);
@@ -253,7 +253,7 @@ namespace Disco.BI.Interop.Pdf
{
AcroFields.Item fields = pdfStamper.AcroFields.Fields[pdfFieldKey];
string fieldValue = dt.CreateUniqueIdentifier(Database, Data, CreatorUser, TimeStamp, 0).ToJson();
if (flattenFields)
if (FlattenFields)
pdfStamper.AcroFields.SetField(pdfFieldKey, string.Empty);
else
pdfStamper.AcroFields.SetField(pdfFieldKey, fieldValue);
@@ -268,15 +268,16 @@ namespace Disco.BI.Interop.Pdf
var pageUniqueIdBytes = pageUniqueId.ToQRCodeBytes();
// Encode to QRCode byte array
var pageUniqueIdEncoded = QRCodeBinaryEncoder.Encode(pageUniqueIdBytes, out var qrWidth, out var qrHeight);
var pageUniqueIdWidth = (int)pdfFieldPosition.position.Width;
var pageUniqueIdHeight = (int)pdfFieldPosition.position.Height;
var pageUniqueIdEncoded = QRCodeBinaryEncoder.Encode(pageUniqueIdBytes, pageUniqueIdWidth, pageUniqueIdHeight);
// 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);
var pageUniqueIdImageData = CCITTG4Encoder.Compress(pageUniqueIdEncoded, pageUniqueIdWidth, pageUniqueIdHeight);
var pageUniqueIdImage = iTextSharp.text.Image.GetInstance(pageUniqueIdWidth, pageUniqueIdHeight, 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
@@ -296,7 +297,8 @@ namespace Disco.BI.Interop.Pdf
}
else
{
if (expressionCache.TryGetValue(pdfFieldKey, out var fieldExpression))
Expression fieldExpression = null;
if (expressionCache.TryGetValue(pdfFieldKey, out fieldExpression))
{
if (fieldExpression.IsDynamic)
{
@@ -313,22 +315,7 @@ namespace Disco.BI.Interop.Pdf
for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++)
{
AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal];
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}");
iTextSharp.text.Image pdfImage = iTextSharp.text.Image.GetInstance(imageResult.GetImage((int)(pdfFieldPosition.position.Width * 1.6), (int)(pdfFieldPosition.position.Height * 1.6)));
pdfImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom);
pdfImage.ScaleToFit(pdfFieldPosition.position.Width, pdfFieldPosition.position.Height);
pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfImage);
@@ -396,7 +383,7 @@ namespace Disco.BI.Interop.Pdf
TechUserId = CreatorUser.UserId,
Timestamp = DateTime.Now
};
jl.Comments = $"# Document Generated\r\n**{dt.Description}** [{dt.Id}]";
jl.Comments = string.Format("# Document Generated\r\n**{0}** [{1}]", dt.Description, dt.Id);
Database.JobLogs.Add(jl);
}
+1 -8
View File
@@ -1,7 +1,5 @@
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.codec;
using System;
using System.Collections.Generic;
using System.IO;
@@ -9,15 +7,10 @@ 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));
throw new System.ArgumentNullException(nameof(Pdfs));
// Only One PDF - Possible Reference Bug v's Memory/Speed (Returning Param Memory Stream)
if (Pdfs.Count == 1)
+3 -1
View File
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Disco</RootNamespace>
<AssemblyName>Disco.BI</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
@@ -53,6 +53,7 @@
<Reference Include="System.DirectoryServices" />
<Reference Include="System.Drawing" />
<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">
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Extensions.dll</HintPath>
<Private>True</Private>
@@ -61,6 +62,7 @@
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Primitives.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Web" />
+3 -3
View File
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.5.25262.0000")]
[assembly: AssemblyFileVersion("2.5.25262.0000")]
[assembly: AssemblyVersion("2.2.16326.0500")]
[assembly: AssemblyFileVersion("2.2.16326.0500")]
+1 -1
View File
@@ -19,7 +19,7 @@ namespace Disco.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
+1 -13
View File
@@ -16,7 +16,7 @@
</defaultConnectionFactory>
</entityFramework>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
@@ -52,18 +52,6 @@
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<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>
</assemblyBinding>
</runtime>
</configuration>
-1
View File
@@ -141,7 +141,6 @@
<Compile Include="Extensions\EnrolExtensions.cs" />
<Compile Include="Extensions\WhoAmIExtensions.cs" />
<Compile Include="Interop\Certificates.cs" />
<Compile Include="Interop\EndpointDiscovery.cs" />
<Compile Include="Interop\Hardware.cs" />
<Compile Include="Interop\LocalAuthentication.cs" />
<Compile Include="Interop\Native\NetworkConnectionStatuses.cs" />
+14 -12
View File
@@ -1,27 +1,31 @@
using Disco.Client.Extensions;
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using Disco.Client.Extensions;
using Newtonsoft.Json;
namespace Disco.Client
{
public static class ErrorReporting
{
private const string ServicePathTemplate = "http://DISCO:9292/Services/Client/ClientError";
public static string DeviceIdentifier { get; set; }
public static string EnrolmentSessionId { get; set; }
public static void ReportError(Exception exception, bool reportToServer)
public static void ReportError(Exception Ex, bool ReportToServer)
{
bool isClientServiceException = exception is ClientServiceException;
bool isClientServiceException = Ex is ClientServiceException;
ErrorReport report = new ErrorReport()
{
DeviceIdentifier = DeviceIdentifier,
SessionId = EnrolmentSessionId,
JsonException = exception.IntenseExceptionSerialization()
JsonException = Ex.IntenseExceptionSerialization()
};
try
@@ -37,7 +41,7 @@ namespace Disco.Client
catch (Exception) { }
// Don't log server errors back to the server
if (!isClientServiceException && reportToServer)
if (!isClientServiceException && ReportToServer)
{
try
{
@@ -48,7 +52,7 @@ namespace Disco.Client
try
{
Presentation.WriteFatalError(exception);
Presentation.WriteFatalError(Ex);
}
catch (Exception) { }
}
@@ -84,9 +88,7 @@ namespace Disco.Client
string reportJson = JsonConvert.SerializeObject(report);
string reportResponse;
var serverUri = new Uri(Program.ServerUrl ?? new Uri("http://disco:9292"), "/Services/Client/ClientError");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serverUri);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(ServicePathTemplate);
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
request.ContentType = "application/json";
request.Method = WebRequestMethods.Http.Post;
@@ -106,7 +108,7 @@ namespace Disco.Client
}
}
Debug.WriteLine($"Error Report Logged to Server; Response: {reportResponse}");
System.Diagnostics.Debug.WriteLine("Error Report Logged to Server; Response: {0}", reportResponse);
}
#endregion
@@ -1,25 +1,32 @@
using Disco.Models.ClientServices;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Reflection;
namespace Disco.Client.Extensions
{
internal static class ClientServicesExtensions
public static class ClientServicesExtensions
{
public static ResponseType Post<ResponseType>(this ServiceBase<ResponseType> service, bool authenticated)
//#if DEBUG
// public const string ServicePathAuthenticatedTemplate = "http://WS-GSHARP:57252/Services/Client/Authenticated/{0}";
// public const string ServicePathUnauthenticatedTemplate = "http://WS-GSHARP:57252/Services/Client/Unauthenticated/{0}";
//#else
public const string ServicePathAuthenticatedTemplate = "http://DISCO:9292/Services/Client/Authenticated/{0}";
public const string ServicePathUnauthenticatedTemplate = "http://DISCO:9292/Services/Client/Unauthenticated/{0}";
//#endif
public static ResponseType Post<ResponseType>(this ServiceBase<ResponseType> Service, bool Authenticated)
{
ResponseType serviceResponse;
Uri serviceUrl;
string serviceUrl;
if (authenticated)
serviceUrl = new Uri(Program.ServerUrl, $"/Services/Client/Authenticated/{service.Feature}");
if (Authenticated)
serviceUrl = string.Format(ServicePathAuthenticatedTemplate, Service.Feature);
else
serviceUrl = new Uri(Program.ServerUrl, $"/Services/Client/Unauthenticated/{service.Feature}");
serviceUrl = string.Format(ServicePathUnauthenticatedTemplate, Service.Feature);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceUrl);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceUrl);
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
request.ContentType = "application/json";
request.Method = WebRequestMethods.Http.Post;
@@ -32,7 +39,7 @@ namespace Disco.Client.Extensions
{
using (var jsonWriter = new JsonTextWriter(requestWriter))
{
jsonSerializer.Serialize(jsonWriter, service);
jsonSerializer.Serialize(jsonWriter, Service);
}
}
+26 -53
View File
@@ -2,12 +2,14 @@
using Disco.Models.ClientServices;
using Microsoft.Win32;
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
namespace Disco.Client.Extensions
{
internal static class EnrolExtensions
public static class EnrolExtensions
{
public static void Build(this Enrol enrol)
{
enrol.ComputerName = Environment.MachineName;
@@ -39,9 +41,6 @@ namespace Disco.Client.Extensions
if (!string.IsNullOrEmpty(enrolResponse.ErrorMessage))
throw new ClientServiceException("Enrolment", enrolResponse.ErrorMessage);
if (enrolResponse.IsPending)
return;
// Offline Domain Join
bool requireReboot = enrolResponse.ApplyOfflineDomainJoin();
@@ -60,28 +59,6 @@ namespace Disco.Client.Extensions
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>
/// Processes a Client Service Enrol Response for Offline Domain Join Actions
/// </summary>
@@ -93,39 +70,38 @@ namespace Disco.Client.Extensions
{
Presentation.UpdateStatus("Enrolling Device", $"Performing Offline Domain Join:\r\nRenaming Computer: {Environment.MachineName} -> {enrolResponse.ComputerName}", true, -1, 1500);
var provisionData = Convert.FromBase64String(enrolResponse.OfflineDomainJoinManifest);
string systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
string odjFile = Path.GetTempFileName();
File.WriteAllBytes(odjFile, Convert.FromBase64String(enrolResponse.OfflineDomainJoinManifest));
var provisionDataPointer = Marshal.AllocCoTaskMem(provisionData.Length);
Marshal.Copy(provisionData, 0, provisionDataPointer, provisionData.Length);
var joinResult = default(int);
try
{
joinResult = NetRequestOfflineDomainJoin(provisionDataPointer, provisionData.Length, NETSETUP_PROVISION_FLAGS.NETSETUP_PROVISION_ONLINE_CALLER, systemRoot);
}
finally
{
Marshal.FreeCoTaskMem(provisionDataPointer);
}
string odjWindowsPath = Environment.GetEnvironmentVariable("SystemRoot");
string odjProcessArguments = $"/REQUESTODJ /LOADFILE \"{odjFile}\" /WINDOWSPATH \"{odjWindowsPath}\" /LOCALOS";
if (joinResult != 0)
ProcessStartInfo odjProcessStartInfo = new ProcessStartInfo("DJOIN.EXE", odjProcessArguments)
{
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
CreateNoWindow = true,
ErrorDialog = false,
LoadUserProfile = true,
RedirectStandardOutput = true,
UseShellExecute = false
};
string odjResult;
using (Process odjProcess = System.Diagnostics.Process.Start(odjProcessStartInfo))
{
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Succeeded", true, -1, 2000);
odjResult = odjProcess.StandardOutput.ReadToEnd();
odjProcess.WaitForExit(20000); // 20 Seconds
}
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Result:\r\n{odjResult}", true, -1, 3000);
if (File.Exists(odjFile))
File.Delete(odjFile);
// Flush Logged-On History
if (enrolResponse.SetAssignedUserForLogon && !string.IsNullOrEmpty(enrolResponse.DomainName))
if (!string.IsNullOrEmpty(enrolResponse.DomainName))
{
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
{
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))
{
@@ -154,11 +130,8 @@ namespace Disco.Client.Extensions
Presentation.UpdateStatus("Enrolling Device", $"Configuring the device owner:\r\n{enrolResponse.AssignedUserDescription} ({enrolResponse.AssignedUserDomain}\\{enrolResponse.AssignedUserUsername})", true, -1, 3000);
if (enrolResponse.AssignedUserIsLocalAdmin)
LocalAuthentication.AddLocalGroupMembership("Administrators", enrolResponse.AssignedUserSID, enrolResponse.AssignedUserUsername, enrolResponse.AssignedUserDomain);
}
Interop.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
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
{
+5 -1
View File
@@ -1,5 +1,9 @@
using Disco.Models.ClientServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Disco.Models.ClientServices;
namespace Disco.Client.Extensions
{
+2 -1
View File
@@ -105,7 +105,8 @@ namespace Disco.Client.Interop
{
foreach (var thumbprint in RemoveThumbprints)
{
if (existingThumbprints.TryGetValue(thumbprint, out var certificates) && !addedThumbprints.Contains(thumbprint))
List<X509Certificate2> certificates;
if (existingThumbprints.TryGetValue(thumbprint, out certificates) && !addedThumbprints.Contains(thumbprint))
{
foreach (var certificate in certificates)
{
-317
View File
@@ -1,317 +0,0 @@
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,
};
}
}
}
}
+7 -19
View File
@@ -46,7 +46,6 @@ namespace Disco.Client.Interop
audit.ApplyPhysicalMemoryInformation();
audit.ApplyDiskDriveInformation();
audit.ApplyBatteryInformation();
audit.ApplyMobileDeviceManagementInformation();
audit.NetworkAdapters = Network.GetNetworkAdapters();
@@ -332,7 +331,7 @@ namespace Disco.Client.Interop
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve BIOS information from WMI", ex);
throw new Exception("Disco Client was unable to retrieve BIOS information from WMI", ex);
}
}
@@ -395,7 +394,7 @@ namespace Disco.Client.Interop
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve ComputerSystem information from WMI", ex);
throw new Exception("Disco Client was unable to retrieve ComputerSystem information from WMI", ex);
}
}
@@ -428,7 +427,7 @@ namespace Disco.Client.Interop
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve ComputerSystem information from WMI", ex);
throw new Exception("Disco Client was unable to retrieve ComputerSystem information from WMI", ex);
}
}
@@ -505,7 +504,7 @@ namespace Disco.Client.Interop
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve BaseBoard information from WMI", ex);
throw new Exception("Disco Client was unable to retrieve BaseBoard information from WMI", ex);
}
}
@@ -539,7 +538,8 @@ namespace Disco.Client.Interop
// 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)
Guid uuidGuid;
if (Guid.TryParse(deviceHardware.UUID, out uuidGuid) && uuidGuid != Guid.Empty)
{
deviceHardware.SerialNumber = $"UUID{uuidGuid:N}";
}
@@ -556,22 +556,10 @@ namespace Disco.Client.Interop
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve ComputerSystemProduct information from WMI", ex);
throw new Exception("Disco 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)
@@ -1,4 +1,9 @@
namespace Disco.Client.Interop.Native
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.Client.Interop.Native
{
public enum NetworkConnectionStatuses : ushort
{
+1 -1
View File
@@ -68,7 +68,7 @@ namespace Disco.Client.Interop
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve NetworkAdapter information from WMI", ex);
throw new Exception("Disco Client was unable to retrieve NetworkAdapter information from WMI", ex);
}
}
+26 -12
View File
@@ -17,13 +17,16 @@ namespace Disco.Client.Interop
{
try
{
IntPtr wlanHandle;
uint wlanServiceVersion;
if (WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out var wlanServiceVersion, out var wlanHandle) == WlanApi.ERROR_SUCCESS)
if (WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out wlanServiceVersion, out wlanHandle) == WlanApi.ERROR_SUCCESS)
{
try
{
IntPtr wlanInterfacesPtr;
if (WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out var wlanInterfacesPtr) == WlanApi.ERROR_SUCCESS)
if (WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out wlanInterfacesPtr) == WlanApi.ERROR_SUCCESS)
{
try
{
@@ -58,7 +61,7 @@ namespace Disco.Client.Interop
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve Wireless NetworkAdapter information from WlanApi", ex);
throw new Exception("Disco Client was unable to retrieve Wireless NetworkAdapter information from WlanApi", ex);
}
}
@@ -66,10 +69,12 @@ namespace Disco.Client.Interop
{
try
{
IntPtr wlanHandle;
uint wlanServiceVersion;
uint interopResult;
// Connect to wireless service
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out var wlanServiceVersion, out var wlanHandle);
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out wlanServiceVersion, out wlanHandle);
if (interopResult == WlanApi.ERROR_SERVICE_NOT_ACTIVE)
{
// Indicates the Wlan service has not been started on the client
@@ -97,16 +102,17 @@ namespace Disco.Client.Interop
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to retrieve Wireless Profiles from WlanApi", ex);
throw new Exception("Disco Client was unable to retrieve Wireless Profiles from WlanApi", ex);
}
}
private static List<WirelessProfile> GetWirelessProfiles(IntPtr wlanHandle)
{
uint interopResult;
IntPtr wlanInterfacesPtr;
// Enumerate wireless interfaces
interopResult = WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out var wlanInterfacesPtr);
interopResult = WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out wlanInterfacesPtr);
if (interopResult != WlanApi.ERROR_SUCCESS)
{
throw new Exception($"Unable to list interfaces with the local wireless service. WlanEnumInterfaces returned: {interopResult}");
@@ -118,8 +124,9 @@ namespace Disco.Client.Interop
foreach (var wlanInterface in wlanInterfaces.InterfaceInfo)
{
IntPtr wlanProfilesPtr;
// Enumerate wireless profiles for interface
interopResult = WlanApi.WlanGetProfileList(wlanHandle, wlanInterface.InterfaceGuid, IntPtr.Zero, out var wlanProfilesPtr);
interopResult = WlanApi.WlanGetProfileList(wlanHandle, wlanInterface.InterfaceGuid, IntPtr.Zero, out 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}");
@@ -158,10 +165,12 @@ namespace Disco.Client.Interop
try
{
IntPtr wlanHandle;
uint wlanServiceVersion;
uint interopResult;
// Connect to wireless service
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out var wlanServiceVersion, out var wlanHandle);
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out wlanServiceVersion, out wlanHandle);
if (interopResult == WlanApi.ERROR_SERVICE_NOT_ACTIVE)
{
// Indicates the Wlan service has not been started on the client
@@ -234,8 +243,9 @@ namespace Disco.Client.Interop
}
else
{
uint pdwReasonCode;
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);
interopResult = WlanApi.WlanSetProfile(wlanHandle, adapter.ConnectionIdentifier, 0, addProfile.ProfileXml, null, true, IntPtr.Zero, out pdwReasonCode);
if (interopResult != WlanApi.ERROR_SUCCESS)
{
@@ -275,8 +285,11 @@ namespace Disco.Client.Interop
else
{
// Load profile
IntPtr pstrProfileXml;
uint pdwFlags;
IntPtr pdwGrantAccess;
interopResult = WlanApi.WlanGetProfile(wlanHandle, adapter.ConnectionIdentifier, profileName, IntPtr.Zero, out var pstrProfileXml, out var pdwFlags, out var pdwGrantAccess);
interopResult = WlanApi.WlanGetProfile(wlanHandle, adapter.ConnectionIdentifier, profileName, IntPtr.Zero, out pstrProfileXml, out pdwFlags, out pdwGrantAccess);
if (interopResult == WlanApi.ERROR_SUCCESS)
{
@@ -299,9 +312,10 @@ namespace Disco.Client.Interop
if (!XNode.DeepEquals(originalProfileXml, transformedProfileXml))
{
// Set Profile
uint pdwReasonCode;
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);
interopResult = WlanApi.WlanSetProfile(wlanHandle, adapter.ConnectionIdentifier, 0, transformProfileXml, null, true, IntPtr.Zero, out pdwReasonCode);
if (interopResult != WlanApi.ERROR_SUCCESS)
{
@@ -342,7 +356,7 @@ namespace Disco.Client.Interop
}
catch (Exception ex)
{
throw new Exception("Disco ICT Client was unable to apply Wireless Profile Changes using WlanApi", ex);
throw new Exception("Disco Client was unable to apply Wireless Profile Changes using WlanApi", ex);
}
}
+10 -20
View File
@@ -1,10 +1,11 @@
using Disco.Client.Extensions;
using Disco.Client.Interop;
using System;
using System.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using Disco.Client.Extensions;
using Disco.Client.Interop;
namespace Disco.Client
{
@@ -21,13 +22,13 @@ namespace Disco.Client
}
public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress, int TryDelay)
{
UpdateStatus(SubHeading, Message, ShowProgress, Progress);
Presentation.UpdateStatus(SubHeading, Message, ShowProgress, Progress);
if (TryDelay > 0)
Presentation.TryDelay(TryDelay);
}
public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress)
{
Console.WriteLine($"#{SubHeading.EscapeMessage()},{Message.EscapeMessage()},{ShowProgress},{Progress}");
Console.WriteLine("#{0},{1},{2},{3}", SubHeading.EscapeMessage(), Message.EscapeMessage(), ShowProgress.ToString(), Progress.ToString());
}
public static void TryDelay(int Milliseconds)
{
@@ -39,11 +40,6 @@ namespace Disco.Client
{
StringBuilder message = new StringBuilder();
message.AppendLine($"Version: {Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}");
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;
UpdateStatus("Preparation Client Started", message.ToString(), false, 0);
@@ -54,18 +50,12 @@ namespace Disco.Client
{
Console.ForegroundColor = ConsoleColor.Magenta;
if (ex is ClientServiceException clientServiceException)
ClientServiceException clientServiceException = ex as ClientServiceException;
if (clientServiceException != null)
{
UpdateStatus($"An error occurred during {clientServiceException.ServiceFeature}",
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
{
StringBuilder message = new StringBuilder();
@@ -102,7 +92,7 @@ namespace Disco.Client
public static void RegisterBootstrapperPostActions(ShutdownActions ShutdownAction, bool Uninstall)
{
Console.WriteLine($"!{Enum.GetName(typeof(ShutdownActions), ShutdownAction)},{(Uninstall ? "UninstallBootstrapper" : "DontUninstallBootstrapper")}");
Console.WriteLine("!{0},{1}", Enum.GetName(typeof(ShutdownActions), ShutdownAction), Uninstall ? "UninstallBootstrapper" : "DontUninstallBootstrapper");
}
public enum ShutdownActions
{
+15 -88
View File
@@ -1,10 +1,11 @@
using Disco.Client.Extensions;
using Disco.Client.Interop;
using Disco.Models.ClientServices;
using System;
using System.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Disco.Models.ClientServices;
using Disco.Client.Extensions;
namespace Disco.Client
{
@@ -13,9 +14,6 @@ namespace Disco.Client
public static bool IsAuthenticated { get; set; }
public static bool RebootRequired { 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]
public static void Main(string[] args)
@@ -29,31 +27,28 @@ namespace Disco.Client
{
Console.WriteLine("Waiting for Debugger to Attach");
System.Threading.Thread.Sleep(1000);
} while (!Debugger.IsAttached);
} while (!System.Diagnostics.Debugger.IsAttached);
}
#endif
// Initialize Environment Settings
SetupEnvironment(args);
if (ServerUrl == null)
keepProcessing = DiscoverDiscoIct();
SetupEnvironment();
// Report to Bootstrapper
Presentation.WriteBanner();
// WhoAmI Phase
keepProcessing = WhoAmI();
keepProcessing = Program.WhoAmI();
// Enrol Phase
if (keepProcessing)
keepProcessing = Enrol();
keepProcessing = Program.Enrol();
// End conversation with Bootstrapper
Presentation.WriteFooter(RebootRequired, AllowUninstall, !keepProcessing);
Presentation.WriteFooter(Program.RebootRequired, Program.AllowUninstall, !keepProcessing);
}
public static void SetupEnvironment(string[] args)
public static void SetupEnvironment()
{
// Hookup Unhandled Error Handling
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.CurrentDomain_UnhandledException;
@@ -62,66 +57,21 @@ namespace Disco.Client
WebRequest.DefaultWebProxy = new WebProxy();
// Override Http 100 Continue Behaviour
ServicePointManager.Expect100Continue = false;
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
// Assume success unless otherwise notified
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
Presentation.DelayUI = false;
try
{
if (BootstrapperProcessId != -1)
{
var parentProcess = Process.GetProcessById(BootstrapperProcessId);
Presentation.DelayUI = !parentProcess.HasExited;
}
Presentation.DelayUI = (System.Diagnostics.Process.GetProcessesByName("Disco.ClientBootstrapper").Length > 0);
}
catch (Exception)
{
Presentation.DelayUI = true; // Add Delays on Error
}
}
public static bool DiscoverDiscoIct()
{
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()
{
try
@@ -176,37 +126,14 @@ namespace Disco.Client
request = new Enrol();
request.Build();
var startTime = DateTimeOffset.Now;
do
{
// Send Request
Presentation.UpdateStatus("Enrolling Device", "Sending the enrolment request to the server.", true, -1);
response = request.Post(IsAuthenticated);
response = request.Post(Program.IsAuthenticated);
// Process Response
Presentation.UpdateStatus("Enrolling Device", "Processing the enrolment response from the server.", true, -1);
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
return true;
}
+3 -3
View File
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.5.25262.0000")]
[assembly: AssemblyFileVersion("2.5.25262.0000")]
[assembly: AssemblyVersion("2.2.16326.0500")]
[assembly: AssemblyFileVersion("2.2.16326.0500")]
+2 -2
View File
@@ -1,9 +1,9 @@
@ECHO OFF
IF /I "%USERDOMAIN%"=="NT AUTHORITY" GOTO RunAsNetworkService
Disco.Client.exe %1 %2 %3
Disco.Client.exe
EXIT /B 0
:RunAsNetworkService
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 %1 %2 %3"
PsExec -acceptula -i -u "NT AUTHORITY\Network Service" -w "%CD%" "%CD%\Start.bat"
EXIT /B 0
+108 -97
View File
@@ -1,122 +1,146 @@
using Disco.Client.Interop;
using Disco.ClientBootstrapper.Interop;
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using System.Diagnostics;
namespace Disco.ClientBootstrapper
{
internal class BootstrapperLoop
class BootstrapperLoop
{
private readonly Func<CancellationToken, Task> completeCallback;
private readonly CancellationToken cancellationToken;
private readonly IStatus statusUI;
private readonly Uri forcedServerUrl;
public Thread LoopThread;
public delegate void LoopCompleteCallback();
private LoopCompleteCallback mLoopCompleteCallback;
private IStatus statusUI;
private string tempWorkingDirectory;
private StringBuilder errorMessage;
private Process clientProcess;
public BootstrapperLoop(IStatus statusUI, Uri forcedServerUrl, Func<CancellationToken, Task> callback, CancellationToken cancellationToken)
//#if DEBUG
// public const string DiscoServerName = "WS-GSHARP";
// public const int DiscoServerPort = 57252;
//#else
public const string DiscoServerName = "DISCO";
public const int DiscoServerPort = 9292;
//#endif
public BootstrapperLoop(IStatus StatusUI, LoopCompleteCallback Callback)
{
this.statusUI = statusUI;
this.forcedServerUrl = forcedServerUrl;
completeCallback = callback;
this.cancellationToken = cancellationToken;
this.statusUI = StatusUI;
this.mLoopCompleteCallback = Callback;
this.errorMessage = new StringBuilder();
}
public void Start()
{
Task.Factory.StartNew(async () =>
{
await Loop(forcedServerUrl, cancellationToken);
}, cancellationToken);
this.LoopThread = new Thread(new ThreadStart(loopHost));
this.LoopThread.Start();
}
private async Task Loop(Uri forcedServerUrl, CancellationToken cancellationToken)
private void loopHost()
{
try
{
statusUI.UpdateStatus("System Preparation (Bootstrapper)", "Starting", "Please wait...", true, -1);
loop();
}
catch (Exception ex)
{
if (ex.GetType() == typeof(ThreadAbortException))
return;
if (ex.GetType() == typeof(ThreadInterruptedException))
return;
Program.WriteAppError(ex);
throw;
}
}
tempWorkingDirectory = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), @"Disco\Temp");
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);
#endif
tempWorkingDirectory = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco\\Temp");
if (!Directory.Exists(tempWorkingDirectory))
Directory.CreateDirectory(tempWorkingDirectory);
// Check for Network Connectivity
statusUI.UpdateStatus(null, "Detecting Network", "Checking network connectivity, Please wait...", true, -1);
if (!NetworkInterop.HasNetworkConnectivity())
if (!Interop.NetworkInterop.PingDisco(DiscoServerName))
{
statusUI.UpdateStatus(null, "Detecting Network", "No network connectivity detected, Diagnosing...", true, -1);
statusUI_WriteAdapterInfo();
if (!NetworkInterop.HasNetworkConnectivity())
if (!Interop.NetworkInterop.PingDisco(DiscoServerName))
{
// Check for Wireless
var hasWireless = (NetworkInterop.NetworkAdapters.Count(na => na.IsWireless) > 0);
var hasWireless = (Interop.NetworkInterop.NetworkAdapters.Count(na => na.IsWireless) > 0);
if (hasWireless)
{
// True: Do wireless loop
statusUI.UpdateStatus(null, "Configuring Wireless Network", "Wireless adapter detected, Configuring...", true, -1);
await NetworkInterop.ConfigureWireless(cancellationToken);
Interop.NetworkInterop.ConfigureWireless();
statusUI.UpdateStatus(null, "Waiting for Wireless Network", null, true, 0);
for (int i = 0; i < 30; i++)
for (int i = 0; i < 100; i++)
{
statusUI_WriteAdapterInfo();
statusUI.UpdateStatus(null, null, null, true, i);
await Program.SleepThread(2000, false, cancellationToken);
if (NetworkInterop.HasNetworkConnectivity())
Program.SleepThread(500, false);
if (Interop.NetworkInterop.PingDisco(DiscoServerName))
break;
}
if (!NetworkInterop.HasNetworkConnectivity())
if (!Interop.NetworkInterop.PingDisco(DiscoServerName))
{
statusUI.UpdateStatus(null, "Wireless Network Failed", "Unable to connect to the wireless network, please connect the network cable...", false);
await Program.SleepThread(3000, false, cancellationToken);
Program.SleepThread(3000, false);
}
}
if (!NetworkInterop.HasNetworkConnectivity())
if (!Interop.NetworkInterop.PingDisco(DiscoServerName))
{
// Instruct user to connect network cable
statusUI.UpdateStatus(null, "Please connect the network cable", null);
for (int i = 0; i < 30; i++)
for (int i = 0; i < 100; i++)
{
statusUI_WriteAdapterInfo();
statusUI.UpdateStatus(null, null, null, true, i);
await Program.SleepThread(2000, false, cancellationToken);
if (NetworkInterop.HasNetworkConnectivity())
Program.SleepThread(500, false);
if (Interop.NetworkInterop.PingDisco(DiscoServerName))
break;
}
}
}
if (!NetworkInterop.HasNetworkConnectivity())
if (!Interop.NetworkInterop.PingDisco(DiscoServerName))
{
// Client Failed
if (completeCallback != null)
await completeCallback(cancellationToken);
if (this.mLoopCompleteCallback != null)
{
this.mLoopCompleteCallback.BeginInvoke(null, null);
}
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
statusUI.UpdateStatus(null, "Downloading", "Retrieving Preparation Client, Please wait...", true, -1);
string clientSourceLocation = Path.Combine(tempWorkingDirectory, "PreparationClient.zip");
@@ -124,29 +148,8 @@ namespace Disco.ClientBootstrapper
{
// Don't use a proxy when downloading the Client
webClient.Proxy = new WebProxy();
webClient.Headers.Add("X-DiscoICT-Discovery", serverDiscovery.Item2);
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;
}
webClient.DownloadFile($"http://{DiscoServerName}:{DiscoServerPort}/Services/Client/PreparationClient", clientSourceLocation);
}
// Unzip Client
@@ -163,7 +166,7 @@ namespace Disco.ClientBootstrapper
// Launch Client
statusUI.UpdateStatus("System Preparation (Client)", "Running", "Launching Preparation Client, Please wait...", true, -1);
ProcessStartInfo clientProcessStart = new ProcessStartInfo(Path.Combine(clientLocation, "Start.bat"), $"2 {Process.GetCurrentProcess().Id} {serverDiscovery.Item1}")
ProcessStartInfo clientProcessStart = new ProcessStartInfo(Path.Combine(clientLocation, "Start.bat"))
{
WorkingDirectory = clientLocation,
CreateNoWindow = true,
@@ -191,47 +194,45 @@ namespace Disco.ClientBootstrapper
// Cleanup
if (Directory.Exists(tempWorkingDirectory))
Directory.Delete(tempWorkingDirectory, true);
CertificateInterop.RemoveTempCerts();
Interop.CertificateInterop.RemoveTempCerts();
}
catch (Exception ex)
// Pause if Error
if (this.errorMessage.Length > 0)
{
if (ex.GetType() == typeof(ThreadAbortException))
return;
if (ex.GetType() == typeof(ThreadInterruptedException))
return;
if (ex.GetType() == typeof(OperationCanceledException))
return;
Program.WriteAppError(ex);
}
// End Of Loop
if (completeCallback != null)
await completeCallback(cancellationToken);
Program.SleepThread(10000, true);
}
private void statusUI_WriteAdapterInfo()
// End Of Loop
if (this.mLoopCompleteCallback != null)
{
this.mLoopCompleteCallback.BeginInvoke(null, null);
}
}
void statusUI_WriteAdapterInfo()
{
var info = new StringBuilder();
foreach (var na in NetworkInterop.NetworkAdapters)
foreach (var na in Interop.NetworkInterop.NetworkAdapters)
{
if (na.IsWireless)
{
info.AppendLine($"{na.NetConnectionID}: {na.WirelessConnectionStatusMeaning(na.WirelessConnectionStatus)}");
info.AppendLine(string.Format("{0}: {1}", na.NetConnectionID, na.WirelessConnectionStatusMeaning(na.WirelessConnectionStatus)));
}
else
{
info.AppendLine($"{na.NetConnectionID}: {na.ConnectionStatusMeaning(na.ConnectionStatus)}");
info.AppendLine(string.Format("{0}: {1}", na.NetConnectionID, na.ConnectionStatusMeaning(na.ConnectionStatus)));
}
}
statusUI.UpdateStatus(null, null, info.ToString());
}
private void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(e.Data))
{
System.Diagnostics.Debug.WriteLine(string.Format("OUTPUT: {0}", e.Data));
var data = e.Data.Substring(1).Split(new char[] { ',' });
switch (e.Data[0])
{
@@ -248,5 +249,15 @@ 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)
WScript.Echo "Disco ICT Client Bootstrapper Installed"
WScript.Echo "Disco Client Bootstrapper Installed"
@@ -90,9 +90,6 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Disco.Client\Interop\EndpointDiscovery.cs">
<Link>Interop\EndpointDiscovery.cs</Link>
</Compile>
<Compile Include="..\Resources\Libraries\DotNetZip\Source\BZip2\BitWriter.cs">
<Link>DotNetZip\BZip2\BitWriter.cs</Link>
</Compile>
+2 -2
View File
@@ -85,7 +85,7 @@
this.labelVersion.Name = "labelVersion";
this.labelVersion.Size = new System.Drawing.Size(167, 20);
this.labelVersion.TabIndex = 0;
this.labelVersion.Text = "Disco ICT Bootstrapper v";
this.labelVersion.Text = "Disco Bootstrapper v";
this.labelVersion.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// FormStatus
@@ -106,7 +106,7 @@
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Disco ICT - Client Bootstrapper";
this.Text = "Disco - Client Bootstrapper";
this.TopMost = true;
this.TransparencyKey = System.Drawing.Color.Magenta;
this.ResumeLayout(false);
+26 -20
View File
@@ -1,4 +1,10 @@
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;
namespace Disco.ClientBootstrapper
@@ -6,69 +12,69 @@ namespace Disco.ClientBootstrapper
public partial class FormStatus : Form, IStatus
{
private delegate void dUpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress);
private readonly dUpdateStatus mUpdateStatus;
private delegate void dUpdateStatus(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress, Nullable<int> Progress);
private dUpdateStatus mUpdateStatus;
public FormStatus()
{
InitializeComponent();
var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
labelVersion.Text = $"v{version.ToString(3)}";
this.labelVersion.Text = string.Format("v{0}", version.ToString(3));
FormClosed += new FormClosedEventHandler(FormStatus_FormClosed);
this.FormClosed += new FormClosedEventHandler(FormStatus_FormClosed);
mUpdateStatus = new dUpdateStatus(UpdateStatusDo);
Cursor.Hide();
}
private void FormStatus_FormClosed(object sender, FormClosedEventArgs e)
void FormStatus_FormClosed(object sender, FormClosedEventArgs e)
{
Cursor.Show();
Program.ExitApplication();
}
public void UpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress = null, int? Progress = null)
public void UpdateStatus(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress = null, Nullable<int> Progress = null)
{
try
{
Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress);
this.Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress);
}
catch (Exception) { }
}
private void UpdateStatusDo(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress)
private void UpdateStatusDo(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress, Nullable<int> Progress)
{
if (Heading != null)
if (labelHeading.Text != Heading)
labelHeading.Text = Heading;
if (this.labelHeading.Text != Heading)
this.labelHeading.Text = Heading;
if (SubHeading != null)
if (labelSubHeading.Text != SubHeading)
labelSubHeading.Text = SubHeading;
if (this.labelSubHeading.Text != SubHeading)
this.labelSubHeading.Text = SubHeading;
if (Message != null)
if (labelMessage.Text != Message)
labelMessage.Text = Message;
if (this.labelMessage.Text != Message)
this.labelMessage.Text = Message;
if (ShowProgress.HasValue)
{
if (ShowProgress.Value)
{
progressBar.Visible = true;
this.progressBar.Visible = true;
if (Progress.HasValue)
{
if (Progress.Value >= 0)
if (Progress.Value > 0)
{
progressBar.Value = Math.Min(Progress.Value, 100);
progressBar.Style = ProgressBarStyle.Continuous;
this.progressBar.Value = Math.Min(Progress.Value, 100);
this.progressBar.Style = ProgressBarStyle.Continuous;
}
else
{
progressBar.Style = ProgressBarStyle.Marquee;
this.progressBar.Style = ProgressBarStyle.Marquee;
}
}
}
else
{
progressBar.Visible = false;
this.progressBar.Visible = false;
}
}
}
+7 -2
View File
@@ -1,7 +1,12 @@
namespace Disco.ClientBootstrapper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.ClientBootstrapper
{
interface IStatus
{
void UpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress = null, int? Progress = null);
void UpdateStatus(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress = null, Nullable<int> Progress = null);
}
}
+27 -22
View File
@@ -1,36 +1,43 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Disco.ClientBootstrapper
{
internal class InstallLoop
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 InstallLoop(string installLocation, string wimImageId, string tempPath, Action completeCallback, Uri forcedServerUrl)
public Thread LoopThread;
public delegate void CompleteCallback();
private CompleteCallback mCompleteCallback;
private string InstallLocation;
private string WimImageId;
private string TempPath;
public InstallLoop(string InstallLocation, string WimImageId, string TempPath)
{
this.installLocation = installLocation;
this.wimImageId = wimImageId;
this.tempPath = tempPath;
this.completeCallback = completeCallback;
this.forcedServerUrl = forcedServerUrl;
this.InstallLocation = InstallLocation;
this.WimImageId = WimImageId;
this.TempPath = TempPath;
}
public void Start()
public void Start(CompleteCallback Callback)
{
var cancellationToken = cancellationTokenSource.Token;
Task.Run(async () =>
this.mCompleteCallback = Callback;
this.LoopThread = new Thread(new ThreadStart(loopHost));
this.LoopThread.Start();
}
private void loopHost()
{
try
{
await Interop.InstallInterop.Install(installLocation, wimImageId, tempPath, forcedServerUrl, cancellationToken);
completeCallback?.BeginInvoke(null, null);
//Program.Status.UpdateStatus(null, null, "Testing UI");
//Program.SleepThread(5000, false);
Interop.InstallInterop.Install(this.InstallLocation, this.WimImageId, this.TempPath);
if (this.mCompleteCallback != null)
{
this.mCompleteCallback.BeginInvoke(null, null);
}
}
catch (Exception ex)
{
@@ -38,12 +45,10 @@ namespace Disco.ClientBootstrapper
return;
if (ex.GetType() == typeof(ThreadInterruptedException))
return;
if (ex.GetType() == typeof(OperationCanceledException))
return;
Program.WriteAppError(ex);
throw;
}
}, cancellationToken);
}
}
}
@@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
namespace Disco.ClientBootstrapper.Interop
{
@@ -22,12 +21,12 @@ namespace Disco.ClientBootstrapper.Interop
//Remove(StoreName.Root, StoreLocation.LocalMachine, _tempCerts);
}
}
public static async Task AddTempCerts(CancellationToken cancellationToken)
public static void AddTempCerts()
{
if (_tempCerts == null)
_tempCerts = new List<string>();
var inlineCertificateLocation = Path.GetDirectoryName(typeof(Program).Assembly.Location);
var inlineCertificateLocation = Program.InlinePath.Value;
// Root Certificates
try
@@ -37,15 +36,14 @@ namespace Disco.ClientBootstrapper.Interop
{
foreach (var certFile in CertFiles)
{
cancellationToken.ThrowIfCancellationRequested();
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
var result = Add(StoreName.Root, StoreLocation.LocalMachine, cert);
if (result)
{
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
_tempCerts.Add(cert.SerialNumber);
Program.Status.UpdateStatus(null, null, $"Added Root Certificate: {cert.ShortSubjectName()}");
await Program.SleepThread(500, false, cancellationToken);
Program.Status.UpdateStatus(null, null, string.Format("Added Root Certificate: {0}", cert.ShortSubjectName()));
Program.SleepThread(500, false);
}
}
}
@@ -63,15 +61,14 @@ namespace Disco.ClientBootstrapper.Interop
{
foreach (var certFile in CertFiles)
{
cancellationToken.ThrowIfCancellationRequested();
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
var result = Add(StoreName.CertificateAuthority, StoreLocation.LocalMachine, cert);
if (result)
{
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
_tempCerts.Add(cert.SerialNumber);
Program.Status.UpdateStatus(null, null, $"Added Intermediate Certificate: {cert.ShortSubjectName()}");
await Program.SleepThread(500, false, cancellationToken);
Program.Status.UpdateStatus(null, null, string.Format("Added Intermediate Certificate: {0}", cert.ShortSubjectName()));
Program.SleepThread(500, false);
}
}
}
@@ -89,15 +86,14 @@ namespace Disco.ClientBootstrapper.Interop
{
foreach (var certFile in CertFiles)
{
cancellationToken.ThrowIfCancellationRequested();
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
var result = Add(StoreName.My, StoreLocation.LocalMachine, cert);
if (result)
{
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
_tempCerts.Add(cert.SerialNumber);
Program.Status.UpdateStatus(null, null, $"Added Host Certificate: {cert.ShortSubjectName()}");
await Program.SleepThread(500, false, cancellationToken);
Program.Status.UpdateStatus(null, null, string.Format("Added Host Certificate: {0}", cert.ShortSubjectName()));
Program.SleepThread(500, false);
}
}
}
@@ -4,17 +4,15 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Disco.ClientBootstrapper.Interop
{
public static class InstallInterop
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
[Flags]
private enum MoveFileFlags
enum MoveFileFlags
{
MOVEFILE_REPLACE_EXISTING = 0x00000001,
MOVEFILE_COPY_ALLOWED = 0x00000002,
@@ -24,19 +22,19 @@ namespace Disco.ClientBootstrapper.Interop
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
}
private static async Task Install(string rootFilesystemLocation, RegistryKey rootRegistryLocation, string filesystemInstallLocation, string virtualRootFilesystemLocation, Uri forcedServerUrl, CancellationToken cancellationToken)
private static void Install(string RootFilesystemLocation, RegistryKey RootRegistryLocation, string FilesystemInstallLocation, string VirtualRootFilesystemLocation)
{
var SourceLocation = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var InstallLocation = Path.Combine(rootFilesystemLocation, filesystemInstallLocation);
var BootstrapperCmdLinePath = Path.Combine(virtualRootFilesystemLocation, filesystemInstallLocation, "Disco.ClientBootstrapper.exe");
var InstallLocation = Path.Combine(RootFilesystemLocation, FilesystemInstallLocation);
var BootstrapperCmdLinePath = Path.Combine(VirtualRootFilesystemLocation, FilesystemInstallLocation, "Disco.ClientBootstrapper.exe");
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 GroupPolicyScriptsIniLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\scripts.ini");
var GroupPolicyScriptsIniBackupLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\disco_scripts.ini");
// Create file system Location
#region "Create File System Location"
Program.Status.UpdateStatus(null, null, "Creating Installation Location");
await Program.SleepThread(500, false, cancellationToken);
Program.SleepThread(500, false);
if (Directory.Exists(InstallLocation))
{
// Try and Delete Directory
@@ -46,7 +44,7 @@ namespace Disco.ClientBootstrapper.Interop
}
catch (Exception ex)
{
throw new IOException($"Unable to delete folder: {InstallLocation}", ex);
throw new IOException(string.Format("Unable to delete folder: ", InstallLocation), ex);
}
}
if (!Directory.Exists(InstallLocation))
@@ -54,23 +52,19 @@ namespace Disco.ClientBootstrapper.Interop
var installDir = Directory.CreateDirectory(InstallLocation);
installDir.Attributes = installDir.Attributes | FileAttributes.Hidden;
}
cancellationToken.ThrowIfCancellationRequested();
#endregion
// Copy files to file system location
#region "Copy to File System"
Program.Status.UpdateStatus(null, null, "Copying Files");
await Program.SleepThread(500, false, cancellationToken);
Program.SleepThread(500, false);
// Copy Bootstrapper
// ie: Executing Assembly
File.Copy(System.Reflection.Assembly.GetExecutingAssembly().Location, Path.Combine(InstallLocation, "Disco.ClientBootstrapper.exe"));
cancellationToken.ThrowIfCancellationRequested();
foreach (var file in Directory.EnumerateFiles(SourceLocation))
{
cancellationToken.ThrowIfCancellationRequested();
var fileName = Path.GetFileName(file);
// Only Copy Certain Files
@@ -92,7 +86,7 @@ namespace Disco.ClientBootstrapper.Interop
// Backup & Create Group Policy Scripts.ini
#region "Group Policy Scripts.ini"
Program.Status.UpdateStatus(null, null, "Creating Group Policy Script Entry");
await Program.SleepThread(500, false, cancellationToken);
Program.SleepThread(500, false);
// Backup
if (!File.Exists(GroupPolicyScriptsIniBackupLocation))
{
@@ -101,7 +95,6 @@ namespace Disco.ClientBootstrapper.Interop
File.Move(GroupPolicyScriptsIniLocation, GroupPolicyScriptsIniBackupLocation);
}
}
cancellationToken.ThrowIfCancellationRequested();
// Create
if (File.Exists(GroupPolicyScriptsIniLocation))
@@ -112,67 +105,56 @@ namespace Disco.ClientBootstrapper.Interop
{
using (var scriptsIniStreamWriter = new StreamWriter(scriptsIniStream, Encoding.Unicode))
{
scriptsIniStreamWriter.WriteLine("[Startup]");
scriptsIniStreamWriter.WriteLine($"0CmdLine={BootstrapperCmdLinePath}");
if (forcedServerUrl == null)
scriptsIniStreamWriter.WriteLine("0Parameters=/AllowUninstall");
else
scriptsIniStreamWriter.WriteLine($"0Parameters=/AllowUninstall {forcedServerUrl}");
scriptsIniStreamWriter.Write(string.Format("[Startup]{0}0CmdLine={1}{0}0Parameters=/AllowUninstall", Environment.NewLine, BootstrapperCmdLinePath));
scriptsIniStreamWriter.Flush();
}
}
cancellationToken.ThrowIfCancellationRequested();
#endregion
// Backup & Create Group Policy Registry
#region "Group Policy Registry"
Program.Status.UpdateStatus(null, null, "Creating Group Policy Registry Entries");
await Program.SleepThread(500, false, cancellationToken);
Program.SleepThread(500, false);
// 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"))
{
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
}
}
cancellationToken.ThrowIfCancellationRequested();
// Create Scripts
rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown").Dispose();
using (var regScriptsStartup = rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0"))
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Shutdown").Dispose();
using (var regScriptsStartup = RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup\\0"))
{
regScriptsStartup.SetValue("GPO-ID", "LocalGPO", 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("GPOName", "Local Group Policy", RegistryValueKind.String);
regScriptsStartup.SetValue("PSScriptOrder", 1, RegistryValueKind.DWord);
using (var regScriptsStartup0 = regScriptsStartup.CreateSubKey("0"))
{
regScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
if (forcedServerUrl == null)
regScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
else
regScriptsStartup0.SetValue("Parameters", $"/AllowUninstall {forcedServerUrl}", RegistryValueKind.String);
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);
}
}
rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown").Dispose();
cancellationToken.ThrowIfCancellationRequested();
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Shutdown").Dispose();
// 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"))
{
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
}
}
cancellationToken.ThrowIfCancellationRequested();
// 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("SOM-ID", "Local", RegistryValueKind.String);
@@ -183,21 +165,17 @@ namespace Disco.ClientBootstrapper.Interop
using (var regStateScriptsStartup0 = regStateScriptsStartup.CreateSubKey("0"))
{
regStateScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
if (forcedServerUrl == null)
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);
}
}
cancellationToken.ThrowIfCancellationRequested();
#endregion
// Set Registry Startup Environment Policies
#region "Registry Startup Policies"
Program.Status.UpdateStatus(null, null, "Creating Startup Policy Registry Entries");
await Program.SleepThread(500, false, cancellationToken);
using (var regWinlogon = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows NT\CurrentVersion\Winlogon", true))
Program.SleepThread(500, false);
using (var regWinlogon = RootRegistryLocation.OpenSubKey("Microsoft\\Windows NT\\CurrentVersion\\Winlogon", true))
{
regWinlogon.SetValue("HideStartupScripts", 0, RegistryValueKind.DWord);
regWinlogon.SetValue("RunStartupScriptSync", 1, RegistryValueKind.DWord);
@@ -205,112 +183,96 @@ namespace Disco.ClientBootstrapper.Interop
#endregion
}
public static async Task Install(string installLocation, string wimImageId, string tempPath, Uri forcedServerUrl, CancellationToken cancellationToken)
public static void Install(string InstallLocation, string WimImageId, string TempPath)
{
Program.Status.UpdateStatus("Installing Bootstrapper", "Starting", "Please wait...", false);
if (string.IsNullOrWhiteSpace(installLocation))
installLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco");
if (string.IsNullOrWhiteSpace(InstallLocation))
InstallLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco");
cancellationToken.ThrowIfCancellationRequested();
if (installLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase))
if (InstallLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase))
{
// Offline File System (WIM)
Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", $"Install Location: {installLocation}");
await Program.SleepThread(1000, false, cancellationToken);
Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", string.Format("Install Location: {0}", InstallLocation));
Program.SleepThread(1000, false);
// Mount WIM
int wimImageIndex = 0;
using (var wim = new WIMInterop.WindowsImageContainer(installLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write))
using (var wim = new Interop.WIMInterop.WindowsImageContainer(InstallLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write))
{
cancellationToken.ThrowIfCancellationRequested();
if (wimImageId == null)
wimImageId = "1";
if (!int.TryParse(wimImageId, out wimImageIndex))
if (WimImageId == null)
WimImageId = "1";
if (!int.TryParse(WimImageId, out wimImageIndex))
{
Program.Status.UpdateStatus(null, "Analysing WIM", $"Looking for Image Name: {wimImageId}");
await Program.SleepThread(500, false, cancellationToken);
Program.Status.UpdateStatus(null, "Analysing WIM", string.Format("Looking for Image Name: {0}", WimImageId));
Program.SleepThread(500, false);
for (int i = 0; i < wim.ImageCount; i++)
{
var wimImageInfo = new System.Xml.XmlDocument();
using (var wimImage = wim[i])
wimImageInfo.LoadXml(wimImage.ImageInformation);
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;
Program.Status.UpdateStatus(null, "Analysing WIM", $"Found Image Id '{wimImageId}' at Index {wimImageIndex}");
await Program.SleepThread(500, false, cancellationToken);
Program.Status.UpdateStatus(null, "Analysing WIM", string.Format("Found Image Id '{0}' at Index {1}", WimImageId, wimImageIndex));
Program.SleepThread(500, false);
break;
}
}
}
}
cancellationToken.ThrowIfCancellationRequested();
if (wimImageIndex == 0)
{
Program.Status.UpdateStatus(null, "Error", $"Unable to load WIM Image Id: {wimImageId}");
await Program.SleepThread(5000, false, cancellationToken);
Program.Status.UpdateStatus(null, "Error", string.Format("Unable to load WIM Image Id: {0}", WimImageId));
Program.SleepThread(5000, false);
return;
}
// Get Temp Path
var wimMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimMount");
var wimMountPath = Path.Combine(TempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimMount");
if (Directory.Exists(wimMountPath))
Directory.Delete(wimMountPath, true);
Directory.CreateDirectory(wimMountPath);
cancellationToken.ThrowIfCancellationRequested();
var wimTempMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount");
var wimTempMountPath = Path.Combine(TempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount");
if (Directory.Exists(wimTempMountPath))
Directory.Delete(wimTempMountPath, true);
Directory.CreateDirectory(wimTempMountPath);
cancellationToken.ThrowIfCancellationRequested();
bool wimCommitChanges = true;
WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback m_MessageCallback = null;
Interop.WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback m_MessageCallback = null;
try
{
// Mount WIM
Program.Status.UpdateStatus(null, "Mounting WIM", $"Mounting WIM Image to '{wimMountPath}'");
await Program.SleepThread(500, false, cancellationToken);
m_MessageCallback = new WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback(WimImageEventMessagePump);
WIMInterop.WindowsImageContainer.NativeMethods.RegisterCallback(m_MessageCallback);
Program.Status.UpdateStatus(null, "Mounting WIM", string.Format("Mounting WIM Image to '{0}'", wimMountPath));
Program.SleepThread(500, false);
m_MessageCallback = new Interop.WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback(WimImageEventMessagePump);
Interop.WIMInterop.WindowsImageContainer.NativeMethods.RegisterCallback(m_MessageCallback);
WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, installLocation, wimImageIndex, wimTempMountPath);
Interop.WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, InstallLocation, wimImageIndex, wimTempMountPath);
// Load Local Machine Registry
var wimHivePath = Path.Combine(wimMountPath, "Windows\\System32\\config\\SOFTWARE");
Program.Status.UpdateStatus(null, "Mounting Offline Registry Hive", $"Mounting Offline Registry Hive at '{wimHivePath}'");
await Program.SleepThread(500, false, cancellationToken);
using (var wimReg = new RegistryInterop(RegistryInterop.RegistryHives.HKEY_LOCAL_MACHINE, "DiscoClientBootstrapperWimHive", wimHivePath))
Program.Status.UpdateStatus(null, "Mounting Offline Registry Hive", string.Format("Mounting Offline Registry Hive at '{0}'", wimHivePath));
Program.SleepThread(500, false);
using (var wimReg = new Interop.RegistryInterop(RegistryInterop.RegistryHives.HKEY_LOCAL_MACHINE, "DiscoClientBootstrapperWimHive", wimHivePath))
{
try
{
cancellationToken.ThrowIfCancellationRequested();
using (RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("DiscoClientBootstrapperWimHive", true))
{
string rootFileSystemLocation = wimMountPath;
string fileSystemInstallLocation = "Disco";
string virtualRootFileSystemLocation = "C:\\";
cancellationToken.ThrowIfCancellationRequested();
await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation, forcedServerUrl, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation);
}
}
finally
{
// Unload Local Machine Registry
Program.Status.UpdateStatus(null, "Unmounting Offline Registry Hive", $"Unmounting Offline Registry Hive at '{wimHivePath}'");
await Program.SleepThread(500, false, cancellationToken);
Program.Status.UpdateStatus(null, "Unmounting Offline Registry Hive", string.Format("Unmounting Offline Registry Hive at '{0}'", wimHivePath));
Program.SleepThread(500, false);
wimReg.Unload();
}
}
}
catch (Exception)
{
wimCommitChanges = false;
@@ -319,13 +281,13 @@ namespace Disco.ClientBootstrapper.Interop
finally
{
// Unmount WIM
Program.Status.UpdateStatus(null, "Unmounting WIM", $"Unmounting WIM Image at '{wimMountPath}'");
await Program.SleepThread(500, false, cancellationToken);
WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, installLocation, wimImageIndex, wimCommitChanges);
Program.Status.UpdateStatus(null, "Unmounting WIM", string.Format("Unmounting WIM Image at '{0}'", wimMountPath));
Program.SleepThread(500, false);
Interop.WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, InstallLocation, wimImageIndex, wimCommitChanges);
if (m_MessageCallback != null)
{
WIMInterop.WindowsImageContainer.NativeMethods.UnregisterMessageCallback(m_MessageCallback);
Interop.WIMInterop.WindowsImageContainer.NativeMethods.UnregisterMessageCallback(m_MessageCallback);
m_MessageCallback = null;
}
@@ -333,25 +295,23 @@ namespace Disco.ClientBootstrapper.Interop
Directory.Delete(wimMountPath, true);
if (Directory.Exists(wimTempMountPath))
Directory.Delete(wimTempMountPath, true);
cancellationToken.ThrowIfCancellationRequested();
}
}
else
{
// Online File System
Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", $"Install Location: {installLocation}", true, -1);
await Program.SleepThread(1000, false, cancellationToken);
string rootFileSystemLocation = Path.GetPathRoot(installLocation);
Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", string.Format("Install Location: {0}", InstallLocation), true, -1);
Program.SleepThread(1000, false);
string rootFileSystemLocation = Path.GetPathRoot(InstallLocation);
RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("SOFTWARE", true);
string fileSystemInstallLocation = installLocation.Substring(rootFileSystemLocation.Length);
string fileSystemInstallLocation = InstallLocation.Substring(rootFileSystemLocation.Length);
await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation, forcedServerUrl, cancellationToken);
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation);
Program.Status.UpdateStatus(null, "Online File System Installation Complete", string.Empty, true, -1);
await Program.SleepThread(1000, false, cancellationToken);
Program.SleepThread(1000, false);
}
Program.Status.UpdateStatus(null, "Complete", "Finished Installing Bootstrapper");
await Program.SleepThread(1500, false, cancellationToken);
Program.SleepThread(1500, false);
}
private static uint WimImageEventMessagePump(
@@ -361,16 +321,16 @@ namespace Disco.ClientBootstrapper.Interop
IntPtr UserData
)
{
uint status = (uint)WIMInterop.WindowsImageContainer.NativeMethods.WIMMessage.WIM_MSG_SUCCESS;
WIMInterop.DefaultImageEventArgs eventArgs = new WIMInterop.DefaultImageEventArgs(wParam, lParam, UserData);
uint status = (uint)Interop.WIMInterop.WindowsImageContainer.NativeMethods.WIMMessage.WIM_MSG_SUCCESS;
Interop.WIMInterop.DefaultImageEventArgs eventArgs = new Interop.WIMInterop.DefaultImageEventArgs(wParam, lParam, UserData);
//System.Diagnostics.Debug.WriteLine(MessageId);
switch ((WIMInterop.WindowsImageContainer.ImageEventMessage)MessageId)
switch ((Interop.WIMInterop.WindowsImageContainer.ImageEventMessage)MessageId)
{
case WIMInterop.WindowsImageContainer.ImageEventMessage.Progress:
case WIMInterop.WindowsImageContainer.ImageEventMessage.MountCleanupProgress:
case Interop.WIMInterop.WindowsImageContainer.ImageEventMessage.Progress:
case Interop.WIMInterop.WindowsImageContainer.ImageEventMessage.MountCleanupProgress:
var timeRemainingMil = eventArgs.LeftParameter.ToInt32();
string timeRemainingMessage;
if (timeRemainingMil > 0)
@@ -379,7 +339,7 @@ namespace Disco.ClientBootstrapper.Interop
timeRemainingMessage = "Calculating, please wait...";
var progress = eventArgs.WideParameter.ToInt32();
Program.Status.UpdateStatus(null, null, $"Time remaining: {timeRemainingMessage}", true, progress);
Program.Status.UpdateStatus(null, null, string.Format("Time remaining: {0}", timeRemainingMessage), true, progress);
break;
default:
@@ -389,28 +349,41 @@ namespace Disco.ClientBootstrapper.Interop
return status;
}
public static async Task Uninstall(CancellationToken cancellationToken)
public static void Uninstall()
{
// Application Directory
var appDirectory = Path.GetDirectoryName(typeof(Program).Assembly.Location);
if (Program.AllowUninstall && !appDirectory.StartsWith(@"\\"))
var appDirectory = Program.InlinePath.Value;
if (Program.AllowUninstall && !appDirectory.StartsWith("\\\\"))
{
Program.Status.UpdateStatus("System Preparation (Bootstrapper)", "Uninstalling Bootstrapper...", string.Empty, false, 0);
await Program.SleepThread(1000, true, cancellationToken);
Program.SleepThread(1000, true);
//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
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("RunStartupScriptSync", 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\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\\Scripts\\Shutdown", 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\\Startup", false);
// 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"))
{
@@ -418,7 +391,7 @@ namespace Disco.ClientBootstrapper.Interop
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"))
{
@@ -428,10 +401,10 @@ namespace Disco.ClientBootstrapper.Interop
}
// 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))
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))
File.Move(groupPolicyScriptsBackupPath, groupPolicyScriptsPath);
@@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using System.Runtime.InteropServices;
@@ -14,8 +17,8 @@ namespace Disco.ClientBootstrapper.Interop
public string Name { get; set; }
public string NetConnectionID { get; set; }
public string MACAddress { get; set; }
public ulong Speed { get; set; }
public ushort LastConnectionStatus { get; set; }
public UInt64 Speed { get; set; }
public UInt16 LastConnectionStatus { get; set; }
public bool IsWireless { get; set; }
public string WirelessInterfaceDescription { get; set; }
public int LastWirelessConnectionStatus { get; set; }
@@ -27,51 +30,51 @@ namespace Disco.ClientBootstrapper.Interop
private void UpdateFromWmi(ManagementObject wmiObject)
{
WmiPath = (string)wmiObject.GetPropertyValue("__PATH");
Index = (uint)wmiObject.GetPropertyValue("Index");
Guid = Guid.Parse((string)wmiObject.GetPropertyValue("GUID"));
MACAddress = (string)wmiObject.GetPropertyValue("MACAddress");
Name = (string)wmiObject.GetPropertyValue("Name");
NetConnectionID = (string)wmiObject.GetPropertyValue("NetConnectionID");
Speed = (ulong)wmiObject.GetPropertyValue("Speed");
_ = ConnectionStatus;
IsWireless = true;
this.WmiPath = (string)wmiObject.GetPropertyValue("__PATH");
this.Index = (UInt32)wmiObject.GetPropertyValue("Index");
this.Guid = Guid.Parse((string)wmiObject.GetPropertyValue("GUID"));
this.MACAddress = (string)wmiObject.GetPropertyValue("MACAddress");
this.Name = (string)wmiObject.GetPropertyValue("Name");
this.NetConnectionID = (string)wmiObject.GetPropertyValue("NetConnectionID");
this.Speed = (UInt64)wmiObject.GetPropertyValue("Speed");
var connectionStatus = ConnectionStatus;
this.IsWireless = true;
try
{
var wirelessConnectionStatus = WirelessConnectionStatus;
}
catch (Exception)
{
IsWireless = false;
}
catch (Exception) {
this.IsWireless = false;
};
}
public int WirelessConnectionStatus
{
get
{
if (IsWireless)
get {
if (this.IsWireless)
{
IntPtr handle = IntPtr.Zero;
uint negotiatedVersion;
try
{
if (NetworkInterop.WlanOpenHandle(1, IntPtr.Zero, out var negotiatedVersion, ref handle) != 0)
if (NetworkInterop.WlanOpenHandle(1, IntPtr.Zero, out negotiatedVersion, ref handle) != 0)
throw new NotSupportedException("This network adapter does not support Wireless");
IntPtr ptr = new IntPtr();
uint dataSize;
var interfaceGuid = Guid;
var interfaceGuid = this.Guid;
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)
if (NetworkInterop.WlanQueryInterface(handle, ref interfaceGuid, NetworkInterop.WLAN_INTF_OPCODE.wlan_intf_opcode_interface_state, IntPtr.Zero, out dataSize, ref ptr, IntPtr.Zero) != 0)
throw new NotSupportedException("This network adapter does not support Wireless");
LastWirelessConnectionStatus = Marshal.ReadInt32(ptr);
this.LastWirelessConnectionStatus = Marshal.ReadInt32(ptr);
NetworkInterop.WlanFreeMemory(ptr);
return LastWirelessConnectionStatus;
return this.LastWirelessConnectionStatus;
}
finally
{
@@ -111,46 +114,46 @@ namespace Disco.ClientBootstrapper.Interop
}
}
public ushort ConnectionStatus
public UInt16 ConnectionStatus
{
get
{
using (var wmiObject = new ManagementObject(WmiPath))
using (var wmiObject = new ManagementObject(this.WmiPath))
{
LastConnectionStatus = (ushort)wmiObject.GetPropertyValue("NetConnectionStatus");
this.LastConnectionStatus = (UInt16)wmiObject.GetPropertyValue("NetConnectionStatus");
}
return LastConnectionStatus;
return this.LastConnectionStatus;
}
}
public string ConnectionStatusMeaning(ushort status)
public string ConnectionStatusMeaning(UInt16 status)
{
switch (status)
{
case (ushort)0:
case (UInt16)0:
return "Disconnected";
case (ushort)1:
case (UInt16)1:
return "Connecting";
case (ushort)2:
case (UInt16)2:
return "Connected";
case (ushort)3:
case (UInt16)3:
return "Disconnecting";
case (ushort)4:
case (UInt16)4:
return "Hardware not present";
case (ushort)5:
case (UInt16)5:
return "Hardware disabled";
case (ushort)6:
case (UInt16)6:
return "Hardware malfunction";
case (ushort)7:
case (UInt16)7:
return "Media disconnected";
case (ushort)8:
case (UInt16)8:
return "Authenticating";
case (ushort)9:
case (UInt16)9:
return "Authentication succeeded";
case (ushort)10:
case (UInt16)10:
return "Authentication failed";
case (ushort)11:
case (UInt16)11:
return "Invalid address";
case (ushort)12:
case (UInt16)12:
return "Credentials required";
default:
return "Unknown";
@@ -1,17 +1,15 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using System.Text;
using System.Net.NetworkInformation;
using System.Management;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace Disco.ClientBootstrapper.Interop
{
internal static class NetworkInterop
static class NetworkInterop
{
#region PInvoke
@@ -167,35 +165,30 @@ namespace Disco.ClientBootstrapper.Interop
}
}
public static bool HasNetworkConnectivity()
public static bool PingDisco(string ServerName)
{
var nics = NetworkInterface.GetAllNetworkInterfaces()
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
.ToList();
foreach (var nic in nics)
using (Ping p = new Ping())
{
if (nic.Supports(NetworkInterfaceComponent.IPv4))
try
{
var ipProps = nic.GetIPProperties();
var ipv4Props = ipProps.GetIPv4Properties();
if (ipv4Props.IsAutomaticPrivateAddressingActive)
continue;
return ipProps.UnicastAddresses
.Where(ua => ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
.Any();
}
}
PingReply pr = p.Send(ServerName, 2000);
if (pr.Status == IPStatus.Success)
return true;
else
return false;
}
catch (Exception)
{
return false;
}
}
}
public static async Task ConfigureWireless(CancellationToken cancellationToken)
public static void ConfigureWireless()
{
// Add Certificates
Program.Status.UpdateStatus(null, null, "Configuring Wireless Certificates");
await CertificateInterop.AddTempCerts(cancellationToken);
CertificateInterop.AddTempCerts();
// Add Wireless Profiles
Program.Status.UpdateStatus(null, null, "Configuring Wireless Profiles");
@@ -204,10 +197,11 @@ namespace Disco.ClientBootstrapper.Interop
{
IntPtr wlanHandle = IntPtr.Zero;
uint negotiatedVersion;
try
{
if (WlanOpenHandle(1, IntPtr.Zero, out var negotiatedVersion, ref wlanHandle) != 0)
if (WlanOpenHandle(1, IntPtr.Zero, out negotiatedVersion, ref wlanHandle) != 0)
throw new NotSupportedException("This device does not support Wireless");
// Add Profile to Each Wireless Adapter
@@ -216,16 +210,15 @@ namespace Disco.ClientBootstrapper.Interop
{
foreach (var inlineWirelessProfile in wirelessInlineProfiles)
{
cancellationToken.ThrowIfCancellationRequested();
if (inlineWirelessProfile.AddProfile(wlanHandle, na.Guid))
{
Program.Status.UpdateStatus(null, null, $"Added Wireless Profile: {inlineWirelessProfile.ProfileName}");
await Program.SleepThread(500, false, cancellationToken);
Program.Status.UpdateStatus(null, null, string.Format("Added Wireless Profile: {0}", inlineWirelessProfile.ProfileName));
Program.SleepThread(500, false);
}
else
{
Program.Status.UpdateStatus(null, null, $"Unable to add Wireless Profile: {inlineWirelessProfile.ProfileName}");
await Program.SleepThread(5000, false, cancellationToken);
Program.Status.UpdateStatus(null, null, string.Format("Unable to add Wireless Profile: {0}", inlineWirelessProfile.ProfileName));
Program.SleepThread(5000, false);
}
}
}
@@ -233,7 +226,7 @@ namespace Disco.ClientBootstrapper.Interop
finally
{
if (wlanHandle != IntPtr.Zero)
WlanCloseHandle(wlanHandle, IntPtr.Zero);
NetworkInterop.WlanCloseHandle(wlanHandle, IntPtr.Zero);
}
}
@@ -247,23 +240,23 @@ namespace Disco.ClientBootstrapper.Interop
public bool AddProfile(IntPtr WlanHandle, Guid interfaceGuid)
{
var pInterfaceGuid = interfaceGuid;
var pProfileXml = ProfileXml;
var pProfileXml = this.ProfileXml;
uint pFlag = 0;
return WlanSetProfile(WlanHandle, ref pInterfaceGuid, pFlag, pProfileXml, null, true, IntPtr.Zero, out _) == 0;
uint failReason;
return (WlanSetProfile(WlanHandle, ref pInterfaceGuid, pFlag, pProfileXml, null, true, IntPtr.Zero, out failReason) == 0);
}
}
private static List<WirelessProfile> GetInlineWirelessProfiles()
{
var directoryPath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
var inlineProfileFiles = Directory.EnumerateFiles(directoryPath, "WLAN_Profile_*.xml").ToList();
var inlineProfileFiles = System.IO.Directory.EnumerateFiles(Program.InlinePath.Value, "WLAN_Profile_*.xml").ToList();
var inlineProfiles = new List<WirelessProfile>(inlineProfileFiles.Count);
foreach (var filename in inlineProfileFiles)
{
var profile = new WirelessProfile()
{
Filename = filename,
ProfileXml = File.ReadAllText(filename)
ProfileXml = System.IO.File.ReadAllText(filename)
};
var profileXml = new XmlDocument();
profileXml.LoadXml(profile.ProfileXml);
@@ -287,7 +280,7 @@ namespace Disco.ClientBootstrapper.Interop
WlanGetProfileList(WlanHandle, ref pInterfaceGuid, new IntPtr(), ref ppProfileList);
WLAN_PROFILE_INFO_LIST wlanProfileInfoList = new WLAN_PROFILE_INFO_LIST(ppProfileList);
WlanFreeMemory(ppProfileList);
NetworkInterop.WlanFreeMemory(ppProfileList);
return wlanProfileInfoList;
}
@@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace Disco.ClientBootstrapper.Interop
@@ -30,7 +33,7 @@ namespace Disco.ClientBootstrapper.Interop
private static extern int LookupPrivilegeValue(string lpsystemname, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
[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)]
private static extern int RegLoadKey(uint hKey, string lpSubKey, string lpFile);
@@ -58,15 +61,16 @@ namespace Disco.ClientBootstrapper.Interop
public RegistryInterop(RegistryHives hive, string subKey, string filePath)
{
int token = 0;
int retval = 0;
TOKEN_PRIVILEGES TP = new TOKEN_PRIVILEGES();
TOKEN_PRIVILEGES TP2 = new TOKEN_PRIVILEGES();
LUID RestoreLuid = new LUID();
LUID BackupLuid = new LUID();
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token);
LookupPrivilegeValue(null, SE_RESTORE_NAME, ref RestoreLuid);
LookupPrivilegeValue(null, SE_BACKUP_NAME, ref BackupLuid);
retval = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token);
retval = LookupPrivilegeValue(null, SE_RESTORE_NAME, ref RestoreLuid);
retval = LookupPrivilegeValue(null, SE_BACKUP_NAME, ref BackupLuid);
TP.PrivilegeCount = 1;
TP.Attributes = SE_PRIVILEGE_ENABLED;
TP.Luid = RestoreLuid;
@@ -74,25 +78,25 @@ namespace Disco.ClientBootstrapper.Interop
TP2.Attributes = SE_PRIVILEGE_ENABLED;
TP2.Luid = BackupLuid;
AdjustTokenPrivileges(token, 0, ref TP, 1024, 0, 0);
AdjustTokenPrivileges(token, 0, ref TP2, 1024, 0, 0);
retval = AdjustTokenPrivileges(token, 0, ref TP, 1024, 0, 0);
retval = AdjustTokenPrivileges(token, 0, ref TP2, 1024, 0, 0);
uint regHive = (uint)hive;
Hive = hive;
SubKey = subKey;
this.Hive = hive;
this.SubKey = subKey;
RegLoadKey(regHive, subKey, filePath);
IsUnloaded = false;
this.IsUnloaded = false;
}
public void Unload()
{
if (!IsUnloaded)
{
uint regHive = (uint)Hive;
string subKey = SubKey;
uint regHive = (uint)this.Hive;
string subKey = this.SubKey;
RegUnLoadKey(regHive, subKey);
IsUnloaded = true;
this.IsUnloaded = true;
}
}
@@ -1,4 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using System.Runtime.InteropServices;
namespace Disco.ClientBootstrapper.Interop
@@ -39,20 +43,21 @@ namespace Disco.ClientBootstrapper.Interop
// End Removed 2012-11-23 G#
// Added 2012-11-23 G# - Migrate to Win32 PInvoke Shutdown
bool result;
TokPriv1Luid tp;
IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
result = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
ExitWindowsEx(flag, 0);
result = LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
result = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
result = ExitWindowsEx(flag, 0);
// End Added 2012-11-23 G#
}
+64 -43
View File
@@ -1,8 +1,10 @@
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
namespace Disco.ClientBootstrapper.Interop.WIMInterop
{
@@ -138,7 +140,8 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
CreateFileAccessPrivate fileAccess = GetMappedFileAccess(access);
if (fileAccess == CreateFileAccessPrivate.Read && (!File.Exists(imageFilePath) || (CreateFileMode.OpenExisting != mode)))
{
throw new UnauthorizedAccessException("Read access can be specified only with OpenExisting mode or OpenAlways mode when the .wim file does not exist.");
throw new System.UnauthorizedAccessException(string.Format(CultureInfo.CurrentCulture,
"Read access can be specified only with OpenExisting mode or OpenAlways mode when the .wim file does not exist."));
}
//
@@ -149,9 +152,9 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
m_ImageContainerHandle = NativeMethods.CreateFile(imageFilePath, (uint)fileAccess, (uint)mode);
m_WindowsImageFilePath = imageFilePath;
}
catch (DllNotFoundException ex)
catch (System.DllNotFoundException ex)
{
throw new DllNotFoundException(string.Format(
throw new System.DllNotFoundException(string.Format(CultureInfo.CurrentCulture,
"Unable to load WIM libraries. Make sure the correct DLLs are present (Wimgapi.dll and Xmlrw.dll)."), ex.InnerException);
}
@@ -161,7 +164,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//Set the temporary path so that we can write to an image. This
//cannot be %TEMP% as it does not exist on Windows PE
//
string tempDirectory = Environment.GetEnvironmentVariable("systemdrive");
string tempDirectory = System.Environment.GetEnvironmentVariable("systemdrive");
NativeMethods.SetTemporaryPath(m_ImageContainerHandle, tempDirectory);
}
@@ -170,7 +173,8 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to open the .wim file {imageFilePath}.");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Unable to open the .wim file {0}.", imageFilePath));
}
//
@@ -477,7 +481,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//
//Never commit changes when destroying this object.
//
DismountImage(false);
this.DismountImage(false);
}
GC.KeepAlive(this);
}
@@ -572,7 +576,12 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//1. Leading 0xFEFF
//2. Be in <IMAGE></IMAGE>
//
string formattedXml = $"{UNICODE_FILE_MARKER}{"<IMAGE>"}{imageInformation}{"</IMAGE>"}";
string formattedXml = String.Format(CultureInfo.InvariantCulture,
"{0}{1}{2}{3}",
UNICODE_FILE_MARKER,
"<IMAGE>",
imageInformation,
"</IMAGE>");
NativeMethods.SetImageInformation(m_ImageHandle, formattedXml);
GC.KeepAlive(this);
@@ -588,7 +597,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//Mount the image
//
m_MountedPath = pathToMountTo;
NativeMethods.MountImage(pathToMountTo, m_ParentWindowsImageFilePath, m_Index, Path.GetTempPath());
NativeMethods.MountImage(pathToMountTo, m_ParentWindowsImageFilePath, m_Index, System.IO.Path.GetTempPath());
m_Mounted = true;
}
@@ -641,8 +650,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
///Private null constructor
///</summary>
private
NativeMethods()
{ }
NativeMethods() { }
[DllImport("Wimgapi.dll", ExactSpelling = true,
EntryPoint = "WIMCreateFile",
@@ -676,14 +684,16 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
IntPtr windowsImageHandle = IntPtr.Zero;
int rc = -1;
windowsImageHandle = WimCreateFile(imageFile, access, mode, 0, 0, out _);
windowsImageHandle = NativeMethods.WimCreateFile(imageFile, access, mode, 0, 0, out creationResult);
rc = Marshal.GetLastWin32Error();
if (windowsImageHandle == IntPtr.Zero)
{
//
//Everything failed; throw an exception
//
throw new InvalidOperationException($"Unable to open/create .wim file {imageFile}. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Unable to open/create .wim file {0}. Error = {1}",
imageFile, rc));
}
return windowsImageHandle;
@@ -711,14 +721,15 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
void
CloseHandle(IntPtr handle)
{
bool status = WimCloseHandle(handle);
bool status = NativeMethods.WimCloseHandle(handle);
int rc = Marshal.GetLastWin32Error();
if (status == false)
{
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to close image handle. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Unable to close image handle. Error = {0}", rc));
}
}
@@ -746,14 +757,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
void
SetTemporaryPath(IntPtr handle, string temporaryPath)
{
bool status = WimSetTemporaryPath(handle, temporaryPath);
bool status = NativeMethods.WimSetTemporaryPath(handle, temporaryPath);
int rc = Marshal.GetLastWin32Error();
if (status == false)
{
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to set temporary path. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to set temporary path. Error = {0}", rc));
}
}
@@ -783,14 +794,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
{
//Load the image data based on the .wim handle
//
IntPtr hWim = WimLoadImage(handle, (uint)imageIndex);
IntPtr hWim = NativeMethods.WimLoadImage(handle, (uint)imageIndex);
int rc = Marshal.GetLastWin32Error();
if (hWim == IntPtr.Zero)
{
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to load image. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to load image. Error = {0}", rc));
}
return hWim;
@@ -821,14 +832,15 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
IntPtr
CaptureImage(IntPtr handle, string path)
{
IntPtr hImage = WimCaptureImage(handle, path, 0);
IntPtr hImage = NativeMethods.WimCaptureImage(handle, path, 0);
int rc = Marshal.GetLastWin32Error();
if (hImage == IntPtr.Zero)
{
//
//Throw an exception
//
throw new InvalidOperationException($"Failed to capture image from {path}. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Failed to capture image from {0}. Error = {1}", path, rc));
}
return hImage;
}
@@ -861,14 +873,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
int
GetImageCount(IntPtr windowsImageHandle)
{
int count = WimGetImageCount(windowsImageHandle);
int count = NativeMethods.WimGetImageCount(windowsImageHandle);
int rc = Marshal.GetLastWin32Error();
if (count == -1)
{
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to get image count. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to get image count. Error = {0}", rc));
}
return count;
@@ -959,25 +971,28 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
try
{
status = WimMountImage(mountPath,
status = NativeMethods.WimMountImage(mountPath,
windowsImageFileName,
(uint)imageIndex,
temporaryPath);
rc = Marshal.GetLastWin32Error();
}
catch (StackOverflowException)
catch (System.StackOverflowException)
{
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to mount image {windowsImageFileName} to {mountPath}.");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Unable to mount image {0} to {1}.", windowsImageFileName, mountPath));
}
if (status == false)
{
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to mount image {windowsImageFileName} to {mountPath}. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Unable to mount image {0} to {1}. Error = {2}",
windowsImageFileName, mountPath, rc));
}
}
@@ -1007,14 +1022,15 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//
//Call WimApplyImage always with the Index flag for performance reasons.
//
bool status = WimApplyImage(imageHandle, applicationPath, WIM_FLAG_INDEX);
bool status = NativeMethods.WimApplyImage(imageHandle, applicationPath, NativeMethods.WIM_FLAG_INDEX);
int rc = Marshal.GetLastWin32Error();
if (status == false)
{
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to apply image to {applicationPath}. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Unable to apply image to {0}. Error = {1}", applicationPath, rc));
}
}
@@ -1046,7 +1062,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
IntPtr info = IntPtr.Zero, sizeOfInfo = IntPtr.Zero;
bool status;
status = WimGetImageInformation(handle, out info, out _);
status = NativeMethods.WimGetImageInformation(handle, out info, out sizeOfInfo);
int rc = Marshal.GetLastWin32Error();
if (status == false)
@@ -1054,7 +1070,8 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to get image information. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Unable to get image information. Error = {0}", rc));
}
string s = Marshal.PtrToStringUni(info);
@@ -1095,14 +1112,15 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
IntPtr xmlBuffer = Marshal.AllocHGlobal(byteBufferSize);
Marshal.Copy(byteBuffer, 0, xmlBuffer, byteBufferSize);
bool status = WimSetImageInformation(handle, xmlBuffer, (uint)byteBufferSize);
bool status = NativeMethods.WimSetImageInformation(handle, xmlBuffer, (uint)byteBufferSize);
int rc = Marshal.GetLastWin32Error();
if (status == false)
{
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to set image information. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Unable to set image information. Error = {0}", rc));
}
}
@@ -1135,15 +1153,16 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
try
{
status = WimUnmountImage(mountPath, wimdowsImageFileName, (uint)imageIndex, commitChanges);
status = NativeMethods.WimUnmountImage(mountPath, wimdowsImageFileName, (uint)imageIndex, commitChanges);
rc = Marshal.GetLastWin32Error();
}
catch (StackOverflowException ex)
catch (System.StackOverflowException ex)
{
//
//Throw an exception
//
throw new StackOverflowException($"Unable to unmount image {wimdowsImageFileName} from {mountPath}.",
throw new System.StackOverflowException(string.Format(CultureInfo.CurrentCulture,
"Unable to unmount image {0} from {1}.", wimdowsImageFileName, mountPath),
ex.InnerException);
}
if (status == false)
@@ -1151,7 +1170,9 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
//
//Throw an exception
//
throw new InvalidOperationException($"Unable to unmount image {wimdowsImageFileName} from {mountPath}. Error = {rc}");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Unable to unmount image {0} from {1}. Error = {2}",
wimdowsImageFileName, mountPath, rc));
}
}
@@ -1200,14 +1221,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
void
RegisterCallback(MessageCallback callback)
{
WimRegisterMessageCallback(IntPtr.Zero, callback, IntPtr.Zero);
uint callbackZeroBasedIndex = NativeMethods.WimRegisterMessageCallback(IntPtr.Zero, callback, IntPtr.Zero);
int rc = Marshal.GetLastWin32Error();
if (rc != 0)
{
//
//Throw an exception
//
throw new InvalidOperationException("Unable to register message callback.");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to register message callback."));
}
}
@@ -1232,14 +1253,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
void
UnregisterMessageCallback(MessageCallback registeredCallback)
{
bool status = WimUnregisterMessageCallback(IntPtr.Zero, registeredCallback);
_ = Marshal.GetLastWin32Error();
bool status = NativeMethods.WimUnregisterMessageCallback(IntPtr.Zero, registeredCallback);
int rc = Marshal.GetLastWin32Error();
if (status != true)
{
//
// Throw an exception
//
throw new InvalidOperationException("Unable to unregister message callback.");
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to unregister message callback."));
}
}
@@ -1337,13 +1358,13 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
fileAccess = CreateFileAccessPrivate.Write;
break;
default:
throw new ArgumentException("No file access level specified.");
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "No file access level specified."));
}
return fileAccess;
}
//[CLSCompliant(false)]
[Flags]
[FlagsAttribute]
private
enum
CreateFileAccessPrivate : uint
+6 -1
View File
@@ -1,4 +1,9 @@
namespace Disco.ClientBootstrapper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.ClientBootstrapper
{
class NullStatus : IStatus
{
+56 -59
View File
@@ -1,58 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Disco.ClientBootstrapper
{
internal static class Program
static class Program
{
private static readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
public static IStatus Status { get; private set; }
public static IStatus Status { get; set; }
public static BootstrapperLoop BootstrapperLoop { get; set; }
public static InstallLoop InstallLoop { get; set; }
public static List<string> PostBootstrapperActions { get; set; }
public static bool AllowUninstall { get; private set; }
public static Uri ForcedServerUrl { get; private set; } = null;
public static bool AllowUninstall { get; set; }
public static bool ApplicationExiting { get; set; }
public static Lazy<string> InlinePath = new Lazy<string>(() =>
{
return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
});
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main(string[] args)
static void Main(string[] args)
{
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
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())
{
case "/install":
@@ -68,17 +46,14 @@ namespace Disco.ClientBootstrapper
wimImage = args[2];
if (args.Length > 3)
tempPath = args[3];
var installLoop = new InstallLoop(installLocation, wimImage, tempPath, InstallComplete, ForcedServerUrl);
installLoop.Start();
InstallLoop = new InstallLoop(installLocation, wimImage, tempPath);
InstallLoop.Start(new InstallLoop.CompleteCallback(InstallComplete));
Application.Run();
return;
case "/uninstall":
AllowUninstall = true;
Status = new NullStatus();
Task.Run(async () =>
{
await Interop.InstallInterop.Uninstall(cancellationTokenSource.Token);
}).Wait(cancellationTokenSource.Token);
Interop.InstallInterop.Uninstall();
return;
case "/allowuninstall":
AllowUninstall = true;
@@ -96,13 +71,13 @@ namespace Disco.ClientBootstrapper
statusForm.Show();
}
var bootstrapperLoop = new BootstrapperLoop(Status, ForcedServerUrl, LoopComplete, cancellationTokenSource.Token);
bootstrapperLoop.Start();
BootstrapperLoop = new BootstrapperLoop(Status, new BootstrapperLoop.LoopCompleteCallback(LoopComplete));
BootstrapperLoop.Start();
Application.Run();
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
WriteAppError(e.Exception);
}
@@ -111,21 +86,21 @@ namespace Disco.ClientBootstrapper
{
try
{
string AppErrorPath = $"{System.Reflection.Assembly.GetExecutingAssembly().Location}.errors.txt";
string AppErrorPath = string.Format("{0}{1}", System.Reflection.Assembly.GetExecutingAssembly().Location, ".errors.txt");
System.Text.StringBuilder ErrorMessage = new System.Text.StringBuilder();
ErrorMessage.AppendLine();
ErrorMessage.AppendLine(DateTime.Now.ToLongDateString());
ErrorMessage.AppendLine(DateTime.Now.ToLongTimeString());
ErrorMessage.AppendLine($"Type: {ex.GetType().FullName}");
ErrorMessage.AppendLine($"Message: {ex.Message}");
ErrorMessage.AppendLine($"Source: {ex.Source}");
ErrorMessage.AppendLine($"Stack: {ex.StackTrace}");
ErrorMessage.AppendLine(string.Format("Type: {0}", ex.GetType().FullName));
ErrorMessage.AppendLine(string.Format("Message: {0}", ex.Message));
ErrorMessage.AppendLine(string.Format("Source: {0}", ex.Source));
ErrorMessage.AppendLine(string.Format("Stack: {0}", ex.StackTrace));
System.IO.File.AppendAllText(AppErrorPath, ErrorMessage.ToString());
}
catch (Exception) { }
}
public static async Task LoopComplete(CancellationToken cancellationToken)
public static void LoopComplete()
{
// Run Post Actions
if (PostBootstrapperActions != null)
@@ -133,32 +108,33 @@ namespace Disco.ClientBootstrapper
// Check Uninstall
if (AllowUninstall && PostBootstrapperActions.Contains("UninstallBootstrapper"))
{
await Interop.InstallInterop.Uninstall(cancellationToken);
Interop.InstallInterop.Uninstall();
}
// Check ShutdownActions
if (PostBootstrapperActions.Contains("Shutdown"))
{
Status.UpdateStatus("System Preparation (Bootstrapper)", "Shutting Down; Finished...", string.Empty, false, 0);
await SleepThread(4000, true, cancellationToken);
SleepThread(4000, true);
Interop.ShutdownInterop.Shutdown();
}
else if (PostBootstrapperActions.Contains("Reboot"))
else
if (PostBootstrapperActions.Contains("Reboot"))
{
Status.UpdateStatus("System Preparation (Bootstrapper)", "Rebooting; Finished...", string.Empty, false, 0);
await SleepThread(4000, true, cancellationToken);
SleepThread(4000, true);
Interop.ShutdownInterop.Reboot();
}
else
{
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
await SleepThread(2000, true, cancellationToken);
SleepThread(2000, true);
}
}
else
{
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
await SleepThread(2000, true, cancellationToken);
SleepThread(2000, true);
}
ExitApplication();
@@ -171,12 +147,33 @@ namespace Disco.ClientBootstrapper
public static void ExitApplication()
{
if (!cancellationTokenSource.IsCancellationRequested)
cancellationTokenSource.Cancel();
if (!ApplicationExiting)
{
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();
}
}
public static async Task SleepThread(int millisecondsTimeout, bool updateUI, CancellationToken cancellationToken)
public static void Trace(string Format, params string[] args)
{
System.Diagnostics.Debug.WriteLine(Format, args);
}
public static void SleepThread(int millisecondsTimeout, bool updateUI)
{
if (updateUI)
{
@@ -184,12 +181,12 @@ namespace Disco.ClientBootstrapper
{
int progress = Convert.ToInt32(((Convert.ToDouble(i) / Convert.ToDouble(millisecondsTimeout)) * 100));
Status.UpdateStatus(null, null, null, true, progress);
await Task.Delay(500, cancellationToken);
Thread.Sleep(500);
}
}
else
{
await Task.Delay(millisecondsTimeout, cancellationToken);
Thread.Sleep(millisecondsTimeout);
}
}
}
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.5.25262.0000")]
[assembly: AssemblyFileVersion("2.5.25262.0000")]
[assembly: AssemblyVersion("2.2.16326.0500")]
[assembly: AssemblyFileVersion("2.2.16326.0500")]
+1 -1
View File
@@ -15,6 +15,6 @@
</defaultConnectionFactory>
</entityFramework>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
+13 -13
View File
@@ -28,17 +28,17 @@ namespace Disco.Data.Configuration
{
get
{
return ConfigurationCache.GetScopeKeys(Database, Scope);
return ConfigurationCache.GetScopeKeys(Database, this.Scope);
}
}
private void SetValue<T>(string Key, T Value)
{
ConfigurationCache.Helpers<T>.SetValue(Database, Scope, Key, Value);
ConfigurationCache.Helpers<T>.SetValue(Database, this.Scope, Key, Value);
}
private T GetValue<T>(string Key, T Default)
{
return ConfigurationCache.Helpers<T>.GetValue(Database, Scope, Key, Default);
return ConfigurationCache.Helpers<T>.GetValue(Database, this.Scope, Key, Default);
}
protected void Set<T>(T Value, [CallerMemberName] string Key = null)
@@ -46,7 +46,7 @@ namespace Disco.Data.Configuration
if (Key == null)
throw new ArgumentNullException("Key");
SetValue(Key, Value);
this.SetValue(Key, Value);
}
protected T Get<T>(T Default, [CallerMemberName] string Key = null)
@@ -54,17 +54,17 @@ namespace Disco.Data.Configuration
if (Key == null)
throw new ArgumentNullException("Key");
return GetValue(Key, Default);
return this.GetValue(Key, Default);
}
protected void SetObsfucated(string Value, [CallerMemberName] string Key = null)
{
Set(Value.Obsfucate(), Key);
this.Set(Value.Obsfucate(), Key);
}
protected string GetDeobsfucated(string Default, [CallerMemberName] string Key = null)
{
var obsfucatedValue = Get<string>(null, Key);
var obsfucatedValue = this.Get<string>(null, Key);
if (obsfucatedValue == null)
return Default;
@@ -74,35 +74,35 @@ namespace Disco.Data.Configuration
protected void RemoveScope()
{
ConfigurationCache.RemoveScope(Database, Scope);
ConfigurationCache.RemoveScope(Database, this.Scope);
}
protected void RemoveItem(string Key)
{
ConfigurationCache.RemoveScopeKey(Database, Scope, Key);
ConfigurationCache.RemoveScopeKey(Database, this.Scope, Key);
}
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
protected void SetAsJson<T>(T Value, [CallerMemberName] string Key = null)
{
Set(Value, Key);
this.Set(Value, Key);
}
[Obsolete("Types are automatically serialized/deserialized if required, use Get<T>(T Default) instead.")]
protected T GetFromJson<T>(T Default, [CallerMemberName] string Key = null)
{
return Get(Default, Key);
return this.Get(Default, Key);
}
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
protected void SetAsEnum<T>(T Value, [CallerMemberName] string Key = null)
{
Set(Value, Key);
this.Set(Value, Key);
}
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
protected T GetFromEnum<T>(T Default, [CallerMemberName] string Key = null)
{
return Get(Default, Key);
return this.Get(Default, Key);
}
}
}
+39 -71
View File
@@ -22,11 +22,11 @@ namespace Disco.Data.Configuration
private static ConfigurationCacheType Cache(DiscoDataContext Database)
{
if (cacheStore == null)
if (ConfigurationCache.cacheStore == null)
{
lock (configChangeLock)
{
if (cacheStore == null)
if (ConfigurationCache.cacheStore == null)
{
if (Database == null)
throw new InvalidOperationException("The Configuration must be loaded at least once before a Cache-Only Configuration Context is used");
@@ -46,16 +46,18 @@ namespace Disco.Data.Configuration
}
}
return cacheStore;
return ConfigurationCache.cacheStore;
}
private static ConfigurationCacheItemType CacheGetItem(DiscoDataContext Database, string Scope, string Key)
{
var cache = Cache(Database);
if (cache.TryGetValue(Scope, out var scopeCache))
ConfigurationCacheScopeType scopeCache;
if (cache.TryGetValue(Scope, out scopeCache))
{
if (scopeCache.TryGetValue(Key, out var item))
ConfigurationCacheItemType item = default(ConfigurationCacheItemType);
if (scopeCache.TryGetValue(Key, out item))
return item;
}
@@ -91,7 +93,8 @@ namespace Disco.Data.Configuration
Database.ConfigurationItems.Add(configItem);
// Add Item to Cache
if (!cacheStore.TryGetValue(Scope, out var scopeCache))
ConfigurationCacheScopeType scopeCache;
if (!cacheStore.TryGetValue(Scope, out scopeCache))
{
scopeCache = new ConfigurationCacheScopeType();
cacheStore.TryAdd(Scope, scopeCache);
@@ -131,9 +134,10 @@ namespace Disco.Data.Configuration
Database.ConfigurationItems.Remove(configItem);
// Remove item from Cache
if (cacheStore.TryGetValue(Scope, out var scopeCache))
ConfigurationCacheScopeType scopeCache;
if (cacheStore.TryGetValue(Scope, out scopeCache))
{
scopeCache.TryRemove(Key, out _);
scopeCache.TryRemove(Key, out item);
}
return null;
@@ -144,7 +148,8 @@ namespace Disco.Data.Configuration
configItem.Value = Value;
// Update Cache
if (cacheStore.TryGetValue(Scope, out var scopeCache))
ConfigurationCacheScopeType scopeCache;
if (cacheStore.TryGetValue(Scope, out scopeCache))
{
scopeCache.TryRemove(Key, out item);
item = new ConfigurationCacheItemType(configItem, ObjectValue);
@@ -160,9 +165,10 @@ namespace Disco.Data.Configuration
}
private static ConfigurationCacheItemType SetItemTypeValue(ConfigurationCacheItemType ExistingItem, object Value)
{
var cache = cacheStore;
var cache = ConfigurationCache.cacheStore;
if (cache.TryGetValue(ExistingItem.Item1.Scope, out var scopeCache))
ConfigurationCacheScopeType scopeCache;
if (cache.TryGetValue(ExistingItem.Item1.Scope, out scopeCache))
{
ConfigurationCacheItemType newItem = new ConfigurationCacheItemType(ExistingItem.Item1, Value);
scopeCache.TryUpdate(ExistingItem.Item1.Key, newItem, ExistingItem);
@@ -177,18 +183,18 @@ namespace Disco.Data.Configuration
#region Helpers
private static bool IsConvertableFromString(Type t)
{
if (t == typeof(bool) ||
t == typeof(char) ||
t == typeof(sbyte) ||
t == typeof(byte) ||
t == typeof(short) || t == typeof(ushort) ||
t == typeof(int) || t == typeof(uint) ||
t == typeof(long) || t == typeof(ulong) ||
t == typeof(float) ||
t == typeof(double) ||
t == typeof(decimal) ||
if (t == typeof(Boolean) ||
t == typeof(Char) ||
t == typeof(SByte) ||
t == typeof(Byte) ||
t == typeof(Int16) || t == typeof(UInt16) ||
t == typeof(Int32) || t == typeof(UInt32) ||
t == typeof(Int64) || t == typeof(UInt64) ||
t == typeof(Single) ||
t == typeof(Double) ||
t == typeof(Decimal) ||
t == typeof(DateTime) ||
t == typeof(string))
t == typeof(String))
return true;
else
return false;
@@ -236,34 +242,9 @@ namespace Disco.Data.Configuration
}
else if (itemType.BaseType != null && itemType.BaseType == typeof(Enum))
{
// enum
// Enum
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
{
// JSON Deserialize
@@ -282,13 +263,13 @@ namespace Disco.Data.Configuration
Type valueType = typeof(T);
string stringValue;
if (comparer.Equals(Value, default))
if (comparer.Equals(Value, default(T)))
{
stringValue = null;
}
else if (valueType == typeof(object))
{
throw new ArgumentException($"Cannot serialize the configuration item [{Scope}].[{Key}] which has the type [System.Object]", "Value");
throw new ArgumentException(string.Format("Cannot serialize the configuration item [{0}].[{1}] which defines a type of [System.Object]", Scope, Key), "Value");
}
else if (IsConvertableFromString(valueType))
{
@@ -297,25 +278,9 @@ namespace Disco.Data.Configuration
}
else if (valueType.BaseType != null && valueType.BaseType == typeof(Enum))
{
// enum
// Enum
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
{
// JSON Serialize
@@ -334,7 +299,8 @@ namespace Disco.Data.Configuration
{
var cache = Cache(Database);
if (cache.TryGetValue(Scope, out var scopeCache))
ConfigurationCacheScopeType scopeCache;
if (cache.TryGetValue(Scope, out scopeCache))
{
return scopeCache.Keys.ToList();
}
@@ -358,7 +324,8 @@ namespace Disco.Data.Configuration
// Remove item from Cache
if (cacheStore != null)
{
cacheStore.TryRemove(Scope, out var scopeCache);
ConfigurationCacheScopeType scopeCache;
cacheStore.TryRemove(Scope, out scopeCache);
}
}
}
@@ -398,9 +365,10 @@ namespace Disco.Data.Configuration
// Remove item from Cache
if (cacheItem != null)
{
if (cacheStore.TryGetValue(Scope, out var scopeCache))
ConfigurationCacheScopeType scopeCache;
if (cacheStore.TryGetValue(Scope, out scopeCache))
{
scopeCache.TryRemove(Key, out _);
scopeCache.TryRemove(Key, out cacheItem);
}
}
}
@@ -35,8 +35,7 @@ namespace Disco.Data.Configuration.Modules
public bool? SearchAllServers
{
get
{
get {
var value = Get<bool?>(null);
/// migrate <see cref="SearchAllForestServers"/>
@@ -1,5 +1,4 @@
using Disco.Data.Repository;
using System;
namespace Disco.Data.Configuration.Modules
{
@@ -7,24 +6,33 @@ namespace Disco.Data.Configuration.Modules
{
public BootstrapperConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get; } = "Bootstrapper";
public override string Scope
{
get { return "Bootstrapper"; }
}
public string MacSshUsername
{
get => Get("root");
set => Set(value);
get
{
return this.Get("root");
}
set
{
this.Set(value);
}
}
public string MacSshPassword
{
get => GetDeobsfucated(string.Empty);
set => SetObsfucated(value);
}
public TimeSpan PendingTimeout
get
{
get => TimeSpan.FromSeconds(Get(30 * 60)); // 30 minutes default
set => Set((int)value.TotalSeconds);
return this.GetDeobsfucated(string.Empty);
}
set
{
this.SetObsfucated(value);
}
}
}
}
@@ -1,18 +0,0 @@
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 override string Scope { get; } = "DeviceProfiles";
public override string Scope { get { return "DeviceProfiles"; } }
public int DefaultDeviceProfileId
{
get
{
var v = Get(1);
var v = this.Get(1);
if (v > 0)
return v;
else
@@ -24,21 +24,21 @@ namespace Disco.Data.Configuration.Modules
if (value < 1)
throw new ArgumentOutOfRangeException("value", "Expected >= 1");
Set(value);
this.Set(value);
}
}
public int DefaultAddDeviceOfflineDeviceProfileId
{
get
{
return Get(0);
return this.Get(0);
}
set
{
if (value < 0)
throw new ArgumentOutOfRangeException("value", "Expected >= 0");
Set(value);
this.Set(value);
}
}
@@ -1,5 +1,5 @@
using Disco.Data.Repository;
using Disco.Models.Services.Devices;
using Disco.Models.Services.Devices.Exporting;
namespace Disco.Data.Configuration.Modules
{
@@ -7,18 +7,12 @@ namespace Disco.Data.Configuration.Modules
{
public DevicesConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get; } = "Devices";
public override string Scope { get { return "Devices"; } }
public DeviceExportOptions LastExportOptions
{
get => Get(DeviceExportOptions.DefaultOptions());
set => Set(value);
}
public bool EnrollmentLegacyDiscoveryDisabled
{
get => Get(false);
set => Set(value);
get { return this.Get<DeviceExportOptions>(DeviceExportOptions.DefaultOptions()); }
set { this.Set(value); }
}
}
}
@@ -8,18 +8,12 @@ namespace Disco.Data.Configuration.Modules
{
public DocumentsConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get; } = "Documents";
public override string Scope { get { return "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.Models.Services.Jobs;
using Disco.Models.Services.Job;
using System;
using System.Collections.Generic;
@@ -9,27 +9,18 @@ namespace Disco.Data.Configuration.Modules
{
public JobPreferencesConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get; } = "JobPreferences";
/// <summary>
/// Initial comments template for new jobs
/// </summary>
public string InitialCommentsTemplate
{
get => Get<string>(null);
set => Set(value);
}
public override string Scope { get { return "JobPreferences"; } }
/// <summary>
/// Number of days a job is open before it is considered 'Long Running'
/// </summary>
public int LongRunningJobDaysThreshold
{
get => Get(7);
get { return Get<int>(7); }
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), "The Long Running Job Days Threshold cannot be less than zero");
throw new ArgumentOutOfRangeException("value", "The Long Running Job Days Threshold cannot be less than zero");
Set(value);
}
@@ -40,33 +31,27 @@ namespace Disco.Data.Configuration.Modules
/// </summary>
public int StaleJobMinutesThreshold
{
get => Get(60 * 24 * 2); // Default to 48 Hours (2 days)
get { return Get<int>(60 * 24 * 2); } // Default to 48 Hours (2 days)
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), "The Stale Job Minutes Threshold cannot be less than zero");
throw new ArgumentOutOfRangeException("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");
get { return Get("default"); }
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(DefaultNoticeboardTheme));
throw new ArgumentNullException("DefaultNoticeboardTheme");
Set(value);
}
@@ -74,48 +59,26 @@ namespace Disco.Data.Configuration.Modules
public LocationModes LocationMode
{
get => Get(LocationModes.Unrestricted);
set => Set(value);
get { return Get(LocationModes.Unrestricted); }
set { Set(value); }
}
public List<string> LocationList
{
get => Get(new List<string>());
set => Set(value);
get { return Get(new List<string>()); }
set { Set(value); }
}
public string OnCreateExpression
{
get => Get<string>(null);
set => Set(value);
}
public string OnDeviceReadyForReturnExpression
{
get => Get<string>(null);
set => Set(value);
get { return 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);
get { return Get<string>(null); }
set { Set(value); }
}
}
}
@@ -1,5 +1,6 @@
using Disco.Data.Repository;
using Disco.Models.BI.Config;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
@@ -7,9 +8,11 @@ namespace Disco.Data.Configuration.Modules
{
public class OrganisationAddressesConfiguration : ConfigurationBase
{
private const string scope = "OrganisationAddresses";
public OrganisationAddressesConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get; } = "OrganisationAddresses";
public override string Scope { get { return scope; } }
public OrganisationAddress GetAddress(int Id)
{
@@ -55,5 +58,19 @@ 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();
}
}
}
}
@@ -1,18 +0,0 @@
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);
}
}
}
+114 -84
View File
@@ -1,8 +1,6 @@
using Disco.Data.Repository;
using Disco.Models.Services.Exporting;
using Disco.Models.Services.Interop.DiscoServices;
using System;
using System.Collections.Generic;
using System.IO;
namespace Disco.Data.Configuration
@@ -20,8 +18,6 @@ namespace Disco.Data.Configuration
moduleActiveDirectoryConfiguration = new Lazy<Modules.ActiveDirectoryConfiguration>(() => new Modules.ActiveDirectoryConfiguration(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
@@ -33,22 +29,61 @@ namespace Disco.Data.Configuration
private Lazy<Modules.ActiveDirectoryConfiguration> moduleActiveDirectoryConfiguration;
private Lazy<Modules.DevicesConfiguration> moduleDevicesConfiguration;
private Lazy<Modules.DocumentsConfiguration> moduleDocumentsConfiguration;
private Lazy<Modules.UserFlagsConfiguration> moduleUserFlagsConfiguration;
private Lazy<Modules.DeviceFlagsConfiguration> moduleDeviceFlagsConfiguration;
public Modules.BootstrapperConfiguration Bootstrapper => moduleBootstrapperConfiguration.Value;
public Modules.DeviceProfilesConfiguration DeviceProfiles => moduleDeviceProfilesConfiguration.Value;
public Modules.OrganisationAddressesConfiguration OrganisationAddresses => moduleOrganisationAddressesConfiguration.Value;
public Modules.JobPreferencesConfiguration JobPreferences => moduleJobPreferencesConfiguration.Value;
public Modules.ActiveDirectoryConfiguration ActiveDirectory => moduleActiveDirectoryConfiguration.Value;
public Modules.DevicesConfiguration Devices => moduleDevicesConfiguration.Value;
public Modules.DocumentsConfiguration Documents => moduleDocumentsConfiguration.Value;
public Modules.UserFlagsConfiguration UserFlags => moduleUserFlagsConfiguration.Value;
public Modules.DeviceFlagsConfiguration DeviceFlags => moduleDeviceFlagsConfiguration.Value;
public Modules.BootstrapperConfiguration Bootstrapper
{
get
{
return moduleBootstrapperConfiguration.Value;
}
}
public Modules.DeviceProfilesConfiguration DeviceProfiles
{
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;
}
}
public Modules.DocumentsConfiguration Documents
{
get
{
return moduleDocumentsConfiguration.Value;
}
}
#endregion
public override string Scope { get; } = "System";
public override string Scope { get { return "System"; } }
public string DataStoreLocation
{
@@ -58,27 +93,26 @@ namespace Disco.Data.Configuration
if (result == null)
{
var appDataPath = System.Web.HttpContext.Current.Server.MapPath("~/App_Data");
if (!appDataPath.EndsWith(@"\"))
appDataPath += @"\";
if (appDataPath.EndsWith("\\"))
return appDataPath;
else
return string.Concat(appDataPath, '\\');
}
else
return result;
}
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentNullException(nameof(value));
if (!Directory.Exists(value))
throw new DirectoryNotFoundException($"DataStoreLocation: '{value}' could not be found");
if (!value.EndsWith(@"\"))
value += @"\";
Set(value);
if (value == null)
throw new ArgumentNullException("value");
if (!System.IO.Directory.Exists(value))
throw new System.IO.DirectoryNotFoundException(string.Format("DataStoreLocation: '{0}' could not be found", value));
string storePath;
if (value.EndsWith("\\"))
storePath = value;
else
storePath = string.Concat(value, '\\');
Set(storePath);
}
}
@@ -95,10 +129,30 @@ namespace Disco.Data.Configuration
}
#region Plugin Locations
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 string PluginsLocation
{
get
{
return System.IO.Path.Combine(DataStoreLocation, @"Plugins\");
}
}
public string PluginStorageLocation
{
get
{
return System.IO.Path.Combine(DataStoreLocation, @"PluginStorage\");
}
}
public string PluginPackagesLocation
{
get
{
return System.IO.Path.Combine(DataStoreLocation, @"PluginPackages\");
}
}
public string PluginUserPhotosLocation
=> Path.Combine(DataStoreLocation, @"PluginUserPhotos\");
public DateTime PluginDetailsCacheExpiration
{
@@ -113,7 +167,7 @@ namespace Disco.Data.Configuration
{
get
{
return Path.Combine(DataStoreLocation, "OrganisationLogo.png");
return System.IO.Path.Combine(DataStoreLocation, "OrganisationLogo.png");
}
}
public string OrganisationLogoHash
@@ -135,15 +189,15 @@ namespace Disco.Data.Configuration
if (File.Exists(path))
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
else
return new MemoryStream(Properties.Resources.EmptyLogo);
return new MemoryStream(Disco.Data.Properties.Resources.EmptyLogo);
}
set
{
string organisationLogoPath = OrganisationLogoPath;
if (value == null)
{
if (File.Exists(organisationLogoPath))
File.Delete(organisationLogoPath);
if (System.IO.File.Exists(organisationLogoPath))
System.IO.File.Delete(organisationLogoPath);
}
else
{
@@ -265,50 +319,20 @@ namespace Disco.Data.Configuration
#endregion
#region UpdateCheck
public bool IsActivated => ActivationId.HasValue;
public DateTime? ActivatedOn
public string DeploymentId
{
get => Get((DateTime?)null);
set => Set(value);
}
public string ActivatedBy
get
{
get => Get((string)null);
set => Set(value);
return Get<string>(null);
}
public Guid? ActivationId
}
public string DeploymentSecret
{
get => Get((Guid?)null);
set => Set(value);
}
public byte[] ActivationKey
get
{
get => Get((byte[])null);
set => Set(value);
return Get<string>(null);
}
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
@@ -327,10 +351,22 @@ namespace Disco.Data.Configuration
}
public UpdateResponseV2 UpdateLastCheckResponse
{
get => Get<UpdateResponseV2>(null);
set => Set(value);
get
{
return Get<UpdateResponseV2>(null);
}
set
{
Set(value);
}
}
public bool UpdateBetaDeployment
{
get
{
return Get(false);
}
}
public bool UpdateBetaDeployment => Get(false);
public Version InstalledDatabaseVersion
{
get
@@ -348,11 +384,5 @@ namespace Disco.Data.Configuration
}
#endregion
public List<SavedExport> SavedExports
{
get => Get(new List<SavedExport>());
set => Set(value);
}
}
}
+1 -59
View File
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Disco.Data</RootNamespace>
<AssemblyName>Disco.Data</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
@@ -77,8 +77,6 @@
<Compile Include="Configuration\ConfigurationCache.cs" />
<Compile Include="Configuration\Modules\ActiveDirectoryConfiguration.cs" />
<Compile Include="Configuration\Modules\DevicesConfiguration.cs" />
<Compile Include="Configuration\Modules\DeviceFlagsConfiguration.cs" />
<Compile Include="Configuration\Modules\UserFlagsConfiguration.cs" />
<Compile Include="Configuration\Modules\DocumentsConfiguration.cs" />
<Compile Include="Configuration\Modules\JobPreferencesConfiguration.cs" />
<Compile Include="Configuration\SystemConfiguration.cs" />
@@ -177,38 +175,6 @@
<Compile Include="Migrations\202304150715559_DBv22.Designer.cs">
<DependentUpon>202304150715559_DBv22.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\202401130531317_DBv23.cs" />
<Compile Include="Migrations\202401130531317_DBv23.Designer.cs">
<DependentUpon>202401130531317_DBv23.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\202403030134280_DBv24.cs" />
<Compile Include="Migrations\202403030134280_DBv24.Designer.cs">
<DependentUpon>202403030134280_DBv24.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\202412180604170_DBv25.cs" />
<Compile Include="Migrations\202412180604170_DBv25.Designer.cs">
<DependentUpon>202412180604170_DBv25.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\202503140520548_DBv26.cs" />
<Compile Include="Migrations\202503140520548_DBv26.Designer.cs">
<DependentUpon>202503140520548_DBv26.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\202507110430252_DBv27.cs" />
<Compile Include="Migrations\202507110430252_DBv27.Designer.cs">
<DependentUpon>202507110430252_DBv27.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\202507170522576_DBv28.cs" />
<Compile Include="Migrations\202507170522576_DBv28.Designer.cs">
<DependentUpon>202507170522576_DBv28.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\202509070209304_DBv29.cs" />
<Compile Include="Migrations\202509070209304_DBv29.Designer.cs">
<DependentUpon>202509070209304_DBv29.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\202509180416385_DBv30.cs" />
<Compile Include="Migrations\202509180416385_DBv30.Designer.cs">
<DependentUpon>202509180416385_DBv30.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\Configuration.cs" />
<Compile Include="Migrations\DiscoDataMigrator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@@ -290,30 +256,6 @@
<EmbeddedResource Include="Migrations\202304150715559_DBv22.resx">
<DependentUpon>202304150715559_DBv22.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Migrations\202401130531317_DBv23.resx">
<DependentUpon>202401130531317_DBv23.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Migrations\202403030134280_DBv24.resx">
<DependentUpon>202403030134280_DBv24.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Migrations\202412180604170_DBv25.resx">
<DependentUpon>202412180604170_DBv25.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Migrations\202503140520548_DBv26.resx">
<DependentUpon>202503140520548_DBv26.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Migrations\202507110430252_DBv27.resx">
<DependentUpon>202507110430252_DBv27.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Migrations\202507170522576_DBv28.resx">
<DependentUpon>202507170522576_DBv28.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Migrations\202509070209304_DBv29.resx">
<DependentUpon>202509070209304_DBv29.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Migrations\202509180416385_DBv30.resx">
<DependentUpon>202509180416385_DBv30.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
-27
View File
@@ -1,27 +0,0 @@
// <auto-generated />
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.Resources;
public sealed partial class DBv23 : IMigrationMetadata
{
private readonly ResourceManager Resources = new ResourceManager(typeof(DBv23));
string IMigrationMetadata.Id
{
get { return "202401130531317_DBv23"; }
}
string IMigrationMetadata.Source
{
get { return null; }
}
string IMigrationMetadata.Target
{
get { return Resources.GetString("Target"); }
}
}
}
@@ -1,67 +0,0 @@
namespace Disco.Data.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class DBv23 : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.DeviceFlagAssignments",
c => new
{
Id = c.Int(nullable: false, identity: true),
DeviceFlagId = c.Int(nullable: false),
DeviceSerialNumber = c.String(nullable: false, maxLength: 60),
AddedDate = c.DateTime(nullable: false),
AddedUserId = c.String(nullable: false, maxLength: 50),
RemovedDate = c.DateTime(),
RemovedUserId = c.String(maxLength: 50),
Comments = c.String(),
OnAssignmentExpressionResult = c.String(),
OnUnassignmentExpressionResult = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.DeviceFlags", t => t.DeviceFlagId)
.ForeignKey("dbo.Devices", t => t.DeviceSerialNumber)
.ForeignKey("dbo.Users", t => t.AddedUserId)
.ForeignKey("dbo.Users", t => t.RemovedUserId)
.Index(t => t.DeviceFlagId)
.Index(t => t.DeviceSerialNumber)
.Index(t => t.AddedUserId)
.Index(t => t.RemovedUserId);
CreateTable(
"dbo.DeviceFlags",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 100),
Description = c.String(maxLength: 500),
Icon = c.String(nullable: false, maxLength: 25),
IconColour = c.String(nullable: false, maxLength: 10),
DevicesLinkedGroup = c.String(),
DeviceUsersLinkedGroup = c.String(),
OnAssignmentExpression = c.String(),
OnUnassignmentExpression = c.String(),
})
.PrimaryKey(t => t.Id);
}
public override void Down()
{
DropIndex("dbo.DeviceFlagAssignments", new[] { "RemovedUserId" });
DropIndex("dbo.DeviceFlagAssignments", new[] { "AddedUserId" });
DropIndex("dbo.DeviceFlagAssignments", new[] { "DeviceSerialNumber" });
DropIndex("dbo.DeviceFlagAssignments", new[] { "DeviceFlagId" });
DropForeignKey("dbo.DeviceFlagAssignments", "RemovedUserId", "dbo.Users");
DropForeignKey("dbo.DeviceFlagAssignments", "AddedUserId", "dbo.Users");
DropForeignKey("dbo.DeviceFlagAssignments", "DeviceSerialNumber", "dbo.Devices");
DropForeignKey("dbo.DeviceFlagAssignments", "DeviceFlagId", "dbo.DeviceFlags");
DropTable("dbo.DeviceFlags");
DropTable("dbo.DeviceFlagAssignments");
}
}
}
File diff suppressed because one or more lines are too long
-27
View File
@@ -1,27 +0,0 @@
// <auto-generated />
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.Resources;
public sealed partial class DBv24 : IMigrationMetadata
{
private readonly ResourceManager Resources = new ResourceManager(typeof(DBv24));
string IMigrationMetadata.Id
{
get { return "202403030134280_DBv24"; }
}
string IMigrationMetadata.Source
{
get { return null; }
}
string IMigrationMetadata.Target
{
get { return Resources.GetString("Target"); }
}
}
}
@@ -1,20 +0,0 @@
namespace Disco.Data.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class DBv24 : DbMigration
{
public override void Up()
{
AddColumn("dbo.JobMetaInsurances", "Insurer", c => c.String(maxLength: 200));
AddColumn("dbo.JobMetaInsurances", "InsurerReference", c => c.String(maxLength: 200));
}
public override void Down()
{
DropColumn("dbo.JobMetaInsurances", "InsurerReference");
DropColumn("dbo.JobMetaInsurances", "Insurer");
}
}
}
File diff suppressed because one or more lines are too long
-27
View File
@@ -1,27 +0,0 @@
// <auto-generated />
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.Resources;
public sealed partial class DBv25 : IMigrationMetadata
{
private readonly ResourceManager Resources = new ResourceManager(typeof(DBv25));
string IMigrationMetadata.Id
{
get { return "202412180604170_DBv25"; }
}
string IMigrationMetadata.Source
{
get { return null; }
}
string IMigrationMetadata.Target
{
get { return Resources.GetString("Target"); }
}
}
}
@@ -1,26 +0,0 @@
namespace Disco.Data.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class DBv25 : DbMigration
{
public override void Up()
{
AlterColumn("dbo.UserAttachments", "Comments", c => c.String(maxLength: 500));
AlterColumn("dbo.JobAttachments", "Comments", c => c.String(maxLength: 500));
AlterColumn("dbo.DeviceAttachments", "Comments", c => c.String(maxLength: 500));
Sql("DELETE [dbo].[Configuration] WHERE [Scope]='System' AND [Key] IN ('ActivatedOn', 'ActivationId', 'LicenseExpiresOn')");
Sql("DELETE [dbo].[Configuration] WHERE [Scope]='DocFill' AND [Key] IN ('LicenseExpiresOn')");
Sql("DELETE [dbo].[Configuration] WHERE [Scope]='JobPreferences' AND [Key] IN ('LastExportDate')");
}
public override void Down()
{
AlterColumn("dbo.DeviceAttachments", "Comments", c => c.String(nullable: false, maxLength: 500));
AlterColumn("dbo.JobAttachments", "Comments", c => c.String(nullable: false, maxLength: 500));
AlterColumn("dbo.UserAttachments", "Comments", c => c.String(nullable: false, maxLength: 500));
}
}
}
File diff suppressed because one or more lines are too long
-27
View File
@@ -1,27 +0,0 @@
// <auto-generated />
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.Resources;
public sealed partial class DBv26 : IMigrationMetadata
{
private readonly ResourceManager Resources = new ResourceManager(typeof(DBv26));
string IMigrationMetadata.Id
{
get { return "202503140520548_DBv26"; }
}
string IMigrationMetadata.Source
{
get { return null; }
}
string IMigrationMetadata.Target
{
get { return Resources.GetString("Target"); }
}
}
}
@@ -1,18 +0,0 @@
namespace Disco.Data.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class DBv26 : DbMigration
{
public override void Up()
{
AlterColumn("dbo.DeviceBatchAttachments", "Comments", c => c.String(maxLength: 500));
}
public override void Down()
{
AlterColumn("dbo.DeviceBatchAttachments", "Comments", c => c.String(nullable: false, maxLength: 500));
}
}
}
File diff suppressed because one or more lines are too long
-27
View File
@@ -1,27 +0,0 @@
// <auto-generated />
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.Resources;
public sealed partial class DBv27 : IMigrationMetadata
{
private readonly ResourceManager Resources = new ResourceManager(typeof(DBv27));
string IMigrationMetadata.Id
{
get { return "202507110430252_DBv27"; }
}
string IMigrationMetadata.Source
{
get { return null; }
}
string IMigrationMetadata.Target
{
get { return Resources.GetString("Target"); }
}
}
}
@@ -1,58 +0,0 @@
namespace Disco.Data.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class DBv27 : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.UserComments",
c => new
{
Id = c.Int(nullable: false, identity: true),
UserId = c.String(maxLength: 50),
TechUserId = c.String(nullable: false, maxLength: 50),
Timestamp = c.DateTime(nullable: false),
Comments = c.String(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Users", t => t.TechUserId)
.ForeignKey("dbo.Users", t => t.UserId)
.Index(t => t.TechUserId)
.Index(t => t.UserId);
CreateTable(
"dbo.DeviceComments",
c => new
{
Id = c.Int(nullable: false, identity: true),
DeviceSerialNumber = c.String(maxLength: 60),
TechUserId = c.String(nullable: false, maxLength: 50),
Timestamp = c.DateTime(nullable: false),
Comments = c.String(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Users", t => t.TechUserId)
.ForeignKey("dbo.Devices", t => t.DeviceSerialNumber)
.Index(t => t.TechUserId)
.Index(t => t.DeviceSerialNumber);
}
public override void Down()
{
DropIndex("dbo.DeviceComments", new[] { "DeviceSerialNumber" });
DropIndex("dbo.DeviceComments", new[] { "TechUserId" });
DropIndex("dbo.UserComments", new[] { "UserId" });
DropIndex("dbo.UserComments", new[] { "TechUserId" });
DropForeignKey("dbo.DeviceComments", "DeviceSerialNumber", "dbo.Devices");
DropForeignKey("dbo.DeviceComments", "TechUserId", "dbo.Users");
DropForeignKey("dbo.UserComments", "UserId", "dbo.Users");
DropForeignKey("dbo.UserComments", "TechUserId", "dbo.Users");
DropTable("dbo.DeviceComments");
DropTable("dbo.UserComments");
}
}
}
File diff suppressed because one or more lines are too long
-27
View File
@@ -1,27 +0,0 @@
// <auto-generated />
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.Resources;
public sealed partial class DBv28 : IMigrationMetadata
{
private readonly ResourceManager Resources = new ResourceManager(typeof(DBv28));
string IMigrationMetadata.Id
{
get { return "202507170522576_DBv28"; }
}
string IMigrationMetadata.Source
{
get { return null; }
}
string IMigrationMetadata.Target
{
get { return Resources.GetString("Target"); }
}
}
}
@@ -1,40 +0,0 @@
namespace Disco.Data.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class DBv28 : DbMigration
{
public override void Up()
{
AddColumn("dbo.UserFlagAssignments", "RemoveDate", c => c.DateTime());
AddColumn("dbo.UserFlagAssignments", "RemoveUserId", c => c.String(maxLength: 50));
AddColumn("dbo.UserFlags", "Permissions", c => c.String());
AddColumn("dbo.UserFlags", "DefaultRemoveDays", c => c.Int());
AddColumn("dbo.DeviceFlagAssignments", "RemoveDate", c => c.DateTime());
AddColumn("dbo.DeviceFlagAssignments", "RemoveUserId", c => c.String(maxLength: 50));
AddColumn("dbo.DeviceFlags", "Permissions", c => c.String());
AddColumn("dbo.DeviceFlags", "DefaultRemoveDays", c => c.Int());
AddForeignKey("dbo.UserFlagAssignments", "RemoveUserId", "dbo.Users", "Id");
AddForeignKey("dbo.DeviceFlagAssignments", "RemoveUserId", "dbo.Users", "Id");
CreateIndex("dbo.UserFlagAssignments", "RemoveUserId");
CreateIndex("dbo.DeviceFlagAssignments", "RemoveUserId");
}
public override void Down()
{
DropIndex("dbo.DeviceFlagAssignments", new[] { "RemoveUserId" });
DropIndex("dbo.UserFlagAssignments", new[] { "RemoveUserId" });
DropForeignKey("dbo.DeviceFlagAssignments", "RemoveUserId", "dbo.Users");
DropForeignKey("dbo.UserFlagAssignments", "RemoveUserId", "dbo.Users");
DropColumn("dbo.DeviceFlags", "DefaultRemoveDays");
DropColumn("dbo.DeviceFlags", "Permissions");
DropColumn("dbo.DeviceFlagAssignments", "RemoveUserId");
DropColumn("dbo.DeviceFlagAssignments", "RemoveDate");
DropColumn("dbo.UserFlags", "DefaultRemoveDays");
DropColumn("dbo.UserFlags", "Permissions");
DropColumn("dbo.UserFlagAssignments", "RemoveUserId");
DropColumn("dbo.UserFlagAssignments", "RemoveDate");
}
}
}
File diff suppressed because one or more lines are too long
-27
View File
@@ -1,27 +0,0 @@
// <auto-generated />
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.Resources;
public sealed partial class DBv29 : IMigrationMetadata
{
private readonly ResourceManager Resources = new ResourceManager(typeof(DBv29));
string IMigrationMetadata.Id
{
get { return "202509070209304_DBv29"; }
}
string IMigrationMetadata.Source
{
get { return null; }
}
string IMigrationMetadata.Target
{
get { return Resources.GetString("Target"); }
}
}
}
@@ -1,18 +0,0 @@
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
public partial class DBv29 : DbMigration
{
public override void Up()
{
AddColumn("dbo.DeviceProfiles", "SetAssignedUserForLogon", c => c.Boolean(nullable: false));
Sql("UPDATE dbo.DeviceProfiles SET SetAssignedUserForLogon = 1");
}
public override void Down()
{
DropColumn("dbo.DeviceProfiles", "SetAssignedUserForLogon");
}
}
}
File diff suppressed because one or more lines are too long
-27
View File
@@ -1,27 +0,0 @@
// <auto-generated />
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.Resources;
public sealed partial class DBv30 : IMigrationMetadata
{
private readonly ResourceManager Resources = new ResourceManager(typeof(DBv30));
string IMigrationMetadata.Id
{
get { return "202509180416385_DBv30"; }
}
string IMigrationMetadata.Source
{
get { return null; }
}
string IMigrationMetadata.Target
{
get { return Resources.GetString("Target"); }
}
}
}
@@ -1,17 +0,0 @@
namespace Disco.Data.Migrations
{
using System.Data.Entity.Migrations;
public partial class DBv30 : DbMigration
{
public override void Up()
{
AddColumn("dbo.DeviceProfiles", "ProvisionFromOtherDomain", c => c.Boolean(nullable: false, defaultValue: false));
}
public override void Down()
{
DropColumn("dbo.DeviceProfiles", "ProvisionFromOtherDomain");
}
}
}
File diff suppressed because one or more lines are too long
+3
View File
@@ -1,6 +1,9 @@
namespace Disco.Data.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using Disco.Data.Repository;
internal sealed class Configuration : DbMigrationsConfiguration<DiscoDataContext>
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using Disco.Data.Repository;
+3 -3
View File
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.5.25262.0000")]
[assembly: AssemblyFileVersion("2.5.25262.0000")]
[assembly: AssemblyVersion("2.2.16326.0500")]
[assembly: AssemblyFileVersion("2.2.16326.0500")]
+2 -2
View File
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -19,7 +19,7 @@ namespace Disco.Data.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
+7 -8
View File
@@ -1,6 +1,9 @@
using Disco.Models.Repository;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using Disco.Models.Repository;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace Disco.Data.Repository
@@ -11,7 +14,7 @@ namespace Disco.Data.Repository
public DiscoDataContext()
{
_Configuration = new Lazy<Configuration.SystemConfiguration>(() => new Configuration.SystemConfiguration(this));
this._Configuration = new Lazy<Configuration.SystemConfiguration>(() => new Configuration.SystemConfiguration(this));
}
public virtual DbSet<ConfigurationItem> ConfigurationItems { get; set; }
@@ -19,7 +22,6 @@ namespace Disco.Data.Repository
public virtual DbSet<DocumentTemplate> DocumentTemplates { get; set; }
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<UserComment> UserComments { get; set; }
public virtual DbSet<UserDetail> UserDetails { get; set; }
public virtual DbSet<UserAttachment> UserAttachments { get; set; }
public virtual DbSet<UserFlag> UserFlags { get; set; }
@@ -29,7 +31,6 @@ namespace Disco.Data.Repository
public virtual DbSet<DeviceUserAssignment> DeviceUserAssignments { get; set; }
public virtual DbSet<Device> Devices { get; set; }
public virtual DbSet<DeviceComment> DeviceComments { get; set; }
public virtual DbSet<DeviceDetail> DeviceDetails { get; set; }
public virtual DbSet<DeviceModel> DeviceModels { get; set; }
public virtual DbSet<DeviceProfile> DeviceProfiles { get; set; }
@@ -37,8 +38,6 @@ namespace Disco.Data.Repository
public virtual DbSet<DeviceBatchAttachment> DeviceBatchAttachments { get; set; }
public virtual DbSet<DeviceComponent> DeviceComponents { get; set; }
public virtual DbSet<DeviceAttachment> DeviceAttachments { get; set; }
public virtual DbSet<DeviceFlag> DeviceFlags { get; set; }
public virtual DbSet<DeviceFlagAssignment> DeviceFlagAssignments { get; set; }
public virtual DbSet<DeviceCertificate> DeviceCertificates { get; set; }
@@ -60,7 +59,7 @@ namespace Disco.Data.Repository
{
get
{
return _Configuration.Value;
return this._Configuration.Value;
}
}
+33 -29
View File
@@ -1,9 +1,11 @@
using Disco.Models.Repository;
using System;
using System.Data.SqlClient;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Models.Repository;
using System.Data.SqlClient;
using System.DirectoryServices.ActiveDirectory;
using System.DirectoryServices;
namespace Disco.Data.Repository
{
@@ -20,6 +22,8 @@ namespace Disco.Data.Repository
Database.SaveChanges();
// Migration Maintenance
Database.MigrateConfiguration();
Database.MigratePreDomainObjects();
}
@@ -58,7 +62,7 @@ namespace Disco.Data.Repository
{
if (Database.DeviceModels.Count() == 0)
{
Database.DeviceModels.Add(new DeviceModel { Manufacturer = "Unknown", Model = "Unknown", Description = "Unknown Device Model", ModelType = "Unknown" });
Database.DeviceModels.Add(new DeviceModel { Manufacturer = "Unknown", Model = "Unknown", Description = "Unknown Device Model" });
}
UpdateDeviceModelConfiguration(Database);
// Removed: 2013-01-14 G#
@@ -208,15 +212,6 @@ namespace Disco.Data.Repository
#endregion
// End
// 2025-07-11
#region "User Management - BYOD" Added
if (Database.JobSubTypes.Count(jst => jst.JobTypeId == JobType.JobTypeIds.UMgmt && jst.Id == JobSubType.UserManagementJobSubTypes.BYOD) == 0)
{
Database.JobSubTypes.Add(new JobSubType { Id = JobSubType.UserManagementJobSubTypes.BYOD, JobTypeId = JobType.JobTypeIds.UMgmt, Description = JobSubType.UserManagementJobSubTypes.BYOD });
}
#endregion
// End
// 2012-05-29 - Audits
#region "Audit" Added
if (Database.JobSubTypes.Count(jst => jst.JobTypeId == JobType.JobTypeIds.HMisc && jst.Id == "Audit") == 0)
@@ -264,7 +259,8 @@ namespace Disco.Data.Repository
foreach (var configurationItem in configurationItems)
{
int profileId = int.Parse(configurationItem.Scope.Substring(configurationItem.Scope.IndexOf(":") + 1));
if (deviceProfiles.TryGetValue(profileId, out var dp))
DeviceProfile dp;
if (deviceProfiles.TryGetValue(profileId, out dp))
{
switch (configurationItem.Key)
{
@@ -272,7 +268,7 @@ namespace Disco.Data.Repository
dp.ComputerNameTemplate = configurationItem.Value;
break;
case "DistributionType":
dp.DistributionType = (DeviceProfile.DistributionTypes)int.Parse(configurationItem.Value);
dp.DistributionType = (DeviceProfile.DistributionTypes)(int.Parse(configurationItem.Value));
break;
case "OrganisationalUnit":
dp.OrganisationalUnit = configurationItem.Value;
@@ -295,7 +291,7 @@ namespace Disco.Data.Repository
private static void UpdateDeviceModelDuplicates(this DiscoDataContext Database)
{
var deviceModels = Database.DeviceModels.ToList();
var duplicateModels = deviceModels.GroupBy(g => $"{g.Manufacturer}|{g.Model}").Where(g => g.Count() > 1);
var duplicateModels = deviceModels.GroupBy(g => string.Format("{0}|{1}", g.Manufacturer, g.Model)).Where(g => g.Count() > 1);
foreach (var duplicateModel in duplicateModels)
{
var primaryModel = duplicateModel.OrderBy(dm => dm.Id).First();
@@ -309,15 +305,23 @@ namespace Disco.Data.Repository
if (!redundantModel.Description.EndsWith("** REDUNDANT **"))
{
if (redundantModel.Description.Length > 484)
redundantModel.Description = $"{redundantModel.Description.Substring(0, 484)} ** REDUNDANT **";
redundantModel.Description = string.Format("{0} ** REDUNDANT **", redundantModel.Description.Substring(0, 484));
else
redundantModel.Description = $"{redundantModel.Description} ** REDUNDANT **";
redundantModel.Description = string.Format("{0} ** REDUNDANT **", redundantModel.Description);
}
}
}
}
// End Added: 2013-02-07 G#
public static void MigrateConfiguration(this DiscoDataContext Database)
{
// Organisation Addresses - Force all to JSON serializing
Configuration.Modules.OrganisationAddressesConfiguration.MigrateDatabase(Database);
Database.SaveChanges();
}
#region Migrate Users SQL
private const string MigratePreDomainUsers_Sql = @"INSERT INTO [Users] SELECT @IdNew, u.DisplayName, u.Surname, u.GivenName, u.PhoneNumber, u.EmailAddress FROM [Users] u WHERE [Id]=@IdExisting;
@@ -366,7 +370,7 @@ DELETE [Users] WHERE [Id]=@IdExisting;";
string defaultNamingContext;
using (Domain d = Domain.GetComputerDomain())
{
string ldapPath = $"LDAP://{d.Name}/";
string ldapPath = string.Format("LDAP://{0}/", d.Name);
string configurationNamingContext;
using (var adRootDSE = new DirectoryEntry(ldapPath + "RootDSE"))
@@ -377,7 +381,7 @@ DELETE [Users] WHERE [Id]=@IdExisting;";
using (var configSearchRoot = new DirectoryEntry(ldapPath + "CN=Partitions," + configurationNamingContext))
{
var configSearchFilter = $"(&(objectcategory=Crossref)(dnsRoot={d.Name})(netBIOSName=*))";
var configSearchFilter = string.Format("(&(objectcategory=Crossref)(dnsRoot={0})(netBIOSName=*))", d.Name);
var configSearchLoadProperites = new string[] { "NetBIOSName" };
using (var configSearcher = new DirectorySearcher(configSearchRoot, configSearchFilter, configSearchLoadProperites, SearchScope.OneLevel))
@@ -399,14 +403,14 @@ DELETE [Users] WHERE [Id]=@IdExisting;";
// Authorization Roles
foreach (var authRole in Database.AuthorizationRoles.Where(ar => ar.SubjectIds != null).ToList())
{
var ids = string.Join(",", authRole.SubjectIds.Split(',').Select(id => id.Contains('\\') ? id : $@"{netBiosName}\{id}"));
var ids = string.Join(",", authRole.SubjectIds.Split(',').Select(id => id.Contains('\\') ? id : string.Format("{0}\\{1}", netBiosName, id)));
if (ids != authRole.SubjectIds)
authRole.SubjectIds = ids;
}
// Job Queues
foreach (var jobQueue in Database.JobQueues.Where(jq => jq.SubjectIds != null).ToList())
{
var ids = string.Join(",", jobQueue.SubjectIds.Split(',').Select(id => id.Contains('\\') ? id : $@"{netBiosName}\{id}"));
var ids = string.Join(",", jobQueue.SubjectIds.Split(',').Select(id => id.Contains('\\') ? id : string.Format("{0}\\{1}", netBiosName, id)));
if (ids != jobQueue.SubjectIds)
jobQueue.SubjectIds = ids;
}
@@ -414,9 +418,9 @@ DELETE [Users] WHERE [Id]=@IdExisting;";
foreach (var deviceProfile in Database.DeviceProfiles.Where(dp => dp.OrganisationalUnit == null || !dp.OrganisationalUnit.Contains(@"DC=")).ToList())
{
if (string.IsNullOrWhiteSpace(deviceProfile.OrganisationalUnit))
deviceProfile.OrganisationalUnit = $"CN=Computers,{defaultNamingContext}";
deviceProfile.OrganisationalUnit = string.Format("CN=Computers,{0}", defaultNamingContext);
else
deviceProfile.OrganisationalUnit = $"{deviceProfile.OrganisationalUnit},{defaultNamingContext}";
deviceProfile.OrganisationalUnit = string.Format("{0},{1}", deviceProfile.OrganisationalUnit, defaultNamingContext);
}
Database.SaveChanges();
@@ -424,7 +428,7 @@ DELETE [Users] WHERE [Id]=@IdExisting;";
var dataStoreLocation = Database.ConfigurationItems.Where(ci => ci.Scope == "System" && ci.Key == "DataStoreLocation").Select(ci => ci.Value).FirstOrDefault();
if (!string.IsNullOrWhiteSpace(dataStoreLocation) && System.IO.Directory.Exists(dataStoreLocation))
{
string filePrefix = $"{netBiosName}_";
string filePrefix = string.Format("{0}_", netBiosName);
var userAttachmentsDirectory = System.IO.Path.Combine(dataStoreLocation, "UserAttachments");
if (System.IO.Directory.Exists(userAttachmentsDirectory))
@@ -447,7 +451,7 @@ DELETE [Users] WHERE [Id]=@IdExisting;";
// MIGRATE DEVICES
foreach (var device in Database.Devices.Where(d => d.DeviceDomainId != null && !d.DeviceDomainId.Contains(@"\")).ToList())
{
device.DeviceDomainId = $@"{netBiosName}\{device.DeviceDomainId}";
device.DeviceDomainId = string.Format("{0}\\{1}", netBiosName, device.DeviceDomainId);
}
Database.SaveChanges();
@@ -458,7 +462,7 @@ DELETE [Users] WHERE [Id]=@IdExisting;";
idExisting.Value = user.UserId;
SqlParameter idNew = new SqlParameter("@IdNew", System.Data.SqlDbType.NVarChar, 50);
idNew.Value = $@"{netBiosName}\{user.UserId}";
idNew.Value = string.Format("{0}\\{1}", netBiosName, user.UserId);
Database.Database.ExecuteSqlCommand(MigratePreDomainUsers_Sql, idExisting, idNew);
}
@@ -1,8 +1,12 @@
using Microsoft.Win32;
using System;
using System.Data.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity.Infrastructure;
using Microsoft.Win32;
using System.Data.Common;
using System.Security;
using System.Security.Permissions;
namespace Disco.Data.Repository
{
@@ -48,18 +52,18 @@ namespace Disco.Data.Repository
}
catch (UnauthorizedAccessException ex)
{
throw new SecurityException($"Unable to write to the Registry Location: HKML\\{DiscoRegistryKey}[DatabaseConnectionString]", ex);
throw new SecurityException(string.Format("Unable to write to the Registry Location: HKML\\{0}[DatabaseConnectionString]", DiscoRegistryKey), ex);
}
}
}
public DiscoDatabaseConnectionFactory(IDbConnectionFactory Default)
{
DefaultConnectionFactory = Default;
SqlCeConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
this.DefaultConnectionFactory = Default;
this.SqlCeConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
}
public DbConnection CreateConnection(string nameOrConnectionString)
public System.Data.Common.DbConnection CreateConnection(string nameOrConnectionString)
{
if (nameOrConnectionString == "Disco.Data.Repository.DiscoDataContext")
{
@@ -67,7 +71,7 @@ namespace Disco.Data.Repository
var connectionString = DiscoDataContextConnectionString;
if (connectionString == null)
{
throw new InvalidOperationException("The Disco ICT DataContext Connection String has not been configured");
throw new InvalidOperationException("The Disco DataContext Connection String has not been configured");
}
// Build DiscoDataContext - Use Default Connection Factory (SQLClient)
@@ -1,11 +1,14 @@
using Disco.Models.Repository;
using System;
using System.Collections.Concurrent;
using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive;
using System.Reactive.Subjects;
using System.Data.Entity.Infrastructure;
using System.Collections.Concurrent;
using System.Data.Objects;
using Disco.Models.Repository;
namespace Disco.Data.Repository.Monitor
{
@@ -49,8 +52,9 @@ namespace Disco.Data.Repository.Monitor
private static Type EntityTypeFromProxy(Type EntityProxyType)
{
Type EntityType;
if (entityProxyTypeCache.TryGetValue(EntityProxyType, out var EntityType))
if (entityProxyTypeCache.TryGetValue(EntityProxyType, out EntityType))
return EntityType;
EntityType = EntityProxyType;
@@ -109,7 +113,7 @@ namespace Disco.Data.Repository.Monitor
eventType = RepositoryMonitorEventType.Unchanged;
break;
default:
throw new NotSupportedException($"Database Entry State not supported: {entryState.State.ToString()}");
throw new NotSupportedException(string.Format("Database Entry State not supported: {0}", entryState.State.ToString()));
}
entityType = EntityTypeFromProxy(entryState.Entity.GetType());
@@ -3,6 +3,9 @@ using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Data.Repository.Monitor
{
@@ -52,8 +55,7 @@ namespace Disco.Data.Repository.Monitor
return (T)dbEntityState.CurrentValues[PropertyName];
}
public void ExecuteAfterCommit(Action<RepositoryMonitorEvent> action)
{
public void ExecuteAfterCommit(Action<RepositoryMonitorEvent> action){
if (afterCommit)
{
// Execute Immediately
@@ -1,4 +1,10 @@
namespace Disco.Data.Repository.Monitor
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Data.Repository.Monitor
{
public enum RepositoryMonitorEventType
{
+1 -1
View File
@@ -12,6 +12,6 @@
</defaultConnectionFactory>
</entityFramework>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
+49 -3
View File
@@ -1,9 +1,12 @@
using System.ComponentModel.DataAnnotations;
using System;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Disco.Models.BI.Config
{
public class OrganisationAddress
{
public int? Id { get; set; }
[Required]
public string Name { get; set; }
@@ -20,11 +23,54 @@ namespace Disco.Models.BI.Config
[Required]
public string ShortName { get; set; }
// Added 2012-12-11 G#
// http://discoict.com.au/forum/support/2012/12/address-details.aspx
public string PhoneNumber { get; set; }
public string FaxNumber { get; set; }
public string EmailAddress { get; set; }
// End Added 2012-12-11 G#
public string ToConfigurationEntry()
{
StringBuilder entryBuilder = new StringBuilder();
entryBuilder.AppendLine(Name.Trim());
entryBuilder.AppendLine(Address.Trim());
entryBuilder.AppendLine(Suburb.Trim());
entryBuilder.AppendLine(Postcode.Trim());
entryBuilder.AppendLine(State.Trim());
entryBuilder.AppendLine(Country.Trim());
if (!string.IsNullOrEmpty(ShortName))
{
entryBuilder.AppendLine(ShortName.Trim());
}
return entryBuilder.ToString();
}
public static OrganisationAddress FromConfigurationEntry(int Id, string Entry)
{
string[] entryLines = Entry.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
if (entryLines.Length >= 6)
{
return new OrganisationAddress()
{
Id = Id,
Name = entryLines[0].Trim(),
Address = entryLines[1].Trim(),
Suburb = entryLines[2].Trim(),
Postcode = entryLines[3].Trim(),
State = entryLines[4].Trim(),
Country = entryLines[5].Trim(),
ShortName = (entryLines.Length > 6 ? entryLines[6].Trim() : string.Empty)
};
}
throw new ArgumentException("Invalid Configuration Address Entry", "entry");
}
public override string ToString()
=> $"{Name} ({ShortName})";
{
return string.Format("{0} ({1})", this.Name, this.ShortName);
}
}
}
@@ -1,13 +1,13 @@
using System.IO;
namespace Disco.Models.Services.Expressions.Extensions
namespace Disco.Models.BI.Expressions
{
public interface IImageExpressionResult
{
MemoryStream GetImage(int width, int height);
MemoryStream GetImage(out int width, out int height);
Stream GetImage(int Width, int Height);
Stream GetImage();
byte Quality { get; set; }
ImageExpressionFormat Format { get; set; }
bool LosslessFormat { get; set; }
bool ShowField { get; set; }
string BackgroundColour { get; set; }
bool BackgroundPreferTransparent { get; set; }
-3
View File
@@ -25,8 +25,5 @@ namespace Disco.Models.ClientServices
public List<Certificate> Certificates { get; set; }
public List<WirelessProfile> WirelessProfiles { get; set; }
public string PendingSessionId { get; set; }
public string PendingAuthorization { get; set; }
}
}
@@ -1,6 +1,4 @@
using Disco.Models.ClientServices.EnrolmentInformation;
using Newtonsoft.Json;
using System;
namespace Disco.Models.ClientServices
{
@@ -11,18 +9,12 @@ namespace Disco.Models.ClientServices
public string DomainName { get; set; }
public string ComputerName { get; set; }
[JsonIgnore]
public int? DeviceProfileId { get; set; }
[JsonIgnore]
public int? DeviceBatchId { get; set; }
public string AssignedUserDomain { get; set; }
public string AssignedUserUsername { get; set; }
public string AssignedUserSID { get; set; }
public string AssignedUserDescription { get; set; }
public bool AssignedUserIsLocalAdmin { get; set; }
public bool SetAssignedUserForLogon { get; set; }
public string OfflineDomainJoinManifest { get; set; }
@@ -34,11 +26,5 @@ namespace Disco.Models.ClientServices
public bool RequireReboot { get; set; }
public string ErrorMessage { get; set; }
public bool IsPending { get; set; }
public string PendingAuthorization { get; set; }
public string PendingReason { get; set; }
public DateTimeOffset PendingTimeout { get; set; }
public string PendingIdentifier { get; set; }
}
}
@@ -3,7 +3,7 @@
public class ComputerSystem
{
public string ChassisSKUNumber { get; set; }
public short? CurrentTimeZone { get; set; }
public short? CurrentTimeZone { get; set;}
public string Description { get; set; }
public string[] OEMStringArray { get; set; }
public string PCSystemType { get; set; }
@@ -11,8 +11,6 @@ namespace Disco.Models.ClientServices.EnrolmentInformation
public string Model { get; set; }
public string ModelType { get; set; }
public string MdmHardwareData { get; set; }
public List<Bios> Bios { get; set; }
public List<BaseBoard> BasebBoard { get; set; }
public List<ComputerSystem> ComputerSystem { get; set; }
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace Disco.Models.ClientServices.EnrolmentInformation
{
+6 -1
View File
@@ -1,4 +1,9 @@
namespace Disco.Models.ClientServices
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.Models.ClientServices
{
public class MacEnrol : ServiceBase<MacEnrolResponse>
{
@@ -1,4 +1,9 @@
namespace Disco.Models.ClientServices
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.Models.ClientServices
{
public class MacEnrolResponse
{
@@ -1,4 +1,9 @@
namespace Disco.Models.ClientServices
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.Models.ClientServices
{
public class MacSecureEnrolResponse
{
+6 -1
View File
@@ -1,4 +1,9 @@
namespace Disco.Models.ClientServices
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.Models.ClientServices
{
public abstract class ServiceBase<ResponseType>
{

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