Imports DigitalData.Modules.Database Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Base Imports GdPicture14 Imports Quartz Imports System.Security.Cryptography Imports System.IO Imports EnvelopeGenerator.Common.Jobs.FinalizeDocument.FinalizeDocumentExceptions Imports EnvelopeGenerator.Common.Jobs.FinalizeDocument Imports EnvelopeGenerator.Common.My.Resources Imports EnvelopeGenerator.Common.Constants Imports DevExpress.XtraBars.Docking Imports System.ServiceModel Imports DevExpress.XtraRichEdit.Export Imports DevExpress.Pdf.Native.BouncyCastle.Asn1.Cms Imports DevExpress.DataAccess.Sql Imports DevExpress.DataProcessing Imports Quartz.Logging.OperationName Namespace Jobs Public Class FinalizeDocumentJob Implements IJob Private ReadOnly LicenseManager As New LicenseManager() Private GdViewer As GdViewer Private LogConfig As LogConfig Private Logger As Logger Private Database As MSSQLServer Private Config As DbConfig Private ConfigModel As ConfigModel Private EnvelopeModel As EnvelopeModel Private ReportModel As ReportModel Private ActionService As ActionService Private PDFBurner As PDFBurner Private PDFMerger As PDFMerger Private ReportCreator As ReportCreator Private ReadOnly CompleteWaitTime As Integer = 5 Private ParentFolderUID As String = "" Private Class EnvelopeData Public EnvelopeId As Integer Public DocumentPath As String Public AnnotationData As List(Of String) End Class Public Function Execute(pContext As IJobExecutionContext) As Task Implements IJob.Execute Dim oGdPictureKey As String = pContext.MergedJobDataMap.Item(Constants.GDPICTURE) LogConfig = pContext.MergedJobDataMap.Item(Constants.LOGCONFIG) Logger = LogConfig.GetLogger() Dim JobId = pContext.JobDetail.Key Logger.Info("Starting job {0}", JobId) Try Logger.Debug("Loading GdViewer..") GdViewer = New GdViewer() LicenseManager.RegisterKEY(oGdPictureKey) Logger.Debug("Loading Database..") Database = GetDatabase(pContext, LogConfig) Logger.Debug("Loading Models & Services") Dim oState = GetState() InitializeModels(oState) Logger.Debug("Loading Configuration..") Config = ConfigModel.LoadConfiguration() oState.DbConfig = Config InitializeServices(oState) Logger.Debug("Loading PDFBurner..") PDFBurner = New PDFBurner(LogConfig, oGdPictureKey) Logger.Debug("Loading PDFMerger..") PDFMerger = New PDFMerger(LogConfig, oGdPictureKey) Logger.Debug("Loading ReportCreator..") ReportCreator = New ReportCreator(LogConfig, oState) Logger.Debug("My.Settings.RuninDMZ: [{0}]", My.Settings.RuninDMZ.ToString) Logger.Debug("My.Settings.NetUse_Usr: [{0}]", My.Settings.NetUse_Usr) If My.Settings.RuninDMZ = True Then If Config.DocumentPath_DMZ <> String.Empty Then Logger.Debug("RuninDMZ - Using DocumentPath_DMZ: [{0}] - Overwrite Document-Path", Config.DocumentPath_DMZ) Config.DocumentPath = Config.DocumentPath_DMZ Config.NetUse_necessary = True Else Config.DocumentPath = Config.DocumentPath End If Else If Config.DOCUMENT_PATH_MOVE_AFTSEND <> String.Empty Then Logger.Debug("Using DMZRemotePath: [{0}] - Overwrite Document-Path ...", Config.DOCUMENT_PATH_MOVE_AFTSEND) Config.DocumentPath = Config.DOCUMENT_PATH_MOVE_AFTSEND Config.NetUse_Finish = True Else Config.DocumentPath = Config.DocumentPath End If End If Logger.Debug("DocumentPath: [{0}]", Config.DocumentPath) If My.Settings.RuninDMZ = True Then If Config.FINISHED_PATH_EX_DMZ <> String.Empty Then Logger.Debug("RuninDMZ - FINISHED_PATH_EX_DMZ configured: [{0}]", Config.FINISHED_PATH_EX_DMZ) Config.NetUse_Finish = True End If If Config.ExportPath_DMZ <> String.Empty Then Logger.Debug("RuninDMZ - Using ExportPath_DMZ: [{0}] - Overwrite ExportPath", Config.ExportPath_DMZ) Config.ExportPath = Config.ExportPath_DMZ End If End If Logger.Debug("ExportPath: [{0}]", Config.ExportPath) If Config.NetUse_Finish = True Then If NetUse_Command(Config.DocumentPath, My.Settings.NetUse_Usr, My.Settings.NetUse_PW) = True Then Logger.Debug("NetUse_Finish = successful!") End If End If Dim oCompleteStatus As Integer = Constants.EnvelopeStatus.EnvelopeCompletelySigned Dim oSql = $"SELECT * FROM TBSIG_ENVELOPE WHERE STATUS = {oCompleteStatus} AND DATEDIFF(minute, CHANGED_WHEN, GETDATE()) >= {CompleteWaitTime}" Dim oTable = Database.GetDatatable(oSql) Dim oEnvelopeIds As List(Of Integer) = oTable.Rows.Cast(Of DataRow). Select(Function(r) r.Item("GUID")). Cast(Of Integer). ToList() If oEnvelopeIds.Count > 0 Then Logger.Info("Found [{0}] completed envelopes.", oEnvelopeIds.Count) End If Dim oTotal As Integer = oEnvelopeIds.Count Dim oCurrent As Integer = 1 For Each oId In oEnvelopeIds Logger.Info("Finalizing Envelope [{0}] ({1}/{2})", oId, oCurrent, oTotal) Logger.Debug("Loading Envelope..") Dim oEnvelope = EnvelopeModel.GetById(oId) If oEnvelope Is Nothing Then Logger.Warn("Envelope could not be loaded for Id [{0}]!", oId) Throw New ArgumentNullException("EnvelopeData") End If Logger.Debug("Loading Envelope Data..") Dim oEnvelopeData = GetEnvelopeData(oId) If oEnvelopeData Is Nothing Then Logger.Warn("EnvelopeData could not be loaded for Id [{0}]!", oId) Throw New ArgumentNullException("EnvelopeData") End If If Config.DOCUMENT_PATH_MOVE_AFTSEND <> String.Empty Then oEnvelopeData.DocumentPath.Replace(Config.DocumentPathOrigin, Config.DOCUMENT_PATH_MOVE_AFTSEND) Logger.Debug("Replaced Path in oEnvelopeData.DocumentPath!") End If Logger.Debug("Burning Annotations to pdf ...") Dim oBurnedDocument As Byte() = BurnAnnotationsToPdf(oEnvelopeData) If oBurnedDocument Is Nothing Then Logger.Warn("Document could not be finalized!") Throw New ApplicationException("Document could not be finalized") End If If ActionService.CreateReport(oEnvelope) = False Then Logger.Warn("Document Report could not be created!") Throw New ApplicationException("Document Report could not be created") End If Logger.Debug("Creating report..") Dim oReport As Byte() = ReportCreator.CreateReport(oEnvelope) Logger.Debug("Report created!") Logger.Debug("Merging documents ...") Dim oMergedDocument As Byte() = PDFMerger.MergeDocuments(oBurnedDocument, oReport) Logger.Debug("Documents merged!") Dim oOutputDirectoryPath = Path.Combine(Config.ExportPath, ParentFolderUID) If Not Directory.Exists(oOutputDirectoryPath) Then Directory.CreateDirectory(oOutputDirectoryPath) End If Dim oOutputFilePath = Path.Combine(oOutputDirectoryPath, $"{oEnvelope.Uuid}.pdf") Logger.Info("Writing finalized Pdf to disk..") Logger.Info("Output path is [{0}]", oOutputFilePath) Try File.WriteAllBytes(oOutputFilePath, oMergedDocument) Catch ex As Exception Logger.Warn("Could not export final document to disk!") Throw New ExportDocumentException("Could not export final document to disk!", ex) End Try If Config.NetUse_Finish = True Then If Config.FINISHED_PATH_EX_DMZ <> String.Empty Then If My.Settings.NetUse_PW <> String.Empty And My.Settings.NetUse_Usr <> String.Empty Then Clean_DNZ_PAth(Config.FINISHED_PATH_EX_DMZ) 'Dim oReturnPath = MoveFileWithNetUse(Config.FINISHED_PATH_EX_DMZ, oOutputFilePath, My.Settings.NetUse_Usr, My.Settings.NetUse_PW) 'If oReturnPath <> String.Empty Then ' oOutputFilePath = oReturnPath ' If Config.EML_PATH_EX_DMZ <> String.Empty Then ' Logger.Debug($"Now replacing [{Config.FINISHED_PATH_EX_DMZ}] with ") ' Logger.Debug($"............. [{Config.EML_PATH_EX_DMZ}] ") ' oOutputFilePath = oOutputFilePath.Replace(Config.FINISHED_PATH_EX_DMZ, Config.EML_PATH_EX_DMZ) ' Logger.Debug($"oOutputFilePath [{oOutputFilePath}] ") ' End If 'Else ' Logger.Info($"Exiting/Aborting FinalizeDocuments!") ' Return Task.FromResult(False) 'End If End If End If End If Logger.Info("Sending finalized report-mails..") If SendFinalEmails(oEnvelope, oOutputFilePath) = False Then Throw New ApplicationException("Final emails could not be sent!") End If Logger.Debug("Setting envelope status..") If ActionService.FinalizeEnvelope(oEnvelope) = False Then Logger.Warn("Envelope could not be finalized!") Throw New ApplicationException("Envelope could not be finalized") End If oCurrent += 1 Logger.Info("Envelope finalized!") Next Logger.Debug("Completed job {0} successfully!", JobId) Catch ex As MergeDocumentException Logger.Warn("Certificate Document job failed at step: Merging documents!") Logger.Error(ex) Catch ex As ExportDocumentException Logger.Warn("Certificate Document job failed at step: Exporting document!") Logger.Error(ex) Catch ex As Exception Logger.Warn("Certificate Document job failed!") Logger.Error(ex) Finally Logger.Info("Job execution for [{0}] ended", JobId) End Try Return Task.FromResult(True) End Function Private Function NetUse_Command(pDestinationPath As String, pUsername As String, pPassword As String) Dim oDectryptedPW = Helpers.Decrypt(My.Settings.NetUse_PW) Dim netUseCommand As String = $"net use {pDestinationPath} /user:{pUsername} {oDectryptedPW}" Logger.Debug("EXECUTING NetUse_Command for " & pDestinationPath) Dim processInfo As New ProcessStartInfo("cmd.exe", $"/C {netUseCommand}") processInfo.RedirectStandardOutput = True processInfo.UseShellExecute = False processInfo.CreateNoWindow = True Using process As Process = Process.Start(processInfo) process.WaitForExit() ' Prüfe den Rückgabewert des net use Befehls If process.ExitCode = 0 Then Return True Else Return False End If End Using End Function Private Function Clean_DNZ_PAth(pSourcePath As String) As Boolean Dim oFilename = System.IO.Path.GetFileName(pSourcePath) Logger.Debug("## Starting Clean_DNZ_PAth ...") Logger.Debug("## pSourcePath {0}", pSourcePath) Dim oDirectorySource = Path.Combine(pSourcePath, ParentFolderUID) Try Logger.Debug($"Deleting oDirectorySource {oDirectorySource} ...") Directory.Delete(oDirectorySource, True) Console.WriteLine($"Folder successfully deleted!") Logger.Debug($"...Deleted!") Return True Catch ex As Exception Logger.Error(ex) Return False End Try End Function Private Function SendFinalEmails(pEnvelope As Envelope, pAttachment As String) As Boolean Dim oMailToCreator = pEnvelope.FinalEmailToCreator Dim oMailToReceivers = pEnvelope.FinalEmailToReceivers If oMailToCreator <> FinalEmailType.No Then Logger.Debug("Sending email to creator ...") SendFinalEmailToCreator(pEnvelope, pAttachment) End If If oMailToReceivers <> FinalEmailType.No Then Logger.Debug("Sending emails to receivers..") SendFinalEmailToReceivers(pEnvelope, pAttachment) End If Return True End Function Private Function SendFinalEmailToCreator(pEnvelope As Envelope, pAttachment As String) As Boolean Dim oIncludeAttachment = SendFinalEmailWithAttachment(pEnvelope.FinalEmailToCreator) Dim oAttachment = String.Empty Logger.Debug("Attachment included: [{0}]", oIncludeAttachment) If oIncludeAttachment Then oAttachment = pAttachment End If If ActionService.CompleteEnvelope(pEnvelope, oAttachment) = False Then Logger.Error("Envelope could not be completed for receiver [{0}]", pEnvelope.User.Email) Return False End If Return True End Function Private Function SendFinalEmailToReceivers(pEnvelope As Envelope, pAttachment As String) As Boolean Dim oIncludeAttachment = SendFinalEmailWithAttachment(pEnvelope.FinalEmailToReceivers) Dim oAttachment = String.Empty Logger.Debug("Attachment included: [{0}]", oIncludeAttachment) If oIncludeAttachment Then oAttachment = pAttachment End If For Each oReceiver In pEnvelope.Receivers If ActionService.CompleteEnvelope(pEnvelope, oReceiver, oAttachment) = False Then Logger.Error("Envelope could not be completed for receiver [{0}]", oReceiver.Email) Return False End If Next Return True End Function Private Function SendFinalEmailWithAttachment(pType As FinalEmailType) If pType = FinalEmailType.YesWithAttachment Then Return True Else Return False End If End Function Private Function BurnAnnotationsToPdf(pEnvelopeData As EnvelopeData) As Byte() Dim pEnvelopeId = pEnvelopeData.EnvelopeId Logger.Info($"Burning [{pEnvelopeData.AnnotationData.Count}] signatures") Dim oAnnotations = pEnvelopeData.AnnotationData Dim oInputPath = "" If My.Settings.RuninDMZ Then Logger.Debug("Replacing Path in pData.DocumentPath ...") oInputPath = pEnvelopeData.DocumentPath.Replace(Config.DocumentPathOrigin, Config.DocumentPath) ElseIf Config.DOCUMENT_PATH_MOVE_AFTSEND <> String.Empty Then Logger.Debug("Replacing Path in pData.DocumentPath ...") oInputPath = pEnvelopeData.DocumentPath.Replace(Config.DocumentPathOrigin, Config.DOCUMENT_PATH_MOVE_AFTSEND) Else oInputPath = pEnvelopeData.DocumentPath End If Logger.Info($"Input path: [{oInputPath}]") Dim oDirectorySource As String = Path.GetDirectoryName(oInputPath) Dim split As String() = oDirectorySource.Split("\") ParentFolderUID = split(split.Length - 1) Logger.Info("ParentFolderUID: [{0}]", ParentFolderUID) Dim oInputDocumentBuffer As Byte() Try oInputDocumentBuffer = File.ReadAllBytes(oInputPath) Catch ex As Exception Logger.Error(ex) Throw New BurnAnnotationException("Source document could not be read from disk!", ex) End Try Return PDFBurner.BurnInstantJSONAnnotationsToPDF(oInputDocumentBuffer, oAnnotations) End Function Private Function GetEnvelopeData(pEnvelopeId As Integer) As EnvelopeData Dim oSql = $"SELECT T.GUID, T2.FILEPATH FROM [dbo].[TBSIG_ENVELOPE] T JOIN TBSIG_ENVELOPE_DOCUMENT T2 ON T.GUID = T2.ENVELOPE_ID WHERE T.GUID = {pEnvelopeId}" Dim oTable As DataTable = Database.GetDatatable(oSql) Dim oRow As DataRow = oTable.Rows.Cast(Of DataRow).SingleOrDefault() If oRow Is Nothing Then Return Nothing End If Dim oAnnotationData = GetAnnotationData(pEnvelopeId) Dim oData As New EnvelopeData With { .EnvelopeId = pEnvelopeId, .DocumentPath = oRow.ItemEx("FILEPATH", ""), .AnnotationData = oAnnotationData } Logger.Debug("Document path: [{0}]", oData.DocumentPath) Return oData End Function Private Function GetAnnotationData(pEnvelopeId As Integer) As List(Of String) Dim oSql = $"SELECT VALUE FROM TBSIG_DOCUMENT_STATUS WHERE ENVELOPE_ID = {pEnvelopeId}" Dim oTable As DataTable = Database.GetDatatable(oSql) Return oTable.Rows.Cast(Of DataRow). Select(Function(r) r.ItemEx("VALUE", String.Empty)). Cast(Of String). ToList() End Function Private Sub InitializeServices(pState As State) ActionService = New ActionService(pState) End Sub Private Sub InitializeModels(pState As State) ConfigModel = New ConfigModel(pState) EnvelopeModel = New EnvelopeModel(pState) ReportModel = New ReportModel(pState) End Sub Private Function GetDatabase(pContext As IJobExecutionContext, pLogConfig As LogConfig) As MSSQLServer Dim oConnectionString As String = pContext.MergedJobDataMap.Item(Constants.DATABASE) Dim Database = New MSSQLServer(pLogConfig, MSSQLServer.DecryptConnectionString(oConnectionString)) Return Database End Function Private Function GetState() As State Return New State With { .LogConfig = LogConfig, .Database = Database, .UserId = 0, .Config = Nothing, .DbConfig = Nothing } End Function End Class End Namespace