Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1aee95d35 | |||
| 72a709c11d | |||
| 4f7f6db804 | |||
| 892299a791 | |||
| 48512fa9d1 | |||
| 204d57a4a5 | |||
| f807d75162 | |||
| e809c63e37 | |||
| e1f1973520 | |||
| 71fa53bfb2 | |||
| 834d2f8fae | |||
| 2ab765f2d7 | |||
| 04be92a1df | |||
| 4e7c7c117b | |||
| f975c55b8a | |||
| 9e0d832aa1 | |||
| 94a31282eb | |||
| 529bba5c72 | |||
| 8424a9a9a2 | |||
| 202bbb163b | |||
| 0e15d2a880 | |||
| 89c14084f5 | |||
| e2c07f019c | |||
| 7603cac01a | |||
| 356762c811 | |||
| deaac0f073 | |||
| de10f92c02 | |||
| 3e514fdf9a | |||
| 36c24542e1 | |||
| ca7193a8fc | |||
| 676ff82e4b | |||
| 53fdea5325 | |||
| 0c0a7bf297 | |||
| 57aeaa3eaa | |||
| d291ad464f | |||
| be7ee4cae8 | |||
| 7deead494b | |||
| fd43d85778 | |||
| 4c27b0ff3c | |||
| 58546ed16d | |||
| 9656c15c4b | |||
| 1add4ee0f5 | |||
| 5792771ea1 | |||
| 49da389c2c | |||
| 7faebe56a8 | |||
| 4e518d6684 | |||
| b4e54c9edf | |||
| 3aeb9374a9 | |||
| 0905a02aea | |||
| f8fd1a58a3 | |||
| 2184c9e22e | |||
| 42e9045d5e | |||
| 092c6249ee | |||
| b56e82d5c4 | |||
| 54a578f4a1 | |||
| 3f1fa3d7de | |||
| caa0bedc93 | |||
| 6e99b4503b | |||
| 583552ffdd | |||
| 4660425ccc | |||
| 8b8b26116e | |||
| 57ae665070 | |||
| f06a37ebb4 | |||
| ee273ffe03 | |||
| 0672bdb7c0 | |||
| 662e1ed231 | |||
| cc1f224456 | |||
| 6e2c36d4ae | |||
| bccbb20b84 | |||
| fd3eb80648 | |||
| 9a171a8417 | |||
| eb1ffae212 | |||
| d31c2db43b | |||
| 62a5da7513 | |||
| eb3ed7f819 | |||
| a9687b5f25 | |||
| 539503133a | |||
| d2b5e7ab6a | |||
| 04430bba50 | |||
| 162ca20046 | |||
| 786d4809b5 | |||
| d3cef11796 | |||
| 2c215e4856 | |||
| eec8ba438c | |||
| 59dc0b9d5a | |||
| ac24055365 | |||
| 2fce645066 | |||
| 67f1c2a5d1 | |||
| f946f3250c | |||
| 0853bcee50 | |||
| 408e1c4c14 | |||
| dee54bb6d7 | |||
| 50603210b0 | |||
| b640e51874 | |||
| 7a336e699a | |||
| a3e1e1d030 | |||
| 963970feeb | |||
| 296f7a13fd | |||
| 4006bdbcc6 | |||
| 26bac92e5a | |||
| b6dfaa3445 | |||
| 39ba206831 | |||
| 7550e0e45d | |||
| 9f10eeeb70 | |||
| 77acf0bfdb | |||
| f0dfe45cd4 | |||
| fb6432a5c8 | |||
| b15917254f | |||
| 8abe31f430 | |||
| a6b9cd1af2 | |||
| 90c709c4c1 | |||
| 25e3a8e1f1 | |||
| 343f20980d | |||
| f36871abe2 | |||
| cb354cd13e |
@@ -17,13 +17,12 @@ namespace Disco.BI.Extensions
|
|||||||
{
|
{
|
||||||
public static class DocumentTemplateExtensions
|
public static class DocumentTemplateExtensions
|
||||||
{
|
{
|
||||||
private static Tuple<Dictionary<string, Expression>, List<DocumentField>> CreateExpressions(DocumentTemplate dt, DiscoDataContext database)
|
public static Tuple<Dictionary<string, Expression>, List<DocumentField>> CreateExpressions(string templateFileName, DiscoDataContext database)
|
||||||
{
|
{
|
||||||
Dictionary<string, Expression> expressions = new Dictionary<string, Expression>();
|
Dictionary<string, Expression> expressions = new Dictionary<string, Expression>();
|
||||||
List<DocumentField> fields = new List<DocumentField>();
|
List<DocumentField> fields = new List<DocumentField>();
|
||||||
|
|
||||||
string templateFilename = dt.RepositoryFilename(database);
|
PdfReader pdfReader = new PdfReader(templateFileName);
|
||||||
PdfReader pdfReader = new PdfReader(templateFilename);
|
|
||||||
int pdfFieldOrdinal = 0;
|
int pdfFieldOrdinal = 0;
|
||||||
foreach (string pdfFieldKey in pdfReader.AcroFields.Fields.Keys)
|
foreach (string pdfFieldKey in pdfReader.AcroFields.Fields.Keys)
|
||||||
{
|
{
|
||||||
@@ -61,54 +60,60 @@ namespace Disco.BI.Extensions
|
|||||||
return Tuple.Create(expressions, fields);
|
return Tuple.Create(expressions, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dictionary<string, Expression> PdfExpressionsFromCache(this DocumentTemplate dt, DiscoDataContext Database)
|
private static Tuple<Dictionary<string, Expression>, List<DocumentField>> CreateExpressions(DocumentTemplate dt, DiscoDataContext database)
|
||||||
{
|
{
|
||||||
return ExpressionCache.GetOrCreateExpressions(dt, () => CreateExpressions(dt, Database));
|
string templateFileName = dt.RepositoryFilename(database);
|
||||||
|
return CreateExpressions(templateFileName, database);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<DocumentField> PdfFieldsFromCache(this DocumentTemplate dt, DiscoDataContext Database)
|
public static Dictionary<string, Expression> PdfExpressionsFromCache(this DocumentTemplate dt, DiscoDataContext database)
|
||||||
{
|
{
|
||||||
return ExpressionCache.GetOrCreateFields(dt, () => CreateExpressions(dt, Database));
|
return ExpressionCache.GetOrCreateExpressions(dt, () => CreateExpressions(dt, database));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Expression> ExtractPdfExpressions(this DocumentTemplate dt, DiscoDataContext Database)
|
public static List<DocumentField> PdfFieldsFromCache(this DocumentTemplate dt, DiscoDataContext database)
|
||||||
{
|
{
|
||||||
return dt.PdfExpressionsFromCache(Database).Values.OrderBy(e => e.Ordinal).ToList();
|
return ExpressionCache.GetOrCreateFields(dt, () => CreateExpressions(dt, database));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream GeneratePdf(this DocumentTemplate dt, DiscoDataContext Database, IAttachmentTarget Target, User CreatorUser, DateTime TimeStamp, DocumentState State, bool FlattenFields = false)
|
public static List<Expression> ExtractPdfExpressions(this DocumentTemplate dt, DiscoDataContext database)
|
||||||
|
{
|
||||||
|
return dt.PdfExpressionsFromCache(database).Values.OrderBy(e => e.Ordinal).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream GeneratePdf(this DocumentTemplate dt, DiscoDataContext database, IAttachmentTarget target, User creatorUser, DateTime timeStamp, DocumentState state, bool flattenFields = false)
|
||||||
{
|
{
|
||||||
bool generateExpression = !string.IsNullOrEmpty(dt.OnGenerateExpression);
|
bool generateExpression = !string.IsNullOrEmpty(dt.OnGenerateExpression);
|
||||||
string generateExpressionResult = null;
|
string generateExpressionResult = null;
|
||||||
|
|
||||||
if (generateExpression)
|
if (generateExpression)
|
||||||
generateExpressionResult = dt.EvaluateOnGenerateExpression(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)
|
if (generateExpression)
|
||||||
DocumentsLog.LogDocumentGenerated(dt, Target, CreatorUser, generateExpressionResult);
|
DocumentsLog.LogDocumentGenerated(dt, target, creatorUser, generateExpressionResult);
|
||||||
else
|
else
|
||||||
DocumentsLog.LogDocumentGenerated(dt, Target, CreatorUser);
|
DocumentsLog.LogDocumentGenerated(dt, target, creatorUser);
|
||||||
|
|
||||||
return pdfStream;
|
return pdfStream;
|
||||||
}
|
}
|
||||||
public static Stream GeneratePdfPackage(this DocumentTemplatePackage package, DiscoDataContext Database, IAttachmentTarget Target, User CreatorUser, DateTime TimeStamp, DocumentState State)
|
public static 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))
|
if (!File.Exists(templateFilename))
|
||||||
throw new FileNotFoundException("PDF template not found", templateFilename);
|
throw new FileNotFoundException("PDF template not found", templateFilename);
|
||||||
|
|
||||||
@@ -127,30 +132,20 @@ namespace Disco.BI.Extensions
|
|||||||
public static void Delete(this DocumentTemplate dt, DiscoDataContext Database)
|
public static void Delete(this DocumentTemplate dt, DiscoDataContext Database)
|
||||||
{
|
{
|
||||||
// Find & Rename all references
|
// Find & Rename all references
|
||||||
foreach (DeviceAttachment a in Database.DeviceAttachments.Where(a => a.DocumentTemplateId == dt.Id))
|
void updateAttachment(IAttachment a)
|
||||||
{
|
{
|
||||||
a.Comments = string.Format("{0} - {1}", dt.Description, a.Comments);
|
var comments = $"{dt.Description} - {a.Comments}";
|
||||||
if (a.Comments.Length > 500)
|
if (comments.Length > 500)
|
||||||
a.Comments = a.Comments.Substring(0, 500);
|
comments = comments.Substring(0, 500);
|
||||||
|
a.Comments = comments;
|
||||||
a.DocumentTemplateId = null;
|
a.DocumentTemplateId = null;
|
||||||
a.DocumentTemplate = null;
|
|
||||||
}
|
|
||||||
foreach (JobAttachment a in Database.JobAttachments.Where(a => a.DocumentTemplateId == dt.Id))
|
|
||||||
{
|
|
||||||
a.Comments = string.Format("{0} - {1}", dt.Description, a.Comments);
|
|
||||||
if (a.Comments.Length > 500)
|
|
||||||
a.Comments = a.Comments.Substring(0, 500);
|
|
||||||
a.DocumentTemplateId = null;
|
|
||||||
a.DocumentTemplate = null;
|
|
||||||
}
|
}
|
||||||
|
foreach (var a in Database.DeviceAttachments.Where(a => a.DocumentTemplateId == dt.Id))
|
||||||
|
updateAttachment(a);
|
||||||
|
foreach (var a in Database.JobAttachments.Where(a => a.DocumentTemplateId == dt.Id))
|
||||||
|
updateAttachment(a);
|
||||||
foreach (UserAttachment a in Database.UserAttachments.Where(a => a.DocumentTemplateId == dt.Id))
|
foreach (UserAttachment a in Database.UserAttachments.Where(a => a.DocumentTemplateId == dt.Id))
|
||||||
{
|
updateAttachment(a);
|
||||||
a.Comments = string.Format("{0} - {1}", dt.Description, a.Comments);
|
|
||||||
if (a.Comments.Length > 500)
|
|
||||||
a.Comments = a.Comments.Substring(0, 500);
|
|
||||||
a.DocumentTemplateId = null;
|
|
||||||
a.DocumentTemplate = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove Linked Group
|
// Remove Linked Group
|
||||||
ActiveDirectory.Context.ManagedGroups.Remove(DocumentTemplateDevicesManagedGroup.GetKey(dt));
|
ActiveDirectory.Context.ManagedGroups.Remove(DocumentTemplateDevicesManagedGroup.GetKey(dt));
|
||||||
@@ -161,8 +156,8 @@ namespace Disco.BI.Extensions
|
|||||||
|
|
||||||
// Delete Template
|
// Delete Template
|
||||||
string templateRepositoryFilename = dt.RepositoryFilename(Database);
|
string templateRepositoryFilename = dt.RepositoryFilename(Database);
|
||||||
if (System.IO.File.Exists(templateRepositoryFilename))
|
if (File.Exists(templateRepositoryFilename))
|
||||||
System.IO.File.Delete(templateRepositoryFilename);
|
File.Delete(templateRepositoryFilename);
|
||||||
|
|
||||||
// Remove from Cache
|
// Remove from Cache
|
||||||
dt.FilterExpressionInvalidateCache();
|
dt.FilterExpressionInvalidateCache();
|
||||||
|
|||||||
@@ -21,16 +21,16 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
{
|
{
|
||||||
public static class PdfGenerator
|
public static class PdfGenerator
|
||||||
{
|
{
|
||||||
public static Stream GenerateBulkFromPackage(DocumentTemplatePackage package, DiscoDataContext Database, User CreatorUser, DateTime Timestamp, bool InsertBlankPages, List<IAttachmentTarget> DataObjects)
|
public static 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())
|
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.SequenceNumber++;
|
||||||
state.FlushScopeCache();
|
state.FlushScopeCache();
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Stream bulkPdf = Utilities.JoinPdfs(package.InsertBlankPages || InsertBlankPages, generatedPdfs);
|
Stream bulkPdf = Utilities.JoinPdfs(insertBlankPages.GetValueOrDefault(package.InsertBlankPages), generatedPdfs);
|
||||||
foreach (Stream singlePdf in generatedPdfs)
|
foreach (Stream singlePdf in generatedPdfs)
|
||||||
singlePdf.Dispose();
|
singlePdf.Dispose();
|
||||||
return bulkPdf;
|
return bulkPdf;
|
||||||
@@ -50,25 +50,25 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
return null;
|
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;
|
List<IAttachmentTarget> DataObjects;
|
||||||
|
|
||||||
switch (package.Scope)
|
switch (package.Scope)
|
||||||
{
|
{
|
||||||
case AttachmentTypes.Device:
|
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;
|
break;
|
||||||
case AttachmentTypes.Job:
|
case AttachmentTypes.Job:
|
||||||
int[] intDataObjectsIds = DataObjectsIds.Select(i => int.Parse(i)).ToArray();
|
int[] intDataObjectsIds = dataObjectsIds.Select(i => int.Parse(i)).ToArray();
|
||||||
DataObjects = Database.Jobs.Where(j => intDataObjectsIds.Contains(j.Id)).ToList<IAttachmentTarget>();
|
DataObjects = database.Jobs.Where(j => intDataObjectsIds.Contains(j.Id)).ToList<IAttachmentTarget>();
|
||||||
break;
|
break;
|
||||||
case AttachmentTypes.User:
|
case AttachmentTypes.User:
|
||||||
DataObjects = new List<IAttachmentTarget>(DataObjectsIds.Count);
|
DataObjects = new List<IAttachmentTarget>(dataObjectsIds.Count);
|
||||||
for (int idIndex = 0; idIndex < DataObjectsIds.Count; idIndex++)
|
for (int idIndex = 0; idIndex < dataObjectsIds.Count; idIndex++)
|
||||||
{
|
{
|
||||||
string dataObjectId = DataObjectsIds[idIndex];
|
string dataObjectId = dataObjectsIds[idIndex];
|
||||||
var user = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(dataObjectId), Database, true);
|
var user = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(dataObjectId), database, true);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new Exception($"Unknown Username specified: {dataObjectId}");
|
throw new Exception($"Unknown Username specified: {dataObjectId}");
|
||||||
DataObjects.Add(user);
|
DataObjects.Add(user);
|
||||||
@@ -78,12 +78,12 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
throw new InvalidOperationException("Invalid DocumentType Scope");
|
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)
|
if (templates.Count == 0)
|
||||||
return null;
|
return null;
|
||||||
@@ -92,21 +92,21 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
string generateExpressionResult = null;
|
string generateExpressionResult = null;
|
||||||
|
|
||||||
if (generateExpression)
|
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);
|
List<Stream> generatedPdfs = new List<Stream>(templates.Count);
|
||||||
foreach (var template in templates)
|
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.SequenceNumber++;
|
||||||
State.FlushScopeCache();
|
state.FlushScopeCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (generateExpression)
|
if (generateExpression)
|
||||||
DocumentsLog.LogDocumentPackageGenerated(package, Data, CreatorUser, generateExpressionResult);
|
DocumentsLog.LogDocumentPackageGenerated(package, data, creatorUser, generateExpressionResult);
|
||||||
else
|
else
|
||||||
DocumentsLog.LogDocumentPackageGenerated(package, Data, CreatorUser);
|
DocumentsLog.LogDocumentPackageGenerated(package, data, creatorUser);
|
||||||
|
|
||||||
if (generatedPdfs.Count == 1)
|
if (generatedPdfs.Count == 1)
|
||||||
{
|
{
|
||||||
@@ -120,20 +120,20 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
return bulkPdf;
|
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);
|
List<Stream> generatedPdfs = new List<Stream>(dataObjects.Count);
|
||||||
var progressPerDoc = 80d / DataObjects.Count;
|
var progressPerDoc = 80d / dataObjects.Count;
|
||||||
var progressDoc = 10d;
|
var progressDoc = 10d;
|
||||||
using (var state = DocumentState.DefaultState())
|
using (var state = DocumentState.DefaultState())
|
||||||
{
|
{
|
||||||
taskStatus.UpdateStatus(10, "Rendering", "Starting");
|
taskStatus.UpdateStatus(10, "Rendering", "Starting");
|
||||||
foreach (var d in DataObjects)
|
foreach (var d in dataObjects)
|
||||||
{
|
{
|
||||||
taskStatus.UpdateStatus(progressDoc += progressPerDoc, $"Rendering {d.AttachmentReferenceId}");
|
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.SequenceNumber++;
|
||||||
state.FlushScopeCache();
|
state.FlushScopeCache();
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
taskStatus.UpdateStatus(90, "Merging", "Merging documents");
|
taskStatus.UpdateStatus(90, "Merging", "Merging documents");
|
||||||
Stream bulkPdf = Utilities.JoinPdfs(InsertBlankPages, generatedPdfs);
|
Stream bulkPdf = Utilities.JoinPdfs(insertBlankPages, generatedPdfs);
|
||||||
foreach (Stream singlePdf in generatedPdfs)
|
foreach (Stream singlePdf in generatedPdfs)
|
||||||
singlePdf.Dispose();
|
singlePdf.Dispose();
|
||||||
return bulkPdf;
|
return bulkPdf;
|
||||||
@@ -154,28 +154,28 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
return null;
|
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;
|
Dictionary<string, IAttachmentTarget> dataObjectLookup;
|
||||||
List<string> dataObjectIds = DataObjectsIds;
|
List<string> dataObjectIds = dataObjectsIds;
|
||||||
|
|
||||||
taskStatus.UpdateStatus(0, "Resolving targets", "Resolving render targets");
|
taskStatus.UpdateStatus(0, "Resolving targets", "Resolving render targets");
|
||||||
|
|
||||||
switch (dt.Scope)
|
switch (dt.Scope)
|
||||||
{
|
{
|
||||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
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;
|
break;
|
||||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||||
var intDataObjectsIds = DataObjectsIds.Select(i => int.Parse(i)).ToList();
|
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);
|
dataObjectLookup = database.Jobs.Where(j => intDataObjectsIds.Contains(j.Id)).AsEnumerable().Cast<IAttachmentTarget>().ToDictionary(i => i.AttachmentReferenceId, StringComparer.OrdinalIgnoreCase);
|
||||||
break;
|
break;
|
||||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||||
dataObjectLookup = new Dictionary<string, IAttachmentTarget>(DataObjectsIds.Count, StringComparer.OrdinalIgnoreCase);
|
dataObjectLookup = new Dictionary<string, IAttachmentTarget>(dataObjectsIds.Count, StringComparer.OrdinalIgnoreCase);
|
||||||
dataObjectIds = new List<string>(DataObjectsIds.Count);
|
dataObjectIds = new List<string>(dataObjectsIds.Count);
|
||||||
foreach (var userId in DataObjectsIds)
|
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)
|
if (user == null)
|
||||||
{
|
{
|
||||||
dataObjectIds.Add(userId);
|
dataObjectIds.Add(userId);
|
||||||
@@ -190,7 +190,7 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
}
|
}
|
||||||
|
|
||||||
// recreate list to honor the sort-order provided in DataObjectsIds
|
// 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>();
|
var missingIds = new List<string>();
|
||||||
foreach (var id in dataObjectIds)
|
foreach (var id in dataObjectIds)
|
||||||
{
|
{
|
||||||
@@ -204,10 +204,10 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
throw new Exception($"Unknown id specified: {string.Join("; ", missingIds)}");
|
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
|
// Validate Data
|
||||||
switch (dt.Scope)
|
switch (dt.Scope)
|
||||||
@@ -232,7 +232,7 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
|
|
||||||
// Override FlattenFields if Document Template instructs.
|
// Override FlattenFields if Document Template instructs.
|
||||||
if (dt.FlattenForm)
|
if (dt.FlattenForm)
|
||||||
FlattenFields = true;
|
flattenFields = true;
|
||||||
|
|
||||||
var expressionCache = dt.PdfExpressionsFromCache(Database);
|
var expressionCache = dt.PdfExpressionsFromCache(Database);
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
MemoryStream pdfGeneratedStream = new MemoryStream();
|
MemoryStream pdfGeneratedStream = new MemoryStream();
|
||||||
PdfStamper pdfStamper = new PdfStamper(pdfReader, pdfGeneratedStream);
|
PdfStamper pdfStamper = new PdfStamper(pdfReader, pdfGeneratedStream);
|
||||||
|
|
||||||
pdfStamper.FormFlattening = FlattenFields;
|
pdfStamper.FormFlattening = flattenFields;
|
||||||
pdfStamper.Writer.CloseStream = false;
|
pdfStamper.Writer.CloseStream = false;
|
||||||
|
|
||||||
IDictionary expressionVariables = Expression.StandardVariables(dt, Database, CreatorUser, TimeStamp, State, Data);
|
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];
|
AcroFields.Item fields = pdfStamper.AcroFields.Fields[pdfFieldKey];
|
||||||
string fieldValue = dt.CreateUniqueIdentifier(Database, Data, CreatorUser, TimeStamp, 0).ToJson();
|
string fieldValue = dt.CreateUniqueIdentifier(Database, Data, CreatorUser, TimeStamp, 0).ToJson();
|
||||||
if (FlattenFields)
|
if (flattenFields)
|
||||||
pdfStamper.AcroFields.SetField(pdfFieldKey, string.Empty);
|
pdfStamper.AcroFields.SetField(pdfFieldKey, string.Empty);
|
||||||
else
|
else
|
||||||
pdfStamper.AcroFields.SetField(pdfFieldKey, fieldValue);
|
pdfStamper.AcroFields.SetField(pdfFieldKey, fieldValue);
|
||||||
@@ -296,8 +296,7 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Expression fieldExpression = null;
|
if (expressionCache.TryGetValue(pdfFieldKey, out var fieldExpression))
|
||||||
if (expressionCache.TryGetValue(pdfFieldKey, out fieldExpression))
|
|
||||||
{
|
{
|
||||||
if (fieldExpression.IsDynamic)
|
if (fieldExpression.IsDynamic)
|
||||||
{
|
{
|
||||||
@@ -397,7 +396,7 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
TechUserId = CreatorUser.UserId,
|
TechUserId = CreatorUser.UserId,
|
||||||
Timestamp = DateTime.Now
|
Timestamp = DateTime.Now
|
||||||
};
|
};
|
||||||
jl.Comments = string.Format("# Document Generated\r\n**{0}** [{1}]", dt.Description, dt.Id);
|
jl.Comments = $"# Document Generated\r\n**{dt.Description}** [{dt.Id}]";
|
||||||
Database.JobLogs.Add(jl);
|
Database.JobLogs.Add(jl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Disco.BI.Interop.Pdf
|
|||||||
public static Stream JoinPdfs(bool InsertBlankPages, List<Stream> Pdfs)
|
public static Stream JoinPdfs(bool InsertBlankPages, List<Stream> Pdfs)
|
||||||
{
|
{
|
||||||
if (Pdfs.Count == 0)
|
if (Pdfs.Count == 0)
|
||||||
throw new System.ArgumentNullException(nameof(Pdfs));
|
throw new ArgumentNullException(nameof(Pdfs));
|
||||||
|
|
||||||
// Only One PDF - Possible Reference Bug v's Memory/Speed (Returning Param Memory Stream)
|
// Only One PDF - Possible Reference Bug v's Memory/Speed (Returning Param Memory Stream)
|
||||||
if (Pdfs.Count == 1)
|
if (Pdfs.Count == 1)
|
||||||
|
|||||||
@@ -53,7 +53,6 @@
|
|||||||
<Reference Include="System.DirectoryServices" />
|
<Reference Include="System.DirectoryServices" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.Management" />
|
<Reference Include="System.Management" />
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Net.Http.Extensions, Version=2.2.22.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
<Reference Include="System.Net.Http.Extensions, 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>
|
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Extensions.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
@@ -62,7 +61,6 @@
|
|||||||
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Primitives.dll</HintPath>
|
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Primitives.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System.Net.Http.WebRequest" />
|
|
||||||
<Reference Include="System.Runtime.Serialization" />
|
<Reference Include="System.Runtime.Serialization" />
|
||||||
<Reference Include="System.ServiceModel" />
|
<Reference Include="System.ServiceModel" />
|
||||||
<Reference Include="System.Web" />
|
<Reference Include="System.Web" />
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("2.4.24270.0000")]
|
[assembly: AssemblyVersion("2.5.25262.0000")]
|
||||||
[assembly: AssemblyFileVersion("2.4.24270.0000")]
|
[assembly: AssemblyFileVersion("2.5.25262.0000")]
|
||||||
+34
-22
@@ -1,56 +1,68 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<configSections>
|
<configSections>
|
||||||
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
|
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
|
||||||
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
|
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
|
||||||
</configSections>
|
</configSections>
|
||||||
<system.serviceModel>
|
<system.serviceModel>
|
||||||
<bindings/>
|
<bindings />
|
||||||
<client/>
|
<client />
|
||||||
</system.serviceModel>
|
</system.serviceModel>
|
||||||
<entityFramework>
|
<entityFramework>
|
||||||
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
|
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
|
||||||
<parameters>
|
<parameters>
|
||||||
<parameter value="Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=True"/>
|
<parameter value="Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=True" />
|
||||||
</parameters>
|
</parameters>
|
||||||
</defaultConnectionFactory>
|
</defaultConnectionFactory>
|
||||||
</entityFramework>
|
</entityFramework>
|
||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
|
||||||
</startup>
|
</startup>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.Razor" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="System.Web.Razor" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.WebPages.Razor" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="System.Web.WebPages.Razor" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Common.Logging" publicKeyToken="af08829b84f0328e" culture="neutral"/>
|
<assemblyIdentity name="Common.Logging" publicKeyToken="af08829b84f0328e" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.1.1.3" newVersion="4.1.1.3" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
|
|||||||
@@ -141,6 +141,7 @@
|
|||||||
<Compile Include="Extensions\EnrolExtensions.cs" />
|
<Compile Include="Extensions\EnrolExtensions.cs" />
|
||||||
<Compile Include="Extensions\WhoAmIExtensions.cs" />
|
<Compile Include="Extensions\WhoAmIExtensions.cs" />
|
||||||
<Compile Include="Interop\Certificates.cs" />
|
<Compile Include="Interop\Certificates.cs" />
|
||||||
|
<Compile Include="Interop\EndpointDiscovery.cs" />
|
||||||
<Compile Include="Interop\Hardware.cs" />
|
<Compile Include="Interop\Hardware.cs" />
|
||||||
<Compile Include="Interop\LocalAuthentication.cs" />
|
<Compile Include="Interop\LocalAuthentication.cs" />
|
||||||
<Compile Include="Interop\Native\NetworkConnectionStatuses.cs" />
|
<Compile Include="Interop\Native\NetworkConnectionStatuses.cs" />
|
||||||
|
|||||||
@@ -1,31 +1,27 @@
|
|||||||
using System;
|
using Disco.Client.Extensions;
|
||||||
using System.Collections.Generic;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
|
||||||
using Disco.Client.Extensions;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Disco.Client
|
namespace Disco.Client
|
||||||
{
|
{
|
||||||
public static class ErrorReporting
|
public static class ErrorReporting
|
||||||
{
|
{
|
||||||
private const string ServicePathTemplate = "http://DISCO:9292/Services/Client/ClientError";
|
|
||||||
public static string DeviceIdentifier { get; set; }
|
public static string DeviceIdentifier { get; set; }
|
||||||
public static string EnrolmentSessionId { get; set; }
|
public static string EnrolmentSessionId { get; set; }
|
||||||
|
|
||||||
public static void ReportError(Exception Ex, bool ReportToServer)
|
public static void ReportError(Exception exception, bool reportToServer)
|
||||||
{
|
{
|
||||||
bool isClientServiceException = Ex is ClientServiceException;
|
bool isClientServiceException = exception is ClientServiceException;
|
||||||
|
|
||||||
ErrorReport report = new ErrorReport()
|
ErrorReport report = new ErrorReport()
|
||||||
{
|
{
|
||||||
DeviceIdentifier = DeviceIdentifier,
|
DeviceIdentifier = DeviceIdentifier,
|
||||||
SessionId = EnrolmentSessionId,
|
SessionId = EnrolmentSessionId,
|
||||||
JsonException = Ex.IntenseExceptionSerialization()
|
JsonException = exception.IntenseExceptionSerialization()
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -41,7 +37,7 @@ namespace Disco.Client
|
|||||||
catch (Exception) { }
|
catch (Exception) { }
|
||||||
|
|
||||||
// Don't log server errors back to the server
|
// Don't log server errors back to the server
|
||||||
if (!isClientServiceException && ReportToServer)
|
if (!isClientServiceException && reportToServer)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -52,7 +48,7 @@ namespace Disco.Client
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Presentation.WriteFatalError(Ex);
|
Presentation.WriteFatalError(exception);
|
||||||
}
|
}
|
||||||
catch (Exception) { }
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
@@ -88,7 +84,9 @@ namespace Disco.Client
|
|||||||
string reportJson = JsonConvert.SerializeObject(report);
|
string reportJson = JsonConvert.SerializeObject(report);
|
||||||
string reportResponse;
|
string reportResponse;
|
||||||
|
|
||||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(ServicePathTemplate);
|
var serverUri = new Uri(Program.ServerUrl ?? new Uri("http://disco:9292"), "/Services/Client/ClientError");
|
||||||
|
|
||||||
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serverUri);
|
||||||
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
|
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
|
||||||
request.ContentType = "application/json";
|
request.ContentType = "application/json";
|
||||||
request.Method = WebRequestMethods.Http.Post;
|
request.Method = WebRequestMethods.Http.Post;
|
||||||
@@ -108,7 +106,7 @@ namespace Disco.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine("Error Report Logged to Server; Response: {0}", reportResponse);
|
Debug.WriteLine($"Error Report Logged to Server; Response: {reportResponse}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -1,32 +1,25 @@
|
|||||||
using Disco.Models.ClientServices;
|
using Disco.Models.ClientServices;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Disco.Client.Extensions
|
namespace Disco.Client.Extensions
|
||||||
{
|
{
|
||||||
public static class ClientServicesExtensions
|
internal static class ClientServicesExtensions
|
||||||
{
|
{
|
||||||
//#if DEBUG
|
public static ResponseType Post<ResponseType>(this ServiceBase<ResponseType> service, bool authenticated)
|
||||||
// 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;
|
ResponseType serviceResponse;
|
||||||
string serviceUrl;
|
Uri serviceUrl;
|
||||||
|
|
||||||
if (Authenticated)
|
if (authenticated)
|
||||||
serviceUrl = string.Format(ServicePathAuthenticatedTemplate, Service.Feature);
|
serviceUrl = new Uri(Program.ServerUrl, $"/Services/Client/Authenticated/{service.Feature}");
|
||||||
else
|
else
|
||||||
serviceUrl = string.Format(ServicePathUnauthenticatedTemplate, Service.Feature);
|
serviceUrl = new Uri(Program.ServerUrl, $"/Services/Client/Unauthenticated/{service.Feature}");
|
||||||
|
|
||||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceUrl);
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceUrl);
|
||||||
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
|
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
|
||||||
request.ContentType = "application/json";
|
request.ContentType = "application/json";
|
||||||
request.Method = WebRequestMethods.Http.Post;
|
request.Method = WebRequestMethods.Http.Post;
|
||||||
@@ -39,7 +32,7 @@ namespace Disco.Client.Extensions
|
|||||||
{
|
{
|
||||||
using (var jsonWriter = new JsonTextWriter(requestWriter))
|
using (var jsonWriter = new JsonTextWriter(requestWriter))
|
||||||
{
|
{
|
||||||
jsonSerializer.Serialize(jsonWriter, Service);
|
jsonSerializer.Serialize(jsonWriter, service);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,12 @@
|
|||||||
using Disco.Models.ClientServices;
|
using Disco.Models.ClientServices;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Runtime.InteropServices;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Disco.Client.Extensions
|
namespace Disco.Client.Extensions
|
||||||
{
|
{
|
||||||
public static class EnrolExtensions
|
internal static class EnrolExtensions
|
||||||
{
|
{
|
||||||
|
|
||||||
public static void Build(this Enrol enrol)
|
public static void Build(this Enrol enrol)
|
||||||
{
|
{
|
||||||
enrol.ComputerName = Environment.MachineName;
|
enrol.ComputerName = Environment.MachineName;
|
||||||
@@ -62,6 +60,28 @@ namespace Disco.Client.Extensions
|
|||||||
Program.AllowUninstall = enrolResponse.AllowBootstrapperUninstall;
|
Program.AllowUninstall = enrolResponse.AllowBootstrapperUninstall;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
private enum NETSETUP_PROVISION_FLAGS : int
|
||||||
|
{
|
||||||
|
NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT = 0x00000001,
|
||||||
|
NETSETUP_PROVISION_REUSE_ACCOUNT = 0x00000002,
|
||||||
|
NETSETUP_PROVISION_USE_DEFAULT_PASSWORD = 0x00000004,
|
||||||
|
NETSETUP_PROVISION_SKIP_ACCOUNT_SEARCH = 0x00000008,
|
||||||
|
NETSETUP_PROVISION_ROOT_CA_CERTS = 0x00000010,
|
||||||
|
NETSETUP_PROVISION_PERSISTENTSITE = 0x00000020,
|
||||||
|
NETSETUP_PROVISION_ONLINE_CALLER = 0x40000000,
|
||||||
|
NETSETUP_PROVISION_CHECK_PWD_ONLY = unchecked((int)0x80000000),
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("Netapi32.dll", CallingConvention = CallingConvention.Winapi)]
|
||||||
|
[return: MarshalAs(UnmanagedType.I4)]
|
||||||
|
private static extern int NetRequestOfflineDomainJoin(
|
||||||
|
[In] IntPtr pProvisionBinData,
|
||||||
|
[In, MarshalAs(UnmanagedType.I4)] int cbProvisionBinDataSize,
|
||||||
|
[In, MarshalAs(UnmanagedType.I4)] NETSETUP_PROVISION_FLAGS dwOptions,
|
||||||
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpWindowsPath
|
||||||
|
);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes a Client Service Enrol Response for Offline Domain Join Actions
|
/// Processes a Client Service Enrol Response for Offline Domain Join Actions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -73,38 +93,39 @@ namespace Disco.Client.Extensions
|
|||||||
{
|
{
|
||||||
Presentation.UpdateStatus("Enrolling Device", $"Performing Offline Domain Join:\r\nRenaming Computer: {Environment.MachineName} -> {enrolResponse.ComputerName}", true, -1, 1500);
|
Presentation.UpdateStatus("Enrolling Device", $"Performing Offline Domain Join:\r\nRenaming Computer: {Environment.MachineName} -> {enrolResponse.ComputerName}", true, -1, 1500);
|
||||||
|
|
||||||
string odjFile = Path.GetTempFileName();
|
var provisionData = Convert.FromBase64String(enrolResponse.OfflineDomainJoinManifest);
|
||||||
File.WriteAllBytes(odjFile, Convert.FromBase64String(enrolResponse.OfflineDomainJoinManifest));
|
string systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
|
||||||
|
|
||||||
string odjWindowsPath = Environment.GetEnvironmentVariable("SystemRoot");
|
var provisionDataPointer = Marshal.AllocCoTaskMem(provisionData.Length);
|
||||||
string odjProcessArguments = $"/REQUESTODJ /LOADFILE \"{odjFile}\" /WINDOWSPATH \"{odjWindowsPath}\" /LOCALOS";
|
Marshal.Copy(provisionData, 0, provisionDataPointer, provisionData.Length);
|
||||||
|
var joinResult = default(int);
|
||||||
ProcessStartInfo odjProcessStartInfo = new ProcessStartInfo("DJOIN.EXE", odjProcessArguments)
|
try
|
||||||
{
|
{
|
||||||
CreateNoWindow = true,
|
joinResult = NetRequestOfflineDomainJoin(provisionDataPointer, provisionData.Length, NETSETUP_PROVISION_FLAGS.NETSETUP_PROVISION_ONLINE_CALLER, systemRoot);
|
||||||
ErrorDialog = false,
|
}
|
||||||
LoadUserProfile = true,
|
finally
|
||||||
RedirectStandardOutput = true,
|
{
|
||||||
UseShellExecute = false
|
Marshal.FreeCoTaskMem(provisionDataPointer);
|
||||||
};
|
|
||||||
string odjResult;
|
|
||||||
using (Process odjProcess = System.Diagnostics.Process.Start(odjProcessStartInfo))
|
|
||||||
{
|
|
||||||
odjResult = odjProcess.StandardOutput.ReadToEnd();
|
|
||||||
odjProcess.WaitForExit(20000); // 20 Seconds
|
|
||||||
}
|
}
|
||||||
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Result:\r\n{odjResult}", true, -1, 3000);
|
|
||||||
|
|
||||||
if (File.Exists(odjFile))
|
if (joinResult != 0)
|
||||||
File.Delete(odjFile);
|
{
|
||||||
|
var win32Exception = new System.ComponentModel.Win32Exception(joinResult);
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Failed:\r\n{win32Exception.Message} [{joinResult}]", true, -1, 3000);
|
||||||
|
throw new InvalidOperationException($"Offline Domain Join Failed:\r\n{win32Exception.Message} [{joinResult}]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Succeeded", true, -1, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
// Flush Logged-On History
|
// Flush Logged-On History
|
||||||
if (!string.IsNullOrEmpty(enrolResponse.DomainName))
|
if (enrolResponse.SetAssignedUserForLogon && !string.IsNullOrEmpty(enrolResponse.DomainName))
|
||||||
{
|
{
|
||||||
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||||
{
|
{
|
||||||
regWinlogon.SetValue("DefaultDomainName", enrolResponse.DomainName, RegistryValueKind.String);
|
regWinlogon.SetValue("DefaultDomainName", enrolResponse.DomainName, RegistryValueKind.String);
|
||||||
regWinlogon.SetValue("DefaultUserName", String.Empty, RegistryValueKind.String);
|
regWinlogon.SetValue("DefaultUserName", string.Empty, RegistryValueKind.String);
|
||||||
}
|
}
|
||||||
using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true))
|
using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true))
|
||||||
{
|
{
|
||||||
@@ -133,8 +154,11 @@ namespace Disco.Client.Extensions
|
|||||||
Presentation.UpdateStatus("Enrolling Device", $"Configuring the device owner:\r\n{enrolResponse.AssignedUserDescription} ({enrolResponse.AssignedUserDomain}\\{enrolResponse.AssignedUserUsername})", true, -1, 3000);
|
Presentation.UpdateStatus("Enrolling Device", $"Configuring the device owner:\r\n{enrolResponse.AssignedUserDescription} ({enrolResponse.AssignedUserDomain}\\{enrolResponse.AssignedUserUsername})", true, -1, 3000);
|
||||||
|
|
||||||
if (enrolResponse.AssignedUserIsLocalAdmin)
|
if (enrolResponse.AssignedUserIsLocalAdmin)
|
||||||
Interop.LocalAuthentication.AddLocalGroupMembership("Administrators", enrolResponse.AssignedUserSID, enrolResponse.AssignedUserUsername, enrolResponse.AssignedUserDomain);
|
LocalAuthentication.AddLocalGroupMembership("Administrators", enrolResponse.AssignedUserSID, enrolResponse.AssignedUserUsername, enrolResponse.AssignedUserDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enrolResponse.SetAssignedUserForLogon && !string.IsNullOrEmpty(enrolResponse.AssignedUserDomain) && !string.IsNullOrEmpty(enrolResponse.AssignedUserUsername))
|
||||||
|
{
|
||||||
// Make Windows think this user was the last to logon
|
// Make Windows think this user was the last to logon
|
||||||
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
using System;
|
using Disco.Models.ClientServices;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Disco.Models.ClientServices;
|
|
||||||
|
|
||||||
namespace Disco.Client.Extensions
|
namespace Disco.Client.Extensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -105,8 +105,7 @@ namespace Disco.Client.Interop
|
|||||||
{
|
{
|
||||||
foreach (var thumbprint in RemoveThumbprints)
|
foreach (var thumbprint in RemoveThumbprints)
|
||||||
{
|
{
|
||||||
List<X509Certificate2> certificates;
|
if (existingThumbprints.TryGetValue(thumbprint, out var certificates) && !addedThumbprints.Contains(thumbprint))
|
||||||
if (existingThumbprints.TryGetValue(thumbprint, out certificates) && !addedThumbprints.Contains(thumbprint))
|
|
||||||
{
|
{
|
||||||
foreach (var certificate in certificates)
|
foreach (var certificate in certificates)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,317 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Disco.Client.Interop
|
||||||
|
{
|
||||||
|
internal class EndpointDiscovery
|
||||||
|
{
|
||||||
|
[DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
|
||||||
|
private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)] ref string pszName, NativeDnsQueryTypes wType, NativeDnsQueryOptions options, int aipServers, ref IntPtr ppQueryResults, int pReserved);
|
||||||
|
|
||||||
|
[DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);
|
||||||
|
private const int DNS_ERROR_RCODE_NAME_ERROR = 0x232B;
|
||||||
|
private const int DNS_ERROR_BAD_PACKET = 0x251E;
|
||||||
|
public static Tuple<Uri, string> DiscoverServer(Uri forcedServerUri)
|
||||||
|
{
|
||||||
|
// 1. Check first command line argument for server name
|
||||||
|
if (forcedServerUri != null)
|
||||||
|
return Tuple.Create(forcedServerUri, "Manual");
|
||||||
|
|
||||||
|
// 2. Check for a DNS SRV record for _discoict._tcp.domain
|
||||||
|
var domainSuffixes = new List<string>();
|
||||||
|
var primaryDomain = IPGlobalProperties.GetIPGlobalProperties().DomainName;
|
||||||
|
if (!string.IsNullOrEmpty(primaryDomain))
|
||||||
|
domainSuffixes.Add(primaryDomain);
|
||||||
|
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces()
|
||||||
|
.Where(ni => ni.OperationalStatus == OperationalStatus.Up);
|
||||||
|
foreach (var ni in networkInterfaces)
|
||||||
|
{
|
||||||
|
var domainSuffix = ni.GetIPProperties().DnsSuffix;
|
||||||
|
if (!string.IsNullOrWhiteSpace(domainSuffix))
|
||||||
|
{
|
||||||
|
if (domainSuffix.Equals("mshome.net", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!domainSuffixes.Contains(domainSuffix, StringComparer.OrdinalIgnoreCase))
|
||||||
|
domainSuffixes.Add(domainSuffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var domain in domainSuffixes)
|
||||||
|
{
|
||||||
|
var dnsRecords = GetSRVRecords("_discoict._tcp." + domain);
|
||||||
|
if (dnsRecords.Count > 0)
|
||||||
|
{
|
||||||
|
var firstRecord = dnsRecords.OrderBy(r => r.Priority).ThenByDescending(r => r.Weight).First();
|
||||||
|
if (firstRecord.Port == 443)
|
||||||
|
return Tuple.Create(new Uri($"https://{firstRecord.Target}"), "SRV");
|
||||||
|
else
|
||||||
|
return Tuple.Create(new Uri($"https://{firstRecord.Target}:{firstRecord.Port}"), "SRV");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Detect VicSmart network and try resolving with Disco ICT Online Services
|
||||||
|
if (TryResolveVicSmartServer(domainSuffixes, out var vicSmartServerUrl))
|
||||||
|
return Tuple.Create(vicSmartServerUrl, "VicSmart");
|
||||||
|
|
||||||
|
// 4. Legacy: Ping 'disco' and assume port 9292
|
||||||
|
using (Ping p = new Ping())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PingReply pr = p.Send("disco", 2000);
|
||||||
|
if (pr.Status == IPStatus.Success)
|
||||||
|
return Tuple.Create(new Uri("http://disco:9292"), "Legacy");
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception("Could not locate Disco ICT server on the network.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryResolveVicSmartServer(List<string> domainSuffixes, out Uri serverUrl)
|
||||||
|
{
|
||||||
|
if (IsVicSmartNetwork(domainSuffixes))
|
||||||
|
{
|
||||||
|
var potentialVicSmartAddresses = NetworkInterface.GetAllNetworkInterfaces()
|
||||||
|
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||||
|
.SelectMany(ni => ni.GetIPProperties().UnicastAddresses)
|
||||||
|
.Where(ua => ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||||
|
.Select(ua => ua.Address.GetAddressBytes())
|
||||||
|
.Where(a => a[0] == 10)
|
||||||
|
.Select(a => (ushort)((a[1] >> 4) & 0x000F) | ((a[1] << 4) & 0x00F0) | ((a[2] << 12) & 0xF000) | ((a[2] << 4) & 0x0F00))
|
||||||
|
.Distinct()
|
||||||
|
.Select(a => $"{a:x4}.vicsmart.discoict.com")
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var potentialAddress in potentialVicSmartAddresses)
|
||||||
|
{
|
||||||
|
var records = GetTxtRecords(potentialAddress);
|
||||||
|
|
||||||
|
foreach (var record in records)
|
||||||
|
{
|
||||||
|
if (!record.Content.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Uri.TryCreate(record.Content, UriKind.Absolute, out var discoveredUri))
|
||||||
|
{
|
||||||
|
serverUrl = discoveredUri;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverUrl = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsVicSmartNetwork(List<string> domainSuffixes)
|
||||||
|
{
|
||||||
|
if (domainSuffixes.Any(s => string.Equals("services.education.vic.gov.au", s, StringComparison.OrdinalIgnoreCase)) ||
|
||||||
|
domainSuffixes.Any(s => string.Equals("education.vic.gov.au", s, StringComparison.OrdinalIgnoreCase))
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
IPHostEntry doeWanDnsEntry;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
doeWanDnsEntry = Dns.GetHostEntry("broadband.doe.wan");
|
||||||
|
if (doeWanDnsEntry.AddressList.Length > 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{ }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<DnsTxtRecord> GetTxtRecords(string name)
|
||||||
|
{
|
||||||
|
IntPtr resourceRecordsPointer = IntPtr.Zero;
|
||||||
|
var records = new List<DnsTxtRecord>();
|
||||||
|
var retry = 5;
|
||||||
|
retry:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int queryResult = DnsQuery(ref name, NativeDnsQueryTypes.DNS_TYPE_TEXT, NativeDnsQueryOptions.DNS_QUERY_STANDARD, 0, ref resourceRecordsPointer, 0);
|
||||||
|
if (queryResult != 0)
|
||||||
|
{
|
||||||
|
if (queryResult == DNS_ERROR_RCODE_NAME_ERROR)
|
||||||
|
return records;
|
||||||
|
else if (queryResult == DNS_ERROR_BAD_PACKET && retry > 0)
|
||||||
|
{
|
||||||
|
// Sometimes a BAD_PACKET error is returned, retry a few times
|
||||||
|
Thread.Sleep(200);
|
||||||
|
retry--;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Win32Exception(queryResult);
|
||||||
|
}
|
||||||
|
NativeDnsTxtRecord record;
|
||||||
|
for (var resourceRecordPointer = resourceRecordsPointer; !resourceRecordPointer.Equals(IntPtr.Zero); resourceRecordPointer = record.pNext)
|
||||||
|
{
|
||||||
|
record = Marshal.PtrToStructure<NativeDnsTxtRecord>(resourceRecordPointer);
|
||||||
|
if (record.wType == (ushort)NativeDnsQueryTypes.DNS_TYPE_TEXT)
|
||||||
|
records.Add(DnsTxtRecord.FromNativeRecord(record));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (resourceRecordsPointer != IntPtr.Zero)
|
||||||
|
DnsRecordListFree(resourceRecordsPointer, 0);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<DnsSrvRecord> GetSRVRecords(string name)
|
||||||
|
{
|
||||||
|
IntPtr resourceRecordsPointer = IntPtr.Zero;
|
||||||
|
var records = new List<DnsSrvRecord>();
|
||||||
|
var retry = 5;
|
||||||
|
retry:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int queryResult = DnsQuery(ref name, NativeDnsQueryTypes.DNS_TYPE_SRV, NativeDnsQueryOptions.DNS_QUERY_STANDARD, 0, ref resourceRecordsPointer, 0);
|
||||||
|
if (queryResult != 0)
|
||||||
|
{
|
||||||
|
if (queryResult == DNS_ERROR_RCODE_NAME_ERROR)
|
||||||
|
return records;
|
||||||
|
else if (queryResult == DNS_ERROR_BAD_PACKET && retry > 0)
|
||||||
|
{
|
||||||
|
// Sometimes a BAD_PACKET error is returned, retry a few times
|
||||||
|
Thread.Sleep(200);
|
||||||
|
retry--;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Win32Exception(queryResult);
|
||||||
|
}
|
||||||
|
NativeDnsSrvRecord record;
|
||||||
|
for (var resourceRecordPointer = resourceRecordsPointer; !resourceRecordPointer.Equals(IntPtr.Zero); resourceRecordPointer = record.pNext)
|
||||||
|
{
|
||||||
|
record = Marshal.PtrToStructure<NativeDnsSrvRecord>(resourceRecordPointer);
|
||||||
|
if (record.wType == (ushort)NativeDnsQueryTypes.DNS_TYPE_SRV)
|
||||||
|
records.Add(DnsSrvRecord.FromNativeRecord(record));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (resourceRecordsPointer != IntPtr.Zero)
|
||||||
|
DnsRecordListFree(resourceRecordsPointer, 0);
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum NativeDnsQueryOptions
|
||||||
|
{
|
||||||
|
DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,
|
||||||
|
DNS_QUERY_BYPASS_CACHE = 8,
|
||||||
|
DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
|
||||||
|
DNS_QUERY_NO_HOSTS_FILE = 0x40,
|
||||||
|
DNS_QUERY_NO_LOCAL_NAME = 0x20,
|
||||||
|
DNS_QUERY_NO_NETBT = 0x80,
|
||||||
|
DNS_QUERY_NO_RECURSION = 4,
|
||||||
|
DNS_QUERY_NO_WIRE_QUERY = 0x10,
|
||||||
|
DNS_QUERY_RESERVED = -16777216,
|
||||||
|
DNS_QUERY_RETURN_MESSAGE = 0x200,
|
||||||
|
DNS_QUERY_STANDARD = 0,
|
||||||
|
DNS_QUERY_TREAT_AS_FQDN = 0x1000,
|
||||||
|
DNS_QUERY_USE_TCP_ONLY = 2,
|
||||||
|
DNS_QUERY_WIRE_ONLY = 0x100
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum NativeDnsQueryTypes
|
||||||
|
{
|
||||||
|
DNS_TYPE_TEXT = 0x0010,
|
||||||
|
DNS_TYPE_SRV = 0x0021
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct NativeDnsSrvRecord
|
||||||
|
{
|
||||||
|
public IntPtr pNext;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string pName;
|
||||||
|
public ushort wType;
|
||||||
|
public ushort wDataLength;
|
||||||
|
public int flags;
|
||||||
|
public int dwTtl;
|
||||||
|
public int dwReserved;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string pNameTarget;
|
||||||
|
public ushort wPriority;
|
||||||
|
public ushort wWeight;
|
||||||
|
public ushort wPort;
|
||||||
|
public ushort Pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DnsSrvRecord
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Type { get; set; }
|
||||||
|
public int Ttl { get; set; }
|
||||||
|
public string Target { get; set; }
|
||||||
|
public int Priority { get; set; }
|
||||||
|
public int Weight { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
public static DnsSrvRecord FromNativeRecord(NativeDnsSrvRecord nativeRecord)
|
||||||
|
{
|
||||||
|
return new DnsSrvRecord
|
||||||
|
{
|
||||||
|
Name = nativeRecord.pName,
|
||||||
|
Type = nativeRecord.wType,
|
||||||
|
Ttl = nativeRecord.dwTtl,
|
||||||
|
Target = nativeRecord.pNameTarget,
|
||||||
|
Priority = nativeRecord.wPriority,
|
||||||
|
Weight = nativeRecord.wWeight,
|
||||||
|
Port = nativeRecord.wPort
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct NativeDnsTxtRecord
|
||||||
|
{
|
||||||
|
public IntPtr pNext;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string pName;
|
||||||
|
public ushort wType;
|
||||||
|
public ushort wDataLength;
|
||||||
|
public int flags;
|
||||||
|
public int dwTtl;
|
||||||
|
public int dwReserved;
|
||||||
|
public uint dwStringLength;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string pStringArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DnsTxtRecord
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Type { get; set; }
|
||||||
|
public int Ttl { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
|
||||||
|
public static DnsTxtRecord FromNativeRecord(NativeDnsTxtRecord nativeRecord)
|
||||||
|
{
|
||||||
|
return new DnsTxtRecord
|
||||||
|
{
|
||||||
|
Name = nativeRecord.pName,
|
||||||
|
Type = nativeRecord.wType,
|
||||||
|
Ttl = nativeRecord.dwTtl,
|
||||||
|
Content = nativeRecord.pStringArray,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,7 @@ namespace Disco.Client.Interop
|
|||||||
audit.ApplyPhysicalMemoryInformation();
|
audit.ApplyPhysicalMemoryInformation();
|
||||||
audit.ApplyDiskDriveInformation();
|
audit.ApplyDiskDriveInformation();
|
||||||
audit.ApplyBatteryInformation();
|
audit.ApplyBatteryInformation();
|
||||||
|
audit.ApplyMobileDeviceManagementInformation();
|
||||||
|
|
||||||
audit.NetworkAdapters = Network.GetNetworkAdapters();
|
audit.NetworkAdapters = Network.GetNetworkAdapters();
|
||||||
|
|
||||||
@@ -538,8 +539,7 @@ namespace Disco.Client.Interop
|
|||||||
// if serial number is absent attempt using UUID if valid
|
// if serial number is absent attempt using UUID if valid
|
||||||
if (string.IsNullOrWhiteSpace(deviceHardware.SerialNumber))
|
if (string.IsNullOrWhiteSpace(deviceHardware.SerialNumber))
|
||||||
{
|
{
|
||||||
Guid uuidGuid;
|
if (Guid.TryParse(deviceHardware.UUID, out var uuidGuid) && uuidGuid != Guid.Empty)
|
||||||
if (Guid.TryParse(deviceHardware.UUID, out uuidGuid) && uuidGuid != Guid.Empty)
|
|
||||||
{
|
{
|
||||||
deviceHardware.SerialNumber = $"UUID{uuidGuid:N}";
|
deviceHardware.SerialNumber = $"UUID{uuidGuid:N}";
|
||||||
}
|
}
|
||||||
@@ -560,6 +560,18 @@ namespace Disco.Client.Interop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
private static string Description(this PCSystemTypes type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
namespace Disco.Client.Interop.Native
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.Client.Interop.Native
|
|
||||||
{
|
{
|
||||||
public enum NetworkConnectionStatuses : ushort
|
public enum NetworkConnectionStatuses : ushort
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Disco.Client.Interop.Native
|
|||||||
/// The number of wireless profile entries in the ProfileInfo member.
|
/// The number of wireless profile entries in the ProfileInfo member.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint dwNumberOfItems;
|
public uint dwNumberOfItems;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The index of the current item. The index of the first item is 0. The dwIndex member must be less than the dwNumberOfItems member.
|
/// The index of the current item. The index of the first item is 0. The dwIndex member must be less than the dwNumberOfItems member.
|
||||||
/// This member is not used by the wireless service. Applications can use this member when processing individual profiles in the
|
/// This member is not used by the wireless service. Applications can use this member when processing individual profiles in the
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace Disco.Client.Interop
|
|||||||
|
|
||||||
return adapters;
|
return adapters;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<NetworkAdapter> GetWmiNetworkAdapters()
|
private static List<NetworkAdapter> GetWmiNetworkAdapters()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -17,16 +17,13 @@ namespace Disco.Client.Interop
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IntPtr wlanHandle;
|
|
||||||
uint wlanServiceVersion;
|
|
||||||
|
|
||||||
if (WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out wlanServiceVersion, out wlanHandle) == WlanApi.ERROR_SUCCESS)
|
if (WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out var wlanServiceVersion, out var wlanHandle) == WlanApi.ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IntPtr wlanInterfacesPtr;
|
|
||||||
|
|
||||||
if (WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out wlanInterfacesPtr) == WlanApi.ERROR_SUCCESS)
|
if (WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out var wlanInterfacesPtr) == WlanApi.ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -69,12 +66,10 @@ namespace Disco.Client.Interop
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IntPtr wlanHandle;
|
|
||||||
uint wlanServiceVersion;
|
|
||||||
uint interopResult;
|
uint interopResult;
|
||||||
|
|
||||||
// Connect to wireless service
|
// Connect to wireless service
|
||||||
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out wlanServiceVersion, out wlanHandle);
|
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out var wlanServiceVersion, out var wlanHandle);
|
||||||
if (interopResult == WlanApi.ERROR_SERVICE_NOT_ACTIVE)
|
if (interopResult == WlanApi.ERROR_SERVICE_NOT_ACTIVE)
|
||||||
{
|
{
|
||||||
// Indicates the Wlan service has not been started on the client
|
// Indicates the Wlan service has not been started on the client
|
||||||
@@ -109,10 +104,9 @@ namespace Disco.Client.Interop
|
|||||||
private static List<WirelessProfile> GetWirelessProfiles(IntPtr wlanHandle)
|
private static List<WirelessProfile> GetWirelessProfiles(IntPtr wlanHandle)
|
||||||
{
|
{
|
||||||
uint interopResult;
|
uint interopResult;
|
||||||
IntPtr wlanInterfacesPtr;
|
|
||||||
|
|
||||||
// Enumerate wireless interfaces
|
// Enumerate wireless interfaces
|
||||||
interopResult = WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out wlanInterfacesPtr);
|
interopResult = WlanApi.WlanEnumInterfaces(wlanHandle, IntPtr.Zero, out var wlanInterfacesPtr);
|
||||||
if (interopResult != WlanApi.ERROR_SUCCESS)
|
if (interopResult != WlanApi.ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
throw new Exception($"Unable to list interfaces with the local wireless service. WlanEnumInterfaces returned: {interopResult}");
|
throw new Exception($"Unable to list interfaces with the local wireless service. WlanEnumInterfaces returned: {interopResult}");
|
||||||
@@ -124,9 +118,8 @@ namespace Disco.Client.Interop
|
|||||||
|
|
||||||
foreach (var wlanInterface in wlanInterfaces.InterfaceInfo)
|
foreach (var wlanInterface in wlanInterfaces.InterfaceInfo)
|
||||||
{
|
{
|
||||||
IntPtr wlanProfilesPtr;
|
|
||||||
// Enumerate wireless profiles for interface
|
// Enumerate wireless profiles for interface
|
||||||
interopResult = WlanApi.WlanGetProfileList(wlanHandle, wlanInterface.InterfaceGuid, IntPtr.Zero, out wlanProfilesPtr);
|
interopResult = WlanApi.WlanGetProfileList(wlanHandle, wlanInterface.InterfaceGuid, IntPtr.Zero, out var wlanProfilesPtr);
|
||||||
if (interopResult != WlanApi.ERROR_SUCCESS)
|
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}");
|
throw new Exception($"Unable to list wireless profiles for the {wlanInterface.InterfaceGuid} interface with the local wireless service. WlanGetProfileList returned: {interopResult}");
|
||||||
@@ -165,12 +158,10 @@ namespace Disco.Client.Interop
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IntPtr wlanHandle;
|
|
||||||
uint wlanServiceVersion;
|
|
||||||
uint interopResult;
|
uint interopResult;
|
||||||
|
|
||||||
// Connect to wireless service
|
// Connect to wireless service
|
||||||
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out wlanServiceVersion, out wlanHandle);
|
interopResult = WlanApi.WlanOpenHandle(WlanApi.WLAN_API_VERSION_2_0, IntPtr.Zero, out var wlanServiceVersion, out var wlanHandle);
|
||||||
if (interopResult == WlanApi.ERROR_SERVICE_NOT_ACTIVE)
|
if (interopResult == WlanApi.ERROR_SERVICE_NOT_ACTIVE)
|
||||||
{
|
{
|
||||||
// Indicates the Wlan service has not been started on the client
|
// Indicates the Wlan service has not been started on the client
|
||||||
@@ -243,9 +234,8 @@ namespace Disco.Client.Interop
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint pdwReasonCode;
|
|
||||||
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nAdding Wireless Profile '{addProfile.Name}' on '{adapter.NetConnectionID}'", true, -1, 1000);
|
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 pdwReasonCode);
|
interopResult = WlanApi.WlanSetProfile(wlanHandle, adapter.ConnectionIdentifier, 0, addProfile.ProfileXml, null, true, IntPtr.Zero, out var pdwReasonCode);
|
||||||
|
|
||||||
if (interopResult != WlanApi.ERROR_SUCCESS)
|
if (interopResult != WlanApi.ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
@@ -285,11 +275,8 @@ namespace Disco.Client.Interop
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Load profile
|
// Load profile
|
||||||
IntPtr pstrProfileXml;
|
|
||||||
uint pdwFlags;
|
|
||||||
IntPtr pdwGrantAccess;
|
|
||||||
|
|
||||||
interopResult = WlanApi.WlanGetProfile(wlanHandle, adapter.ConnectionIdentifier, profileName, IntPtr.Zero, out pstrProfileXml, out pdwFlags, out pdwGrantAccess);
|
interopResult = WlanApi.WlanGetProfile(wlanHandle, adapter.ConnectionIdentifier, profileName, IntPtr.Zero, out var pstrProfileXml, out var pdwFlags, out var pdwGrantAccess);
|
||||||
|
|
||||||
if (interopResult == WlanApi.ERROR_SUCCESS)
|
if (interopResult == WlanApi.ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
@@ -312,10 +299,9 @@ namespace Disco.Client.Interop
|
|||||||
if (!XNode.DeepEquals(originalProfileXml, transformedProfileXml))
|
if (!XNode.DeepEquals(originalProfileXml, transformedProfileXml))
|
||||||
{
|
{
|
||||||
// Set Profile
|
// Set Profile
|
||||||
uint pdwReasonCode;
|
|
||||||
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nModifying Wireless Profile '{profileName}' on '{adapter.NetConnectionID}'", true, -1, 1000);
|
Presentation.UpdateStatus("Enrolling Device", $"Configuring Wireless Profiles\r\nModifying Wireless Profile '{profileName}' on '{adapter.NetConnectionID}'", true, -1, 1000);
|
||||||
transformProfileXml = transformedProfileXml.ToString(SaveOptions.None);
|
transformProfileXml = transformedProfileXml.ToString(SaveOptions.None);
|
||||||
interopResult = WlanApi.WlanSetProfile(wlanHandle, adapter.ConnectionIdentifier, 0, transformProfileXml, null, true, IntPtr.Zero, out pdwReasonCode);
|
interopResult = WlanApi.WlanSetProfile(wlanHandle, adapter.ConnectionIdentifier, 0, transformProfileXml, null, true, IntPtr.Zero, out var pdwReasonCode);
|
||||||
|
|
||||||
if (interopResult != WlanApi.ERROR_SUCCESS)
|
if (interopResult != WlanApi.ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using System;
|
using Disco.Client.Extensions;
|
||||||
using System.Collections.Generic;
|
using Disco.Client.Interop;
|
||||||
using System.Linq;
|
using System;
|
||||||
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Disco.Client.Extensions;
|
|
||||||
using Disco.Client.Interop;
|
|
||||||
|
|
||||||
namespace Disco.Client
|
namespace Disco.Client
|
||||||
{
|
{
|
||||||
@@ -22,13 +21,13 @@ namespace Disco.Client
|
|||||||
}
|
}
|
||||||
public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress, int TryDelay)
|
public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress, int TryDelay)
|
||||||
{
|
{
|
||||||
Presentation.UpdateStatus(SubHeading, Message, ShowProgress, Progress);
|
UpdateStatus(SubHeading, Message, ShowProgress, Progress);
|
||||||
if (TryDelay > 0)
|
if (TryDelay > 0)
|
||||||
Presentation.TryDelay(TryDelay);
|
Presentation.TryDelay(TryDelay);
|
||||||
}
|
}
|
||||||
public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress)
|
public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress)
|
||||||
{
|
{
|
||||||
Console.WriteLine("#{0},{1},{2},{3}", SubHeading.EscapeMessage(), Message.EscapeMessage(), ShowProgress.ToString(), Progress.ToString());
|
Console.WriteLine($"#{SubHeading.EscapeMessage()},{Message.EscapeMessage()},{ShowProgress},{Progress}");
|
||||||
}
|
}
|
||||||
public static void TryDelay(int Milliseconds)
|
public static void TryDelay(int Milliseconds)
|
||||||
{
|
{
|
||||||
@@ -40,6 +39,11 @@ namespace Disco.Client
|
|||||||
{
|
{
|
||||||
StringBuilder message = new StringBuilder();
|
StringBuilder message = new StringBuilder();
|
||||||
message.AppendLine($"Version: {Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}");
|
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})");
|
message.AppendLine($"Device: {Hardware.Information.SerialNumber} ({Hardware.Information.Manufacturer} {Hardware.Information.Model})");
|
||||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
UpdateStatus("Preparation Client Started", message.ToString(), false, 0);
|
UpdateStatus("Preparation Client Started", message.ToString(), false, 0);
|
||||||
@@ -50,12 +54,18 @@ namespace Disco.Client
|
|||||||
{
|
{
|
||||||
|
|
||||||
Console.ForegroundColor = ConsoleColor.Magenta;
|
Console.ForegroundColor = ConsoleColor.Magenta;
|
||||||
ClientServiceException clientServiceException = ex as ClientServiceException;
|
if (ex is ClientServiceException clientServiceException)
|
||||||
if (clientServiceException != null)
|
|
||||||
{
|
{
|
||||||
UpdateStatus($"An error occurred during {clientServiceException.ServiceFeature}",
|
UpdateStatus($"An error occurred during {clientServiceException.ServiceFeature}",
|
||||||
clientServiceException.Message, false, 0);
|
clientServiceException.Message, false, 0);
|
||||||
}
|
}
|
||||||
|
else if (ex is WebException exWeb &&
|
||||||
|
exWeb.Response is HttpWebResponse webResponse &&
|
||||||
|
webResponse.StatusCode == HttpStatusCode.InternalServerError)
|
||||||
|
{
|
||||||
|
UpdateStatus("Something went wrong on the server",
|
||||||
|
"Review logs for more information (Configuration > Logging)", false, 0);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder message = new StringBuilder();
|
StringBuilder message = new StringBuilder();
|
||||||
@@ -92,7 +102,7 @@ namespace Disco.Client
|
|||||||
|
|
||||||
public static void RegisterBootstrapperPostActions(ShutdownActions ShutdownAction, bool Uninstall)
|
public static void RegisterBootstrapperPostActions(ShutdownActions ShutdownAction, bool Uninstall)
|
||||||
{
|
{
|
||||||
Console.WriteLine("!{0},{1}", Enum.GetName(typeof(ShutdownActions), ShutdownAction), Uninstall ? "UninstallBootstrapper" : "DontUninstallBootstrapper");
|
Console.WriteLine($"!{Enum.GetName(typeof(ShutdownActions), ShutdownAction)},{(Uninstall ? "UninstallBootstrapper" : "DontUninstallBootstrapper")}");
|
||||||
}
|
}
|
||||||
public enum ShutdownActions
|
public enum ShutdownActions
|
||||||
{
|
{
|
||||||
|
|||||||
+69
-19
@@ -1,11 +1,10 @@
|
|||||||
using System;
|
using Disco.Client.Extensions;
|
||||||
using System.Collections.Generic;
|
using Disco.Client.Interop;
|
||||||
|
using Disco.Models.ClientServices;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Disco.Models.ClientServices;
|
|
||||||
using Disco.Client.Extensions;
|
|
||||||
|
|
||||||
namespace Disco.Client
|
namespace Disco.Client
|
||||||
{
|
{
|
||||||
@@ -14,6 +13,9 @@ namespace Disco.Client
|
|||||||
public static bool IsAuthenticated { get; set; }
|
public static bool IsAuthenticated { get; set; }
|
||||||
public static bool RebootRequired { get; set; }
|
public static bool RebootRequired { get; set; }
|
||||||
public static bool AllowUninstall { get; set; }
|
public static bool AllowUninstall { get; set; }
|
||||||
|
public static int BootstrapperVersion { get; private set; } = 1;
|
||||||
|
public static int BootstrapperProcessId { get; private set; } = -1;
|
||||||
|
public static Uri ServerUrl { get; private set; }
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
@@ -27,28 +29,31 @@ namespace Disco.Client
|
|||||||
{
|
{
|
||||||
Console.WriteLine("Waiting for Debugger to Attach");
|
Console.WriteLine("Waiting for Debugger to Attach");
|
||||||
System.Threading.Thread.Sleep(1000);
|
System.Threading.Thread.Sleep(1000);
|
||||||
} while (!System.Diagnostics.Debugger.IsAttached);
|
} while (!Debugger.IsAttached);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Initialize Environment Settings
|
// Initialize Environment Settings
|
||||||
SetupEnvironment();
|
SetupEnvironment(args);
|
||||||
|
|
||||||
|
if (ServerUrl == null)
|
||||||
|
keepProcessing = DiscoverDiscoIct();
|
||||||
|
|
||||||
// Report to Bootstrapper
|
// Report to Bootstrapper
|
||||||
Presentation.WriteBanner();
|
Presentation.WriteBanner();
|
||||||
|
|
||||||
// WhoAmI Phase
|
// WhoAmI Phase
|
||||||
keepProcessing = Program.WhoAmI();
|
keepProcessing = WhoAmI();
|
||||||
|
|
||||||
// Enrol Phase
|
// Enrol Phase
|
||||||
if (keepProcessing)
|
if (keepProcessing)
|
||||||
keepProcessing = Program.Enrol();
|
keepProcessing = Enrol();
|
||||||
|
|
||||||
// End conversation with Bootstrapper
|
// End conversation with Bootstrapper
|
||||||
Presentation.WriteFooter(Program.RebootRequired, Program.AllowUninstall, !keepProcessing);
|
Presentation.WriteFooter(RebootRequired, AllowUninstall, !keepProcessing);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetupEnvironment()
|
public static void SetupEnvironment(string[] args)
|
||||||
{
|
{
|
||||||
// Hookup Unhandled Error Handling
|
// Hookup Unhandled Error Handling
|
||||||
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.CurrentDomain_UnhandledException;
|
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.CurrentDomain_UnhandledException;
|
||||||
@@ -57,21 +62,66 @@ namespace Disco.Client
|
|||||||
WebRequest.DefaultWebProxy = new WebProxy();
|
WebRequest.DefaultWebProxy = new WebProxy();
|
||||||
// Override Http 100 Continue Behaviour
|
// Override Http 100 Continue Behaviour
|
||||||
ServicePointManager.Expect100Continue = false;
|
ServicePointManager.Expect100Continue = false;
|
||||||
|
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||||
|
|
||||||
// Assume success unless otherwise notified
|
// Assume success unless otherwise notified
|
||||||
AllowUninstall = true;
|
AllowUninstall = true;
|
||||||
|
|
||||||
|
if (args != null && args.Length == 3)
|
||||||
|
{
|
||||||
|
// Parse Bootstrapper Version
|
||||||
|
int parsedVersion;
|
||||||
|
if (int.TryParse(args[0], out parsedVersion))
|
||||||
|
BootstrapperVersion = parsedVersion;
|
||||||
|
// Parse Bootstrapper Process ID
|
||||||
|
int parsedProcessId;
|
||||||
|
if (int.TryParse(args[1], out parsedProcessId))
|
||||||
|
BootstrapperProcessId = parsedProcessId;
|
||||||
|
// Parse Server URL
|
||||||
|
Uri parsedUri;
|
||||||
|
if (Uri.TryCreate(args[2], UriKind.Absolute, out parsedUri))
|
||||||
|
ServerUrl = parsedUri;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BootstrapperVersion = 1;
|
||||||
|
BootstrapperProcessId = -1;
|
||||||
|
ServerUrl = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Detect Disco.Bootstrapper - Create Enable UI Delay if Running
|
// Detect Disco.Bootstrapper - Create Enable UI Delay if Running
|
||||||
|
Presentation.DelayUI = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Presentation.DelayUI = (System.Diagnostics.Process.GetProcessesByName("Disco.ClientBootstrapper").Length > 0);
|
if (BootstrapperProcessId != -1)
|
||||||
|
{
|
||||||
|
var parentProcess = Process.GetProcessById(BootstrapperProcessId);
|
||||||
|
Presentation.DelayUI = !parentProcess.HasExited;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
Presentation.DelayUI = true; // Add Delays on Error
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
public static bool WhoAmI()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -122,7 +172,7 @@ namespace Disco.Client
|
|||||||
EnrolResponse response = null;
|
EnrolResponse response = null;
|
||||||
|
|
||||||
// Build Request
|
// Build Request
|
||||||
Presentation.UpdateStatus("Enrolling Device", "Building enrollment request and preparing to send data to the server.", true, -1);
|
Presentation.UpdateStatus("Enrolling Device", "Building enrolment request and preparing to send data to the server.", true, -1);
|
||||||
request = new Enrol();
|
request = new Enrol();
|
||||||
request.Build();
|
request.Build();
|
||||||
|
|
||||||
@@ -130,11 +180,11 @@ namespace Disco.Client
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Send Request
|
// Send Request
|
||||||
Presentation.UpdateStatus("Enrolling Device", "Sending the enrollment request to the server.", true, -1);
|
Presentation.UpdateStatus("Enrolling Device", "Sending the enrolment request to the server.", true, -1);
|
||||||
response = request.Post(Program.IsAuthenticated);
|
response = request.Post(IsAuthenticated);
|
||||||
|
|
||||||
// Process Response
|
// Process Response
|
||||||
Presentation.UpdateStatus("Enrolling Device", "Processing the enrollment response from the server.", true, -1);
|
Presentation.UpdateStatus("Enrolling Device", "Processing the enrolment response from the server.", true, -1);
|
||||||
response.Process();
|
response.Process();
|
||||||
|
|
||||||
if (response.IsPending)
|
if (response.IsPending)
|
||||||
@@ -147,7 +197,7 @@ namespace Disco.Client
|
|||||||
var secondsConsumed = (DateTimeOffset.Now - startTime).TotalSeconds;
|
var secondsConsumed = (DateTimeOffset.Now - startTime).TotalSeconds;
|
||||||
var progress = (int)((secondsConsumed / totalSeconds) * 100);
|
var progress = (int)((secondsConsumed / totalSeconds) * 100);
|
||||||
|
|
||||||
Presentation.UpdateStatus($"Pending Device Enrollment Approval: {response.PendingIdentifier}", $"Waiting for enrollment session '{response.PendingIdentifier}' to be approved.{Environment.NewLine}Reason: {response.PendingReason}", true, progress);
|
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));
|
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(10));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("2.4.24270.0000")]
|
[assembly: AssemblyVersion("2.5.25262.0000")]
|
||||||
[assembly: AssemblyFileVersion("2.4.24270.0000")]
|
[assembly: AssemblyFileVersion("2.5.25262.0000")]
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
@ECHO OFF
|
@ECHO OFF
|
||||||
IF /I "%USERDOMAIN%"=="NT AUTHORITY" GOTO RunAsNetworkService
|
IF /I "%USERDOMAIN%"=="NT AUTHORITY" GOTO RunAsNetworkService
|
||||||
Disco.Client.exe
|
Disco.Client.exe %1 %2 %3
|
||||||
EXIT /B 0
|
EXIT /B 0
|
||||||
|
|
||||||
:RunAsNetworkService
|
:RunAsNetworkService
|
||||||
ECHO #Running,Launching Preparation Client, Please wait...{newline}Starting client as 'NT AUTHORITY\Network Service',true,-1
|
ECHO #Running,Launching Preparation Client, Please wait...{newline}Starting client as 'NT AUTHORITY\Network Service',true,-1
|
||||||
PsExec -acceptula -i -u "NT AUTHORITY\Network Service" -w "%CD%" "%CD%\Start.bat"
|
PsExec -acceptula -i -u "NT AUTHORITY\Network Service" -w "%CD%" "%CD%\Start.bat %1 %2 %3"
|
||||||
EXIT /B 0
|
EXIT /B 0
|
||||||
@@ -1,51 +1,198 @@
|
|||||||
using System;
|
using Disco.Client.Interop;
|
||||||
|
using Disco.ClientBootstrapper.Interop;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Net;
|
using System.Threading.Tasks;
|
||||||
using System.IO;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper
|
namespace Disco.ClientBootstrapper
|
||||||
{
|
{
|
||||||
class BootstrapperLoop
|
internal class BootstrapperLoop
|
||||||
{
|
{
|
||||||
|
private readonly Func<CancellationToken, Task> completeCallback;
|
||||||
public Thread LoopThread;
|
private readonly CancellationToken cancellationToken;
|
||||||
public delegate void LoopCompleteCallback();
|
private readonly IStatus statusUI;
|
||||||
private LoopCompleteCallback mLoopCompleteCallback;
|
private readonly Uri forcedServerUrl;
|
||||||
private IStatus statusUI;
|
|
||||||
private string tempWorkingDirectory;
|
private string tempWorkingDirectory;
|
||||||
private StringBuilder errorMessage;
|
|
||||||
private Process clientProcess;
|
private Process clientProcess;
|
||||||
|
|
||||||
//#if DEBUG
|
public BootstrapperLoop(IStatus statusUI, Uri forcedServerUrl, Func<CancellationToken, Task> callback, CancellationToken cancellationToken)
|
||||||
// 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.statusUI = statusUI;
|
||||||
this.mLoopCompleteCallback = Callback;
|
this.forcedServerUrl = forcedServerUrl;
|
||||||
this.errorMessage = new StringBuilder();
|
completeCallback = callback;
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
this.LoopThread = new Thread(new ThreadStart(loopHost));
|
Task.Factory.StartNew(async () =>
|
||||||
this.LoopThread.Start();
|
{
|
||||||
|
await Loop(forcedServerUrl, cancellationToken);
|
||||||
|
}, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loopHost()
|
private async Task Loop(Uri forcedServerUrl, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
loop();
|
statusUI.UpdateStatus("System Preparation (Bootstrapper)", "Starting", "Please wait...", true, -1);
|
||||||
|
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
statusUI.UpdateStatus(null, "Detecting Network", "No network connectivity detected, Diagnosing...", true, -1);
|
||||||
|
statusUI_WriteAdapterInfo();
|
||||||
|
|
||||||
|
if (!NetworkInterop.HasNetworkConnectivity())
|
||||||
|
{
|
||||||
|
// Check for Wireless
|
||||||
|
var hasWireless = (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);
|
||||||
|
statusUI.UpdateStatus(null, "Waiting for Wireless Network", null, true, 0);
|
||||||
|
for (int i = 0; i < 30; i++)
|
||||||
|
{
|
||||||
|
statusUI_WriteAdapterInfo();
|
||||||
|
statusUI.UpdateStatus(null, null, null, true, i);
|
||||||
|
await Program.SleepThread(2000, false, cancellationToken);
|
||||||
|
if (NetworkInterop.HasNetworkConnectivity())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!NetworkInterop.HasNetworkConnectivity())
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NetworkInterop.HasNetworkConnectivity())
|
||||||
|
{
|
||||||
|
// Instruct user to connect network cable
|
||||||
|
statusUI.UpdateStatus(null, "Please connect the network cable", null);
|
||||||
|
for (int i = 0; i < 30; i++)
|
||||||
|
{
|
||||||
|
statusUI_WriteAdapterInfo();
|
||||||
|
statusUI.UpdateStatus(null, null, null, true, i);
|
||||||
|
await Program.SleepThread(2000, false, cancellationToken);
|
||||||
|
if (NetworkInterop.HasNetworkConnectivity())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NetworkInterop.HasNetworkConnectivity())
|
||||||
|
{
|
||||||
|
// Client Failed
|
||||||
|
if (completeCallback != null)
|
||||||
|
await completeCallback(cancellationToken);
|
||||||
|
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");
|
||||||
|
using (var webClient = new WebClient())
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip Client
|
||||||
|
statusUI.UpdateStatus(null, "Extracting", "Retrieving Preparation Client, Please wait...", true, -1);
|
||||||
|
string clientLocation = Path.Combine(tempWorkingDirectory, "PreparationClient");
|
||||||
|
if (Directory.Exists(clientLocation))
|
||||||
|
Directory.Delete(clientLocation, true);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(clientLocation);
|
||||||
|
using (var clientSource = Ionic.Zip.ZipFile.Read(clientSourceLocation))
|
||||||
|
{
|
||||||
|
clientSource.ExtractAll(clientLocation, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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}")
|
||||||
|
{
|
||||||
|
WorkingDirectory = clientLocation,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
};
|
||||||
|
using (clientProcess = Process.Start(clientProcessStart))
|
||||||
|
{
|
||||||
|
// Read StdOutput until End
|
||||||
|
try
|
||||||
|
{
|
||||||
|
clientProcess.OutputDataReceived += new DataReceivedEventHandler(clientProcess_OutputDataReceived);
|
||||||
|
clientProcess.BeginOutputReadLine();
|
||||||
|
clientProcess.WaitForExit();
|
||||||
|
}
|
||||||
|
catch (Exception) { throw; }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try { clientProcess.CloseMainWindow(); }
|
||||||
|
catch (Exception) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clientProcess = null;
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
if (Directory.Exists(tempWorkingDirectory))
|
||||||
|
Directory.Delete(tempWorkingDirectory, true);
|
||||||
|
CertificateInterop.RemoveTempCerts();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -53,186 +200,38 @@ namespace Disco.ClientBootstrapper
|
|||||||
return;
|
return;
|
||||||
if (ex.GetType() == typeof(ThreadInterruptedException))
|
if (ex.GetType() == typeof(ThreadInterruptedException))
|
||||||
return;
|
return;
|
||||||
Program.WriteAppError(ex);
|
if (ex.GetType() == typeof(OperationCanceledException))
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loop()
|
|
||||||
{
|
|
||||||
|
|
||||||
#if Debug
|
|
||||||
statusUI.UpdateStatus("Waiting for Debugger", "Please wait...", true, -1);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
System.Threading.Thread.Sleep(10);
|
|
||||||
} while (!System.Diagnostics.Debugger.IsAttached);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
statusUI.UpdateStatus("Error", ex.Message, true, -1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
statusUI.UpdateStatus("System Preparation (Bootstrapper)", "Starting", "Please wait...", true, -1);
|
|
||||||
#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 (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
|
||||||
{
|
|
||||||
statusUI.UpdateStatus(null, "Detecting Network", "No network connectivity detected, Diagnosing...", true, -1);
|
|
||||||
statusUI_WriteAdapterInfo();
|
|
||||||
|
|
||||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
|
||||||
{
|
|
||||||
// Check for Wireless
|
|
||||||
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);
|
|
||||||
Interop.NetworkInterop.ConfigureWireless();
|
|
||||||
statusUI.UpdateStatus(null, "Waiting for Wireless Network", null, true, 0);
|
|
||||||
for (int i = 0; i < 100; i++)
|
|
||||||
{
|
|
||||||
statusUI_WriteAdapterInfo();
|
|
||||||
statusUI.UpdateStatus(null, null, null, true, i);
|
|
||||||
Program.SleepThread(500, false);
|
|
||||||
if (Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
|
||||||
{
|
|
||||||
statusUI.UpdateStatus(null, "Wireless Network Failed", "Unable to connect to the wireless network, please connect the network cable...", false);
|
|
||||||
Program.SleepThread(3000, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
|
||||||
{
|
|
||||||
// Instruct user to connect network cable
|
|
||||||
statusUI.UpdateStatus(null, "Please connect the network cable", null);
|
|
||||||
for (int i = 0; i < 100; i++)
|
|
||||||
{
|
|
||||||
statusUI_WriteAdapterInfo();
|
|
||||||
statusUI.UpdateStatus(null, null, null, true, i);
|
|
||||||
Program.SleepThread(500, false);
|
|
||||||
if (Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
|
||||||
{
|
|
||||||
// Client Failed
|
|
||||||
if (this.mLoopCompleteCallback != null)
|
|
||||||
{
|
|
||||||
this.mLoopCompleteCallback.BeginInvoke(null, null);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
Program.WriteAppError(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download Client
|
|
||||||
statusUI.UpdateStatus(null, "Downloading", "Retrieving Preparation Client, Please wait...", true, -1);
|
|
||||||
string clientSourceLocation = Path.Combine(tempWorkingDirectory, "PreparationClient.zip");
|
|
||||||
using (var webClient = new WebClient())
|
|
||||||
{
|
|
||||||
// Don't use a proxy when downloading the Client
|
|
||||||
webClient.Proxy = new WebProxy();
|
|
||||||
|
|
||||||
webClient.DownloadFile($"http://{DiscoServerName}:{DiscoServerPort}/Services/Client/PreparationClient", clientSourceLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unzip Client
|
|
||||||
statusUI.UpdateStatus(null, "Extracting", "Retrieving Preparation Client, Please wait...", true, -1);
|
|
||||||
string clientLocation = Path.Combine(tempWorkingDirectory, "PreparationClient");
|
|
||||||
if (Directory.Exists(clientLocation))
|
|
||||||
Directory.Delete(clientLocation, true);
|
|
||||||
|
|
||||||
Directory.CreateDirectory(clientLocation);
|
|
||||||
using (var clientSource = Ionic.Zip.ZipFile.Read(clientSourceLocation))
|
|
||||||
{
|
|
||||||
clientSource.ExtractAll(clientLocation, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Launch Client
|
|
||||||
statusUI.UpdateStatus("System Preparation (Client)", "Running", "Launching Preparation Client, Please wait...", true, -1);
|
|
||||||
ProcessStartInfo clientProcessStart = new ProcessStartInfo(Path.Combine(clientLocation, "Start.bat"))
|
|
||||||
{
|
|
||||||
WorkingDirectory = clientLocation,
|
|
||||||
CreateNoWindow = true,
|
|
||||||
RedirectStandardOutput = true,
|
|
||||||
UseShellExecute = false,
|
|
||||||
};
|
|
||||||
using (clientProcess = Process.Start(clientProcessStart))
|
|
||||||
{
|
|
||||||
// Read StdOutput until End
|
|
||||||
try
|
|
||||||
{
|
|
||||||
clientProcess.OutputDataReceived += new DataReceivedEventHandler(clientProcess_OutputDataReceived);
|
|
||||||
clientProcess.BeginOutputReadLine();
|
|
||||||
clientProcess.WaitForExit();
|
|
||||||
}
|
|
||||||
catch (Exception) { throw; }
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
try { clientProcess.CloseMainWindow(); }
|
|
||||||
catch (Exception) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clientProcess = null;
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
if (Directory.Exists(tempWorkingDirectory))
|
|
||||||
Directory.Delete(tempWorkingDirectory, true);
|
|
||||||
Interop.CertificateInterop.RemoveTempCerts();
|
|
||||||
|
|
||||||
// Pause if Error
|
|
||||||
if (this.errorMessage.Length > 0)
|
|
||||||
{
|
|
||||||
Program.SleepThread(10000, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// End Of Loop
|
// End Of Loop
|
||||||
if (this.mLoopCompleteCallback != null)
|
if (completeCallback != null)
|
||||||
{
|
await completeCallback(cancellationToken);
|
||||||
this.mLoopCompleteCallback.BeginInvoke(null, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void statusUI_WriteAdapterInfo()
|
private void statusUI_WriteAdapterInfo()
|
||||||
{
|
{
|
||||||
|
|
||||||
var info = new StringBuilder();
|
var info = new StringBuilder();
|
||||||
foreach (var na in Interop.NetworkInterop.NetworkAdapters)
|
foreach (var na in NetworkInterop.NetworkAdapters)
|
||||||
{
|
{
|
||||||
if (na.IsWireless)
|
if (na.IsWireless)
|
||||||
{
|
{
|
||||||
info.AppendLine(string.Format("{0}: {1}", na.NetConnectionID, na.WirelessConnectionStatusMeaning(na.WirelessConnectionStatus)));
|
info.AppendLine($"{na.NetConnectionID}: {na.WirelessConnectionStatusMeaning(na.WirelessConnectionStatus)}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
info.AppendLine(string.Format("{0}: {1}", na.NetConnectionID, na.ConnectionStatusMeaning(na.ConnectionStatus)));
|
info.AppendLine($"{na.NetConnectionID}: {na.ConnectionStatusMeaning(na.ConnectionStatus)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statusUI.UpdateStatus(null, null, info.ToString());
|
statusUI.UpdateStatus(null, null, info.ToString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
|
private void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(e.Data))
|
if (!string.IsNullOrWhiteSpace(e.Data))
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine(string.Format("OUTPUT: {0}", e.Data));
|
|
||||||
var data = e.Data.Substring(1).Split(new char[] { ',' });
|
var data = e.Data.Substring(1).Split(new char[] { ',' });
|
||||||
switch (e.Data[0])
|
switch (e.Data[0])
|
||||||
{
|
{
|
||||||
@@ -249,15 +248,5 @@ namespace Disco.ClientBootstrapper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//void clientProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
|
|
||||||
//{
|
|
||||||
// if (!string.IsNullOrEmpty(e.Data))
|
|
||||||
// {
|
|
||||||
// System.Diagnostics.Debug.WriteLine(string.Format("ERROR: {0}", e.Data));
|
|
||||||
// this.errorMessage.AppendLine(e.Data);
|
|
||||||
// statusUI.UpdateStatus(null, "An Error Occurred", this.errorMessage.ToString(), false);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,9 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Disco.Client\Interop\EndpointDiscovery.cs">
|
||||||
|
<Link>Interop\EndpointDiscovery.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\Resources\Libraries\DotNetZip\Source\BZip2\BitWriter.cs">
|
<Compile Include="..\Resources\Libraries\DotNetZip\Source\BZip2\BitWriter.cs">
|
||||||
<Link>DotNetZip\BZip2\BitWriter.cs</Link>
|
<Link>DotNetZip\BZip2\BitWriter.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Data;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper
|
namespace Disco.ClientBootstrapper
|
||||||
@@ -12,69 +6,69 @@ namespace Disco.ClientBootstrapper
|
|||||||
public partial class FormStatus : Form, IStatus
|
public partial class FormStatus : Form, IStatus
|
||||||
{
|
{
|
||||||
|
|
||||||
private delegate void dUpdateStatus(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress, Nullable<int> Progress);
|
private delegate void dUpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress);
|
||||||
private dUpdateStatus mUpdateStatus;
|
private readonly dUpdateStatus mUpdateStatus;
|
||||||
|
|
||||||
public FormStatus()
|
public FormStatus()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
this.labelVersion.Text = string.Format("v{0}", version.ToString(3));
|
labelVersion.Text = $"v{version.ToString(3)}";
|
||||||
|
|
||||||
this.FormClosed += new FormClosedEventHandler(FormStatus_FormClosed);
|
FormClosed += new FormClosedEventHandler(FormStatus_FormClosed);
|
||||||
|
|
||||||
mUpdateStatus = new dUpdateStatus(UpdateStatusDo);
|
mUpdateStatus = new dUpdateStatus(UpdateStatusDo);
|
||||||
Cursor.Hide();
|
Cursor.Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormStatus_FormClosed(object sender, FormClosedEventArgs e)
|
private void FormStatus_FormClosed(object sender, FormClosedEventArgs e)
|
||||||
{
|
{
|
||||||
Cursor.Show();
|
Cursor.Show();
|
||||||
Program.ExitApplication();
|
Program.ExitApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateStatus(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress = null, Nullable<int> Progress = null)
|
public void UpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress = null, int? Progress = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress);
|
Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress);
|
||||||
}
|
}
|
||||||
catch (Exception) { }
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
private void UpdateStatusDo(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress, Nullable<int> Progress)
|
private void UpdateStatusDo(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress)
|
||||||
{
|
{
|
||||||
if (Heading != null)
|
if (Heading != null)
|
||||||
if (this.labelHeading.Text != Heading)
|
if (labelHeading.Text != Heading)
|
||||||
this.labelHeading.Text = Heading;
|
labelHeading.Text = Heading;
|
||||||
if (SubHeading != null)
|
if (SubHeading != null)
|
||||||
if (this.labelSubHeading.Text != SubHeading)
|
if (labelSubHeading.Text != SubHeading)
|
||||||
this.labelSubHeading.Text = SubHeading;
|
labelSubHeading.Text = SubHeading;
|
||||||
if (Message != null)
|
if (Message != null)
|
||||||
if (this.labelMessage.Text != Message)
|
if (labelMessage.Text != Message)
|
||||||
this.labelMessage.Text = Message;
|
labelMessage.Text = Message;
|
||||||
|
|
||||||
if (ShowProgress.HasValue)
|
if (ShowProgress.HasValue)
|
||||||
{
|
{
|
||||||
if (ShowProgress.Value)
|
if (ShowProgress.Value)
|
||||||
{
|
{
|
||||||
this.progressBar.Visible = true;
|
progressBar.Visible = true;
|
||||||
if (Progress.HasValue)
|
if (Progress.HasValue)
|
||||||
{
|
{
|
||||||
if (Progress.Value >= 0)
|
if (Progress.Value >= 0)
|
||||||
{
|
{
|
||||||
this.progressBar.Value = Math.Min(Progress.Value, 100);
|
progressBar.Value = Math.Min(Progress.Value, 100);
|
||||||
this.progressBar.Style = ProgressBarStyle.Continuous;
|
progressBar.Style = ProgressBarStyle.Continuous;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.progressBar.Style = ProgressBarStyle.Marquee;
|
progressBar.Style = ProgressBarStyle.Marquee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.progressBar.Visible = false;
|
progressBar.Visible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
using System;
|
namespace Disco.ClientBootstrapper
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper
|
|
||||||
{
|
{
|
||||||
interface IStatus
|
interface IStatus
|
||||||
{
|
{
|
||||||
void UpdateStatus(string Heading, string SubHeading, string Message, Nullable<bool> ShowProgress = null, Nullable<int> Progress = null);
|
void UpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress = null, int? Progress = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,49 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper
|
namespace Disco.ClientBootstrapper
|
||||||
{
|
{
|
||||||
class InstallLoop
|
internal class InstallLoop
|
||||||
{
|
{
|
||||||
|
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
private readonly string installLocation;
|
||||||
|
private readonly string wimImageId;
|
||||||
|
private readonly string tempPath;
|
||||||
|
private readonly Action completeCallback;
|
||||||
|
private readonly Uri forcedServerUrl;
|
||||||
|
|
||||||
public Thread LoopThread;
|
public InstallLoop(string installLocation, string wimImageId, string tempPath, Action completeCallback, Uri forcedServerUrl)
|
||||||
public delegate void CompleteCallback();
|
|
||||||
private CompleteCallback mCompleteCallback;
|
|
||||||
private string InstallLocation;
|
|
||||||
private string WimImageId;
|
|
||||||
private string TempPath;
|
|
||||||
|
|
||||||
public InstallLoop(string InstallLocation, string WimImageId, string TempPath)
|
|
||||||
{
|
{
|
||||||
this.InstallLocation = InstallLocation;
|
this.installLocation = installLocation;
|
||||||
this.WimImageId = WimImageId;
|
this.wimImageId = wimImageId;
|
||||||
this.TempPath = TempPath;
|
this.tempPath = tempPath;
|
||||||
|
this.completeCallback = completeCallback;
|
||||||
|
this.forcedServerUrl = forcedServerUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(CompleteCallback Callback)
|
public void Start()
|
||||||
{
|
{
|
||||||
this.mCompleteCallback = Callback;
|
var cancellationToken = cancellationTokenSource.Token;
|
||||||
this.LoopThread = new Thread(new ThreadStart(loopHost));
|
Task.Run(async () =>
|
||||||
this.LoopThread.Start();
|
|
||||||
}
|
|
||||||
private void loopHost()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
|
try
|
||||||
//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);
|
await Interop.InstallInterop.Install(installLocation, wimImageId, tempPath, forcedServerUrl, cancellationToken);
|
||||||
|
completeCallback?.BeginInvoke(null, null);
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
catch (Exception ex)
|
{
|
||||||
{
|
if (ex.GetType() == typeof(ThreadAbortException))
|
||||||
if (ex.GetType() == typeof(ThreadAbortException))
|
return;
|
||||||
return;
|
if (ex.GetType() == typeof(ThreadInterruptedException))
|
||||||
if (ex.GetType() == typeof(ThreadInterruptedException))
|
return;
|
||||||
return;
|
if (ex.GetType() == typeof(OperationCanceledException))
|
||||||
Program.WriteAppError(ex);
|
return;
|
||||||
throw;
|
Program.WriteAppError(ex);
|
||||||
}
|
throw;
|
||||||
|
}
|
||||||
|
}, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.IO;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper.Interop
|
namespace Disco.ClientBootstrapper.Interop
|
||||||
{
|
{
|
||||||
@@ -21,12 +22,12 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
//Remove(StoreName.Root, StoreLocation.LocalMachine, _tempCerts);
|
//Remove(StoreName.Root, StoreLocation.LocalMachine, _tempCerts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static void AddTempCerts()
|
public static async Task AddTempCerts(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_tempCerts == null)
|
if (_tempCerts == null)
|
||||||
_tempCerts = new List<string>();
|
_tempCerts = new List<string>();
|
||||||
|
|
||||||
var inlineCertificateLocation = Program.InlinePath.Value;
|
var inlineCertificateLocation = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||||
|
|
||||||
// Root Certificates
|
// Root Certificates
|
||||||
try
|
try
|
||||||
@@ -36,14 +37,15 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
{
|
{
|
||||||
foreach (var certFile in CertFiles)
|
foreach (var certFile in CertFiles)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
|
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
|
||||||
var result = Add(StoreName.Root, StoreLocation.LocalMachine, cert);
|
var result = Add(StoreName.Root, StoreLocation.LocalMachine, cert);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
||||||
_tempCerts.Add(cert.SerialNumber);
|
_tempCerts.Add(cert.SerialNumber);
|
||||||
Program.Status.UpdateStatus(null, null, string.Format("Added Root Certificate: {0}", cert.ShortSubjectName()));
|
Program.Status.UpdateStatus(null, null, $"Added Root Certificate: {cert.ShortSubjectName()}");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,14 +63,15 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
{
|
{
|
||||||
foreach (var certFile in CertFiles)
|
foreach (var certFile in CertFiles)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
|
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
|
||||||
var result = Add(StoreName.CertificateAuthority, StoreLocation.LocalMachine, cert);
|
var result = Add(StoreName.CertificateAuthority, StoreLocation.LocalMachine, cert);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
||||||
_tempCerts.Add(cert.SerialNumber);
|
_tempCerts.Add(cert.SerialNumber);
|
||||||
Program.Status.UpdateStatus(null, null, string.Format("Added Intermediate Certificate: {0}", cert.ShortSubjectName()));
|
Program.Status.UpdateStatus(null, null, $"Added Intermediate Certificate: {cert.ShortSubjectName()}");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,14 +89,15 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
{
|
{
|
||||||
foreach (var certFile in CertFiles)
|
foreach (var certFile in CertFiles)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
|
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
|
||||||
var result = Add(StoreName.My, StoreLocation.LocalMachine, cert);
|
var result = Add(StoreName.My, StoreLocation.LocalMachine, cert);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
||||||
_tempCerts.Add(cert.SerialNumber);
|
_tempCerts.Add(cert.SerialNumber);
|
||||||
Program.Status.UpdateStatus(null, null, string.Format("Added Host Certificate: {0}", cert.ShortSubjectName()));
|
Program.Status.UpdateStatus(null, null, $"Added Host Certificate: {cert.ShortSubjectName()}");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper.Interop
|
namespace Disco.ClientBootstrapper.Interop
|
||||||
{
|
{
|
||||||
public static class InstallInterop
|
public static class InstallInterop
|
||||||
{
|
{
|
||||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||||
static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
|
private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
|
||||||
[Flags]
|
[Flags]
|
||||||
enum MoveFileFlags
|
private enum MoveFileFlags
|
||||||
{
|
{
|
||||||
MOVEFILE_REPLACE_EXISTING = 0x00000001,
|
MOVEFILE_REPLACE_EXISTING = 0x00000001,
|
||||||
MOVEFILE_COPY_ALLOWED = 0x00000002,
|
MOVEFILE_COPY_ALLOWED = 0x00000002,
|
||||||
@@ -22,19 +24,19 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
|
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Install(string RootFilesystemLocation, RegistryKey RootRegistryLocation, string FilesystemInstallLocation, string VirtualRootFilesystemLocation)
|
private static async Task Install(string rootFilesystemLocation, RegistryKey rootRegistryLocation, string filesystemInstallLocation, string virtualRootFilesystemLocation, Uri forcedServerUrl, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var SourceLocation = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
var SourceLocation = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||||
var InstallLocation = Path.Combine(RootFilesystemLocation, FilesystemInstallLocation);
|
var InstallLocation = Path.Combine(rootFilesystemLocation, filesystemInstallLocation);
|
||||||
var BootstrapperCmdLinePath = Path.Combine(VirtualRootFilesystemLocation, FilesystemInstallLocation, "Disco.ClientBootstrapper.exe");
|
var BootstrapperCmdLinePath = Path.Combine(virtualRootFilesystemLocation, filesystemInstallLocation, "Disco.ClientBootstrapper.exe");
|
||||||
|
|
||||||
var GroupPolicyScriptsIniLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\scripts.ini");
|
var GroupPolicyScriptsIniLocation = Path.Combine(rootFilesystemLocation, @"Windows\System32\GroupPolicy\Machine\Scripts\scripts.ini");
|
||||||
var GroupPolicyScriptsIniBackupLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\disco_scripts.ini");
|
var GroupPolicyScriptsIniBackupLocation = Path.Combine(rootFilesystemLocation, @"Windows\System32\GroupPolicy\Machine\Scripts\disco_scripts.ini");
|
||||||
|
|
||||||
// Create file system Location
|
// Create file system Location
|
||||||
#region "Create File System Location"
|
#region "Create File System Location"
|
||||||
Program.Status.UpdateStatus(null, null, "Creating Installation Location");
|
Program.Status.UpdateStatus(null, null, "Creating Installation Location");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
if (Directory.Exists(InstallLocation))
|
if (Directory.Exists(InstallLocation))
|
||||||
{
|
{
|
||||||
// Try and Delete Directory
|
// Try and Delete Directory
|
||||||
@@ -44,7 +46,7 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new IOException(string.Format("Unable to delete folder: ", InstallLocation), ex);
|
throw new IOException($"Unable to delete folder: {InstallLocation}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Directory.Exists(InstallLocation))
|
if (!Directory.Exists(InstallLocation))
|
||||||
@@ -52,21 +54,25 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
var installDir = Directory.CreateDirectory(InstallLocation);
|
var installDir = Directory.CreateDirectory(InstallLocation);
|
||||||
installDir.Attributes = installDir.Attributes | FileAttributes.Hidden;
|
installDir.Attributes = installDir.Attributes | FileAttributes.Hidden;
|
||||||
}
|
}
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// Copy files to file system location
|
// Copy files to file system location
|
||||||
#region "Copy to File System"
|
#region "Copy to File System"
|
||||||
Program.Status.UpdateStatus(null, null, "Copying Files");
|
Program.Status.UpdateStatus(null, null, "Copying Files");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
|
|
||||||
// Copy Bootstrapper
|
// Copy Bootstrapper
|
||||||
// ie: Executing Assembly
|
// ie: Executing Assembly
|
||||||
File.Copy(System.Reflection.Assembly.GetExecutingAssembly().Location, Path.Combine(InstallLocation, "Disco.ClientBootstrapper.exe"));
|
File.Copy(System.Reflection.Assembly.GetExecutingAssembly().Location, Path.Combine(InstallLocation, "Disco.ClientBootstrapper.exe"));
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
foreach (var file in Directory.EnumerateFiles(SourceLocation))
|
foreach (var file in Directory.EnumerateFiles(SourceLocation))
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var fileName = Path.GetFileName(file);
|
var fileName = Path.GetFileName(file);
|
||||||
|
|
||||||
// Only Copy Certain Files
|
// Only Copy Certain Files
|
||||||
|
|
||||||
// Copy Wireless Certificates
|
// Copy Wireless Certificates
|
||||||
@@ -79,14 +85,14 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
if (fileName.StartsWith("WLAN_Profile_", StringComparison.OrdinalIgnoreCase) &&
|
if (fileName.StartsWith("WLAN_Profile_", StringComparison.OrdinalIgnoreCase) &&
|
||||||
fileName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
|
fileName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
|
||||||
File.Copy(file, Path.Combine(InstallLocation, fileName));
|
File.Copy(file, Path.Combine(InstallLocation, fileName));
|
||||||
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// Backup & Create Group Policy Scripts.ini
|
// Backup & Create Group Policy Scripts.ini
|
||||||
#region "Group Policy Scripts.ini"
|
#region "Group Policy Scripts.ini"
|
||||||
Program.Status.UpdateStatus(null, null, "Creating Group Policy Script Entry");
|
Program.Status.UpdateStatus(null, null, "Creating Group Policy Script Entry");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
// Backup
|
// Backup
|
||||||
if (!File.Exists(GroupPolicyScriptsIniBackupLocation))
|
if (!File.Exists(GroupPolicyScriptsIniBackupLocation))
|
||||||
{
|
{
|
||||||
@@ -95,6 +101,7 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
File.Move(GroupPolicyScriptsIniLocation, GroupPolicyScriptsIniBackupLocation);
|
File.Move(GroupPolicyScriptsIniLocation, GroupPolicyScriptsIniBackupLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
if (File.Exists(GroupPolicyScriptsIniLocation))
|
if (File.Exists(GroupPolicyScriptsIniLocation))
|
||||||
@@ -105,56 +112,67 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
{
|
{
|
||||||
using (var scriptsIniStreamWriter = new StreamWriter(scriptsIniStream, Encoding.Unicode))
|
using (var scriptsIniStreamWriter = new StreamWriter(scriptsIniStream, Encoding.Unicode))
|
||||||
{
|
{
|
||||||
scriptsIniStreamWriter.Write(string.Format("[Startup]{0}0CmdLine={1}{0}0Parameters=/AllowUninstall", Environment.NewLine, BootstrapperCmdLinePath));
|
scriptsIniStreamWriter.WriteLine("[Startup]");
|
||||||
scriptsIniStreamWriter.Flush();
|
scriptsIniStreamWriter.WriteLine($"0CmdLine={BootstrapperCmdLinePath}");
|
||||||
|
if (forcedServerUrl == null)
|
||||||
|
scriptsIniStreamWriter.WriteLine("0Parameters=/AllowUninstall");
|
||||||
|
else
|
||||||
|
scriptsIniStreamWriter.WriteLine($"0Parameters=/AllowUninstall {forcedServerUrl}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// Backup & Create Group Policy Registry
|
// Backup & Create Group Policy Registry
|
||||||
#region "Group Policy Registry"
|
#region "Group Policy Registry"
|
||||||
Program.Status.UpdateStatus(null, null, "Creating Group Policy Registry Entries");
|
Program.Status.UpdateStatus(null, null, "Creating Group Policy Registry Entries");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
// Backup Scripts
|
// Backup Scripts
|
||||||
using (var regGroupPolicy = RootRegistryLocation.OpenSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy", true))
|
using (var regGroupPolicy = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy", true))
|
||||||
{
|
{
|
||||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||||
{
|
{
|
||||||
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
|
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// Create Scripts
|
// Create Scripts
|
||||||
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Shutdown").Dispose();
|
rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown").Dispose();
|
||||||
using (var regScriptsStartup = RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup\\0"))
|
using (var regScriptsStartup = rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0"))
|
||||||
{
|
{
|
||||||
regScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
|
regScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
|
||||||
regScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
|
regScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
|
||||||
regScriptsStartup.SetValue("FileSysPath", Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine"), RegistryValueKind.String);
|
regScriptsStartup.SetValue("FileSysPath", Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine"), RegistryValueKind.String);
|
||||||
regScriptsStartup.SetValue("DisplayName", "Local Group Policy", RegistryValueKind.String);
|
regScriptsStartup.SetValue("DisplayName", "Local Group Policy", RegistryValueKind.String);
|
||||||
regScriptsStartup.SetValue("GPOName", "Local Group Policy", RegistryValueKind.String);
|
regScriptsStartup.SetValue("GPOName", "Local Group Policy", RegistryValueKind.String);
|
||||||
regScriptsStartup.SetValue("PSScriptOrder", 1, RegistryValueKind.DWord);
|
regScriptsStartup.SetValue("PSScriptOrder", 1, RegistryValueKind.DWord);
|
||||||
using (var regScriptsStartup0 = regScriptsStartup.CreateSubKey("0"))
|
using (var regScriptsStartup0 = regScriptsStartup.CreateSubKey("0"))
|
||||||
{
|
{
|
||||||
regScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
|
regScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
|
||||||
regScriptsStartup0.SetValue("Parameters", "/AllowUninstall", 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("IsPowershell", 0, RegistryValueKind.DWord);
|
||||||
regScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary);
|
regScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Shutdown").Dispose();
|
rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown").Dispose();
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// Backup Scripts State
|
// Backup Scripts State
|
||||||
using (var regGroupPolicy = RootRegistryLocation.OpenSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine", true))
|
using (var regGroupPolicy = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine", true))
|
||||||
{
|
{
|
||||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||||
{
|
{
|
||||||
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
|
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// Create Scripts State
|
// Create Scripts State
|
||||||
using (var regStateScriptsStartup = RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Startup\\0"))
|
using (var regStateScriptsStartup = rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0"))
|
||||||
{
|
{
|
||||||
regStateScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
|
regStateScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
|
||||||
regStateScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
|
regStateScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
|
||||||
@@ -165,17 +183,21 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
using (var regStateScriptsStartup0 = regStateScriptsStartup.CreateSubKey("0"))
|
using (var regStateScriptsStartup0 = regStateScriptsStartup.CreateSubKey("0"))
|
||||||
{
|
{
|
||||||
regStateScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
|
regStateScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
|
||||||
regStateScriptsStartup0.SetValue("Parameters", "/AllowUninstall", 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);
|
regStateScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// Set Registry Startup Environment Policies
|
// Set Registry Startup Environment Policies
|
||||||
#region "Registry Startup Policies"
|
#region "Registry Startup Policies"
|
||||||
Program.Status.UpdateStatus(null, null, "Creating Startup Policy Registry Entries");
|
Program.Status.UpdateStatus(null, null, "Creating Startup Policy Registry Entries");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
using (var regWinlogon = RootRegistryLocation.OpenSubKey("Microsoft\\Windows NT\\CurrentVersion\\Winlogon", true))
|
using (var regWinlogon = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||||
{
|
{
|
||||||
regWinlogon.SetValue("HideStartupScripts", 0, RegistryValueKind.DWord);
|
regWinlogon.SetValue("HideStartupScripts", 0, RegistryValueKind.DWord);
|
||||||
regWinlogon.SetValue("RunStartupScriptSync", 1, RegistryValueKind.DWord);
|
regWinlogon.SetValue("RunStartupScriptSync", 1, RegistryValueKind.DWord);
|
||||||
@@ -183,94 +205,110 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Install(string InstallLocation, string WimImageId, string TempPath)
|
public static async Task Install(string installLocation, string wimImageId, string tempPath, Uri forcedServerUrl, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Program.Status.UpdateStatus("Installing Bootstrapper", "Starting", "Please wait...", false);
|
Program.Status.UpdateStatus("Installing Bootstrapper", "Starting", "Please wait...", false);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(InstallLocation))
|
if (string.IsNullOrWhiteSpace(installLocation))
|
||||||
InstallLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco");
|
installLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco");
|
||||||
|
|
||||||
if (InstallLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase))
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (installLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// Offline File System (WIM)
|
// Offline File System (WIM)
|
||||||
Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", string.Format("Install Location: {0}", InstallLocation));
|
Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", $"Install Location: {installLocation}");
|
||||||
Program.SleepThread(1000, false);
|
await Program.SleepThread(1000, false, cancellationToken);
|
||||||
|
|
||||||
// Mount WIM
|
// Mount WIM
|
||||||
int wimImageIndex = 0;
|
int wimImageIndex = 0;
|
||||||
using (var wim = new Interop.WIMInterop.WindowsImageContainer(InstallLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write))
|
using (var wim = new WIMInterop.WindowsImageContainer(installLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write))
|
||||||
{
|
{
|
||||||
if (WimImageId == null)
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
WimImageId = "1";
|
if (wimImageId == null)
|
||||||
if (!int.TryParse(WimImageId, out wimImageIndex))
|
wimImageId = "1";
|
||||||
|
if (!int.TryParse(wimImageId, out wimImageIndex))
|
||||||
{
|
{
|
||||||
Program.Status.UpdateStatus(null, "Analysing WIM", string.Format("Looking for Image Name: {0}", WimImageId));
|
Program.Status.UpdateStatus(null, "Analysing WIM", $"Looking for Image Name: {wimImageId}");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
for (int i = 0; i < wim.ImageCount; i++)
|
for (int i = 0; i < wim.ImageCount; i++)
|
||||||
{
|
{
|
||||||
var wimImageInfo = new System.Xml.XmlDocument();
|
var wimImageInfo = new System.Xml.XmlDocument();
|
||||||
using (var wimImage = wim[i])
|
using (var wimImage = wim[i])
|
||||||
wimImageInfo.LoadXml(wimImage.ImageInformation);
|
wimImageInfo.LoadXml(wimImage.ImageInformation);
|
||||||
var wimImageInfoName = wimImageInfo.SelectSingleNode("//IMAGE/NAME");
|
var wimImageInfoName = wimImageInfo.SelectSingleNode("//IMAGE/NAME");
|
||||||
if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(WimImageId, StringComparison.OrdinalIgnoreCase))
|
if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(wimImageId, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
wimImageIndex = i + 1;
|
wimImageIndex = i + 1;
|
||||||
Program.Status.UpdateStatus(null, "Analysing WIM", string.Format("Found Image Id '{0}' at Index {1}", WimImageId, wimImageIndex));
|
Program.Status.UpdateStatus(null, "Analysing WIM", $"Found Image Id '{wimImageId}' at Index {wimImageIndex}");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
if (wimImageIndex == 0)
|
if (wimImageIndex == 0)
|
||||||
{
|
{
|
||||||
Program.Status.UpdateStatus(null, "Error", string.Format("Unable to load WIM Image Id: {0}", WimImageId));
|
Program.Status.UpdateStatus(null, "Error", $"Unable to load WIM Image Id: {wimImageId}");
|
||||||
Program.SleepThread(5000, false);
|
await Program.SleepThread(5000, false, cancellationToken);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Temp Path
|
// Get Temp Path
|
||||||
var wimMountPath = Path.Combine(TempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimMount");
|
var wimMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimMount");
|
||||||
if (Directory.Exists(wimMountPath))
|
if (Directory.Exists(wimMountPath))
|
||||||
Directory.Delete(wimMountPath, true);
|
Directory.Delete(wimMountPath, true);
|
||||||
Directory.CreateDirectory(wimMountPath);
|
Directory.CreateDirectory(wimMountPath);
|
||||||
|
|
||||||
var wimTempMountPath = Path.Combine(TempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount");
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var wimTempMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount");
|
||||||
if (Directory.Exists(wimTempMountPath))
|
if (Directory.Exists(wimTempMountPath))
|
||||||
Directory.Delete(wimTempMountPath, true);
|
Directory.Delete(wimTempMountPath, true);
|
||||||
Directory.CreateDirectory(wimTempMountPath);
|
Directory.CreateDirectory(wimTempMountPath);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
bool wimCommitChanges = true;
|
bool wimCommitChanges = true;
|
||||||
Interop.WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback m_MessageCallback = null;
|
WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback m_MessageCallback = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Mount WIM
|
// Mount WIM
|
||||||
Program.Status.UpdateStatus(null, "Mounting WIM", string.Format("Mounting WIM Image to '{0}'", wimMountPath));
|
Program.Status.UpdateStatus(null, "Mounting WIM", $"Mounting WIM Image to '{wimMountPath}'");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
m_MessageCallback = new Interop.WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback(WimImageEventMessagePump);
|
m_MessageCallback = new WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback(WimImageEventMessagePump);
|
||||||
Interop.WIMInterop.WindowsImageContainer.NativeMethods.RegisterCallback(m_MessageCallback);
|
WIMInterop.WindowsImageContainer.NativeMethods.RegisterCallback(m_MessageCallback);
|
||||||
|
|
||||||
Interop.WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, InstallLocation, wimImageIndex, wimTempMountPath);
|
WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, installLocation, wimImageIndex, wimTempMountPath);
|
||||||
|
|
||||||
// Load Local Machine Registry
|
// Load Local Machine Registry
|
||||||
var wimHivePath = Path.Combine(wimMountPath, "Windows\\System32\\config\\SOFTWARE");
|
var wimHivePath = Path.Combine(wimMountPath, "Windows\\System32\\config\\SOFTWARE");
|
||||||
Program.Status.UpdateStatus(null, "Mounting Offline Registry Hive", string.Format("Mounting Offline Registry Hive at '{0}'", wimHivePath));
|
Program.Status.UpdateStatus(null, "Mounting Offline Registry Hive", $"Mounting Offline Registry Hive at '{wimHivePath}'");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
using (var wimReg = new Interop.RegistryInterop(RegistryInterop.RegistryHives.HKEY_LOCAL_MACHINE, "DiscoClientBootstrapperWimHive", wimHivePath))
|
using (var wimReg = new RegistryInterop(RegistryInterop.RegistryHives.HKEY_LOCAL_MACHINE, "DiscoClientBootstrapperWimHive", wimHivePath))
|
||||||
{
|
{
|
||||||
using (RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("DiscoClientBootstrapperWimHive", true))
|
try
|
||||||
{
|
{
|
||||||
string rootFileSystemLocation = wimMountPath;
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
string fileSystemInstallLocation = "Disco";
|
using (RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("DiscoClientBootstrapperWimHive", true))
|
||||||
string virtualRootFileSystemLocation = "C:\\";
|
{
|
||||||
|
string rootFileSystemLocation = wimMountPath;
|
||||||
|
string fileSystemInstallLocation = "Disco";
|
||||||
|
string virtualRootFileSystemLocation = "C:\\";
|
||||||
|
|
||||||
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation);
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation, forcedServerUrl, cancellationToken);
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Unload Local Machine Registry
|
||||||
|
Program.Status.UpdateStatus(null, "Unmounting Offline Registry Hive", $"Unmounting Offline Registry Hive at '{wimHivePath}'");
|
||||||
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
|
wimReg.Unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unload Local Machine Registry
|
|
||||||
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)
|
catch (Exception)
|
||||||
@@ -281,13 +319,13 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// Unmount WIM
|
// Unmount WIM
|
||||||
Program.Status.UpdateStatus(null, "Unmounting WIM", string.Format("Unmounting WIM Image at '{0}'", wimMountPath));
|
Program.Status.UpdateStatus(null, "Unmounting WIM", $"Unmounting WIM Image at '{wimMountPath}'");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
Interop.WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, InstallLocation, wimImageIndex, wimCommitChanges);
|
WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, installLocation, wimImageIndex, wimCommitChanges);
|
||||||
|
|
||||||
if (m_MessageCallback != null)
|
if (m_MessageCallback != null)
|
||||||
{
|
{
|
||||||
Interop.WIMInterop.WindowsImageContainer.NativeMethods.UnregisterMessageCallback(m_MessageCallback);
|
WIMInterop.WindowsImageContainer.NativeMethods.UnregisterMessageCallback(m_MessageCallback);
|
||||||
m_MessageCallback = null;
|
m_MessageCallback = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,23 +333,25 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
Directory.Delete(wimMountPath, true);
|
Directory.Delete(wimMountPath, true);
|
||||||
if (Directory.Exists(wimTempMountPath))
|
if (Directory.Exists(wimTempMountPath))
|
||||||
Directory.Delete(wimTempMountPath, true);
|
Directory.Delete(wimTempMountPath, true);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Online File System
|
// Online File System
|
||||||
Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", string.Format("Install Location: {0}", InstallLocation), true, -1);
|
Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", $"Install Location: {installLocation}", true, -1);
|
||||||
Program.SleepThread(1000, false);
|
await Program.SleepThread(1000, false, cancellationToken);
|
||||||
string rootFileSystemLocation = Path.GetPathRoot(InstallLocation);
|
string rootFileSystemLocation = Path.GetPathRoot(installLocation);
|
||||||
RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("SOFTWARE", true);
|
RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("SOFTWARE", true);
|
||||||
string fileSystemInstallLocation = InstallLocation.Substring(rootFileSystemLocation.Length);
|
string fileSystemInstallLocation = installLocation.Substring(rootFileSystemLocation.Length);
|
||||||
|
|
||||||
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation);
|
await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation, forcedServerUrl, cancellationToken);
|
||||||
Program.Status.UpdateStatus(null, "Online File System Installation Complete", string.Empty, true, -1);
|
Program.Status.UpdateStatus(null, "Online File System Installation Complete", string.Empty, true, -1);
|
||||||
Program.SleepThread(1000, false);
|
await Program.SleepThread(1000, false, cancellationToken);
|
||||||
}
|
}
|
||||||
Program.Status.UpdateStatus(null, "Complete", "Finished Installing Bootstrapper");
|
Program.Status.UpdateStatus(null, "Complete", "Finished Installing Bootstrapper");
|
||||||
Program.SleepThread(1500, false);
|
await Program.SleepThread(1500, false, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uint WimImageEventMessagePump(
|
private static uint WimImageEventMessagePump(
|
||||||
@@ -321,16 +361,16 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
IntPtr UserData
|
IntPtr UserData
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint status = (uint)Interop.WIMInterop.WindowsImageContainer.NativeMethods.WIMMessage.WIM_MSG_SUCCESS;
|
uint status = (uint)WIMInterop.WindowsImageContainer.NativeMethods.WIMMessage.WIM_MSG_SUCCESS;
|
||||||
Interop.WIMInterop.DefaultImageEventArgs eventArgs = new Interop.WIMInterop.DefaultImageEventArgs(wParam, lParam, UserData);
|
WIMInterop.DefaultImageEventArgs eventArgs = new WIMInterop.DefaultImageEventArgs(wParam, lParam, UserData);
|
||||||
|
|
||||||
//System.Diagnostics.Debug.WriteLine(MessageId);
|
//System.Diagnostics.Debug.WriteLine(MessageId);
|
||||||
|
|
||||||
switch ((Interop.WIMInterop.WindowsImageContainer.ImageEventMessage)MessageId)
|
switch ((WIMInterop.WindowsImageContainer.ImageEventMessage)MessageId)
|
||||||
{
|
{
|
||||||
|
|
||||||
case Interop.WIMInterop.WindowsImageContainer.ImageEventMessage.Progress:
|
case WIMInterop.WindowsImageContainer.ImageEventMessage.Progress:
|
||||||
case Interop.WIMInterop.WindowsImageContainer.ImageEventMessage.MountCleanupProgress:
|
case WIMInterop.WindowsImageContainer.ImageEventMessage.MountCleanupProgress:
|
||||||
var timeRemainingMil = eventArgs.LeftParameter.ToInt32();
|
var timeRemainingMil = eventArgs.LeftParameter.ToInt32();
|
||||||
string timeRemainingMessage;
|
string timeRemainingMessage;
|
||||||
if (timeRemainingMil > 0)
|
if (timeRemainingMil > 0)
|
||||||
@@ -339,8 +379,8 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
timeRemainingMessage = "Calculating, please wait...";
|
timeRemainingMessage = "Calculating, please wait...";
|
||||||
|
|
||||||
var progress = eventArgs.WideParameter.ToInt32();
|
var progress = eventArgs.WideParameter.ToInt32();
|
||||||
Program.Status.UpdateStatus(null, null, string.Format("Time remaining: {0}", timeRemainingMessage), true, progress);
|
Program.Status.UpdateStatus(null, null, $"Time remaining: {timeRemainingMessage}", true, progress);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -349,41 +389,28 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Uninstall()
|
public static async Task Uninstall(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Application Directory
|
// Application Directory
|
||||||
var appDirectory = Program.InlinePath.Value;
|
var appDirectory = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||||
if (Program.AllowUninstall && !appDirectory.StartsWith("\\\\"))
|
if (Program.AllowUninstall && !appDirectory.StartsWith(@"\\"))
|
||||||
{
|
{
|
||||||
Program.Status.UpdateStatus("System Preparation (Bootstrapper)", "Uninstalling Bootstrapper...", string.Empty, false, 0);
|
Program.Status.UpdateStatus("System Preparation (Bootstrapper)", "Uninstalling Bootstrapper...", string.Empty, false, 0);
|
||||||
Program.SleepThread(1000, true);
|
await Program.SleepThread(1000, true, cancellationToken);
|
||||||
//var uninstallScriptLocation = System.IO.Path.Combine(appDirectory, "UninstallBootstrapper.vbs");
|
|
||||||
//if (System.IO.File.Exists(uninstallScriptLocation))
|
|
||||||
//{
|
|
||||||
// var bootstrapperPID = System.Diagnostics.Process.GetCurrentProcess().Id;
|
|
||||||
// var cscriptPath = System.IO.Path.Combine(Environment.SystemDirectory, "cscript.exe");
|
|
||||||
// var cscriptArgs = string.Format("\"{0}\" /WaitForProcessID:{1}", uninstallScriptLocation, bootstrapperPID);
|
|
||||||
|
|
||||||
// var startProc = new ProcessStartInfo(cscriptPath, cscriptArgs);
|
|
||||||
// startProc.WorkingDirectory = Environment.SystemDirectory;
|
|
||||||
// startProc.WindowStyle = ProcessWindowStyle.Hidden;
|
|
||||||
|
|
||||||
// Process.Start(startProc);
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Remove Registry Entries
|
// Remove Registry Entries
|
||||||
using (var regWinlogon = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", true))
|
using (var regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||||
{
|
{
|
||||||
regWinlogon.DeleteValue("HideStartupScripts", false);
|
regWinlogon.DeleteValue("HideStartupScripts", false);
|
||||||
regWinlogon.DeleteValue("RunStartupScriptSync", false);
|
regWinlogon.DeleteValue("RunStartupScriptSync", false);
|
||||||
}
|
}
|
||||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Shutdown", false);
|
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown", false);
|
||||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup", false);
|
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup", false);
|
||||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Shutdown", false);
|
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown", false);
|
||||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Startup", false);
|
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup", false);
|
||||||
|
|
||||||
// Restore Registry Backups
|
// Restore Registry Backups
|
||||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy", true))
|
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy", true))
|
||||||
{
|
{
|
||||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||||
{
|
{
|
||||||
@@ -391,7 +418,7 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
RegistryUtilities.RenameSubKey(regGroupPolicy, "Disco_Scripts", "Scripts");
|
RegistryUtilities.RenameSubKey(regGroupPolicy, "Disco_Scripts", "Scripts");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine", true))
|
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine", true))
|
||||||
{
|
{
|
||||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||||
{
|
{
|
||||||
@@ -401,10 +428,10 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete Group Policy Script File
|
// Delete Group Policy Script File
|
||||||
var groupPolicyScriptsPath = Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine\\Scripts\\scripts.ini");
|
var groupPolicyScriptsPath = Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine\Scripts\scripts.ini");
|
||||||
if (File.Exists(groupPolicyScriptsPath))
|
if (File.Exists(groupPolicyScriptsPath))
|
||||||
File.Delete(groupPolicyScriptsPath);
|
File.Delete(groupPolicyScriptsPath);
|
||||||
var groupPolicyScriptsBackupPath = Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine\\Scripts\\disco_scripts.ini");
|
var groupPolicyScriptsBackupPath = Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine\Scripts\disco_scripts.ini");
|
||||||
if (File.Exists(groupPolicyScriptsBackupPath))
|
if (File.Exists(groupPolicyScriptsBackupPath))
|
||||||
File.Move(groupPolicyScriptsBackupPath, groupPolicyScriptsPath);
|
File.Move(groupPolicyScriptsBackupPath, groupPolicyScriptsPath);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Management;
|
using System.Management;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -17,8 +14,8 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string NetConnectionID { get; set; }
|
public string NetConnectionID { get; set; }
|
||||||
public string MACAddress { get; set; }
|
public string MACAddress { get; set; }
|
||||||
public UInt64 Speed { get; set; }
|
public ulong Speed { get; set; }
|
||||||
public UInt16 LastConnectionStatus { get; set; }
|
public ushort LastConnectionStatus { get; set; }
|
||||||
public bool IsWireless { get; set; }
|
public bool IsWireless { get; set; }
|
||||||
public string WirelessInterfaceDescription { get; set; }
|
public string WirelessInterfaceDescription { get; set; }
|
||||||
public int LastWirelessConnectionStatus { get; set; }
|
public int LastWirelessConnectionStatus { get; set; }
|
||||||
@@ -30,51 +27,51 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
|
|
||||||
private void UpdateFromWmi(ManagementObject wmiObject)
|
private void UpdateFromWmi(ManagementObject wmiObject)
|
||||||
{
|
{
|
||||||
this.WmiPath = (string)wmiObject.GetPropertyValue("__PATH");
|
WmiPath = (string)wmiObject.GetPropertyValue("__PATH");
|
||||||
this.Index = (UInt32)wmiObject.GetPropertyValue("Index");
|
Index = (uint)wmiObject.GetPropertyValue("Index");
|
||||||
this.Guid = Guid.Parse((string)wmiObject.GetPropertyValue("GUID"));
|
Guid = Guid.Parse((string)wmiObject.GetPropertyValue("GUID"));
|
||||||
this.MACAddress = (string)wmiObject.GetPropertyValue("MACAddress");
|
MACAddress = (string)wmiObject.GetPropertyValue("MACAddress");
|
||||||
this.Name = (string)wmiObject.GetPropertyValue("Name");
|
Name = (string)wmiObject.GetPropertyValue("Name");
|
||||||
this.NetConnectionID = (string)wmiObject.GetPropertyValue("NetConnectionID");
|
NetConnectionID = (string)wmiObject.GetPropertyValue("NetConnectionID");
|
||||||
this.Speed = (UInt64)wmiObject.GetPropertyValue("Speed");
|
Speed = (ulong)wmiObject.GetPropertyValue("Speed");
|
||||||
var connectionStatus = ConnectionStatus;
|
_ = ConnectionStatus;
|
||||||
this.IsWireless = true;
|
IsWireless = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var wirelessConnectionStatus = WirelessConnectionStatus;
|
var wirelessConnectionStatus = WirelessConnectionStatus;
|
||||||
}
|
}
|
||||||
catch (Exception) {
|
catch (Exception)
|
||||||
this.IsWireless = false;
|
{
|
||||||
};
|
IsWireless = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int WirelessConnectionStatus
|
public int WirelessConnectionStatus
|
||||||
{
|
{
|
||||||
get {
|
get
|
||||||
if (this.IsWireless)
|
{
|
||||||
|
if (IsWireless)
|
||||||
{
|
{
|
||||||
IntPtr handle = IntPtr.Zero;
|
IntPtr handle = IntPtr.Zero;
|
||||||
uint negotiatedVersion;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (NetworkInterop.WlanOpenHandle(1, IntPtr.Zero, out negotiatedVersion, ref handle) != 0)
|
if (NetworkInterop.WlanOpenHandle(1, IntPtr.Zero, out var negotiatedVersion, ref handle) != 0)
|
||||||
throw new NotSupportedException("This network adapter does not support Wireless");
|
throw new NotSupportedException("This network adapter does not support Wireless");
|
||||||
|
|
||||||
IntPtr ptr = new IntPtr();
|
IntPtr ptr = new IntPtr();
|
||||||
|
|
||||||
uint dataSize;
|
|
||||||
|
|
||||||
var interfaceGuid = this.Guid;
|
var interfaceGuid = Guid;
|
||||||
|
|
||||||
if (NetworkInterop.WlanQueryInterface(handle, ref interfaceGuid, NetworkInterop.WLAN_INTF_OPCODE.wlan_intf_opcode_interface_state, IntPtr.Zero, out dataSize, ref ptr, IntPtr.Zero) != 0)
|
if (NetworkInterop.WlanQueryInterface(handle, ref interfaceGuid, NetworkInterop.WLAN_INTF_OPCODE.wlan_intf_opcode_interface_state, IntPtr.Zero, out var dataSize, ref ptr, IntPtr.Zero) != 0)
|
||||||
throw new NotSupportedException("This network adapter does not support Wireless");
|
throw new NotSupportedException("This network adapter does not support Wireless");
|
||||||
|
|
||||||
this.LastWirelessConnectionStatus = Marshal.ReadInt32(ptr);
|
LastWirelessConnectionStatus = Marshal.ReadInt32(ptr);
|
||||||
|
|
||||||
|
|
||||||
NetworkInterop.WlanFreeMemory(ptr);
|
NetworkInterop.WlanFreeMemory(ptr);
|
||||||
|
|
||||||
return this.LastWirelessConnectionStatus;
|
return LastWirelessConnectionStatus;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -114,46 +111,46 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UInt16 ConnectionStatus
|
public ushort ConnectionStatus
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
using (var wmiObject = new ManagementObject(this.WmiPath))
|
using (var wmiObject = new ManagementObject(WmiPath))
|
||||||
{
|
{
|
||||||
this.LastConnectionStatus = (UInt16)wmiObject.GetPropertyValue("NetConnectionStatus");
|
LastConnectionStatus = (ushort)wmiObject.GetPropertyValue("NetConnectionStatus");
|
||||||
}
|
}
|
||||||
return this.LastConnectionStatus;
|
return LastConnectionStatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string ConnectionStatusMeaning(UInt16 status)
|
public string ConnectionStatusMeaning(ushort status)
|
||||||
{
|
{
|
||||||
switch (status)
|
switch (status)
|
||||||
{
|
{
|
||||||
case (UInt16)0:
|
case (ushort)0:
|
||||||
return "Disconnected";
|
return "Disconnected";
|
||||||
case (UInt16)1:
|
case (ushort)1:
|
||||||
return "Connecting";
|
return "Connecting";
|
||||||
case (UInt16)2:
|
case (ushort)2:
|
||||||
return "Connected";
|
return "Connected";
|
||||||
case (UInt16)3:
|
case (ushort)3:
|
||||||
return "Disconnecting";
|
return "Disconnecting";
|
||||||
case (UInt16)4:
|
case (ushort)4:
|
||||||
return "Hardware not present";
|
return "Hardware not present";
|
||||||
case (UInt16)5:
|
case (ushort)5:
|
||||||
return "Hardware disabled";
|
return "Hardware disabled";
|
||||||
case (UInt16)6:
|
case (ushort)6:
|
||||||
return "Hardware malfunction";
|
return "Hardware malfunction";
|
||||||
case (UInt16)7:
|
case (ushort)7:
|
||||||
return "Media disconnected";
|
return "Media disconnected";
|
||||||
case (UInt16)8:
|
case (ushort)8:
|
||||||
return "Authenticating";
|
return "Authenticating";
|
||||||
case (UInt16)9:
|
case (ushort)9:
|
||||||
return "Authentication succeeded";
|
return "Authentication succeeded";
|
||||||
case (UInt16)10:
|
case (ushort)10:
|
||||||
return "Authentication failed";
|
return "Authentication failed";
|
||||||
case (UInt16)11:
|
case (ushort)11:
|
||||||
return "Invalid address";
|
return "Invalid address";
|
||||||
case (UInt16)12:
|
case (ushort)12:
|
||||||
return "Credentials required";
|
return "Credentials required";
|
||||||
default:
|
default:
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
using System.Management;
|
using System.Management;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper.Interop
|
namespace Disco.ClientBootstrapper.Interop
|
||||||
{
|
{
|
||||||
static class NetworkInterop
|
internal static class NetworkInterop
|
||||||
{
|
{
|
||||||
|
|
||||||
#region PInvoke
|
#region PInvoke
|
||||||
@@ -165,30 +167,35 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool PingDiscoIct(string ServerName)
|
public static bool HasNetworkConnectivity()
|
||||||
{
|
{
|
||||||
using (Ping p = new Ping())
|
var nics = NetworkInterface.GetAllNetworkInterfaces()
|
||||||
|
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var nic in nics)
|
||||||
{
|
{
|
||||||
try
|
if (nic.Supports(NetworkInterfaceComponent.IPv4))
|
||||||
{
|
{
|
||||||
PingReply pr = p.Send(ServerName, 2000);
|
var ipProps = nic.GetIPProperties();
|
||||||
if (pr.Status == IPStatus.Success)
|
var ipv4Props = ipProps.GetIPv4Properties();
|
||||||
return true;
|
if (ipv4Props.IsAutomaticPrivateAddressingActive)
|
||||||
else
|
continue;
|
||||||
return false;
|
|
||||||
}
|
return ipProps.UnicastAddresses
|
||||||
catch (Exception)
|
.Where(ua => ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||||
{
|
.Any();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ConfigureWireless()
|
public static async Task ConfigureWireless(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Add Certificates
|
// Add Certificates
|
||||||
Program.Status.UpdateStatus(null, null, "Configuring Wireless Certificates");
|
Program.Status.UpdateStatus(null, null, "Configuring Wireless Certificates");
|
||||||
CertificateInterop.AddTempCerts();
|
await CertificateInterop.AddTempCerts(cancellationToken);
|
||||||
|
|
||||||
// Add Wireless Profiles
|
// Add Wireless Profiles
|
||||||
Program.Status.UpdateStatus(null, null, "Configuring Wireless Profiles");
|
Program.Status.UpdateStatus(null, null, "Configuring Wireless Profiles");
|
||||||
@@ -197,11 +204,10 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
{
|
{
|
||||||
|
|
||||||
IntPtr wlanHandle = IntPtr.Zero;
|
IntPtr wlanHandle = IntPtr.Zero;
|
||||||
uint negotiatedVersion;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (WlanOpenHandle(1, IntPtr.Zero, out negotiatedVersion, ref wlanHandle) != 0)
|
if (WlanOpenHandle(1, IntPtr.Zero, out var negotiatedVersion, ref wlanHandle) != 0)
|
||||||
throw new NotSupportedException("This device does not support Wireless");
|
throw new NotSupportedException("This device does not support Wireless");
|
||||||
|
|
||||||
// Add Profile to Each Wireless Adapter
|
// Add Profile to Each Wireless Adapter
|
||||||
@@ -210,15 +216,16 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
{
|
{
|
||||||
foreach (var inlineWirelessProfile in wirelessInlineProfiles)
|
foreach (var inlineWirelessProfile in wirelessInlineProfiles)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
if (inlineWirelessProfile.AddProfile(wlanHandle, na.Guid))
|
if (inlineWirelessProfile.AddProfile(wlanHandle, na.Guid))
|
||||||
{
|
{
|
||||||
Program.Status.UpdateStatus(null, null, string.Format("Added Wireless Profile: {0}", inlineWirelessProfile.ProfileName));
|
Program.Status.UpdateStatus(null, null, $"Added Wireless Profile: {inlineWirelessProfile.ProfileName}");
|
||||||
Program.SleepThread(500, false);
|
await Program.SleepThread(500, false, cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Program.Status.UpdateStatus(null, null, string.Format("Unable to add Wireless Profile: {0}", inlineWirelessProfile.ProfileName));
|
Program.Status.UpdateStatus(null, null, $"Unable to add Wireless Profile: {inlineWirelessProfile.ProfileName}");
|
||||||
Program.SleepThread(5000, false);
|
await Program.SleepThread(5000, false, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +233,7 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (wlanHandle != IntPtr.Zero)
|
if (wlanHandle != IntPtr.Zero)
|
||||||
NetworkInterop.WlanCloseHandle(wlanHandle, IntPtr.Zero);
|
WlanCloseHandle(wlanHandle, IntPtr.Zero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,23 +247,23 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
public bool AddProfile(IntPtr WlanHandle, Guid interfaceGuid)
|
public bool AddProfile(IntPtr WlanHandle, Guid interfaceGuid)
|
||||||
{
|
{
|
||||||
var pInterfaceGuid = interfaceGuid;
|
var pInterfaceGuid = interfaceGuid;
|
||||||
var pProfileXml = this.ProfileXml;
|
var pProfileXml = ProfileXml;
|
||||||
uint pFlag = 0;
|
uint pFlag = 0;
|
||||||
uint failReason;
|
return WlanSetProfile(WlanHandle, ref pInterfaceGuid, pFlag, pProfileXml, null, true, IntPtr.Zero, out _) == 0;
|
||||||
return (WlanSetProfile(WlanHandle, ref pInterfaceGuid, pFlag, pProfileXml, null, true, IntPtr.Zero, out failReason) == 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<WirelessProfile> GetInlineWirelessProfiles()
|
private static List<WirelessProfile> GetInlineWirelessProfiles()
|
||||||
{
|
{
|
||||||
var inlineProfileFiles = System.IO.Directory.EnumerateFiles(Program.InlinePath.Value, "WLAN_Profile_*.xml").ToList();
|
var directoryPath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||||
|
var inlineProfileFiles = Directory.EnumerateFiles(directoryPath, "WLAN_Profile_*.xml").ToList();
|
||||||
var inlineProfiles = new List<WirelessProfile>(inlineProfileFiles.Count);
|
var inlineProfiles = new List<WirelessProfile>(inlineProfileFiles.Count);
|
||||||
foreach (var filename in inlineProfileFiles)
|
foreach (var filename in inlineProfileFiles)
|
||||||
{
|
{
|
||||||
var profile = new WirelessProfile()
|
var profile = new WirelessProfile()
|
||||||
{
|
{
|
||||||
Filename = filename,
|
Filename = filename,
|
||||||
ProfileXml = System.IO.File.ReadAllText(filename)
|
ProfileXml = File.ReadAllText(filename)
|
||||||
};
|
};
|
||||||
var profileXml = new XmlDocument();
|
var profileXml = new XmlDocument();
|
||||||
profileXml.LoadXml(profile.ProfileXml);
|
profileXml.LoadXml(profile.ProfileXml);
|
||||||
@@ -280,7 +287,7 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
WlanGetProfileList(WlanHandle, ref pInterfaceGuid, new IntPtr(), ref ppProfileList);
|
WlanGetProfileList(WlanHandle, ref pInterfaceGuid, new IntPtr(), ref ppProfileList);
|
||||||
WLAN_PROFILE_INFO_LIST wlanProfileInfoList = new WLAN_PROFILE_INFO_LIST(ppProfileList);
|
WLAN_PROFILE_INFO_LIST wlanProfileInfoList = new WLAN_PROFILE_INFO_LIST(ppProfileList);
|
||||||
|
|
||||||
NetworkInterop.WlanFreeMemory(ppProfileList);
|
WlanFreeMemory(ppProfileList);
|
||||||
|
|
||||||
return wlanProfileInfoList;
|
return wlanProfileInfoList;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper.Interop
|
namespace Disco.ClientBootstrapper.Interop
|
||||||
@@ -33,7 +30,7 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
private static extern int LookupPrivilegeValue(string lpsystemname, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
|
private static extern int LookupPrivilegeValue(string lpsystemname, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
|
||||||
|
|
||||||
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
|
||||||
private static extern int AdjustTokenPrivileges(int tokenhandle, int disableprivs, [MarshalAs(UnmanagedType.Struct)]ref TOKEN_PRIVILEGES Newstate, int bufferlength, int PreivousState, int Returnlength);
|
private static extern int AdjustTokenPrivileges(int tokenhandle, int disableprivs, [MarshalAs(UnmanagedType.Struct)] ref TOKEN_PRIVILEGES Newstate, int bufferlength, int PreivousState, int Returnlength);
|
||||||
|
|
||||||
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
private static extern int RegLoadKey(uint hKey, string lpSubKey, string lpFile);
|
private static extern int RegLoadKey(uint hKey, string lpSubKey, string lpFile);
|
||||||
@@ -61,16 +58,15 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
public RegistryInterop(RegistryHives hive, string subKey, string filePath)
|
public RegistryInterop(RegistryHives hive, string subKey, string filePath)
|
||||||
{
|
{
|
||||||
int token = 0;
|
int token = 0;
|
||||||
int retval = 0;
|
|
||||||
|
|
||||||
TOKEN_PRIVILEGES TP = new TOKEN_PRIVILEGES();
|
TOKEN_PRIVILEGES TP = new TOKEN_PRIVILEGES();
|
||||||
TOKEN_PRIVILEGES TP2 = new TOKEN_PRIVILEGES();
|
TOKEN_PRIVILEGES TP2 = new TOKEN_PRIVILEGES();
|
||||||
LUID RestoreLuid = new LUID();
|
LUID RestoreLuid = new LUID();
|
||||||
LUID BackupLuid = new LUID();
|
LUID BackupLuid = new LUID();
|
||||||
|
|
||||||
retval = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token);
|
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token);
|
||||||
retval = LookupPrivilegeValue(null, SE_RESTORE_NAME, ref RestoreLuid);
|
LookupPrivilegeValue(null, SE_RESTORE_NAME, ref RestoreLuid);
|
||||||
retval = LookupPrivilegeValue(null, SE_BACKUP_NAME, ref BackupLuid);
|
LookupPrivilegeValue(null, SE_BACKUP_NAME, ref BackupLuid);
|
||||||
TP.PrivilegeCount = 1;
|
TP.PrivilegeCount = 1;
|
||||||
TP.Attributes = SE_PRIVILEGE_ENABLED;
|
TP.Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
TP.Luid = RestoreLuid;
|
TP.Luid = RestoreLuid;
|
||||||
@@ -78,25 +74,25 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
TP2.Attributes = SE_PRIVILEGE_ENABLED;
|
TP2.Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
TP2.Luid = BackupLuid;
|
TP2.Luid = BackupLuid;
|
||||||
|
|
||||||
retval = AdjustTokenPrivileges(token, 0, ref TP, 1024, 0, 0);
|
AdjustTokenPrivileges(token, 0, ref TP, 1024, 0, 0);
|
||||||
retval = AdjustTokenPrivileges(token, 0, ref TP2, 1024, 0, 0);
|
AdjustTokenPrivileges(token, 0, ref TP2, 1024, 0, 0);
|
||||||
|
|
||||||
uint regHive = (uint)hive;
|
uint regHive = (uint)hive;
|
||||||
|
|
||||||
this.Hive = hive;
|
Hive = hive;
|
||||||
this.SubKey = subKey;
|
SubKey = subKey;
|
||||||
RegLoadKey(regHive, subKey, filePath);
|
RegLoadKey(regHive, subKey, filePath);
|
||||||
this.IsUnloaded = false;
|
IsUnloaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unload()
|
public void Unload()
|
||||||
{
|
{
|
||||||
if (!IsUnloaded)
|
if (!IsUnloaded)
|
||||||
{
|
{
|
||||||
uint regHive = (uint)this.Hive;
|
uint regHive = (uint)Hive;
|
||||||
string subKey = this.SubKey;
|
string subKey = SubKey;
|
||||||
RegUnLoadKey(regHive, subKey);
|
RegUnLoadKey(regHive, subKey);
|
||||||
this.IsUnloaded = true;
|
IsUnloaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Management;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper.Interop
|
namespace Disco.ClientBootstrapper.Interop
|
||||||
@@ -43,21 +39,20 @@ namespace Disco.ClientBootstrapper.Interop
|
|||||||
// End Removed 2012-11-23 G#
|
// End Removed 2012-11-23 G#
|
||||||
|
|
||||||
// Added 2012-11-23 G# - Migrate to Win32 PInvoke Shutdown
|
// Added 2012-11-23 G# - Migrate to Win32 PInvoke Shutdown
|
||||||
bool result;
|
|
||||||
TokPriv1Luid tp;
|
TokPriv1Luid tp;
|
||||||
|
|
||||||
IntPtr hproc = GetCurrentProcess();
|
IntPtr hproc = GetCurrentProcess();
|
||||||
IntPtr htok = IntPtr.Zero;
|
IntPtr htok = IntPtr.Zero;
|
||||||
|
|
||||||
result = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
|
OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
|
||||||
|
|
||||||
tp.Count = 1;
|
tp.Count = 1;
|
||||||
tp.Luid = 0;
|
tp.Luid = 0;
|
||||||
tp.Attr = SE_PRIVILEGE_ENABLED;
|
tp.Attr = SE_PRIVILEGE_ENABLED;
|
||||||
|
|
||||||
result = LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
|
LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
|
||||||
result = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
|
AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
|
||||||
result = ExitWindowsEx(flag, 0);
|
ExitWindowsEx(flag, 0);
|
||||||
// End Added 2012-11-23 G#
|
// End Added 2012-11-23 G#
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
||||||
{
|
{
|
||||||
@@ -140,8 +138,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
CreateFileAccessPrivate fileAccess = GetMappedFileAccess(access);
|
CreateFileAccessPrivate fileAccess = GetMappedFileAccess(access);
|
||||||
if (fileAccess == CreateFileAccessPrivate.Read && (!File.Exists(imageFilePath) || (CreateFileMode.OpenExisting != mode)))
|
if (fileAccess == CreateFileAccessPrivate.Read && (!File.Exists(imageFilePath) || (CreateFileMode.OpenExisting != mode)))
|
||||||
{
|
{
|
||||||
throw new System.UnauthorizedAccessException(string.Format(CultureInfo.CurrentCulture,
|
throw new UnauthorizedAccessException("Read access can be specified only with OpenExisting mode or OpenAlways mode when the .wim file does not exist.");
|
||||||
"Read access can be specified only with OpenExisting mode or OpenAlways mode when the .wim file does not exist."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -152,9 +149,9 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
m_ImageContainerHandle = NativeMethods.CreateFile(imageFilePath, (uint)fileAccess, (uint)mode);
|
m_ImageContainerHandle = NativeMethods.CreateFile(imageFilePath, (uint)fileAccess, (uint)mode);
|
||||||
m_WindowsImageFilePath = imageFilePath;
|
m_WindowsImageFilePath = imageFilePath;
|
||||||
}
|
}
|
||||||
catch (System.DllNotFoundException ex)
|
catch (DllNotFoundException ex)
|
||||||
{
|
{
|
||||||
throw new System.DllNotFoundException(string.Format(CultureInfo.CurrentCulture,
|
throw new DllNotFoundException(string.Format(
|
||||||
"Unable to load WIM libraries. Make sure the correct DLLs are present (Wimgapi.dll and Xmlrw.dll)."), ex.InnerException);
|
"Unable to load WIM libraries. Make sure the correct DLLs are present (Wimgapi.dll and Xmlrw.dll)."), ex.InnerException);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +161,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
//Set the temporary path so that we can write to an image. This
|
//Set the temporary path so that we can write to an image. This
|
||||||
//cannot be %TEMP% as it does not exist on Windows PE
|
//cannot be %TEMP% as it does not exist on Windows PE
|
||||||
//
|
//
|
||||||
string tempDirectory = System.Environment.GetEnvironmentVariable("systemdrive");
|
string tempDirectory = Environment.GetEnvironmentVariable("systemdrive");
|
||||||
NativeMethods.SetTemporaryPath(m_ImageContainerHandle, tempDirectory);
|
NativeMethods.SetTemporaryPath(m_ImageContainerHandle, tempDirectory);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -173,8 +170,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Unable to open the .wim file {imageFilePath}.");
|
||||||
"Unable to open the .wim file {0}.", imageFilePath));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -481,7 +477,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
//
|
//
|
||||||
//Never commit changes when destroying this object.
|
//Never commit changes when destroying this object.
|
||||||
//
|
//
|
||||||
this.DismountImage(false);
|
DismountImage(false);
|
||||||
}
|
}
|
||||||
GC.KeepAlive(this);
|
GC.KeepAlive(this);
|
||||||
}
|
}
|
||||||
@@ -576,12 +572,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
//1. Leading 0xFEFF
|
//1. Leading 0xFEFF
|
||||||
//2. Be in <IMAGE></IMAGE>
|
//2. Be in <IMAGE></IMAGE>
|
||||||
//
|
//
|
||||||
string formattedXml = String.Format(CultureInfo.InvariantCulture,
|
string formattedXml = $"{UNICODE_FILE_MARKER}{"<IMAGE>"}{imageInformation}{"</IMAGE>"}";
|
||||||
"{0}{1}{2}{3}",
|
|
||||||
UNICODE_FILE_MARKER,
|
|
||||||
"<IMAGE>",
|
|
||||||
imageInformation,
|
|
||||||
"</IMAGE>");
|
|
||||||
|
|
||||||
NativeMethods.SetImageInformation(m_ImageHandle, formattedXml);
|
NativeMethods.SetImageInformation(m_ImageHandle, formattedXml);
|
||||||
GC.KeepAlive(this);
|
GC.KeepAlive(this);
|
||||||
@@ -597,7 +588,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
//Mount the image
|
//Mount the image
|
||||||
//
|
//
|
||||||
m_MountedPath = pathToMountTo;
|
m_MountedPath = pathToMountTo;
|
||||||
NativeMethods.MountImage(pathToMountTo, m_ParentWindowsImageFilePath, m_Index, System.IO.Path.GetTempPath());
|
NativeMethods.MountImage(pathToMountTo, m_ParentWindowsImageFilePath, m_Index, Path.GetTempPath());
|
||||||
m_Mounted = true;
|
m_Mounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,7 +641,8 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
///Private null constructor
|
///Private null constructor
|
||||||
///</summary>
|
///</summary>
|
||||||
private
|
private
|
||||||
NativeMethods() { }
|
NativeMethods()
|
||||||
|
{ }
|
||||||
|
|
||||||
[DllImport("Wimgapi.dll", ExactSpelling = true,
|
[DllImport("Wimgapi.dll", ExactSpelling = true,
|
||||||
EntryPoint = "WIMCreateFile",
|
EntryPoint = "WIMCreateFile",
|
||||||
@@ -684,16 +676,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
IntPtr windowsImageHandle = IntPtr.Zero;
|
IntPtr windowsImageHandle = IntPtr.Zero;
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
|
|
||||||
windowsImageHandle = NativeMethods.WimCreateFile(imageFile, access, mode, 0, 0, out creationResult);
|
windowsImageHandle = WimCreateFile(imageFile, access, mode, 0, 0, out _);
|
||||||
rc = Marshal.GetLastWin32Error();
|
rc = Marshal.GetLastWin32Error();
|
||||||
if (windowsImageHandle == IntPtr.Zero)
|
if (windowsImageHandle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Everything failed; throw an exception
|
//Everything failed; throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Unable to open/create .wim file {imageFile}. Error = {rc}");
|
||||||
"Unable to open/create .wim file {0}. Error = {1}",
|
|
||||||
imageFile, rc));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return windowsImageHandle;
|
return windowsImageHandle;
|
||||||
@@ -721,15 +711,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
void
|
void
|
||||||
CloseHandle(IntPtr handle)
|
CloseHandle(IntPtr handle)
|
||||||
{
|
{
|
||||||
bool status = NativeMethods.WimCloseHandle(handle);
|
bool status = WimCloseHandle(handle);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
int rc = Marshal.GetLastWin32Error();
|
||||||
if (status == false)
|
if (status == false)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Unable to close image handle. Error = {rc}");
|
||||||
"Unable to close image handle. Error = {0}", rc));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,14 +746,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
void
|
void
|
||||||
SetTemporaryPath(IntPtr handle, string temporaryPath)
|
SetTemporaryPath(IntPtr handle, string temporaryPath)
|
||||||
{
|
{
|
||||||
bool status = NativeMethods.WimSetTemporaryPath(handle, temporaryPath);
|
bool status = WimSetTemporaryPath(handle, temporaryPath);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
int rc = Marshal.GetLastWin32Error();
|
||||||
if (status == false)
|
if (status == false)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to set temporary path. Error = {0}", rc));
|
throw new InvalidOperationException($"Unable to set temporary path. Error = {rc}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -794,14 +783,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
{
|
{
|
||||||
//Load the image data based on the .wim handle
|
//Load the image data based on the .wim handle
|
||||||
//
|
//
|
||||||
IntPtr hWim = NativeMethods.WimLoadImage(handle, (uint)imageIndex);
|
IntPtr hWim = WimLoadImage(handle, (uint)imageIndex);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
int rc = Marshal.GetLastWin32Error();
|
||||||
if (hWim == IntPtr.Zero)
|
if (hWim == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to load image. Error = {0}", rc));
|
throw new InvalidOperationException($"Unable to load image. Error = {rc}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return hWim;
|
return hWim;
|
||||||
@@ -832,15 +821,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
IntPtr
|
IntPtr
|
||||||
CaptureImage(IntPtr handle, string path)
|
CaptureImage(IntPtr handle, string path)
|
||||||
{
|
{
|
||||||
IntPtr hImage = NativeMethods.WimCaptureImage(handle, path, 0);
|
IntPtr hImage = WimCaptureImage(handle, path, 0);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
int rc = Marshal.GetLastWin32Error();
|
||||||
if (hImage == IntPtr.Zero)
|
if (hImage == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Failed to capture image from {path}. Error = {rc}");
|
||||||
"Failed to capture image from {0}. Error = {1}", path, rc));
|
|
||||||
}
|
}
|
||||||
return hImage;
|
return hImage;
|
||||||
}
|
}
|
||||||
@@ -873,14 +861,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
int
|
int
|
||||||
GetImageCount(IntPtr windowsImageHandle)
|
GetImageCount(IntPtr windowsImageHandle)
|
||||||
{
|
{
|
||||||
int count = NativeMethods.WimGetImageCount(windowsImageHandle);
|
int count = WimGetImageCount(windowsImageHandle);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
int rc = Marshal.GetLastWin32Error();
|
||||||
if (count == -1)
|
if (count == -1)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to get image count. Error = {0}", rc));
|
throw new InvalidOperationException($"Unable to get image count. Error = {rc}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
@@ -971,28 +959,25 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
status = NativeMethods.WimMountImage(mountPath,
|
status = WimMountImage(mountPath,
|
||||||
windowsImageFileName,
|
windowsImageFileName,
|
||||||
(uint)imageIndex,
|
(uint)imageIndex,
|
||||||
temporaryPath);
|
temporaryPath);
|
||||||
rc = Marshal.GetLastWin32Error();
|
rc = Marshal.GetLastWin32Error();
|
||||||
}
|
}
|
||||||
catch (System.StackOverflowException)
|
catch (StackOverflowException)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Unable to mount image {windowsImageFileName} to {mountPath}.");
|
||||||
"Unable to mount image {0} to {1}.", windowsImageFileName, mountPath));
|
|
||||||
}
|
}
|
||||||
if (status == false)
|
if (status == false)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Unable to mount image {windowsImageFileName} to {mountPath}. Error = {rc}");
|
||||||
"Unable to mount image {0} to {1}. Error = {2}",
|
|
||||||
windowsImageFileName, mountPath, rc));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1022,15 +1007,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
//
|
//
|
||||||
//Call WimApplyImage always with the Index flag for performance reasons.
|
//Call WimApplyImage always with the Index flag for performance reasons.
|
||||||
//
|
//
|
||||||
bool status = NativeMethods.WimApplyImage(imageHandle, applicationPath, NativeMethods.WIM_FLAG_INDEX);
|
bool status = WimApplyImage(imageHandle, applicationPath, WIM_FLAG_INDEX);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
int rc = Marshal.GetLastWin32Error();
|
||||||
if (status == false)
|
if (status == false)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Unable to apply image to {applicationPath}. Error = {rc}");
|
||||||
"Unable to apply image to {0}. Error = {1}", applicationPath, rc));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1062,7 +1046,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
IntPtr info = IntPtr.Zero, sizeOfInfo = IntPtr.Zero;
|
IntPtr info = IntPtr.Zero, sizeOfInfo = IntPtr.Zero;
|
||||||
bool status;
|
bool status;
|
||||||
|
|
||||||
status = NativeMethods.WimGetImageInformation(handle, out info, out sizeOfInfo);
|
status = WimGetImageInformation(handle, out info, out _);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
int rc = Marshal.GetLastWin32Error();
|
||||||
|
|
||||||
if (status == false)
|
if (status == false)
|
||||||
@@ -1070,8 +1054,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Unable to get image information. Error = {rc}");
|
||||||
"Unable to get image information. Error = {0}", rc));
|
|
||||||
}
|
}
|
||||||
string s = Marshal.PtrToStringUni(info);
|
string s = Marshal.PtrToStringUni(info);
|
||||||
|
|
||||||
@@ -1112,15 +1095,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
IntPtr xmlBuffer = Marshal.AllocHGlobal(byteBufferSize);
|
IntPtr xmlBuffer = Marshal.AllocHGlobal(byteBufferSize);
|
||||||
Marshal.Copy(byteBuffer, 0, xmlBuffer, byteBufferSize);
|
Marshal.Copy(byteBuffer, 0, xmlBuffer, byteBufferSize);
|
||||||
|
|
||||||
bool status = NativeMethods.WimSetImageInformation(handle, xmlBuffer, (uint)byteBufferSize);
|
bool status = WimSetImageInformation(handle, xmlBuffer, (uint)byteBufferSize);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
int rc = Marshal.GetLastWin32Error();
|
||||||
if (status == false)
|
if (status == false)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Unable to set image information. Error = {rc}");
|
||||||
"Unable to set image information. Error = {0}", rc));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1153,16 +1135,15 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
status = NativeMethods.WimUnmountImage(mountPath, wimdowsImageFileName, (uint)imageIndex, commitChanges);
|
status = WimUnmountImage(mountPath, wimdowsImageFileName, (uint)imageIndex, commitChanges);
|
||||||
rc = Marshal.GetLastWin32Error();
|
rc = Marshal.GetLastWin32Error();
|
||||||
}
|
}
|
||||||
catch (System.StackOverflowException ex)
|
catch (StackOverflowException ex)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.StackOverflowException(string.Format(CultureInfo.CurrentCulture,
|
throw new StackOverflowException($"Unable to unmount image {wimdowsImageFileName} from {mountPath}.",
|
||||||
"Unable to unmount image {0} from {1}.", wimdowsImageFileName, mountPath),
|
|
||||||
ex.InnerException);
|
ex.InnerException);
|
||||||
}
|
}
|
||||||
if (status == false)
|
if (status == false)
|
||||||
@@ -1170,9 +1151,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
throw new InvalidOperationException($"Unable to unmount image {wimdowsImageFileName} from {mountPath}. Error = {rc}");
|
||||||
"Unable to unmount image {0} from {1}. Error = {2}",
|
|
||||||
wimdowsImageFileName, mountPath, rc));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1221,14 +1200,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
void
|
void
|
||||||
RegisterCallback(MessageCallback callback)
|
RegisterCallback(MessageCallback callback)
|
||||||
{
|
{
|
||||||
uint callbackZeroBasedIndex = NativeMethods.WimRegisterMessageCallback(IntPtr.Zero, callback, IntPtr.Zero);
|
WimRegisterMessageCallback(IntPtr.Zero, callback, IntPtr.Zero);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
int rc = Marshal.GetLastWin32Error();
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
//Throw an exception
|
//Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to register message callback."));
|
throw new InvalidOperationException("Unable to register message callback.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1253,14 +1232,14 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
void
|
void
|
||||||
UnregisterMessageCallback(MessageCallback registeredCallback)
|
UnregisterMessageCallback(MessageCallback registeredCallback)
|
||||||
{
|
{
|
||||||
bool status = NativeMethods.WimUnregisterMessageCallback(IntPtr.Zero, registeredCallback);
|
bool status = WimUnregisterMessageCallback(IntPtr.Zero, registeredCallback);
|
||||||
int rc = Marshal.GetLastWin32Error();
|
_ = Marshal.GetLastWin32Error();
|
||||||
if (status != true)
|
if (status != true)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Throw an exception
|
// Throw an exception
|
||||||
//
|
//
|
||||||
throw new System.InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unable to unregister message callback."));
|
throw new InvalidOperationException("Unable to unregister message callback.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1358,13 +1337,13 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
fileAccess = CreateFileAccessPrivate.Write;
|
fileAccess = CreateFileAccessPrivate.Write;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "No file access level specified."));
|
throw new ArgumentException("No file access level specified.");
|
||||||
}
|
}
|
||||||
return fileAccess;
|
return fileAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
//[CLSCompliant(false)]
|
//[CLSCompliant(false)]
|
||||||
[FlagsAttribute]
|
[Flags]
|
||||||
private
|
private
|
||||||
enum
|
enum
|
||||||
CreateFileAccessPrivate : uint
|
CreateFileAccessPrivate : uint
|
||||||
@@ -1436,7 +1415,7 @@ namespace Disco.ClientBootstrapper.Interop.WIMInterop
|
|||||||
///Abort.
|
///Abort.
|
||||||
///</summary>
|
///</summary>
|
||||||
Abort = NativeMethods.WIMMessage.WIM_MSG_ABORT_IMAGE,
|
Abort = NativeMethods.WIMMessage.WIM_MSG_ABORT_IMAGE,
|
||||||
|
|
||||||
MountCleanupProgress = NativeMethods.WIMMessage.WIM_MSG_MOUNT_CLEANUP_PROGRESS
|
MountCleanupProgress = NativeMethods.WIMMessage.WIM_MSG_MOUNT_CLEANUP_PROGRESS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
namespace Disco.ClientBootstrapper
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper
|
|
||||||
{
|
{
|
||||||
class NullStatus : IStatus
|
class NullStatus : IStatus
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,36 +1,58 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace Disco.ClientBootstrapper
|
namespace Disco.ClientBootstrapper
|
||||||
{
|
{
|
||||||
static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
public static IStatus Status { get; set; }
|
private static readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||||
public static BootstrapperLoop BootstrapperLoop { get; set; }
|
public static IStatus Status { get; private set; }
|
||||||
public static InstallLoop InstallLoop { get; set; }
|
|
||||||
|
|
||||||
public static List<string> PostBootstrapperActions { get; set; }
|
public static List<string> PostBootstrapperActions { get; set; }
|
||||||
public static bool AllowUninstall { get; set; }
|
public static bool AllowUninstall { get; private set; }
|
||||||
public static bool ApplicationExiting { get; set; }
|
public static Uri ForcedServerUrl { get; private set; } = null;
|
||||||
public static Lazy<string> InlinePath = new Lazy<string>(() =>
|
|
||||||
{
|
|
||||||
return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
|
||||||
});
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The main entry point for the application.
|
/// The main entry point for the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[STAThread]
|
[STAThread]
|
||||||
static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
|
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
|
||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||||
|
|
||||||
|
|
||||||
if (args.Length > 0)
|
if (args.Length > 0)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (args.Any(a => a.Equals("debug", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Console.WriteLine("Waiting for Debugger to Attach");
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
} while (!System.Diagnostics.Debugger.IsAttached);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (args.Any(a => a.StartsWith("http://", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
throw new ArgumentException("Only HTTPS URLs are supported for a forced server URL.");
|
||||||
|
var forcedServerArg = args.FirstOrDefault(a => a.StartsWith("https://", StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (forcedServerArg != null)
|
||||||
|
{
|
||||||
|
if (Uri.TryCreate(forcedServerArg, UriKind.Absolute, out var forcedUri))
|
||||||
|
ForcedServerUrl = forcedUri;
|
||||||
|
else
|
||||||
|
throw new ArgumentException("The provided forced server URL is not valid.");
|
||||||
|
}
|
||||||
|
|
||||||
switch (args[0].ToLower())
|
switch (args[0].ToLower())
|
||||||
{
|
{
|
||||||
case "/install":
|
case "/install":
|
||||||
@@ -46,14 +68,17 @@ namespace Disco.ClientBootstrapper
|
|||||||
wimImage = args[2];
|
wimImage = args[2];
|
||||||
if (args.Length > 3)
|
if (args.Length > 3)
|
||||||
tempPath = args[3];
|
tempPath = args[3];
|
||||||
InstallLoop = new InstallLoop(installLocation, wimImage, tempPath);
|
var installLoop = new InstallLoop(installLocation, wimImage, tempPath, InstallComplete, ForcedServerUrl);
|
||||||
InstallLoop.Start(new InstallLoop.CompleteCallback(InstallComplete));
|
installLoop.Start();
|
||||||
Application.Run();
|
Application.Run();
|
||||||
return;
|
return;
|
||||||
case "/uninstall":
|
case "/uninstall":
|
||||||
AllowUninstall = true;
|
AllowUninstall = true;
|
||||||
Status = new NullStatus();
|
Status = new NullStatus();
|
||||||
Interop.InstallInterop.Uninstall();
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Interop.InstallInterop.Uninstall(cancellationTokenSource.Token);
|
||||||
|
}).Wait(cancellationTokenSource.Token);
|
||||||
return;
|
return;
|
||||||
case "/allowuninstall":
|
case "/allowuninstall":
|
||||||
AllowUninstall = true;
|
AllowUninstall = true;
|
||||||
@@ -71,13 +96,13 @@ namespace Disco.ClientBootstrapper
|
|||||||
statusForm.Show();
|
statusForm.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
BootstrapperLoop = new BootstrapperLoop(Status, new BootstrapperLoop.LoopCompleteCallback(LoopComplete));
|
var bootstrapperLoop = new BootstrapperLoop(Status, ForcedServerUrl, LoopComplete, cancellationTokenSource.Token);
|
||||||
BootstrapperLoop.Start();
|
bootstrapperLoop.Start();
|
||||||
|
|
||||||
Application.Run();
|
Application.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
|
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
WriteAppError(e.Exception);
|
WriteAppError(e.Exception);
|
||||||
}
|
}
|
||||||
@@ -86,21 +111,21 @@ namespace Disco.ClientBootstrapper
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string AppErrorPath = string.Format("{0}{1}", System.Reflection.Assembly.GetExecutingAssembly().Location, ".errors.txt");
|
string AppErrorPath = $"{System.Reflection.Assembly.GetExecutingAssembly().Location}.errors.txt";
|
||||||
System.Text.StringBuilder ErrorMessage = new System.Text.StringBuilder();
|
System.Text.StringBuilder ErrorMessage = new System.Text.StringBuilder();
|
||||||
ErrorMessage.AppendLine();
|
ErrorMessage.AppendLine();
|
||||||
ErrorMessage.AppendLine(DateTime.Now.ToLongDateString());
|
ErrorMessage.AppendLine(DateTime.Now.ToLongDateString());
|
||||||
ErrorMessage.AppendLine(DateTime.Now.ToLongTimeString());
|
ErrorMessage.AppendLine(DateTime.Now.ToLongTimeString());
|
||||||
ErrorMessage.AppendLine(string.Format("Type: {0}", ex.GetType().FullName));
|
ErrorMessage.AppendLine($"Type: {ex.GetType().FullName}");
|
||||||
ErrorMessage.AppendLine(string.Format("Message: {0}", ex.Message));
|
ErrorMessage.AppendLine($"Message: {ex.Message}");
|
||||||
ErrorMessage.AppendLine(string.Format("Source: {0}", ex.Source));
|
ErrorMessage.AppendLine($"Source: {ex.Source}");
|
||||||
ErrorMessage.AppendLine(string.Format("Stack: {0}", ex.StackTrace));
|
ErrorMessage.AppendLine($"Stack: {ex.StackTrace}");
|
||||||
System.IO.File.AppendAllText(AppErrorPath, ErrorMessage.ToString());
|
System.IO.File.AppendAllText(AppErrorPath, ErrorMessage.ToString());
|
||||||
}
|
}
|
||||||
catch (Exception) { }
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LoopComplete()
|
public static async Task LoopComplete(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Run Post Actions
|
// Run Post Actions
|
||||||
if (PostBootstrapperActions != null)
|
if (PostBootstrapperActions != null)
|
||||||
@@ -108,33 +133,32 @@ namespace Disco.ClientBootstrapper
|
|||||||
// Check Uninstall
|
// Check Uninstall
|
||||||
if (AllowUninstall && PostBootstrapperActions.Contains("UninstallBootstrapper"))
|
if (AllowUninstall && PostBootstrapperActions.Contains("UninstallBootstrapper"))
|
||||||
{
|
{
|
||||||
Interop.InstallInterop.Uninstall();
|
await Interop.InstallInterop.Uninstall(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check ShutdownActions
|
// Check ShutdownActions
|
||||||
if (PostBootstrapperActions.Contains("Shutdown"))
|
if (PostBootstrapperActions.Contains("Shutdown"))
|
||||||
{
|
{
|
||||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Shutting Down; Finished...", string.Empty, false, 0);
|
Status.UpdateStatus("System Preparation (Bootstrapper)", "Shutting Down; Finished...", string.Empty, false, 0);
|
||||||
SleepThread(4000, true);
|
await SleepThread(4000, true, cancellationToken);
|
||||||
Interop.ShutdownInterop.Shutdown();
|
Interop.ShutdownInterop.Shutdown();
|
||||||
}
|
}
|
||||||
|
else if (PostBootstrapperActions.Contains("Reboot"))
|
||||||
|
{
|
||||||
|
Status.UpdateStatus("System Preparation (Bootstrapper)", "Rebooting; Finished...", string.Empty, false, 0);
|
||||||
|
await SleepThread(4000, true, cancellationToken);
|
||||||
|
Interop.ShutdownInterop.Reboot();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
if (PostBootstrapperActions.Contains("Reboot"))
|
{
|
||||||
{
|
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
|
||||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Rebooting; Finished...", string.Empty, false, 0);
|
await SleepThread(2000, true, cancellationToken);
|
||||||
SleepThread(4000, true);
|
}
|
||||||
Interop.ShutdownInterop.Reboot();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
|
|
||||||
SleepThread(2000, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
|
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
|
||||||
SleepThread(2000, true);
|
await SleepThread(2000, true, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitApplication();
|
ExitApplication();
|
||||||
@@ -147,33 +171,12 @@ namespace Disco.ClientBootstrapper
|
|||||||
|
|
||||||
public static void ExitApplication()
|
public static void ExitApplication()
|
||||||
{
|
{
|
||||||
if (!ApplicationExiting)
|
if (!cancellationTokenSource.IsCancellationRequested)
|
||||||
{
|
cancellationTokenSource.Cancel();
|
||||||
ApplicationExiting = true;
|
Application.Exit();
|
||||||
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 void Trace(string Format, params string[] args)
|
public static async Task SleepThread(int millisecondsTimeout, bool updateUI, CancellationToken cancellationToken)
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine(Format, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SleepThread(int millisecondsTimeout, bool updateUI)
|
|
||||||
{
|
{
|
||||||
if (updateUI)
|
if (updateUI)
|
||||||
{
|
{
|
||||||
@@ -181,12 +184,12 @@ namespace Disco.ClientBootstrapper
|
|||||||
{
|
{
|
||||||
int progress = Convert.ToInt32(((Convert.ToDouble(i) / Convert.ToDouble(millisecondsTimeout)) * 100));
|
int progress = Convert.ToInt32(((Convert.ToDouble(i) / Convert.ToDouble(millisecondsTimeout)) * 100));
|
||||||
Status.UpdateStatus(null, null, null, true, progress);
|
Status.UpdateStatus(null, null, null, true, progress);
|
||||||
Thread.Sleep(500);
|
await Task.Delay(500, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Thread.Sleep(millisecondsTimeout);
|
await Task.Delay(millisecondsTimeout, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("2.4.24270.0000")]
|
[assembly: AssemblyVersion("2.5.25262.0000")]
|
||||||
[assembly: AssemblyFileVersion("2.4.24270.0000")]
|
[assembly: AssemblyFileVersion("2.5.25262.0000")]
|
||||||
@@ -28,17 +28,17 @@ namespace Disco.Data.Configuration
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return ConfigurationCache.GetScopeKeys(Database, this.Scope);
|
return ConfigurationCache.GetScopeKeys(Database, Scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetValue<T>(string Key, T Value)
|
private void SetValue<T>(string Key, T Value)
|
||||||
{
|
{
|
||||||
ConfigurationCache.Helpers<T>.SetValue(Database, this.Scope, Key, Value);
|
ConfigurationCache.Helpers<T>.SetValue(Database, Scope, Key, Value);
|
||||||
}
|
}
|
||||||
private T GetValue<T>(string Key, T Default)
|
private T GetValue<T>(string Key, T Default)
|
||||||
{
|
{
|
||||||
return ConfigurationCache.Helpers<T>.GetValue(Database, this.Scope, Key, Default);
|
return ConfigurationCache.Helpers<T>.GetValue(Database, Scope, Key, Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Set<T>(T Value, [CallerMemberName] string Key = null)
|
protected void Set<T>(T Value, [CallerMemberName] string Key = null)
|
||||||
@@ -46,7 +46,7 @@ namespace Disco.Data.Configuration
|
|||||||
if (Key == null)
|
if (Key == null)
|
||||||
throw new ArgumentNullException("Key");
|
throw new ArgumentNullException("Key");
|
||||||
|
|
||||||
this.SetValue(Key, Value);
|
SetValue(Key, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected T Get<T>(T Default, [CallerMemberName] string Key = null)
|
protected T Get<T>(T Default, [CallerMemberName] string Key = null)
|
||||||
@@ -54,17 +54,17 @@ namespace Disco.Data.Configuration
|
|||||||
if (Key == null)
|
if (Key == null)
|
||||||
throw new ArgumentNullException("Key");
|
throw new ArgumentNullException("Key");
|
||||||
|
|
||||||
return this.GetValue(Key, Default);
|
return GetValue(Key, Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetObsfucated(string Value, [CallerMemberName] string Key = null)
|
protected void SetObsfucated(string Value, [CallerMemberName] string Key = null)
|
||||||
{
|
{
|
||||||
this.Set(Value.Obsfucate(), Key);
|
Set(Value.Obsfucate(), Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected string GetDeobsfucated(string Default, [CallerMemberName] string Key = null)
|
protected string GetDeobsfucated(string Default, [CallerMemberName] string Key = null)
|
||||||
{
|
{
|
||||||
var obsfucatedValue = this.Get<string>(null, Key);
|
var obsfucatedValue = Get<string>(null, Key);
|
||||||
|
|
||||||
if (obsfucatedValue == null)
|
if (obsfucatedValue == null)
|
||||||
return Default;
|
return Default;
|
||||||
@@ -74,35 +74,35 @@ namespace Disco.Data.Configuration
|
|||||||
|
|
||||||
protected void RemoveScope()
|
protected void RemoveScope()
|
||||||
{
|
{
|
||||||
ConfigurationCache.RemoveScope(Database, this.Scope);
|
ConfigurationCache.RemoveScope(Database, Scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void RemoveItem(string Key)
|
protected void RemoveItem(string Key)
|
||||||
{
|
{
|
||||||
ConfigurationCache.RemoveScopeKey(Database, this.Scope, Key);
|
ConfigurationCache.RemoveScopeKey(Database, Scope, Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
|
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
|
||||||
protected void SetAsJson<T>(T Value, [CallerMemberName] string Key = null)
|
protected void SetAsJson<T>(T Value, [CallerMemberName] string Key = null)
|
||||||
{
|
{
|
||||||
this.Set(Value, Key);
|
Set(Value, Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Types are automatically serialized/deserialized if required, use Get<T>(T Default) instead.")]
|
[Obsolete("Types are automatically serialized/deserialized if required, use Get<T>(T Default) instead.")]
|
||||||
protected T GetFromJson<T>(T Default, [CallerMemberName] string Key = null)
|
protected T GetFromJson<T>(T Default, [CallerMemberName] string Key = null)
|
||||||
{
|
{
|
||||||
return this.Get(Default, Key);
|
return Get(Default, Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
|
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
|
||||||
protected void SetAsEnum<T>(T Value, [CallerMemberName] string Key = null)
|
protected void SetAsEnum<T>(T Value, [CallerMemberName] string Key = null)
|
||||||
{
|
{
|
||||||
this.Set(Value, Key);
|
Set(Value, Key);
|
||||||
}
|
}
|
||||||
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
|
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
|
||||||
protected T GetFromEnum<T>(T Default, [CallerMemberName] string Key = null)
|
protected T GetFromEnum<T>(T Default, [CallerMemberName] string Key = null)
|
||||||
{
|
{
|
||||||
return this.Get(Default, Key);
|
return Get(Default, Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ namespace Disco.Data.Configuration
|
|||||||
|
|
||||||
private static ConfigurationCacheType Cache(DiscoDataContext Database)
|
private static ConfigurationCacheType Cache(DiscoDataContext Database)
|
||||||
{
|
{
|
||||||
if (ConfigurationCache.cacheStore == null)
|
if (cacheStore == null)
|
||||||
{
|
{
|
||||||
lock (configChangeLock)
|
lock (configChangeLock)
|
||||||
{
|
{
|
||||||
if (ConfigurationCache.cacheStore == null)
|
if (cacheStore == null)
|
||||||
{
|
{
|
||||||
if (Database == null)
|
if (Database == null)
|
||||||
throw new InvalidOperationException("The Configuration must be loaded at least once before a Cache-Only Configuration Context is used");
|
throw new InvalidOperationException("The Configuration must be loaded at least once before a Cache-Only Configuration Context is used");
|
||||||
@@ -46,18 +46,16 @@ namespace Disco.Data.Configuration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConfigurationCache.cacheStore;
|
return cacheStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigurationCacheItemType CacheGetItem(DiscoDataContext Database, string Scope, string Key)
|
private static ConfigurationCacheItemType CacheGetItem(DiscoDataContext Database, string Scope, string Key)
|
||||||
{
|
{
|
||||||
var cache = Cache(Database);
|
var cache = Cache(Database);
|
||||||
|
|
||||||
ConfigurationCacheScopeType scopeCache;
|
if (cache.TryGetValue(Scope, out var scopeCache))
|
||||||
if (cache.TryGetValue(Scope, out scopeCache))
|
|
||||||
{
|
{
|
||||||
ConfigurationCacheItemType item = default(ConfigurationCacheItemType);
|
if (scopeCache.TryGetValue(Key, out var item))
|
||||||
if (scopeCache.TryGetValue(Key, out item))
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,8 +91,7 @@ namespace Disco.Data.Configuration
|
|||||||
Database.ConfigurationItems.Add(configItem);
|
Database.ConfigurationItems.Add(configItem);
|
||||||
|
|
||||||
// Add Item to Cache
|
// Add Item to Cache
|
||||||
ConfigurationCacheScopeType scopeCache;
|
if (!cacheStore.TryGetValue(Scope, out var scopeCache))
|
||||||
if (!cacheStore.TryGetValue(Scope, out scopeCache))
|
|
||||||
{
|
{
|
||||||
scopeCache = new ConfigurationCacheScopeType();
|
scopeCache = new ConfigurationCacheScopeType();
|
||||||
cacheStore.TryAdd(Scope, scopeCache);
|
cacheStore.TryAdd(Scope, scopeCache);
|
||||||
@@ -134,10 +131,9 @@ namespace Disco.Data.Configuration
|
|||||||
Database.ConfigurationItems.Remove(configItem);
|
Database.ConfigurationItems.Remove(configItem);
|
||||||
|
|
||||||
// Remove item from Cache
|
// Remove item from Cache
|
||||||
ConfigurationCacheScopeType scopeCache;
|
if (cacheStore.TryGetValue(Scope, out var scopeCache))
|
||||||
if (cacheStore.TryGetValue(Scope, out scopeCache))
|
|
||||||
{
|
{
|
||||||
scopeCache.TryRemove(Key, out item);
|
scopeCache.TryRemove(Key, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -148,8 +144,7 @@ namespace Disco.Data.Configuration
|
|||||||
configItem.Value = Value;
|
configItem.Value = Value;
|
||||||
|
|
||||||
// Update Cache
|
// Update Cache
|
||||||
ConfigurationCacheScopeType scopeCache;
|
if (cacheStore.TryGetValue(Scope, out var scopeCache))
|
||||||
if (cacheStore.TryGetValue(Scope, out scopeCache))
|
|
||||||
{
|
{
|
||||||
scopeCache.TryRemove(Key, out item);
|
scopeCache.TryRemove(Key, out item);
|
||||||
item = new ConfigurationCacheItemType(configItem, ObjectValue);
|
item = new ConfigurationCacheItemType(configItem, ObjectValue);
|
||||||
@@ -165,10 +160,9 @@ namespace Disco.Data.Configuration
|
|||||||
}
|
}
|
||||||
private static ConfigurationCacheItemType SetItemTypeValue(ConfigurationCacheItemType ExistingItem, object Value)
|
private static ConfigurationCacheItemType SetItemTypeValue(ConfigurationCacheItemType ExistingItem, object Value)
|
||||||
{
|
{
|
||||||
var cache = ConfigurationCache.cacheStore;
|
var cache = cacheStore;
|
||||||
|
|
||||||
ConfigurationCacheScopeType scopeCache;
|
if (cache.TryGetValue(ExistingItem.Item1.Scope, out var scopeCache))
|
||||||
if (cache.TryGetValue(ExistingItem.Item1.Scope, out scopeCache))
|
|
||||||
{
|
{
|
||||||
ConfigurationCacheItemType newItem = new ConfigurationCacheItemType(ExistingItem.Item1, Value);
|
ConfigurationCacheItemType newItem = new ConfigurationCacheItemType(ExistingItem.Item1, Value);
|
||||||
scopeCache.TryUpdate(ExistingItem.Item1.Key, newItem, ExistingItem);
|
scopeCache.TryUpdate(ExistingItem.Item1.Key, newItem, ExistingItem);
|
||||||
@@ -183,18 +177,18 @@ namespace Disco.Data.Configuration
|
|||||||
#region Helpers
|
#region Helpers
|
||||||
private static bool IsConvertableFromString(Type t)
|
private static bool IsConvertableFromString(Type t)
|
||||||
{
|
{
|
||||||
if (t == typeof(Boolean) ||
|
if (t == typeof(bool) ||
|
||||||
t == typeof(Char) ||
|
t == typeof(char) ||
|
||||||
t == typeof(SByte) ||
|
t == typeof(sbyte) ||
|
||||||
t == typeof(Byte) ||
|
t == typeof(byte) ||
|
||||||
t == typeof(Int16) || t == typeof(UInt16) ||
|
t == typeof(short) || t == typeof(ushort) ||
|
||||||
t == typeof(Int32) || t == typeof(UInt32) ||
|
t == typeof(int) || t == typeof(uint) ||
|
||||||
t == typeof(Int64) || t == typeof(UInt64) ||
|
t == typeof(long) || t == typeof(ulong) ||
|
||||||
t == typeof(Single) ||
|
t == typeof(float) ||
|
||||||
t == typeof(Double) ||
|
t == typeof(double) ||
|
||||||
t == typeof(Decimal) ||
|
t == typeof(decimal) ||
|
||||||
t == typeof(DateTime) ||
|
t == typeof(DateTime) ||
|
||||||
t == typeof(String))
|
t == typeof(string))
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
@@ -242,9 +236,34 @@ namespace Disco.Data.Configuration
|
|||||||
}
|
}
|
||||||
else if (itemType.BaseType != null && itemType.BaseType == typeof(Enum))
|
else if (itemType.BaseType != null && itemType.BaseType == typeof(Enum))
|
||||||
{
|
{
|
||||||
// Enum
|
// enum
|
||||||
itemValue = Enum.Parse(typeof(T), item.Item1.Value);
|
itemValue = Enum.Parse(typeof(T), item.Item1.Value);
|
||||||
}
|
}
|
||||||
|
else if (itemType.IsGenericType &&
|
||||||
|
itemType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
|
||||||
|
IsConvertableFromString(Nullable.GetUnderlyingType(itemType)))
|
||||||
|
{
|
||||||
|
// nullable
|
||||||
|
itemValue = (T)Convert.ChangeType(item.Item1.Value, Nullable.GetUnderlyingType(itemType));
|
||||||
|
}
|
||||||
|
else if (itemType == typeof(Guid))
|
||||||
|
{
|
||||||
|
// guid
|
||||||
|
itemValue = new Guid(item.Item1.Value);
|
||||||
|
}
|
||||||
|
else if (itemType == typeof(Guid?))
|
||||||
|
{
|
||||||
|
// guid
|
||||||
|
if (string.IsNullOrEmpty(item.Item1.Value))
|
||||||
|
itemValue = null;
|
||||||
|
else
|
||||||
|
itemValue = new Guid(item.Item1.Value);
|
||||||
|
}
|
||||||
|
else if (itemType == typeof(byte[]))
|
||||||
|
{
|
||||||
|
// byte[]
|
||||||
|
itemValue = Convert.FromBase64String(item.Item1.Value);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// JSON Deserialize
|
// JSON Deserialize
|
||||||
@@ -263,13 +282,13 @@ namespace Disco.Data.Configuration
|
|||||||
Type valueType = typeof(T);
|
Type valueType = typeof(T);
|
||||||
string stringValue;
|
string stringValue;
|
||||||
|
|
||||||
if (comparer.Equals(Value, default(T)))
|
if (comparer.Equals(Value, default))
|
||||||
{
|
{
|
||||||
stringValue = null;
|
stringValue = null;
|
||||||
}
|
}
|
||||||
else if (valueType == typeof(object))
|
else if (valueType == typeof(object))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format("Cannot serialize the configuration item [{0}].[{1}] which defines a type of [System.Object]", Scope, Key), "Value");
|
throw new ArgumentException($"Cannot serialize the configuration item [{Scope}].[{Key}] which has the type [System.Object]", "Value");
|
||||||
}
|
}
|
||||||
else if (IsConvertableFromString(valueType))
|
else if (IsConvertableFromString(valueType))
|
||||||
{
|
{
|
||||||
@@ -278,9 +297,25 @@ namespace Disco.Data.Configuration
|
|||||||
}
|
}
|
||||||
else if (valueType.BaseType != null && valueType.BaseType == typeof(Enum))
|
else if (valueType.BaseType != null && valueType.BaseType == typeof(Enum))
|
||||||
{
|
{
|
||||||
// Enum
|
// enum
|
||||||
stringValue = Value.ToString();
|
stringValue = Value.ToString();
|
||||||
}
|
}
|
||||||
|
else if (valueType.IsGenericType &&
|
||||||
|
valueType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
|
||||||
|
IsConvertableFromString(Nullable.GetUnderlyingType(valueType)))
|
||||||
|
{
|
||||||
|
// nullable
|
||||||
|
stringValue = Value.ToString();
|
||||||
|
}
|
||||||
|
else if (valueType == typeof(Guid) || valueType == typeof(Guid?))
|
||||||
|
{
|
||||||
|
stringValue = Value.ToString();
|
||||||
|
}
|
||||||
|
else if (Value is byte[] valueBytes)
|
||||||
|
{
|
||||||
|
// byte[]
|
||||||
|
stringValue = Convert.ToBase64String(valueBytes);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// JSON Serialize
|
// JSON Serialize
|
||||||
@@ -290,7 +325,7 @@ namespace Disco.Data.Configuration
|
|||||||
CacheSetItem(Database, Scope, Key, stringValue, Value);
|
CacheSetItem(Database, Scope, Key, stringValue, Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Cache Helpers
|
#region Cache Helpers
|
||||||
@@ -299,8 +334,7 @@ namespace Disco.Data.Configuration
|
|||||||
{
|
{
|
||||||
var cache = Cache(Database);
|
var cache = Cache(Database);
|
||||||
|
|
||||||
ConfigurationCacheScopeType scopeCache;
|
if (cache.TryGetValue(Scope, out var scopeCache))
|
||||||
if (cache.TryGetValue(Scope, out scopeCache))
|
|
||||||
{
|
{
|
||||||
return scopeCache.Keys.ToList();
|
return scopeCache.Keys.ToList();
|
||||||
}
|
}
|
||||||
@@ -324,8 +358,7 @@ namespace Disco.Data.Configuration
|
|||||||
// Remove item from Cache
|
// Remove item from Cache
|
||||||
if (cacheStore != null)
|
if (cacheStore != null)
|
||||||
{
|
{
|
||||||
ConfigurationCacheScopeType scopeCache;
|
cacheStore.TryRemove(Scope, out var scopeCache);
|
||||||
cacheStore.TryRemove(Scope, out scopeCache);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,10 +398,9 @@ namespace Disco.Data.Configuration
|
|||||||
// Remove item from Cache
|
// Remove item from Cache
|
||||||
if (cacheItem != null)
|
if (cacheItem != null)
|
||||||
{
|
{
|
||||||
ConfigurationCacheScopeType scopeCache;
|
if (cacheStore.TryGetValue(Scope, out var scopeCache))
|
||||||
if (cacheStore.TryGetValue(Scope, out scopeCache))
|
|
||||||
{
|
{
|
||||||
scopeCache.TryRemove(Key, out cacheItem);
|
scopeCache.TryRemove(Key, out _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
|
|
||||||
public bool? SearchAllServers
|
public bool? SearchAllServers
|
||||||
{
|
{
|
||||||
get {
|
get
|
||||||
|
{
|
||||||
var value = Get<bool?>(null);
|
var value = Get<bool?>(null);
|
||||||
|
|
||||||
/// migrate <see cref="SearchAllForestServers"/>
|
/// migrate <see cref="SearchAllForestServers"/>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.Services.Devices.DeviceFlag;
|
||||||
|
|
||||||
|
namespace Disco.Data.Configuration.Modules
|
||||||
|
{
|
||||||
|
public class DeviceFlagsConfiguration : ConfigurationBase
|
||||||
|
{
|
||||||
|
public DeviceFlagsConfiguration(DiscoDataContext database) : base(database) { }
|
||||||
|
|
||||||
|
public override string Scope { get; } = "DeviceFlags";
|
||||||
|
|
||||||
|
public DeviceFlagExportOptions LastExportOptions
|
||||||
|
{
|
||||||
|
get => Get(DeviceFlagExportOptions.DefaultOptions());
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,13 +7,13 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
{
|
{
|
||||||
public DeviceProfilesConfiguration(DiscoDataContext Database) : base(Database) { }
|
public DeviceProfilesConfiguration(DiscoDataContext Database) : base(Database) { }
|
||||||
|
|
||||||
public override string Scope { get { return "DeviceProfiles"; } }
|
public override string Scope { get; } = "DeviceProfiles";
|
||||||
|
|
||||||
public int DefaultDeviceProfileId
|
public int DefaultDeviceProfileId
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var v = this.Get(1);
|
var v = Get(1);
|
||||||
if (v > 0)
|
if (v > 0)
|
||||||
return v;
|
return v;
|
||||||
else
|
else
|
||||||
@@ -24,21 +24,21 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
if (value < 1)
|
if (value < 1)
|
||||||
throw new ArgumentOutOfRangeException("value", "Expected >= 1");
|
throw new ArgumentOutOfRangeException("value", "Expected >= 1");
|
||||||
|
|
||||||
this.Set(value);
|
Set(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public int DefaultAddDeviceOfflineDeviceProfileId
|
public int DefaultAddDeviceOfflineDeviceProfileId
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return this.Get(0);
|
return Get(0);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
throw new ArgumentOutOfRangeException("value", "Expected >= 0");
|
throw new ArgumentOutOfRangeException("value", "Expected >= 0");
|
||||||
|
|
||||||
this.Set(value);
|
Set(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Disco.Data.Repository;
|
using Disco.Data.Repository;
|
||||||
using Disco.Models.Services.Devices.Exporting;
|
using Disco.Models.Services.Devices;
|
||||||
|
|
||||||
namespace Disco.Data.Configuration.Modules
|
namespace Disco.Data.Configuration.Modules
|
||||||
{
|
{
|
||||||
@@ -7,12 +7,18 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
{
|
{
|
||||||
public DevicesConfiguration(DiscoDataContext Database) : base(Database) { }
|
public DevicesConfiguration(DiscoDataContext Database) : base(Database) { }
|
||||||
|
|
||||||
public override string Scope { get { return "Devices"; } }
|
public override string Scope { get; } = "Devices";
|
||||||
|
|
||||||
public DeviceExportOptions LastExportOptions
|
public DeviceExportOptions LastExportOptions
|
||||||
{
|
{
|
||||||
get { return this.Get<DeviceExportOptions>(DeviceExportOptions.DefaultOptions()); }
|
get => Get(DeviceExportOptions.DefaultOptions());
|
||||||
set { this.Set(value); }
|
set => Set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EnrollmentLegacyDiscoveryDisabled
|
||||||
|
{
|
||||||
|
get => Get(false);
|
||||||
|
set => Set(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,18 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
{
|
{
|
||||||
public DocumentsConfiguration(DiscoDataContext Database) : base(Database) { }
|
public DocumentsConfiguration(DiscoDataContext Database) : base(Database) { }
|
||||||
|
|
||||||
public override string Scope { get { return "Documents"; } }
|
public override string Scope { get; } = "Documents";
|
||||||
|
|
||||||
public List<DocumentTemplatePackage> Packages
|
public List<DocumentTemplatePackage> Packages
|
||||||
{
|
{
|
||||||
get { return Get<List<DocumentTemplatePackage>>(null); }
|
get { return Get<List<DocumentTemplatePackage>>(null); }
|
||||||
set { Set(value); }
|
set { Set(value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DocumentExportOptions LastExportOptions
|
||||||
|
{
|
||||||
|
get => Get(DocumentExportOptions.DefaultOptions());
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Disco.Data.Repository;
|
using Disco.Data.Repository;
|
||||||
using Disco.Models.Services.Job;
|
using Disco.Models.Services.Jobs;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@@ -9,18 +9,27 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
{
|
{
|
||||||
public JobPreferencesConfiguration(DiscoDataContext Database) : base(Database) { }
|
public JobPreferencesConfiguration(DiscoDataContext Database) : base(Database) { }
|
||||||
|
|
||||||
public override string Scope { get { return "JobPreferences"; } }
|
public override string Scope { get; } = "JobPreferences";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initial comments template for new jobs
|
||||||
|
/// </summary>
|
||||||
|
public string InitialCommentsTemplate
|
||||||
|
{
|
||||||
|
get => Get<string>(null);
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of days a job is open before it is considered 'Long Running'
|
/// Number of days a job is open before it is considered 'Long Running'
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int LongRunningJobDaysThreshold
|
public int LongRunningJobDaysThreshold
|
||||||
{
|
{
|
||||||
get { return Get<int>(7); }
|
get => Get(7);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
throw new ArgumentOutOfRangeException("value", "The Long Running Job Days Threshold cannot be less than zero");
|
throw new ArgumentOutOfRangeException(nameof(value), "The Long Running Job Days Threshold cannot be less than zero");
|
||||||
|
|
||||||
Set(value);
|
Set(value);
|
||||||
}
|
}
|
||||||
@@ -31,27 +40,33 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int StaleJobMinutesThreshold
|
public int StaleJobMinutesThreshold
|
||||||
{
|
{
|
||||||
get { return Get<int>(60 * 24 * 2); } // Default to 48 Hours (2 days)
|
get => Get(60 * 24 * 2); // Default to 48 Hours (2 days)
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
throw new ArgumentOutOfRangeException("value", "The Stale Job Minutes Threshold cannot be less than zero");
|
throw new ArgumentOutOfRangeException(nameof(value), "The Stale Job Minutes Threshold cannot be less than zero");
|
||||||
|
|
||||||
Set(value);
|
Set(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool LodgmentIncludeAllAttachmentsByDefault
|
||||||
|
{
|
||||||
|
get => Get(false);
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Theme used in noticeboards by default.
|
/// Theme used in noticeboards by default.
|
||||||
/// <see cref="Disco.Services.Extensions.UIHelpers.NoticeboardThemes"/>
|
/// <see cref="Disco.Services.Extensions.UIHelpers.NoticeboardThemes"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string DefaultNoticeboardTheme
|
public string DefaultNoticeboardTheme
|
||||||
{
|
{
|
||||||
get { return Get("default"); }
|
get => Get("default");
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
throw new ArgumentNullException("DefaultNoticeboardTheme");
|
throw new ArgumentNullException(nameof(DefaultNoticeboardTheme));
|
||||||
|
|
||||||
Set(value);
|
Set(value);
|
||||||
}
|
}
|
||||||
@@ -59,32 +74,48 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
|
|
||||||
public LocationModes LocationMode
|
public LocationModes LocationMode
|
||||||
{
|
{
|
||||||
get { return Get(LocationModes.Unrestricted); }
|
get => Get(LocationModes.Unrestricted);
|
||||||
set { Set(value); }
|
set => Set(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> LocationList
|
public List<string> LocationList
|
||||||
{
|
{
|
||||||
get { return Get(new List<string>()); }
|
get => Get(new List<string>());
|
||||||
set { Set(value); }
|
set => Set(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string OnCreateExpression
|
public string OnCreateExpression
|
||||||
{
|
{
|
||||||
get { return Get<string>(null); }
|
get => Get<string>(null);
|
||||||
set { Set(value); }
|
set => Set(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string OnDeviceReadyForReturnExpression
|
public string OnDeviceReadyForReturnExpression
|
||||||
{
|
{
|
||||||
get { return Get<string>(null); }
|
get => Get<string>(null);
|
||||||
set { Set(value); }
|
set => Set(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string OnCloseExpression
|
public string OnCloseExpression
|
||||||
{
|
{
|
||||||
get { return Get<string>(null); }
|
get => Get<string>(null);
|
||||||
set { Set(value); }
|
set => Set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JobExportOptions LastExportOptions
|
||||||
|
{
|
||||||
|
get => Get(JobExportOptions.DefaultOptions());
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Set(value);
|
||||||
|
LastExportDate = DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime? LastExportDate
|
||||||
|
{
|
||||||
|
get => Get<DateTime?>(null);
|
||||||
|
set => Set(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Disco.Data.Repository;
|
using Disco.Data.Repository;
|
||||||
using Disco.Models.BI.Config;
|
using Disco.Models.BI.Config;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -8,11 +7,9 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
{
|
{
|
||||||
public class OrganisationAddressesConfiguration : ConfigurationBase
|
public class OrganisationAddressesConfiguration : ConfigurationBase
|
||||||
{
|
{
|
||||||
private const string scope = "OrganisationAddresses";
|
|
||||||
|
|
||||||
public OrganisationAddressesConfiguration(DiscoDataContext Database) : base(Database) { }
|
public OrganisationAddressesConfiguration(DiscoDataContext Database) : base(Database) { }
|
||||||
|
|
||||||
public override string Scope { get { return scope; } }
|
public override string Scope { get; } = "OrganisationAddresses";
|
||||||
|
|
||||||
public OrganisationAddress GetAddress(int Id)
|
public OrganisationAddress GetAddress(int Id)
|
||||||
{
|
{
|
||||||
@@ -58,19 +55,5 @@ namespace Disco.Data.Configuration.Modules
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void MigrateDatabase(DiscoDataContext Database)
|
|
||||||
{
|
|
||||||
// Migrate all organisation addresses to JSON
|
|
||||||
if (Database.ConfigurationItems.Count(i => i.Scope == scope && !i.Value.StartsWith("{")) > 0)
|
|
||||||
{
|
|
||||||
var items = Database.ConfigurationItems.Where(i => i.Scope == scope && !i.Value.StartsWith("{")).ToList();
|
|
||||||
items.ForEach(i =>
|
|
||||||
{
|
|
||||||
i.Value = JsonConvert.SerializeObject(OrganisationAddress.FromConfigurationEntry(int.Parse(i.Key), i.Value));
|
|
||||||
});
|
|
||||||
Database.SaveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.Services.Users.UserFlags;
|
||||||
|
|
||||||
|
namespace Disco.Data.Configuration.Modules
|
||||||
|
{
|
||||||
|
public class UserFlagsConfiguration : ConfigurationBase
|
||||||
|
{
|
||||||
|
public UserFlagsConfiguration(DiscoDataContext database) : base(database) { }
|
||||||
|
|
||||||
|
public override string Scope { get; } = "UserFlags";
|
||||||
|
|
||||||
|
public UserFlagExportOptions LastExportOptions
|
||||||
|
{
|
||||||
|
get => Get(UserFlagExportOptions.DefaultOptions());
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using Disco.Data.Repository;
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Models.Services.Exporting;
|
||||||
using Disco.Models.Services.Interop.DiscoServices;
|
using Disco.Models.Services.Interop.DiscoServices;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Disco.Data.Configuration
|
namespace Disco.Data.Configuration
|
||||||
@@ -18,6 +20,8 @@ namespace Disco.Data.Configuration
|
|||||||
moduleActiveDirectoryConfiguration = new Lazy<Modules.ActiveDirectoryConfiguration>(() => new Modules.ActiveDirectoryConfiguration(Database));
|
moduleActiveDirectoryConfiguration = new Lazy<Modules.ActiveDirectoryConfiguration>(() => new Modules.ActiveDirectoryConfiguration(Database));
|
||||||
moduleDevicesConfiguration = new Lazy<Modules.DevicesConfiguration>(() => new Modules.DevicesConfiguration(Database));
|
moduleDevicesConfiguration = new Lazy<Modules.DevicesConfiguration>(() => new Modules.DevicesConfiguration(Database));
|
||||||
moduleDocumentsConfiguration = new Lazy<Modules.DocumentsConfiguration>(() => new Modules.DocumentsConfiguration(Database));
|
moduleDocumentsConfiguration = new Lazy<Modules.DocumentsConfiguration>(() => new Modules.DocumentsConfiguration(Database));
|
||||||
|
moduleUserFlagsConfiguration = new Lazy<Modules.UserFlagsConfiguration>(() => new Modules.UserFlagsConfiguration(Database));
|
||||||
|
moduleDeviceFlagsConfiguration = new Lazy<Modules.DeviceFlagsConfiguration>(() => new Modules.DeviceFlagsConfiguration(Database));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Configuration Modules
|
#region Configuration Modules
|
||||||
@@ -29,61 +33,22 @@ namespace Disco.Data.Configuration
|
|||||||
private Lazy<Modules.ActiveDirectoryConfiguration> moduleActiveDirectoryConfiguration;
|
private Lazy<Modules.ActiveDirectoryConfiguration> moduleActiveDirectoryConfiguration;
|
||||||
private Lazy<Modules.DevicesConfiguration> moduleDevicesConfiguration;
|
private Lazy<Modules.DevicesConfiguration> moduleDevicesConfiguration;
|
||||||
private Lazy<Modules.DocumentsConfiguration> moduleDocumentsConfiguration;
|
private Lazy<Modules.DocumentsConfiguration> moduleDocumentsConfiguration;
|
||||||
|
private Lazy<Modules.UserFlagsConfiguration> moduleUserFlagsConfiguration;
|
||||||
|
private Lazy<Modules.DeviceFlagsConfiguration> moduleDeviceFlagsConfiguration;
|
||||||
|
|
||||||
public Modules.BootstrapperConfiguration Bootstrapper
|
public Modules.BootstrapperConfiguration Bootstrapper => moduleBootstrapperConfiguration.Value;
|
||||||
{
|
public Modules.DeviceProfilesConfiguration DeviceProfiles => moduleDeviceProfilesConfiguration.Value;
|
||||||
get
|
public Modules.OrganisationAddressesConfiguration OrganisationAddresses => moduleOrganisationAddressesConfiguration.Value;
|
||||||
{
|
public Modules.JobPreferencesConfiguration JobPreferences => moduleJobPreferencesConfiguration.Value;
|
||||||
return moduleBootstrapperConfiguration.Value;
|
public Modules.ActiveDirectoryConfiguration ActiveDirectory => moduleActiveDirectoryConfiguration.Value;
|
||||||
}
|
public Modules.DevicesConfiguration Devices => moduleDevicesConfiguration.Value;
|
||||||
}
|
public Modules.DocumentsConfiguration Documents => moduleDocumentsConfiguration.Value;
|
||||||
public Modules.DeviceProfilesConfiguration DeviceProfiles
|
public Modules.UserFlagsConfiguration UserFlags => moduleUserFlagsConfiguration.Value;
|
||||||
{
|
public Modules.DeviceFlagsConfiguration DeviceFlags => moduleDeviceFlagsConfiguration.Value;
|
||||||
get
|
|
||||||
{
|
|
||||||
return moduleDeviceProfilesConfiguration.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public Modules.OrganisationAddressesConfiguration OrganisationAddresses
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return moduleOrganisationAddressesConfiguration.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public Modules.JobPreferencesConfiguration JobPreferences
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return moduleJobPreferencesConfiguration.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public Modules.ActiveDirectoryConfiguration ActiveDirectory
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return moduleActiveDirectoryConfiguration.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public Modules.DevicesConfiguration Devices
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return moduleDevicesConfiguration.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Modules.DocumentsConfiguration Documents
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return moduleDocumentsConfiguration.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public override string Scope { get { return "System"; } }
|
public override string Scope { get; } = "System";
|
||||||
|
|
||||||
public string DataStoreLocation
|
public string DataStoreLocation
|
||||||
{
|
{
|
||||||
@@ -93,26 +58,27 @@ namespace Disco.Data.Configuration
|
|||||||
if (result == null)
|
if (result == null)
|
||||||
{
|
{
|
||||||
var appDataPath = System.Web.HttpContext.Current.Server.MapPath("~/App_Data");
|
var appDataPath = System.Web.HttpContext.Current.Server.MapPath("~/App_Data");
|
||||||
if (appDataPath.EndsWith("\\"))
|
|
||||||
return appDataPath;
|
if (!appDataPath.EndsWith(@"\"))
|
||||||
else
|
appDataPath += @"\";
|
||||||
return string.Concat(appDataPath, '\\');
|
|
||||||
|
return appDataPath;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
throw new ArgumentNullException("value");
|
throw new ArgumentNullException(nameof(value));
|
||||||
if (!System.IO.Directory.Exists(value))
|
|
||||||
throw new System.IO.DirectoryNotFoundException(string.Format("DataStoreLocation: '{0}' could not be found", value));
|
if (!Directory.Exists(value))
|
||||||
string storePath;
|
throw new DirectoryNotFoundException($"DataStoreLocation: '{value}' could not be found");
|
||||||
if (value.EndsWith("\\"))
|
|
||||||
storePath = value;
|
if (!value.EndsWith(@"\"))
|
||||||
else
|
value += @"\";
|
||||||
storePath = string.Concat(value, '\\');
|
|
||||||
Set(storePath);
|
Set(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,30 +95,10 @@ namespace Disco.Data.Configuration
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region Plugin Locations
|
#region Plugin Locations
|
||||||
public string PluginsLocation
|
public string PluginsLocation => Path.Combine(DataStoreLocation, @"Plugins\");
|
||||||
{
|
public string PluginStorageLocation => Path.Combine(DataStoreLocation, @"PluginStorage\");
|
||||||
get
|
public string PluginPackagesLocation => Path.Combine(DataStoreLocation, @"PluginPackages\");
|
||||||
{
|
public string PluginUserPhotosLocation => Path.Combine(DataStoreLocation, @"PluginUserPhotos\");
|
||||||
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
|
public DateTime PluginDetailsCacheExpiration
|
||||||
{
|
{
|
||||||
@@ -167,7 +113,7 @@ namespace Disco.Data.Configuration
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return System.IO.Path.Combine(DataStoreLocation, "OrganisationLogo.png");
|
return Path.Combine(DataStoreLocation, "OrganisationLogo.png");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string OrganisationLogoHash
|
public string OrganisationLogoHash
|
||||||
@@ -189,15 +135,15 @@ namespace Disco.Data.Configuration
|
|||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
else
|
else
|
||||||
return new MemoryStream(Disco.Data.Properties.Resources.EmptyLogo);
|
return new MemoryStream(Properties.Resources.EmptyLogo);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
string organisationLogoPath = OrganisationLogoPath;
|
string organisationLogoPath = OrganisationLogoPath;
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
if (System.IO.File.Exists(organisationLogoPath))
|
if (File.Exists(organisationLogoPath))
|
||||||
System.IO.File.Delete(organisationLogoPath);
|
File.Delete(organisationLogoPath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -319,6 +265,29 @@ namespace Disco.Data.Configuration
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region UpdateCheck
|
#region UpdateCheck
|
||||||
|
public bool IsActivated => ActivationId.HasValue;
|
||||||
|
|
||||||
|
public DateTime? ActivatedOn
|
||||||
|
{
|
||||||
|
get => Get((DateTime?)null);
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
|
public string ActivatedBy
|
||||||
|
{
|
||||||
|
get => Get((string)null);
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
|
public Guid? ActivationId
|
||||||
|
{
|
||||||
|
get => Get((Guid?)null);
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
|
public byte[] ActivationKey
|
||||||
|
{
|
||||||
|
get => Get((byte[])null);
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsLicensed
|
public bool IsLicensed
|
||||||
{
|
{
|
||||||
get => LicenseKey != null && LicenseExpiresOn != null && LicenseExpiresOn > DateTime.UtcNow && LicenseError == null;
|
get => LicenseKey != null && LicenseExpiresOn != null && LicenseExpiresOn > DateTime.UtcNow && LicenseError == null;
|
||||||
@@ -379,5 +348,11 @@ namespace Disco.Data.Configuration
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public List<SavedExport> SavedExports
|
||||||
|
{
|
||||||
|
get => Get(new List<SavedExport>());
|
||||||
|
set => Set(value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,8 @@
|
|||||||
<Compile Include="Configuration\ConfigurationCache.cs" />
|
<Compile Include="Configuration\ConfigurationCache.cs" />
|
||||||
<Compile Include="Configuration\Modules\ActiveDirectoryConfiguration.cs" />
|
<Compile Include="Configuration\Modules\ActiveDirectoryConfiguration.cs" />
|
||||||
<Compile Include="Configuration\Modules\DevicesConfiguration.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\DocumentsConfiguration.cs" />
|
||||||
<Compile Include="Configuration\Modules\JobPreferencesConfiguration.cs" />
|
<Compile Include="Configuration\Modules\JobPreferencesConfiguration.cs" />
|
||||||
<Compile Include="Configuration\SystemConfiguration.cs" />
|
<Compile Include="Configuration\SystemConfiguration.cs" />
|
||||||
@@ -183,6 +185,30 @@
|
|||||||
<Compile Include="Migrations\202403030134280_DBv24.Designer.cs">
|
<Compile Include="Migrations\202403030134280_DBv24.Designer.cs">
|
||||||
<DependentUpon>202403030134280_DBv24.cs</DependentUpon>
|
<DependentUpon>202403030134280_DBv24.cs</DependentUpon>
|
||||||
</Compile>
|
</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\Configuration.cs" />
|
||||||
<Compile Include="Migrations\DiscoDataMigrator.cs" />
|
<Compile Include="Migrations\DiscoDataMigrator.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
@@ -270,6 +296,24 @@
|
|||||||
<EmbeddedResource Include="Migrations\202403030134280_DBv24.resx">
|
<EmbeddedResource Include="Migrations\202403030134280_DBv24.resx">
|
||||||
<DependentUpon>202403030134280_DBv24.cs</DependentUpon>
|
<DependentUpon>202403030134280_DBv24.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</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">
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// <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"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
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
@@ -0,0 +1,27 @@
|
|||||||
|
// <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"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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
@@ -0,0 +1,27 @@
|
|||||||
|
// <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"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
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
@@ -0,0 +1,27 @@
|
|||||||
|
// <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"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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
@@ -0,0 +1,27 @@
|
|||||||
|
// <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"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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
@@ -0,0 +1,27 @@
|
|||||||
|
// <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"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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
@@ -1,9 +1,6 @@
|
|||||||
namespace Disco.Data.Migrations
|
namespace Disco.Data.Migrations
|
||||||
{
|
{
|
||||||
using System;
|
|
||||||
using System.Data.Entity;
|
|
||||||
using System.Data.Entity.Migrations;
|
using System.Data.Entity.Migrations;
|
||||||
using System.Linq;
|
|
||||||
using Disco.Data.Repository;
|
using Disco.Data.Repository;
|
||||||
|
|
||||||
internal sealed class Configuration : DbMigrationsConfiguration<DiscoDataContext>
|
internal sealed class Configuration : DbMigrationsConfiguration<DiscoDataContext>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Data.Entity.Migrations;
|
using System.Data.Entity.Migrations;
|
||||||
using System.Data.Entity.Migrations.Infrastructure;
|
using System.Data.Entity.Migrations.Infrastructure;
|
||||||
using Disco.Data.Repository;
|
using Disco.Data.Repository;
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("2.4.24270.0000")]
|
[assembly: AssemblyVersion("2.5.25262.0000")]
|
||||||
[assembly: AssemblyFileVersion("2.4.24270.0000")]
|
[assembly: AssemblyFileVersion("2.5.25262.0000")]
|
||||||
@@ -11,7 +11,7 @@ namespace Disco.Data.Repository
|
|||||||
|
|
||||||
public DiscoDataContext()
|
public DiscoDataContext()
|
||||||
{
|
{
|
||||||
this._Configuration = new Lazy<Configuration.SystemConfiguration>(() => new Configuration.SystemConfiguration(this));
|
_Configuration = new Lazy<Configuration.SystemConfiguration>(() => new Configuration.SystemConfiguration(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual DbSet<ConfigurationItem> ConfigurationItems { get; set; }
|
public virtual DbSet<ConfigurationItem> ConfigurationItems { get; set; }
|
||||||
@@ -19,6 +19,7 @@ namespace Disco.Data.Repository
|
|||||||
public virtual DbSet<DocumentTemplate> DocumentTemplates { get; set; }
|
public virtual DbSet<DocumentTemplate> DocumentTemplates { get; set; }
|
||||||
|
|
||||||
public virtual DbSet<User> Users { 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<UserDetail> UserDetails { get; set; }
|
||||||
public virtual DbSet<UserAttachment> UserAttachments { get; set; }
|
public virtual DbSet<UserAttachment> UserAttachments { get; set; }
|
||||||
public virtual DbSet<UserFlag> UserFlags { get; set; }
|
public virtual DbSet<UserFlag> UserFlags { get; set; }
|
||||||
@@ -28,6 +29,7 @@ namespace Disco.Data.Repository
|
|||||||
public virtual DbSet<DeviceUserAssignment> DeviceUserAssignments { get; set; }
|
public virtual DbSet<DeviceUserAssignment> DeviceUserAssignments { get; set; }
|
||||||
|
|
||||||
public virtual DbSet<Device> Devices { 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<DeviceDetail> DeviceDetails { get; set; }
|
||||||
public virtual DbSet<DeviceModel> DeviceModels { get; set; }
|
public virtual DbSet<DeviceModel> DeviceModels { get; set; }
|
||||||
public virtual DbSet<DeviceProfile> DeviceProfiles { get; set; }
|
public virtual DbSet<DeviceProfile> DeviceProfiles { get; set; }
|
||||||
@@ -58,7 +60,7 @@ namespace Disco.Data.Repository
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return this._Configuration.Value;
|
return _Configuration.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ namespace Disco.Data.Repository
|
|||||||
Database.SaveChanges();
|
Database.SaveChanges();
|
||||||
|
|
||||||
// Migration Maintenance
|
// Migration Maintenance
|
||||||
Database.MigrateConfiguration();
|
|
||||||
|
|
||||||
Database.MigratePreDomainObjects();
|
Database.MigratePreDomainObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +58,7 @@ namespace Disco.Data.Repository
|
|||||||
{
|
{
|
||||||
if (Database.DeviceModels.Count() == 0)
|
if (Database.DeviceModels.Count() == 0)
|
||||||
{
|
{
|
||||||
Database.DeviceModels.Add(new DeviceModel { Manufacturer = "Unknown", Model = "Unknown", Description = "Unknown Device Model" });
|
Database.DeviceModels.Add(new DeviceModel { Manufacturer = "Unknown", Model = "Unknown", Description = "Unknown Device Model", ModelType = "Unknown" });
|
||||||
}
|
}
|
||||||
UpdateDeviceModelConfiguration(Database);
|
UpdateDeviceModelConfiguration(Database);
|
||||||
// Removed: 2013-01-14 G#
|
// Removed: 2013-01-14 G#
|
||||||
@@ -210,6 +208,15 @@ namespace Disco.Data.Repository
|
|||||||
#endregion
|
#endregion
|
||||||
// End
|
// 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
|
// 2012-05-29 - Audits
|
||||||
#region "Audit" Added
|
#region "Audit" Added
|
||||||
if (Database.JobSubTypes.Count(jst => jst.JobTypeId == JobType.JobTypeIds.HMisc && jst.Id == "Audit") == 0)
|
if (Database.JobSubTypes.Count(jst => jst.JobTypeId == JobType.JobTypeIds.HMisc && jst.Id == "Audit") == 0)
|
||||||
@@ -257,8 +264,7 @@ namespace Disco.Data.Repository
|
|||||||
foreach (var configurationItem in configurationItems)
|
foreach (var configurationItem in configurationItems)
|
||||||
{
|
{
|
||||||
int profileId = int.Parse(configurationItem.Scope.Substring(configurationItem.Scope.IndexOf(":") + 1));
|
int profileId = int.Parse(configurationItem.Scope.Substring(configurationItem.Scope.IndexOf(":") + 1));
|
||||||
DeviceProfile dp;
|
if (deviceProfiles.TryGetValue(profileId, out var dp))
|
||||||
if (deviceProfiles.TryGetValue(profileId, out dp))
|
|
||||||
{
|
{
|
||||||
switch (configurationItem.Key)
|
switch (configurationItem.Key)
|
||||||
{
|
{
|
||||||
@@ -266,7 +272,7 @@ namespace Disco.Data.Repository
|
|||||||
dp.ComputerNameTemplate = configurationItem.Value;
|
dp.ComputerNameTemplate = configurationItem.Value;
|
||||||
break;
|
break;
|
||||||
case "DistributionType":
|
case "DistributionType":
|
||||||
dp.DistributionType = (DeviceProfile.DistributionTypes)(int.Parse(configurationItem.Value));
|
dp.DistributionType = (DeviceProfile.DistributionTypes)int.Parse(configurationItem.Value);
|
||||||
break;
|
break;
|
||||||
case "OrganisationalUnit":
|
case "OrganisationalUnit":
|
||||||
dp.OrganisationalUnit = configurationItem.Value;
|
dp.OrganisationalUnit = configurationItem.Value;
|
||||||
@@ -289,7 +295,7 @@ namespace Disco.Data.Repository
|
|||||||
private static void UpdateDeviceModelDuplicates(this DiscoDataContext Database)
|
private static void UpdateDeviceModelDuplicates(this DiscoDataContext Database)
|
||||||
{
|
{
|
||||||
var deviceModels = Database.DeviceModels.ToList();
|
var deviceModels = Database.DeviceModels.ToList();
|
||||||
var duplicateModels = deviceModels.GroupBy(g => string.Format("{0}|{1}", g.Manufacturer, g.Model)).Where(g => g.Count() > 1);
|
var duplicateModels = deviceModels.GroupBy(g => $"{g.Manufacturer}|{g.Model}").Where(g => g.Count() > 1);
|
||||||
foreach (var duplicateModel in duplicateModels)
|
foreach (var duplicateModel in duplicateModels)
|
||||||
{
|
{
|
||||||
var primaryModel = duplicateModel.OrderBy(dm => dm.Id).First();
|
var primaryModel = duplicateModel.OrderBy(dm => dm.Id).First();
|
||||||
@@ -303,23 +309,15 @@ namespace Disco.Data.Repository
|
|||||||
if (!redundantModel.Description.EndsWith("** REDUNDANT **"))
|
if (!redundantModel.Description.EndsWith("** REDUNDANT **"))
|
||||||
{
|
{
|
||||||
if (redundantModel.Description.Length > 484)
|
if (redundantModel.Description.Length > 484)
|
||||||
redundantModel.Description = string.Format("{0} ** REDUNDANT **", redundantModel.Description.Substring(0, 484));
|
redundantModel.Description = $"{redundantModel.Description.Substring(0, 484)} ** REDUNDANT **";
|
||||||
else
|
else
|
||||||
redundantModel.Description = string.Format("{0} ** REDUNDANT **", redundantModel.Description);
|
redundantModel.Description = $"{redundantModel.Description} ** REDUNDANT **";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// End Added: 2013-02-07 G#
|
// 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
|
#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;
|
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;
|
||||||
|
|
||||||
@@ -426,7 +424,7 @@ DELETE [Users] WHERE [Id]=@IdExisting;";
|
|||||||
var dataStoreLocation = Database.ConfigurationItems.Where(ci => ci.Scope == "System" && ci.Key == "DataStoreLocation").Select(ci => ci.Value).FirstOrDefault();
|
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))
|
if (!string.IsNullOrWhiteSpace(dataStoreLocation) && System.IO.Directory.Exists(dataStoreLocation))
|
||||||
{
|
{
|
||||||
string filePrefix = string.Format("{0}_", netBiosName);
|
string filePrefix = $"{netBiosName}_";
|
||||||
|
|
||||||
var userAttachmentsDirectory = System.IO.Path.Combine(dataStoreLocation, "UserAttachments");
|
var userAttachmentsDirectory = System.IO.Path.Combine(dataStoreLocation, "UserAttachments");
|
||||||
if (System.IO.Directory.Exists(userAttachmentsDirectory))
|
if (System.IO.Directory.Exists(userAttachmentsDirectory))
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
using System;
|
using Microsoft.Win32;
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Data.Entity.Infrastructure;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
|
using System.Data.Entity.Infrastructure;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Security.Permissions;
|
|
||||||
|
|
||||||
namespace Disco.Data.Repository
|
namespace Disco.Data.Repository
|
||||||
{
|
{
|
||||||
@@ -52,18 +48,18 @@ namespace Disco.Data.Repository
|
|||||||
}
|
}
|
||||||
catch (UnauthorizedAccessException ex)
|
catch (UnauthorizedAccessException ex)
|
||||||
{
|
{
|
||||||
throw new SecurityException(string.Format("Unable to write to the Registry Location: HKML\\{0}[DatabaseConnectionString]", DiscoRegistryKey), ex);
|
throw new SecurityException($"Unable to write to the Registry Location: HKML\\{DiscoRegistryKey}[DatabaseConnectionString]", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscoDatabaseConnectionFactory(IDbConnectionFactory Default)
|
public DiscoDatabaseConnectionFactory(IDbConnectionFactory Default)
|
||||||
{
|
{
|
||||||
this.DefaultConnectionFactory = Default;
|
DefaultConnectionFactory = Default;
|
||||||
this.SqlCeConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
|
SqlCeConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
public System.Data.Common.DbConnection CreateConnection(string nameOrConnectionString)
|
public DbConnection CreateConnection(string nameOrConnectionString)
|
||||||
{
|
{
|
||||||
if (nameOrConnectionString == "Disco.Data.Repository.DiscoDataContext")
|
if (nameOrConnectionString == "Disco.Data.Repository.DiscoDataContext")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
using System;
|
using Disco.Models.Repository;
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
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.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data.Entity.Infrastructure;
|
||||||
using System.Data.Objects;
|
using System.Data.Objects;
|
||||||
using Disco.Models.Repository;
|
using System.Linq;
|
||||||
|
using System.Reactive.Subjects;
|
||||||
|
|
||||||
namespace Disco.Data.Repository.Monitor
|
namespace Disco.Data.Repository.Monitor
|
||||||
{
|
{
|
||||||
@@ -52,9 +49,8 @@ namespace Disco.Data.Repository.Monitor
|
|||||||
|
|
||||||
private static Type EntityTypeFromProxy(Type EntityProxyType)
|
private static Type EntityTypeFromProxy(Type EntityProxyType)
|
||||||
{
|
{
|
||||||
Type EntityType;
|
|
||||||
|
|
||||||
if (entityProxyTypeCache.TryGetValue(EntityProxyType, out EntityType))
|
if (entityProxyTypeCache.TryGetValue(EntityProxyType, out var EntityType))
|
||||||
return EntityType;
|
return EntityType;
|
||||||
|
|
||||||
EntityType = EntityProxyType;
|
EntityType = EntityProxyType;
|
||||||
@@ -113,7 +109,7 @@ namespace Disco.Data.Repository.Monitor
|
|||||||
eventType = RepositoryMonitorEventType.Unchanged;
|
eventType = RepositoryMonitorEventType.Unchanged;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException(string.Format("Database Entry State not supported: {0}", entryState.State.ToString()));
|
throw new NotSupportedException($"Database Entry State not supported: {entryState.State.ToString()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
entityType = EntityTypeFromProxy(entryState.Entity.GetType());
|
entityType = EntityTypeFromProxy(entryState.Entity.GetType());
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data.Entity.Infrastructure;
|
using System.Data.Entity.Infrastructure;
|
||||||
using System.Data.Objects;
|
using System.Data.Objects;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Disco.Data.Repository.Monitor
|
namespace Disco.Data.Repository.Monitor
|
||||||
{
|
{
|
||||||
@@ -55,7 +52,8 @@ namespace Disco.Data.Repository.Monitor
|
|||||||
return (T)dbEntityState.CurrentValues[PropertyName];
|
return (T)dbEntityState.CurrentValues[PropertyName];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExecuteAfterCommit(Action<RepositoryMonitorEvent> action){
|
public void ExecuteAfterCommit(Action<RepositoryMonitorEvent> action)
|
||||||
|
{
|
||||||
if (afterCommit)
|
if (afterCommit)
|
||||||
{
|
{
|
||||||
// Execute Immediately
|
// Execute Immediately
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
using System;
|
namespace Disco.Data.Repository.Monitor
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Disco.Data.Repository.Monitor
|
|
||||||
{
|
{
|
||||||
public enum RepositoryMonitorEventType
|
public enum RepositoryMonitorEventType
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
using System;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.Models.BI.Config
|
namespace Disco.Models.BI.Config
|
||||||
{
|
{
|
||||||
public class OrganisationAddress
|
public class OrganisationAddress
|
||||||
{
|
{
|
||||||
|
|
||||||
public int? Id { get; set; }
|
public int? Id { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
@@ -23,54 +20,11 @@ namespace Disco.Models.BI.Config
|
|||||||
[Required]
|
[Required]
|
||||||
public string ShortName { get; set; }
|
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 PhoneNumber { get; set; }
|
||||||
public string FaxNumber { get; set; }
|
public string FaxNumber { get; set; }
|
||||||
// End Added 2012-12-11 G#
|
public string EmailAddress { get; set; }
|
||||||
|
|
||||||
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()
|
public override string ToString()
|
||||||
{
|
=> $"{Name} ({ShortName})";
|
||||||
return string.Format("{0} ({1})", this.Name, this.ShortName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Disco.Models.ClientServices.EnrolmentInformation;
|
using Disco.Models.ClientServices.EnrolmentInformation;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Disco.Models.ClientServices
|
namespace Disco.Models.ClientServices
|
||||||
@@ -10,12 +11,18 @@ namespace Disco.Models.ClientServices
|
|||||||
public string DomainName { get; set; }
|
public string DomainName { get; set; }
|
||||||
public string ComputerName { 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 AssignedUserDomain { get; set; }
|
||||||
public string AssignedUserUsername { get; set; }
|
public string AssignedUserUsername { get; set; }
|
||||||
public string AssignedUserSID { get; set; }
|
public string AssignedUserSID { get; set; }
|
||||||
public string AssignedUserDescription { get; set; }
|
public string AssignedUserDescription { get; set; }
|
||||||
|
|
||||||
public bool AssignedUserIsLocalAdmin { get; set; }
|
public bool AssignedUserIsLocalAdmin { get; set; }
|
||||||
|
public bool SetAssignedUserForLogon { get; set; }
|
||||||
|
|
||||||
public string OfflineDomainJoinManifest { get; set; }
|
public string OfflineDomainJoinManifest { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
public class ComputerSystem
|
public class ComputerSystem
|
||||||
{
|
{
|
||||||
public string ChassisSKUNumber { get; set; }
|
public string ChassisSKUNumber { get; set; }
|
||||||
public short? CurrentTimeZone { get; set;}
|
public short? CurrentTimeZone { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string[] OEMStringArray { get; set; }
|
public string[] OEMStringArray { get; set; }
|
||||||
public string PCSystemType { get; set; }
|
public string PCSystemType { get; set; }
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ namespace Disco.Models.ClientServices.EnrolmentInformation
|
|||||||
public string Model { get; set; }
|
public string Model { get; set; }
|
||||||
public string ModelType { get; set; }
|
public string ModelType { get; set; }
|
||||||
|
|
||||||
|
public string MdmHardwareData { get; set; }
|
||||||
|
|
||||||
public List<Bios> Bios { get; set; }
|
public List<Bios> Bios { get; set; }
|
||||||
public List<BaseBoard> BasebBoard { get; set; }
|
public List<BaseBoard> BasebBoard { get; set; }
|
||||||
public List<ComputerSystem> ComputerSystem { get; set; }
|
public List<ComputerSystem> ComputerSystem { get; set; }
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Disco.Models.ClientServices.EnrolmentInformation
|
|||||||
public string WlanStatus { get; set; }
|
public string WlanStatus { get; set; }
|
||||||
public bool NetEnabled { get; set; }
|
public bool NetEnabled { get; set; }
|
||||||
public bool IPEnabled { get; set; }
|
public bool IPEnabled { get; set; }
|
||||||
|
|
||||||
public List<string> IPAddresses { get; set; }
|
public List<string> IPAddresses { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Disco.Models.ClientServices.EnrolmentInformation
|
namespace Disco.Models.ClientServices.EnrolmentInformation
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
namespace Disco.Models.ClientServices
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.Models.ClientServices
|
|
||||||
{
|
{
|
||||||
public class MacEnrol : ServiceBase<MacEnrolResponse>
|
public class MacEnrol : ServiceBase<MacEnrolResponse>
|
||||||
{
|
{
|
||||||
@@ -16,7 +11,7 @@ namespace Disco.Models.ClientServices
|
|||||||
public string DeviceUUID { get; set; }
|
public string DeviceUUID { get; set; }
|
||||||
|
|
||||||
public string DeviceComputerName { get; set; }
|
public string DeviceComputerName { get; set; }
|
||||||
|
|
||||||
public string DeviceManufacturer { get; set; }
|
public string DeviceManufacturer { get; set; }
|
||||||
public string DeviceModel { get; set; }
|
public string DeviceModel { get; set; }
|
||||||
public string DeviceModelType { get; set; }
|
public string DeviceModelType { get; set; }
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
using System;
|
namespace Disco.Models.ClientServices
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.Models.ClientServices
|
|
||||||
{
|
{
|
||||||
public class MacEnrolResponse
|
public class MacEnrolResponse
|
||||||
{
|
{
|
||||||
public string DeviceComputerName { get; set; }
|
public string DeviceComputerName { get; set; }
|
||||||
|
|
||||||
public string DeviceAssignedUserDomain { get; set; }
|
public string DeviceAssignedUserDomain { get; set; }
|
||||||
public string DeviceAssignedUserName { get; set; }
|
public string DeviceAssignedUserName { get; set; }
|
||||||
public string DeviceAssignedUserSID { get; set; }
|
public string DeviceAssignedUserSID { get; set; }
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
using System;
|
namespace Disco.Models.ClientServices
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.Models.ClientServices
|
|
||||||
{
|
{
|
||||||
public class MacSecureEnrolResponse
|
public class MacSecureEnrolResponse
|
||||||
{
|
{
|
||||||
public string DeviceComputerName { get; set; }
|
public string DeviceComputerName { get; set; }
|
||||||
|
|
||||||
public string DeviceAssignedUserDomain { get; set; }
|
public string DeviceAssignedUserDomain { get; set; }
|
||||||
public string DeviceAssignedUserName { get; set; }
|
public string DeviceAssignedUserName { get; set; }
|
||||||
public string DeviceAssignedUserSID { get; set; }
|
public string DeviceAssignedUserSID { get; set; }
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
namespace Disco.Models.ClientServices
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.Models.ClientServices
|
|
||||||
{
|
{
|
||||||
public abstract class ServiceBase<ResponseType>
|
public abstract class ServiceBase<ResponseType>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
namespace Disco.Models.ClientServices
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.Models.ClientServices
|
|
||||||
{
|
{
|
||||||
public class WhoAmI : ServiceBase<WhoAmIResponse>
|
public class WhoAmI : ServiceBase<WhoAmIResponse>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
namespace Disco.Models.ClientServices
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Disco.Models.ClientServices
|
|
||||||
{
|
{
|
||||||
public class WhoAmIResponse
|
public class WhoAmIResponse
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,6 +41,9 @@
|
|||||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Data.Entity" />
|
<Reference Include="System.Data.Entity" />
|
||||||
|
<Reference Include="System.ValueTuple, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\System.ValueTuple.4.6.2\lib\net462\System.ValueTuple.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
@@ -49,13 +52,25 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="BI\Config\OrganisationAddress.cs" />
|
<Compile Include="BI\Config\OrganisationAddress.cs" />
|
||||||
<Compile Include="Exporting\ExportFieldMetadata.cs" />
|
<Compile Include="Exporting\ExportMetadataField.cs" />
|
||||||
<Compile Include="Exporting\ExportFormat.cs" />
|
<Compile Include="Exporting\ExportFormat.cs" />
|
||||||
|
<Compile Include="Exporting\ExportMetadata.cs" />
|
||||||
<Compile Include="Exporting\IExportRecord.cs" />
|
<Compile Include="Exporting\IExportRecord.cs" />
|
||||||
<Compile Include="Repository\Device\Flag\DeviceFlag.cs" />
|
<Compile Include="Repository\Device\Flag\DeviceFlag.cs" />
|
||||||
<Compile Include="Repository\Device\Flag\DeviceFlagAssignment.cs" />
|
<Compile Include="Repository\Device\Flag\DeviceFlagAssignment.cs" />
|
||||||
|
<Compile Include="Repository\Device\DeviceComment.cs" />
|
||||||
|
<Compile Include="Repository\FlagType.cs" />
|
||||||
|
<Compile Include="Repository\User\UserComment.cs" />
|
||||||
|
<Compile Include="Repository\FlagPermission.cs" />
|
||||||
|
<Compile Include="Services\Devices\DeviceEnrolmentServerDiscoveryMethod.cs" />
|
||||||
<Compile Include="Services\Devices\DeviceFlags\DeviceFlagExportOptions.cs" />
|
<Compile Include="Services\Devices\DeviceFlags\DeviceFlagExportOptions.cs" />
|
||||||
<Compile Include="Services\Devices\DeviceFlags\DeviceFlagExportRecord.cs" />
|
<Compile Include="Services\Devices\DeviceFlags\DeviceFlagExportRecord.cs" />
|
||||||
|
<Compile Include="Services\Documents\DocumentExportOptions.cs" />
|
||||||
|
<Compile Include="Services\Documents\DocumentExportRecord.cs" />
|
||||||
|
<Compile Include="Services\Exporting\ExportOptionField.cs" />
|
||||||
|
<Compile Include="Services\Exporting\ExportOptionGroup.cs" />
|
||||||
|
<Compile Include="Services\Exporting\SavedExport.cs" />
|
||||||
|
<Compile Include="Services\Exporting\SavedExportSchedule.cs" />
|
||||||
<Compile Include="Services\Expressions\Extensions\ImageExpressionFormat.cs" />
|
<Compile Include="Services\Expressions\Extensions\ImageExpressionFormat.cs" />
|
||||||
<Compile Include="ClientServices\EnrolmentInformation\BaseBoard.cs" />
|
<Compile Include="ClientServices\EnrolmentInformation\BaseBoard.cs" />
|
||||||
<Compile Include="ClientServices\EnrolmentInformation\Battery.cs" />
|
<Compile Include="ClientServices\EnrolmentInformation\Battery.cs" />
|
||||||
@@ -80,6 +95,11 @@
|
|||||||
<Compile Include="Services\Documents\DocumentTemplatePackage.cs" />
|
<Compile Include="Services\Documents\DocumentTemplatePackage.cs" />
|
||||||
<Compile Include="Services\Documents\OnImportUserFlagRule.cs" />
|
<Compile Include="Services\Documents\OnImportUserFlagRule.cs" />
|
||||||
<Compile Include="Exporting\IExportOptions.cs" />
|
<Compile Include="Exporting\IExportOptions.cs" />
|
||||||
|
<Compile Include="Services\Interop\DiscoServices\Activation\CallbackModel.cs" />
|
||||||
|
<Compile Include="Services\Interop\DiscoServices\Activation\ChallengeModel.cs" />
|
||||||
|
<Compile Include="Services\Interop\DiscoServices\IConnectNotification.cs" />
|
||||||
|
<Compile Include="Services\Jobs\JobExportOptions.cs" />
|
||||||
|
<Compile Include="Services\Jobs\JobExportRecord.cs" />
|
||||||
<Compile Include="Services\Jobs\LocationModes.cs" />
|
<Compile Include="Services\Jobs\LocationModes.cs" />
|
||||||
<Compile Include="ClientServices\EnrolmentInformation\Certificate.cs" />
|
<Compile Include="ClientServices\EnrolmentInformation\Certificate.cs" />
|
||||||
<Compile Include="ClientServices\Register.cs" />
|
<Compile Include="ClientServices\Register.cs" />
|
||||||
@@ -132,10 +152,10 @@
|
|||||||
<Compile Include="Repository\User\UserAttachment.cs" />
|
<Compile Include="Repository\User\UserAttachment.cs" />
|
||||||
<Compile Include="Repository\User\UserDetail.cs" />
|
<Compile Include="Repository\User\UserDetail.cs" />
|
||||||
<Compile Include="Repository\User\AuthorizationRole.cs" />
|
<Compile Include="Repository\User\AuthorizationRole.cs" />
|
||||||
<Compile Include="Services\Devices\Exporting\DeviceExportRecord.cs" />
|
<Compile Include="Services\Devices\DeviceExportRecord.cs" />
|
||||||
<Compile Include="Exporting\ExportResult.cs" />
|
<Compile Include="Exporting\ExportResult.cs" />
|
||||||
<Compile Include="Services\Devices\Exporting\DeviceExportTypes.cs" />
|
<Compile Include="Services\Devices\DeviceExportTypes.cs" />
|
||||||
<Compile Include="Services\Devices\Exporting\DeviceExportOptions.cs" />
|
<Compile Include="Services\Devices\DeviceExportOptions.cs" />
|
||||||
<Compile Include="Services\Devices\Importing\DeviceImportFieldTypes.cs" />
|
<Compile Include="Services\Devices\Importing\DeviceImportFieldTypes.cs" />
|
||||||
<Compile Include="Services\Devices\Importing\IDeviceImportRecord.cs" />
|
<Compile Include="Services\Devices\Importing\IDeviceImportRecord.cs" />
|
||||||
<Compile Include="Services\Devices\Importing\IDeviceImportContext.cs" />
|
<Compile Include="Services\Devices\Importing\IDeviceImportContext.cs" />
|
||||||
@@ -154,6 +174,7 @@
|
|||||||
<Compile Include="Services\Jobs\JobLists\JobTableStatusQueueItemModel.cs" />
|
<Compile Include="Services\Jobs\JobLists\JobTableStatusQueueItemModel.cs" />
|
||||||
<Compile Include="Services\Jobs\JobQueues\IJobQueueToken.cs" />
|
<Compile Include="Services\Jobs\JobQueues\IJobQueueToken.cs" />
|
||||||
<Compile Include="Services\Jobs\Noticeboards\IHeldDeviceItem.cs" />
|
<Compile Include="Services\Jobs\Noticeboards\IHeldDeviceItem.cs" />
|
||||||
|
<Compile Include="Services\Logging\LogExportOptions.cs" />
|
||||||
<Compile Include="Services\Messaging\Email.cs" />
|
<Compile Include="Services\Messaging\Email.cs" />
|
||||||
<Compile Include="Services\Messaging\EmailAttachment.cs" />
|
<Compile Include="Services\Messaging\EmailAttachment.cs" />
|
||||||
<Compile Include="Services\Users\Contact\UserContact.cs" />
|
<Compile Include="Services\Users\Contact\UserContact.cs" />
|
||||||
@@ -178,6 +199,7 @@
|
|||||||
<Compile Include="UI\Config\DeviceModel\ConfigDeviceModelIndexModelItem.cs" />
|
<Compile Include="UI\Config\DeviceModel\ConfigDeviceModelIndexModelItem.cs" />
|
||||||
<Compile Include="UI\Config\DeviceModel\ConfigDeviceModelComponentsModel.cs" />
|
<Compile Include="UI\Config\DeviceModel\ConfigDeviceModelComponentsModel.cs" />
|
||||||
<Compile Include="UI\Config\DeviceModel\ConfigDeviceModelShowModel.cs" />
|
<Compile Include="UI\Config\DeviceModel\ConfigDeviceModelShowModel.cs" />
|
||||||
|
<Compile Include="UI\Config\DeviceModel\ConfigDeviceModelCreateModel.cs" />
|
||||||
<Compile Include="UI\Config\DeviceProfile\ConfigDeviceProfileCreateModel.cs" />
|
<Compile Include="UI\Config\DeviceProfile\ConfigDeviceProfileCreateModel.cs" />
|
||||||
<Compile Include="UI\Config\DeviceProfile\ConfigDeviceProfileDefaultsModel.cs" />
|
<Compile Include="UI\Config\DeviceProfile\ConfigDeviceProfileDefaultsModel.cs" />
|
||||||
<Compile Include="UI\Config\DeviceProfile\ConfigDeviceProfileIndexModel.cs" />
|
<Compile Include="UI\Config\DeviceProfile\ConfigDeviceProfileIndexModel.cs" />
|
||||||
@@ -186,7 +208,12 @@
|
|||||||
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateBulkGenerate.cs" />
|
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateBulkGenerate.cs" />
|
||||||
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateCreatePackageModel.cs" />
|
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateCreatePackageModel.cs" />
|
||||||
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateCreateModel.cs" />
|
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateCreateModel.cs" />
|
||||||
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateExpressionBrowserModel.cs" />
|
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateExportModel.cs" />
|
||||||
|
<Compile Include="UI\Config\Export\ConfigExportShowModel.cs" />
|
||||||
|
<Compile Include="UI\Config\Export\ConfigExportEditModel.cs" />
|
||||||
|
<Compile Include="UI\Config\Export\ConfigExportIndexModel.cs" />
|
||||||
|
<Compile Include="UI\Config\Export\ConfigExportCreateModel.cs" />
|
||||||
|
<Compile Include="UI\Config\Expressions\ConfigExpressionsBrowserModel.cs" />
|
||||||
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateImportStatusModel.cs" />
|
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateImportStatusModel.cs" />
|
||||||
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateIndexModel.cs" />
|
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateIndexModel.cs" />
|
||||||
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateShowPackageModel.cs" />
|
<Compile Include="UI\Config\DocumentTemplate\ConfigDocumentTemplateShowPackageModel.cs" />
|
||||||
@@ -217,11 +244,13 @@
|
|||||||
<Compile Include="UI\Device\DeviceImportReviewModel.cs" />
|
<Compile Include="UI\Device\DeviceImportReviewModel.cs" />
|
||||||
<Compile Include="UI\Device\DeviceIndexModel.cs" />
|
<Compile Include="UI\Device\DeviceIndexModel.cs" />
|
||||||
<Compile Include="UI\Device\DeviceShowModel.cs" />
|
<Compile Include="UI\Device\DeviceShowModel.cs" />
|
||||||
|
<Compile Include="UI\Job\JobExportModel.cs" />
|
||||||
<Compile Include="UI\Job\JobCreateModel.cs" />
|
<Compile Include="UI\Job\JobCreateModel.cs" />
|
||||||
<Compile Include="UI\Job\JobIndexModel.cs" />
|
<Compile Include="UI\Job\JobIndexModel.cs" />
|
||||||
<Compile Include="UI\Job\JobListModel.cs" />
|
<Compile Include="UI\Job\JobListModel.cs" />
|
||||||
<Compile Include="UI\Job\JobShowModel.cs" />
|
<Compile Include="UI\Job\JobShowModel.cs" />
|
||||||
<Compile Include="UI\Search\SearchQueryModel.cs" />
|
<Compile Include="UI\Search\SearchQueryModel.cs" />
|
||||||
|
<Compile Include="UI\Shared\SharedExportFieldsModel.cs" />
|
||||||
<Compile Include="UI\User\UserIndexModel.cs" />
|
<Compile Include="UI\User\UserIndexModel.cs" />
|
||||||
<Compile Include="UI\User\UserShowModel.cs" />
|
<Compile Include="UI\User\UserShowModel.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Disco.Models.Exporting
|
|
||||||
{
|
|
||||||
public class ExportFieldMetadata<T> where T : IExportRecord
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string ColumnName { get; set; }
|
|
||||||
public Type ValueType { get; set; }
|
|
||||||
public Func<T, object> Accessor { get; set; }
|
|
||||||
public Func<object, string> CsvEncoder { get; set; }
|
|
||||||
|
|
||||||
public ExportFieldMetadata(string name, Type valueType, Func<T, object> accessor, Func<object, string> csvEncoder)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
ValueType = valueType;
|
|
||||||
Accessor = accessor;
|
|
||||||
CsvEncoder = csvEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExportFieldMetadata(string name, string columnName, Type valueType, Func<T, object> accessor, Func<object, string> csvEncoder)
|
|
||||||
: this(name, valueType, accessor, csvEncoder)
|
|
||||||
{
|
|
||||||
ColumnName = columnName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Disco.Models.Services.Exporting;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Disco.Models.Exporting
|
||||||
|
{
|
||||||
|
public class ExportMetadata<O, R>
|
||||||
|
: List<ExportMetadataField<R>>
|
||||||
|
where O : IExportOptions
|
||||||
|
where R : IExportRecord
|
||||||
|
{
|
||||||
|
public List<string> IgnoreGroupNames { get; } = new List<string>();
|
||||||
|
public O Options { get; set; }
|
||||||
|
|
||||||
|
public ExportMetadata(O options)
|
||||||
|
{
|
||||||
|
Options = options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Disco.Models.Exporting
|
||||||
|
{
|
||||||
|
public class ExportMetadataField<T> where T : IExportRecord
|
||||||
|
{
|
||||||
|
public string ColumnName { get; }
|
||||||
|
public Type ValueType { get; }
|
||||||
|
public Func<T, object> Accessor { get; }
|
||||||
|
public Func<object, string> CsvEncoder { get; }
|
||||||
|
|
||||||
|
public ExportMetadataField(string columnName, Type valueType, Func<T, object> accessor, Func<object, string> csvEncoder)
|
||||||
|
{
|
||||||
|
ColumnName = columnName;
|
||||||
|
ValueType = valueType;
|
||||||
|
Accessor = accessor;
|
||||||
|
CsvEncoder = csvEncoder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,7 @@ namespace Disco.Models.Services.Exporting
|
|||||||
{
|
{
|
||||||
public interface IExportOptions
|
public interface IExportOptions
|
||||||
{
|
{
|
||||||
ExportFormat Format { get; }
|
int Version { get; set; }
|
||||||
string FilenamePrefix { get; }
|
ExportFormat Format { get; set; }
|
||||||
string ExcelWorksheetName { get; }
|
|
||||||
string ExcelTableName { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("2.4.24270.0000")]
|
[assembly: AssemblyVersion("2.5.25262.0000")]
|
||||||
[assembly: AssemblyFileVersion("2.4.24270.0000")]
|
[assembly: AssemblyFileVersion("2.5.25262.0000")]
|
||||||
@@ -1,8 +1,4 @@
|
|||||||
using System;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
namespace Disco.Models.Repository
|
namespace Disco.Models.Repository
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
@@ -49,31 +47,19 @@ namespace Disco.Models.Repository
|
|||||||
public virtual IList<DeviceAttachment> DeviceAttachments { get; set; }
|
public virtual IList<DeviceAttachment> DeviceAttachments { get; set; }
|
||||||
public virtual IList<DeviceCertificate> DeviceCertificates { get; set; }
|
public virtual IList<DeviceCertificate> DeviceCertificates { get; set; }
|
||||||
|
|
||||||
[InverseProperty("DeviceSerialNumber")]
|
[InverseProperty(nameof(Job.Device))]
|
||||||
public virtual IList<Job> Jobs { get; set; }
|
public virtual IList<Job> Jobs { get; set; }
|
||||||
public virtual IList<DeviceFlagAssignment> DeviceFlagAssignments { get; set; }
|
public virtual IList<DeviceFlagAssignment> DeviceFlagAssignments { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
[InverseProperty(nameof(DeviceComment.Device))]
|
||||||
/// A list of the current device assignments, ordered by the most recent assignment date.
|
public virtual IList<DeviceComment> DeviceComments { get; set; }
|
||||||
/// </summary>
|
|
||||||
[NotMapped]
|
|
||||||
public IList<DeviceUserAssignment> CurrentDeviceUserAssignments
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return DeviceUserAssignments?
|
|
||||||
.Where(dua => dua.UnassignedDate is null)
|
|
||||||
.OrderByDescending(dua => dua.AssignedDate)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
if (DeviceModel != null)
|
if (DeviceModel != null)
|
||||||
return string.Format("{0} - {1}", this.DeviceModel, this.SerialNumber);
|
return $"{DeviceModel} - {SerialNumber}";
|
||||||
else
|
else
|
||||||
return this.SerialNumber;
|
return SerialNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Disco.Models.Repository
|
|||||||
[Required, StringLength(500)]
|
[Required, StringLength(500)]
|
||||||
public string MimeType { get; set; }
|
public string MimeType { get; set; }
|
||||||
public DateTime Timestamp { get; set; }
|
public DateTime Timestamp { get; set; }
|
||||||
[Required, StringLength(500)]
|
[StringLength(500)]
|
||||||
public string Comments { get; set; }
|
public string Comments { get; set; }
|
||||||
|
|
||||||
public string DocumentTemplateId { get; set; }
|
public string DocumentTemplateId { get; set; }
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
@@ -55,11 +53,11 @@ namespace Disco.Models.Repository
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(this.Name))
|
if (string.IsNullOrWhiteSpace(Name))
|
||||||
{
|
{
|
||||||
return string.Format("{0}: {1}", this.Id, this.PurchaseDate.ToLongDateString());
|
return $"{Id}: {PurchaseDate.ToLongDateString()}";
|
||||||
}
|
}
|
||||||
return this.Name;
|
return Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Disco.Models.Repository
|
|||||||
[Required, StringLength(500)]
|
[Required, StringLength(500)]
|
||||||
public string MimeType { get; set; }
|
public string MimeType { get; set; }
|
||||||
public DateTime Timestamp { get; set; }
|
public DateTime Timestamp { get; set; }
|
||||||
[Required, StringLength(500)]
|
[StringLength(500)]
|
||||||
public string Comments { get; set; }
|
public string Comments { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user