Imports DigitalData.Modules.Database Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Filesystem Imports DigitalData.Modules Imports System.IO Imports System.ServiceModel Imports System.Data.SqlClient Public Class EDMIService Implements IEDMIService Public Shared LogConfig As LogConfig Public Shared MSSQL As MSSQLServer Public Shared Firebird As Firebird Public Shared AppConfig As AppConfig Public Shared Filesystem As Filesystem.File Public Shared EDMIArchive As EDMI.File.Archive Public Shared GlobalState As GlobalState Private ReadOnly _logger As Logger Private _request As Request = Nothing Private _debug As Boolean = False Private _username As String Public Sub New() Dim oOperationContext As OperationContext = OperationContext.Current Dim oInstanceContext As InstanceContext = oOperationContext.InstanceContext Dim oUsername = StripDomainFromUsername(oOperationContext.ServiceSecurityContext.WindowsIdentity.Name) _username = oUsername _logger = LogConfig.GetLogger() _logger.Debug("New Request by User [{0}]", _username) End Sub Public Function StripDomainFromUsername(UserName As String) If UserName.Contains("\") Then Return UserName.Split("\")(1) ElseIf UserName.Contains("@") Then Return UserName.Split("@")(0) Else Return UserName End If End Function #Region "Auth" Private Function TestUserAuth() As Boolean Try 'Dim oSQL As String = $"SELECT FNIDB_AUTH_USER('{_username}') FROM RDB$DATABASE;" 'Dim oResult As Boolean = Database.GetScalarValue(oSQL) 'Return oResult Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function #End Region #Region "Heartbeat" Public Function Heartbeat() As Boolean Implements IEDMIService.Heartbeat Return True End Function #End Region #Region "Request" Public Sub CreateRequest(Name As String, Optional Debug As Boolean = False) _request = New Request(Name, _username, Firebird, Debug) _debug = Debug _logger.Info("Creating request {0}/{1}", _request.Name, _request.RequestId) End Sub Public Function CreateDatabaseRequest(Name As String, Optional Debug As Boolean = False) As String Implements IEDMIService.CreateDatabaseRequest CreateRequest(Name, Debug) Return _request.Name End Function Public Sub CloseDatabaseRequest() Implements IEDMIService.CloseDatabaseRequest If IsNothing(_request) Then Exit Sub End If _logger.Info("Closing request {0}", _request.ToString) _request.Connection.Close() _request = Nothing End Sub #End Region #Region "Database" Private Sub TestRequestCreated() If IsNothing(_request) Then Throw New Exceptions.NoRequestException() End If End Sub Public Function ReturnDatatable(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable Try TestRequestCreated() _logger.Info($"ReturnDatatable, SQL: {SQL}") _request.LogDebug($"ReturnDatatable, SQL: {SQL}") Dim oResult As DataTable = Firebird.GetDatatableWithConnection(SQL, _request.Connection) Return New TableResult(oResult) Catch ex As Exception _logger.Error(ex) _request.LogError(ex.Message) Return New TableResult(ex.Message) End Try End Function Public Function ReturnScalar(SQL As String) As ScalarResult Implements IEDMIService.ReturnScalar Try TestRequestCreated() _logger.Info($"ReturnScalar, SQL: {SQL}") _request.LogDebug($"ReturnScalar, SQL: {SQL}") Dim oResult As Object = Firebird.GetScalarValueWithConnection(SQL, _request.Connection) Return New ScalarResult(oResult) Catch ex As Exception _logger.Error(ex) _request.LogError(ex.Message) Return New ScalarResult(ex.Message) End Try End Function Public Function ExecuteNonQuery(SQL As String) As NonQueryResult Implements IEDMIService.ExecuteNonQuery Try TestRequestCreated() _logger.Info($"ExecuteNonQuery, SQL: {SQL}") _request.LogDebug($"ExecuteNonQuery, SQL: {SQL}") Dim oResult As Boolean = Firebird.ExecuteNonQueryWithConnection(SQL, _request.Connection) Return New NonQueryResult() Catch ex As Exception _logger.Error(ex) _request.LogError(ex.Message) Return New NonQueryResult(ex.Message) End Try End Function #End Region #Region "Document (with FileContainer)" Public Function NewFile(FileName As String, Contents() As Byte) As DocumentResultOld Implements IEDMIService.NewFile Try Dim oContainer As FileContainer Dim oContainerId As String If Not TestUserAuth() Then Throw New Exception($"User {_username} not authorized.") End If oContainer = FileContainer.Create(LogConfig, AppConfig.ContainerPassword) oContainerId = oContainer.ContainerId _logger.Debug("Container created with id {0}", oContainerId) Dim oExtension As String = Path.GetExtension(FileName).Substring(1) _logger.Debug("File extension of file {0} is {1}", FileName, oExtension) Dim oSQL = $"SELECT FNICM_NEW_DOC('010', '{oContainerId}', '{GetContainerName(oContainerId)}', '{FileName}', '{oExtension}', '{_username}') FROM RDB$DATABASE;" Dim oDocId As Int64 = Firebird.GetScalarValue(oSQL) If oDocId = -1 Then _logger.Warn("Database returned -1 while creating Document Entry. File was not saved!") Return Nothing End If _logger.Debug("Database Entry created with DocId {0}", oDocId) oContainer.SetFile(Contents, FileName) oContainer.SaveAs(GetContainerPath(oContainerId)) _logger.Debug("File saved in Container!", FileName) Dim oDocument = New DocumentObject(oContainerId, oDocId, FileName) Return New DocumentResultOld(oDocument) Catch ex As Exception _logger.Error(ex) Return New DocumentResultOld(ex.Message) End Try End Function Public Function UpdateFile(DocObject As DocumentObject, Contents() As Byte) As DocumentResultOld Implements IEDMIService.UpdateFile Try TestFileExists(DocObject.ContainerId) ' TODO: update db Dim oFilePath = GetContainerPath(DocObject.ContainerId) Dim oFileContainer As FileContainer = FileContainer.Load(LogConfig, AppConfig.ContainerPassword, oFilePath) oFileContainer.SetFile(Contents, oFileContainer.GetFile.FileName) oFileContainer.Save() Return New DocumentResultOld(DocObject) Catch ex As Exception _logger.Error(ex) Return Nothing End Try End Function Public Function GetFile(DocObject As DocumentObject) As DocumentResultOld Implements IEDMIService.GetFile Try TestFileExists(DocObject.ContainerId) Dim oContainerPath = GetContainerPath(DocObject.ContainerId) Dim oContainer As FileContainer = FileContainer.Load(LogConfig, AppConfig.ContainerPassword, oContainerPath) Dim oContents As Byte() = oContainer.GetFile().Contents Return New DocumentResultOld(DocObject, oContents) Catch ex As Exception _logger.Error(ex) Return New DocumentResultOld(ex.Message) End Try End Function Public Function DeleteFile(DocObject As DocumentObject) As Boolean Implements IEDMIService.DeleteFile Try TestFileExists(DocObject.ContainerId) Dim oFilePath = GetContainerPath(DocObject.ContainerId) IO.File.Delete(oFilePath) 'TODO: Delete doc from db Return True Catch ex As Exception _logger.Error(ex) Return False End Try End Function Private Function GetContainerPath(ContainerId As String) As String Return Path.Combine(AppConfig.ContainerPath, GetContainerName(ContainerId)) End Function Private Function GetContainerName(ContainerId As String) As String Return ContainerId & ".enc" End Function Private Sub TestFileExists(ContainerId) Dim oContainerPath = GetContainerPath(ContainerId) If Not IO.File.Exists(oContainerPath) Then Throw New FileNotFoundException("Container existiert nicht", oContainerPath) End If End Sub Public Function GetDocumentByDocumentId(DocumentId As Long) As DocumentResultOld Implements IEDMIService.GetDocumentByDocumentId Try Dim oSQL = $"SELECT GUID, CONTAINER_ID, ORIGINAL_FILENAME FROM TBIDB_DOCUMENT WHERE GUID = {DocumentId}" Dim oTable = Firebird.GetDatatable(oSQL) If oTable.Rows.Count = 0 Then Return New DocumentResultOld("Document not found") End If Dim oRow As DataRow = oTable.Rows.Item(0) Dim oDocument As New DocumentObject( oRow.Item("CONTAINER_ID"), oRow.Item("GUID"), oRow.Item("ORIGINAL_FILENAME") ) TestFileExists(oDocument.ContainerId) Dim oContainerPath = GetContainerPath(oDocument.ContainerId) Dim oContainer As FileContainer = FileContainer.Load(LogConfig, AppConfig.ContainerPassword, oContainerPath) Dim oContents As Byte() = oContainer.GetFile().Contents Return New DocumentResultOld(oDocument, oContents) Catch ex As Exception Return New DocumentResultOld(ex.Message) End Try End Function Public Function GetDocumentByContainerId(ContainerId As String) As DocumentResultOld Implements IEDMIService.GetDocumentByContainerId Try Dim oSQL = $"SELECT GUID, CONTAINER_ID, ORIGINAL_FILENAME FROM TBIDB_DOCUMENT WHERE CONTAINER_ID = '{ContainerId}'" Dim oTable = Firebird.GetDatatable(oSQL) If oTable.Rows.Count = 0 Then Return New DocumentResultOld("Document not found") End If Dim oRow As DataRow = oTable.Rows.Item(0) Dim oDocument As New DocumentObject( oRow.Item("CONTAINER_ID"), oRow.Item("GUID"), oRow.Item("ORIGINAL_FILENAME") ) TestFileExists(oDocument.ContainerId) Dim oContainerPath = GetContainerPath(oDocument.ContainerId) Dim oContainer As FileContainer = FileContainer.Load(LogConfig, AppConfig.ContainerPassword, oContainerPath) Dim oContents As Byte() = oContainer.GetFile().Contents Return New DocumentResultOld(oDocument, oContents) Catch ex As Exception Return New DocumentResultOld(ex.Message) End Try End Function #End Region #Region "Document" ''' ''' Imports a file according to ObjectStoreId ''' ''' Public Function ImportFile(Data As Messages.DocumentImportRequest) As Messages.DocumentImportResponse Implements IEDMIService.ImportFile Dim oObjectStore = GlobalState.ObjectStores. Where(Function(s) s.Id = Data.ObjectStoreId). FirstOrDefault() If oObjectStore Is Nothing Then Throw New FaultException($"Object Store with Id [{Data.ObjectStoreId}] does not exist!") End If Dim EDMIPath = New EDMI.File.Path(LogConfig, oObjectStore.Path) ' TODO: ' - Get Object Store -> Object Catalog -> Catalog Path ' - If IS_ARCHIVE = True And RetentionDays <> Nothing: ' DoArchive! ' - Refactor EDMIPath to get ObjectStore at service start up ' and return ObjectStore Path from ObjectStoreId + RelativePath ' VWIDB_OBJECTSTORE Dim oRelativePath As String = EDMIPath.GetRelativePath(Data.DocumentType, Data.FileName) Dim oAbsolutePath As String = EDMIPath.GetFullPath(Data.DocumentType, Data.FileName) Dim oDirectoryPath = EDMIPath.GetFullPath(Data.DocumentType) Try Directory.CreateDirectory(oDirectoryPath) Catch ex As Exception _logger.Error(ex) Throw New FaultException(ex.Message) End Try Try Dim oVersionedFileName As String = Filesystem.GetVersionedFilename(oAbsolutePath) _logger.Info("ImportFile: Saving file [{0}] to path [{1}]", Data.FileName, oVersionedFileName) Using oStream = New FileStream(oVersionedFileName, FileMode.CreateNew) oStream.Write(Data.Contents, 0, Data.Contents.Length) oStream.Flush(True) oStream.Close() End Using ' insert into db Dim oCommand As New SqlCommand("PRIDB_NEW_DOCUMENT") oCommand.Parameters.AddWithValue("@OBJ_ST_ID", 1) oCommand.Parameters.AddWithValue("@REL_PATH", oRelativePath) oCommand.Parameters.AddWithValue("@WHO", _username) oCommand.Parameters.AddWithValue("@REF_DOCID", 0) oCommand.Parameters.Add(New SqlParameter("@IDB_OBJ_ID", SqlDbType.BigInt)) Dim oObjectId = MSSQL.GetScalarValue(oCommand, "@IDB_OBJ_ID") Return New Messages.DocumentImportResponse() With {.ObjectId = oObjectId} Catch ex As Exception _logger.Error(ex) Throw New FaultException(ex.Message) End Try End Function Public Function GetFileByObjectId(Data As Messages.DocumentStreamRequest) As Messages.DocumentStreamResponse Implements IEDMIService.GetFileByObjectId Try Dim oSQL As String = $"SELECT ObjectStoreId FROM VWIDB_DOC_DATA WHERE IDB_OBJ_ID = {Data.ObjectId}" Dim oObjectStoreId = MSSQL.GetScalarValue(oSQL) Dim oObjectStore = GlobalState.ObjectStores. Where(Function(s) s.Id = oObjectStoreId). FirstOrDefault() Dim oSQL2 As String = $"SELECT DocRelativePath FROM VWIDB_DOC_DATA WHERE IDB_OBJ_ID = {Data.ObjectId}" Dim oPath As String = MSSQL.GetScalarValue(oSQL2) If IsNothing(oPath) Then Throw New FaultException($"Object [{Data.ObjectId}] does not exist in database!") End If Dim EDMIPath As New EDMI.File.Path(LogConfig, oObjectStore.Path) Dim oFullPath = EDMIPath.GetFullPathFromRelativePath(oPath) _logger.Debug("GetFileByObjectId: Loading file [{0}]", oFullPath) Dim oFileInfo As New FileInfo(oFullPath) If Not oFileInfo.Exists Then Throw New FaultException($"Object [{Data.ObjectId}] does not exist on filesystem!") End If Dim oDestination As New MemoryStream() Using oSource As FileStream = IO.File.OpenRead(oFullPath) oSource.CopyTo(oDestination) End Using oDestination.Seek(0, SeekOrigin.Begin) Dim oMessage As New Messages.DocumentStreamResponse() With { .FileName = oFileInfo.Name, .FileContents = oDestination } Return oMessage Catch ex As IOException _logger.Error(ex) Throw New FaultException($"Object [{Data.ObjectId}] could not be streamed!") Catch ex As Exception _logger.Error(ex) Throw New FaultException(ex.Message) End Try End Function Public Function ListFilesForUser() As Messages.DocumentListResponse Implements IEDMIService.ListFilesForUser Try Dim oSQL = $"SELECT * FROM VWIDB_DOC_DATA" Dim oDatatable As DataTable = MSSQL.GetDatatable(oSQL) oDatatable.TableName = "DocumentList" Return New Messages.DocumentListResponse() With { .Datatable = oDatatable } Catch ex As Exception _logger.Error(ex) Throw New FaultException(ex.Message) End Try End Function #End Region #Region "Index" Public Function NewFileIndex(DocObject As DocumentObject, Syskey As String, LanguageCode As String, Value As String) As IndexResult Implements IEDMIService.NewFileIndex Try Dim oSQL = $"SELECT FNIDB_NEW_DOC_VALUE({DocObject.DocumentId},'{Syskey}','{LanguageCode}','{Value}','{_username}') FROM RDB$DATABASE;" Dim oIndexId As Int64 = Firebird.GetScalarValue(oSQL) Return New IndexResult(oIndexId) Catch ex As Exception _logger.Error(ex) Return New IndexResult(ex.Message) End Try End Function #End Region End Class