// Client.cpp : implementation file // #include "stdafx.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif extern long glSec; extern CDICOMSRVApp theApp; ///////////////////////////////////////////////////////////////////////////// // CNetClient CNetClient::CNetClient(L_UINT32 nMode) : LDicomNet((L_CHAR*)(LPCTSTR) theApp.m_sTempFilesFolder, nMode, 0) { L_INT nRet = DICOM_SUCCESS; CString strCACertName; L_SSL_CTX_CREATE ctxCreate; memset(&ctxCreate, 0, sizeof(L_SSL_CTX_CREATE)); ctxCreate.uStructSize = sizeof(L_SSL_CTX_CREATE); ctxCreate.uFlags = FLAG_SSL_CTX_CREATE_METHOD_TYPE | FLAG_SSL_CTX_CREATE_VERIFY_MODE | FLAG_SSL_CTX_CREATE_VERIFY_DEPTH | FLAG_SSL_CTX_CREATE_OPTIONS; ctxCreate.nMethodTypeSSL= TYPE_SSLV23_METHOD; theApp.GetCertName(CA_CERT_NAME, strCACertName); if (_access((LPCSTR)strCACertName, 4)==0) { ctxCreate.pszCAfile = (LPSTR)(LPCSTR)strCACertName; ctxCreate.uFlags |= FLAG_SSL_CTX_CREATE_CAFILE; } ctxCreate.pszCAfile = (LPSTR)(LPCSTR)strCACertName; ctxCreate.uVerifyMode = L_SSL_VERIFY_PEER | L_SSL_VERIFY_FAIL_IF_NO_PEER_CERT; ctxCreate.nVerifyDepth = 9; ctxCreate.nOptions = L_SSL_OP_NO_SSLv2|L_SSL_OP_ALL; ctxCreate.nReserved1 = 0; ctxCreate.nReserved2 = 0; m_nValid = DICOM_SUCCESS; nRet = Initialize((LPSTR)(LPCSTR)theApp.m_sTempFilesFolder, nMode, &ctxCreate); m_nValid = ctxCreate.nSuccess; if (nRet != DICOM_SUCCESS) { return; } switch (nMode) { case DICOM_SECURE_NONE: { break; } case DICOM_SECURE_ISCL: { if (LoadFromFileSettings() == 0) { LoadDefaultSettings(); } break; } case DICOM_SECURE_TLS: { CString strServerCertName; SetCipherToIndexTLS(0, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA); // Set password callback for certificate private key // The password callback returns the password (in this case, 'test') // SetCallbacksExt(hClient); theApp.GetCertName(SERVER_CERT_NAME, strServerCertName); nRet = SetClientCertificateTLS((LPSTR)(LPCSTR)strServerCertName, L_TLS_FILETYPE_PEM, NULL); if (nRet != DICOM_SUCCESS) { CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDlg->LogEvent("", "Invalid Server Certificate"); return; } } } } CNetClient::~CNetClient() { Close(); } ///////////////////////////////////////////////////////////////////////////// // Events L_VOID CNetClient::OnReceiveAssociateRequest(LDicomAssociate* pPDU) { CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); CString sCallingAETitle = pPDU->GetCalling(); // We have received an Associate Request pDlg->LogEvent(sCallingAETitle, "Associate Request received"); // Reject based on the Called AE Title CString sCalledAETitle = pPDU->GetCalled(); if (sCalledAETitle != theApp.m_sServerAETitle) { SendAssociateReject(PDU_REJECT_RESULT_PERMANENT, PDU_REJECT_SOURCE_USER, PDU_REJECT_REASON_CALLED); pDlg->LogEvent(sCallingAETitle, "Associate Reject sent - Invalid Called AE Title"); return; } // Reject based on the Calling AE Title L_CHAR szIPAddress[200]; GetPeerInfo(szIPAddress, NULL); if (!pDlg->FindClient(szIPAddress, &sCallingAETitle)) { SendAssociateReject(PDU_REJECT_RESULT_PERMANENT, PDU_REJECT_SOURCE_USER, PDU_REJECT_REASON_CALLING); pDlg->LogEvent(sCallingAETitle, "Associate Reject sent - Invalid Calling AE Title"); return; } pDlg->SetClientInfo(this, szIPAddress, sCallingAETitle); /* Send the Associate response */ LDicomAssociate Associate(FALSE); Associate.Reset(FALSE); // The Protocol Version Associate.SetVersion(PROTOCOL_VERSION); // Titles of the Called and Calling AEs Associate.SetCalled((L_CHAR*)(LPCTSTR) theApp.m_sServerAETitle); Associate.SetCalling((L_CHAR*)(LPCTSTR) sCallingAETitle); // The Application Context Name Associate.SetApplication(UID_APPLICATION_CONTEXT_NAME); L_INT nPresContCount = pPDU->GetPresentationCount(); L_UCHAR nPresContID; // Presentation Contexts L_CHAR* pszAbstractSynName; for (int i = 0; i < nPresContCount; i++) { nPresContID = pPDU->GetPresentation(i); pszAbstractSynName = pPDU->GetAbstract(nPresContID); Associate.AddPresentation(nPresContID, PDU_ACCEPT_RESULT_SUCCESS, pszAbstractSynName); // Do we support the Abstract Syntax of this Presentation Context? if (LDicomUID::Find(pszAbstractSynName)) { L_INT nTransSynCount = pPDU->GetTransferCount(nPresContID); L_CHAR* pszTransSynName; for (int j = 0; j < nTransSynCount; j++) { pszTransSynName = pPDU->GetTransfer(nPresContID, j); // Select this Transfer Syntax if we support it if (LDicomUID::Find(pszTransSynName)) { Associate.AddTransfer(nPresContID, pszTransSynName); break; } } // Role Selection Associate.SetRoleSelect(nPresContID, pPDU->IsRoleSelect(nPresContID), PDU_ROLE_SUPPORT, PDU_ROLE_SUPPORT); // Reject this Presentation Context if we didn't find a suitable Transfer Syntax if (j >= nTransSynCount) { Associate.SetResult(nPresContID, PDU_ACCEPT_RESULT_TRANSFER_SYNTAX); } } else { // Reject this Presentation Context because we don't support the Abstract Syntax Associate.SetResult(nPresContID, PDU_ACCEPT_RESULT_ABSTRACT_SYNTAX); } } // Maximum Length Application PDU if(pPDU->IsMaxLength()) { Associate.SetMaxLength(TRUE, pPDU->GetMaxLength()); } // Implementation Class UID Associate.SetImplementClass(TRUE, IMPLEMENTATION_CLASS_UID); // Implementation Version Name Associate.SetImplementVersion(TRUE, IMPLEMENTATION_VERSION_NAME); // Send the response SendAssociateAccept(&Associate); pDlg->LogEvent(sCallingAETitle, "Associate Accept sent"); pDlg->EnableClientTimeout(this, TRUE); pDlg->DisplayAssociate(this, &Associate); pDlg->LastClientAction(this, "Associated", FALSE); } L_VOID CNetClient::OnReceiveReleaseRequest(L_VOID) { CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDlg->EnableClientTimeout(this, FALSE); // We have received a Release Request CString sAETitle; pDlg->GetClientInfo(this, sAETitle); pDlg->LogEvent(sAETitle, "Release Request received"); // Send Release Response SendReleaseResponse(); pDlg->EnableClientTimeout(this, TRUE); pDlg->LastClientAction(this, "Association Released", FALSE); } L_VOID CNetClient::OnClose(L_INT nError, LDicomNet* pClient) { CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); if (((&pDlg->m_LEADDICOMClient) == this) && (pDlg->m_MoveMatches[0].GetSize() > 0)) { pDlg->m_pMoveNet->SendCMoveResponse(pDlg->m_nMovePresentationID, pDlg->m_uMoveMessageID, (L_CHAR*)(LPCTSTR) pDlg->m_sMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); } } L_VOID CNetClient::OnReceiveAbort(L_UCHAR nSource, L_UCHAR nReason) { CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); // Are we processing a C-MOVE-RQ? if (pDlg->m_LEADDICOMClient.IsConnected() && pDlg->m_pMoveNet == this) { pDlg->KillTimer(MOVE_TIMER_ID); if (pDlg->m_LEADDICOMClient.IsAssociated()) { pDlg->m_LEADDICOMClient.SendAbort(nSource, nReason); pDlg->m_pMoveNet->Close(); } else { pDlg->m_pMoveNet->Close(); pDlg->m_LEADDICOMClient.Close(); } } else // we received an Abort on the SCU which is storing images if ((&pDlg->m_LEADDICOMClient) == this) { pDlg->m_pMoveNet->SendCMoveResponse(pDlg->m_nMovePresentationID, pDlg->m_uMoveMessageID, (L_CHAR*)(LPCTSTR) pDlg->m_sMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); pDlg->m_LEADDICOMClient.Close(); } else { Close(); } // We have received an Abort pDlg->LogEvent(GetAssociate()->GetCalling(), "Abort received"); pDlg->LastClientAction(this, "Association Aborted", FALSE); } L_VOID CNetClient::OnReceiveReleaseResponse(L_VOID) { Close(); // We have received a Release Response CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDlg->LogEvent(theApp.m_sServerAETitle, "Release Response received"); } L_VOID CNetClient::OnReceiveCEchoRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR * pszClass) { short nStatus; CString sUser; CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); LDicomAssociate* pPDU = GetAssociate(); if (!pPDU) { // The connection is not associated, so ignore the message return; } pDlg->EnableClientTimeout(this, FALSE); // We have received an C-ECHO-REQUEST sUser = pPDU->GetCalling(); pDlg->LogEvent(sUser, "C-ECHO-REQUEST received"); pDlg->LastClientAction(this, "C-ECHO-REQUEST", TRUE); pDICOMUID pUID = LDicomUID::Find(pszClass); if (!pUID) nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; else { L_UCHAR nID = pPDU->FindAbstract(pszClass); if ((nID == 0) || (pPDU->GetResult(nID) != PDU_ACCEPT_RESULT_SUCCESS)) { CString szTemp = pUID->pszName; pDlg->LogEvent(sUser, "C-ECHO-REQUEST - Abstract Syntax, " + szTemp + ", Not Supported by Association!"); nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; } else nStatus = COMMAND_STATUS_SUCCESS; } SendCEchoResponse(nPresentationID, nMessageID, pszClass, nStatus); pDlg->LogEvent(sUser, "C-ECHO-RESPONSE sent"); pDlg->EnableClientTimeout(this, TRUE); } L_VOID CNetClient::OnReceiveCStoreRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR * pszClass, L_CHAR * pszInstance, L_UINT16 nPriority, L_CHAR * pszMoveAE, L_UINT16 nMoveMessageID, LDicomDS *pDS) { L_UINT16 nStatus; CString sUser; CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); LDicomAssociate* pPDU = GetAssociate(); if (!pPDU) { // The connection is not associated, so ignore the message return; } pDlg->EnableClientTimeout(this, FALSE); CString sMsg; sMsg = "C-STORE-REQUEST received"; if (sLastReceivedFileName.GetLength()) { sMsg += " and logged into: " + sLastReceivedFileName; } sLastReceivedFileName.Empty(); // We have received an C-STORE-REQUEST sUser = pPDU->GetCalling(); pDlg->LogEvent(sUser, sMsg); pDlg->LastClientAction(this, "C-STORE-REQUEST", TRUE); pDICOMUID pUID = LDicomUID::Find(pszClass); if (!pUID) nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; else { L_UCHAR nID = pPDU->FindAbstract(pszClass); if((nID == 0) || (pPDU->GetResult(nID) != PDU_ACCEPT_RESULT_SUCCESS)) { CString szTemp = pUID->pszName; pDlg->LogEvent(sUser, "C-STORE-REQUEST - Abstract Syntax, " + szTemp + ", Not Supported by Association!"); nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; } else nStatus = COMMAND_STATUS_SUCCESS; } // Save the SOP Instance if (nStatus == COMMAND_STATUS_SUCCESS) { pDlg->m_bImport = FALSE; nStatus = COMMAND_STATUS_PROCESSING_FAILURE; CString sMessage = "Error saving DICOM Data Set"; pDICOMELEMENT pElement = pDS->FindFirstElement(NULL, TAG_SOP_INSTANCE_UID, FALSE); if (pElement) { CString sFile = pDS->GetStringValue(pElement, 0, 1); if (sFile.IsEmpty() == FALSE) { sFile += ".dcm"; L_INT nRet = pDlg->InsertDataSet(pDS, sFile); if (nRet == 2) // Already exists? { sMessage = "Data Set already exists in Database!"; } else if (nRet == 0) { CString sFilename = theApp.m_sImagesFolder + sFile; if (SaveDataSet(pDS, sFilename)) { // Add the DICOM Data Set (we just saved) to our Directory if (pDlg->m_SrvDicomDir.InsertDicomDS(*pDS, (L_CHAR*)(LPCTSTR) sFilename) == DICOM_SUCCESS) { nStatus = COMMAND_STATUS_SUCCESS; } } } } pDS->FreeValue(pElement); } if (nStatus != COMMAND_STATUS_SUCCESS) { pDlg->LogEvent(sUser, sMessage); } // Refresh the DICOM Directory display pDlg->DisplayDICOMDir(); } SendCStoreResponse(nPresentationID, nMessageID, pszClass, pszInstance, nStatus); pDlg->LogEvent(sUser, "C-STORE-RESPONSE sent"); pDlg->EnableClientTimeout(this, TRUE); } L_VOID CNetClient::OnReceiveCFindRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR * pszClass, L_UINT16 nPriority, LDicomDS *pDS) { LDicomAssociate* pPDU = GetAssociate(); if (!pPDU) { // The connection is not associated, so ignore the message return; } CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDlg->EnableClientTimeout(this, FALSE); CString sUser; CString sMsg; // We have received a C-FIND-RQ sMsg = "C-FIND-REQUEST received"; if (sLastReceivedFileName.GetLength()) { sMsg += " and logged into: " + sLastReceivedFileName; } sLastReceivedFileName.Empty(); sUser = pPDU->GetCalling(); pDlg->LogEvent(sUser, sMsg); pDlg->LastClientAction(this, "C-FIND-REQUEST", TRUE); L_UINT16 nStatus = COMMAND_STATUS_SUCCESS; pDICOMUID pUID = LDicomUID::Find(pszClass); if (!pUID) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; } else { L_UCHAR nID = pPDU->FindAbstract(pszClass); if (nID == 0 || pPDU->GetResult(nID) != PDU_ACCEPT_RESULT_SUCCESS) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; pDlg->LogEvent(sUser, CString("C-FIND-REQUEST - Abstract Syntax, ") + pUID->pszName + ", Not Supported by Association!"); } } if (nStatus == COMMAND_STATUS_SUCCESS) { if (pDS) { // Get the Query/Retrieve Level pDICOMELEMENT pElement = pDS->FindFirstElement(NULL, TAG_QUERY_RETRIEVE_LEVEL, FALSE); if (pElement) { CString sQRLevel = pDS->GetStringValue(pElement, 0, 1); if (lstrcmp(pszClass, UID_PATIENT_ROOT_QUERY_FIND) == 0) { if (sQRLevel == "PATIENT") { FindPatients(pDS, nPresentationID, nMessageID, pszClass, sUser); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "STUDY") { FindStudies(pDS, nPresentationID, nMessageID, pszClass, sUser, TRUE); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "SERIES") { FindSeries(pDS, nPresentationID, nMessageID, pszClass, sUser, TRUE); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "IMAGE") { FindImages(pDS, nPresentationID, nMessageID, pszClass, sUser, TRUE); pDlg->EnableClientTimeout(this, TRUE); return; } else { nStatus = sQRLevel.IsEmpty() ? COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE : COMMAND_STATUS_INVALID_ATTRIBUTE_VALUE; } } else if (lstrcmp(pszClass, UID_STUDY_ROOT_QUERY_FIND) == 0) { if (sQRLevel == "STUDY") { FindStudies(pDS, nPresentationID, nMessageID, pszClass, sUser, FALSE); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "SERIES") { FindSeries(pDS, nPresentationID, nMessageID, pszClass, sUser, FALSE); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "IMAGE") { FindImages(pDS, nPresentationID, nMessageID, pszClass, sUser, FALSE); pDlg->EnableClientTimeout(this, TRUE); return; } else { nStatus = sQRLevel.IsEmpty() ? COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE : COMMAND_STATUS_INVALID_ATTRIBUTE_VALUE; } } else { nStatus = COMMAND_STATUS_INVALID_ARGUMENT_VALUE; } } else { nStatus = COMMAND_STATUS_MISSING_ATTRIBUTE; } } else { nStatus = COMMAND_STATUS_INVALID_ARGUMENT_VALUE; } } SendCFindResponse(nPresentationID, nMessageID, pszClass, nStatus, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); pDlg->EnableClientTimeout(this, TRUE); } L_VOID CNetClient::OnReceiveUnknown(L_UCHAR nPresentationID, LDicomDS *pCS, LDicomDS *pDS) { LDicomAssociate* pPDU = GetAssociate(); CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); if (!pPDU || !pDlg) { // The connection is not associated, so ignore the message return; } SendAbort(PDU_ABORT_SOURCE_PROVIDER, PDU_ABORT_REASON_UNKNOWN); pDlg->LogEvent(pPDU->GetCalling(), "Unknown data received, sending Abort."); } L_VOID CNetClient::OnReceiveCMoveRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR* pszClass, L_UINT16 nPriority, L_CHAR* pszMoveAE, LDicomDS* pDS) { LDicomAssociate* pPDU = GetAssociate(); if (!pPDU) { // The connection is not associated, so ignore the message return; } CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDlg->EnableClientTimeout(this, FALSE); CString sUser; // We have received a C-MOVE-RQ CString sMsg = "C-MOVE-REQUEST received"; if (sLastReceivedFileName.GetLength()) { sMsg += " and logged into: " + sLastReceivedFileName; } sLastReceivedFileName.Empty(); sUser = pPDU->GetCalling(); pDlg->LogEvent(sUser, sMsg ); pDlg->LastClientAction(this, "C-MOVE-REQUEST", TRUE); L_UINT16 nStatus = COMMAND_STATUS_SUCCESS; // Are we still processing a previous C-MOVE-RQ? if (pDlg->m_LEADDICOMClient.IsConnected()) { nStatus = COMMAND_STATUS_REFUSED_UNABLE_PERFORM_SUB_OPS; } else { pDICOMUID pUID = LDicomUID::Find(pszClass); if (!pUID) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; } else { L_UCHAR nID = pPDU->FindAbstract(pszClass); if (nID == 0 || pPDU->GetResult(nID) != PDU_ACCEPT_RESULT_SUCCESS) { nStatus = COMMAND_STATUS_CLASS_NOT_SUPPORTED; pDlg->LogEvent(sUser, CString("C-MOVE-REQUEST - Abstract Syntax, ") + pUID->pszName + ", Not Supported by Association!"); } } } if (nStatus == COMMAND_STATUS_SUCCESS) { // Do we know the Move Destination? CString sMoveDstAE = pszMoveAE; CString sMoveDstIP; L_UINT uMoveDstPort; int iClientsCount = pDlg->m_UserList.GetItemCount(); for (int iClientIndex = 0; iClientIndex < iClientsCount; iClientIndex++) { if (sMoveDstAE == pDlg->m_UserList.GetItemText(iClientIndex, 0)) { sMoveDstIP = pDlg->m_UserList.GetItemText(iClientIndex, 1); uMoveDstPort = atoi(pDlg->m_UserList.GetItemText(iClientIndex, 2)); break; } } if (iClientIndex < iClientsCount) { if (pDS) { // Get the Query/Retrieve Level pDICOMELEMENT pElement = pDS->FindFirstElement(NULL, TAG_QUERY_RETRIEVE_LEVEL, FALSE); if (pElement) { CString sQRLevel = pDS->GetStringValue(pElement, 0, 1); pDlg->m_pMoveNet = this; pDlg->m_sMoveDstAE = sMoveDstAE; pDlg->m_nMovePresentationID = nPresentationID; pDlg->m_uMoveMessageID = nMessageID; pDlg->m_sMoveClass = pszClass; if (lstrcmp(pszClass, UID_PATIENT_ROOT_QUERY_MOVE) == 0) { if (sQRLevel == "PATIENT") { pDlg->MoveImages(pDS, sMoveDstIP, uMoveDstPort, 1, TRUE, sUser); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "STUDY") { pDlg->MoveImages(pDS, sMoveDstIP, uMoveDstPort, 2, TRUE, sUser); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "SERIES") { pDlg->MoveImages(pDS, sMoveDstIP, uMoveDstPort, 3, TRUE, sUser); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "IMAGE") { pDlg->MoveImages(pDS, sMoveDstIP, uMoveDstPort, 4, TRUE, sUser); pDlg->EnableClientTimeout(this, TRUE); return; } else { nStatus = sQRLevel.IsEmpty() ? COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE : COMMAND_STATUS_INVALID_ATTRIBUTE_VALUE; } } else if (lstrcmp(pszClass, UID_STUDY_ROOT_QUERY_MOVE) == 0) { if (sQRLevel == "STUDY") { pDlg->MoveImages(pDS, sMoveDstIP, uMoveDstPort, 2, FALSE, sUser); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "SERIES") { pDlg->MoveImages(pDS, sMoveDstIP, uMoveDstPort, 3, FALSE, sUser); pDlg->EnableClientTimeout(this, TRUE); return; } else if (sQRLevel == "IMAGE") { pDlg->MoveImages(pDS, sMoveDstIP, uMoveDstPort, 4, FALSE, sUser); pDlg->EnableClientTimeout(this, TRUE); return; } else { nStatus = sQRLevel.IsEmpty() ? COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE : COMMAND_STATUS_INVALID_ATTRIBUTE_VALUE; } } else { nStatus = COMMAND_STATUS_INVALID_ARGUMENT_VALUE; } } else { nStatus = COMMAND_STATUS_MISSING_ATTRIBUTE; } } else { nStatus = COMMAND_STATUS_INVALID_ARGUMENT_VALUE; } } else { nStatus = COMMAND_STATUS_REFUSED_MOVE_DESTINATION_UNKNOWN; } } SendCMoveResponse(nPresentationID, nMessageID, pszClass, nStatus, 0, 0, 0, 0, NULL); pDlg->LogEvent(sUser, "C-MOVE-RESPONSE sent"); pDlg->EnableClientTimeout(this, TRUE); } L_VOID CNetClient::OnConnect(L_INT nError) { CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); // Failed to connect to the Move Destination AE? if (nError != DICOM_SUCCESS) { pDlg->m_pMoveNet->SendCMoveResponse(pDlg->m_nMovePresentationID, pDlg->m_uMoveMessageID, (L_CHAR*)(LPCTSTR) pDlg->m_sMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); pDlg->LogEvent(pDlg->m_pMoveNet->GetAssociate()->GetCalling(), "C-MOVE-RESPONSE sent"); return; } /* Prepare the Association request */ LDicomAssociate Associate(TRUE); // The Protocol Version Associate.SetVersion(PROTOCOL_VERSION); // Titles of the Called and Calling AEs Associate.SetCalling((L_CHAR*)(LPCTSTR) theApp.m_sServerAETitle); Associate.SetCalled((L_CHAR*)(LPCTSTR) pDlg->m_sMoveDstAE); // The Application Context Name Associate.SetApplication(UID_APPLICATION_CONTEXT_NAME); // Presentation Contexts L_UCHAR nPresContID = 1; // Add a Presentation Context for the Verification Abstract Syntax Associate.AddPresentation(nPresContID, 0, UID_VERIFICATION_CLASS); Associate.AddTransfer(nPresContID, UID_IMPLICIT_VR_LITTLE_ENDIAN); CString sAbstractSynName, sTransSynName; int i, j, iPresContsCount, iTransSynsCount; int iMatchesCount = pDlg->m_MoveMatches[0].GetSize(); for (i = 0; i < iMatchesCount; i++) { sAbstractSynName = pDlg->m_MoveMatches[0].GetAt(i); // Add a Presentation Context for this Abstract Syntax if we haven't already done this iPresContsCount = Associate.GetPresentationCount(); for (j = 0; j < iPresContsCount; j++) { nPresContID = Associate.GetPresentation(j); if (sAbstractSynName == Associate.GetAbstract(nPresContID)) { break; } } if (j >= iPresContsCount) // A new Abstract Syntax? { nPresContID += 2; Associate.AddPresentation(nPresContID, 0, (L_CHAR*)(LPCTSTR) sAbstractSynName); } sTransSynName = pDlg->m_MoveMatches[1].GetAt(i); // Add the Transfer Syntax sTransSynName for the Abstract Syntax if we haven't already // done this iTransSynsCount = Associate.GetTransferCount(nPresContID); for (j = 0; j < iTransSynsCount; j++) { if (sTransSynName == Associate.GetTransfer(nPresContID, j)) { break; } } if (j >= iTransSynsCount) // A new Transfer Syntax? { Associate.AddTransfer(nPresContID, (L_CHAR*)(LPCTSTR) sTransSynName); } } // Ensure that the default Transfer Syntax is added to all Presentation Contexts. Also, set // the role. iPresContsCount = Associate.GetPresentationCount(); for (i = 0; i < iPresContsCount; i++) { nPresContID = Associate.GetPresentation(i); iTransSynsCount = Associate.GetTransferCount(nPresContID); for (j = 0; j < iTransSynsCount; j++) { if (lstrcmp(Associate.GetTransfer(nPresContID, j), UID_IMPLICIT_VR_LITTLE_ENDIAN) == 0) { break; } } if (j >= iTransSynsCount) { Associate.AddTransfer(nPresContID, UID_IMPLICIT_VR_LITTLE_ENDIAN); } // Role Selection Associate.SetRoleSelect(nPresContID, TRUE, PDU_ROLE_SUPPORT, PDU_ROLE_SUPPORT); } // Maximum Length Application PDU Associate.SetMaxLength(TRUE, 0x100000); // Implementation Class UID Associate.SetImplementClass(TRUE, IMPLEMENTATION_CLASS_UID); // Implementation Version Name Associate.SetImplementVersion(TRUE, IMPLEMENTATION_VERSION_NAME); // Now send the Association request pDlg->m_nSecondsElapsed = 0; pDlg->SetTimer(MOVE_TIMER_ID, 1000, NULL); SendAssociateRequest(&Associate); pDlg->LogEvent(theApp.m_sServerAETitle, "Associate Request sent"); } L_VOID CNetClient::OnReceiveAssociateAccept(LDicomAssociate* pPDU) { CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDlg->KillTimer(MOVE_TIMER_ID); pDlg->LogEvent(theApp.m_sServerAETitle, "Associate Accept received"); if (!SendMatchedInstance(NULL)) { SendReleaseRequest(); pDlg->LogEvent(theApp.m_sServerAETitle, "Release Request sent"); pDlg->m_pMoveNet->SendCMoveResponse(pDlg->m_nMovePresentationID, pDlg->m_uMoveMessageID, (L_CHAR*)(LPCTSTR) pDlg->m_sMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); pDlg->LogEvent(pDlg->m_pMoveNet->GetAssociate()->GetCalling(), "C-MOVE-RESPONSE sent"); } } L_VOID CNetClient::OnReceiveAssociateReject(L_UCHAR nResult, L_UCHAR nSource, L_UCHAR nReason) { CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDlg->KillTimer(MOVE_TIMER_ID); pDlg->LogEvent(theApp.m_sServerAETitle, "Associate Reject received"); Close(); pDlg->m_pMoveNet->SendCMoveResponse(pDlg->m_nMovePresentationID, pDlg->m_uMoveMessageID, (L_CHAR*)(LPCTSTR) pDlg->m_sMoveClass, COMMAND_STATUS_PROCESSING_FAILURE, 0, 0, 0, 0, NULL); pDlg->LogEvent(pDlg->m_pMoveNet->GetAssociate()->GetCalling(), "C-MOVE-RESPONSE sent"); } L_VOID CNetClient::OnReceiveCStoreResponse ( L_UCHAR nPresentationID, L_UINT16 nMessageID, L_CHAR* pszClass, L_CHAR* pszInstance, L_UINT16 nStatus ) { CString strFailedSOPInstanceUID; CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDlg->KillTimer(MOVE_TIMER_ID); pDlg->LogEvent(theApp.m_sServerAETitle, "C-STORE-RESPONSE received"); switch (nStatus) { case COMMAND_STATUS_SUCCESS: pDlg->m_nSuccessfulSubOperations++; break; case COMMAND_STATUS_WARNING: case 0xB007: case 0xB006: pDlg->m_nWarningSubOperations++; break; default: pDlg->m_nFailedSubOperations++; } if (pDlg->m_MoveMatches[0].GetSize()) { pDlg->m_pMoveNet->SendCMoveResponse(pDlg->m_nMovePresentationID, pDlg->m_uMoveMessageID, (L_CHAR*)(LPCTSTR) pDlg->m_sMoveClass, COMMAND_STATUS_PENDING, pDlg->m_MoveMatches[0].GetSize(), pDlg->m_nSuccessfulSubOperations, pDlg->m_nFailedSubOperations, pDlg->m_nWarningSubOperations, NULL); pDlg->LogEvent(pDlg->m_pMoveNet->GetAssociate()->GetCalling(), "C-MOVE-RESPONSE sent"); if (!SendMatchedInstance(&strFailedSOPInstanceUID)) { OnReceiveCStoreResponse( pDlg->m_nMovePresentationID, pDlg->m_uMoveMessageID, (L_CHAR*)(LPCTSTR) pDlg->m_sMoveClass, (L_CHAR*)(LPCTSTR) strFailedSOPInstanceUID, COMMAND_STATUS_PROCESSING_FAILURE); } } else { SendReleaseRequest(); pDlg->LogEvent(theApp.m_sServerAETitle, "Release Request sent"); // Done! pDlg->m_pMoveNet->SendCMoveResponse(pDlg->m_nMovePresentationID, pDlg->m_uMoveMessageID, (L_CHAR L_FAR*)(LPCTSTR)pDlg->m_sMoveClass, COMMAND_STATUS_SUCCESS, 0, pDlg->m_nSuccessfulSubOperations, pDlg->m_nFailedSubOperations, pDlg->m_nWarningSubOperations, NULL); pDlg->LogEvent(pDlg->m_pMoveNet->GetAssociate()->GetCalling(), "C-MOVE-RESPONSE sent"); } } L_BOOL CNetClient::SendMatchedInstance(CString *pFailedSOPInstanceUID) { CString sLoggedFile; CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); if(pFailedSOPInstanceUID) { *pFailedSOPInstanceUID = ""; } int iMatchIndex = pDlg->m_MoveMatches[0].GetSize() - 1; if (iMatchIndex < 0) { return FALSE; } // Get the information of the match to be sent CString sSOPClassUID = pDlg->m_MoveMatches[0].GetAt(iMatchIndex); CString sTransferSyntaxUID = pDlg->m_MoveMatches[1].GetAt(iMatchIndex); CString sReferencedFile = pDlg->m_MoveMatches[2].GetAt(iMatchIndex); // Remove the match from the arrays pDlg->m_MoveMatches[0].RemoveAt(iMatchIndex); pDlg->m_MoveMatches[1].RemoveAt(iMatchIndex); pDlg->m_MoveMatches[2].RemoveAt(iMatchIndex); LDicomAssociate* pAssociate = pDlg->m_LEADDICOMClient.GetAssociate(); if(pAssociate == NULL ) { return FALSE; } L_UCHAR nPresentationID = pAssociate->FindAbstract((L_CHAR*)(LPCTSTR) sSOPClassUID); if ((nPresentationID == 0 )|| (pAssociate->GetResult(nPresentationID) != PDU_ACCEPT_RESULT_SUCCESS)) { return FALSE; } LDicomDS DataSet((L_CHAR*)(LPCTSTR) theApp.m_sTempFilesFolder); L_UINT16 uRet = DataSet.LoadDS((L_CHAR*)(LPCTSTR) (theApp.m_sImagesFolder + sReferencedFile), DS_LOAD_CLOSE); if (uRet != DICOM_SUCCESS) { return FALSE; } ChangeTransferSyntax(DataSet, sTransferSyntaxUID, pAssociate->GetTransfer(nPresentationID, 0)); CString sSOPInstanceUID; pDICOMELEMENT pElement = DataSet.FindFirstElement(NULL, TAG_SOP_INSTANCE_UID, FALSE); if (pElement) { sSOPInstanceUID = DataSet.GetStringValue(pElement, 0, 1); } // Now send the instance pDlg->m_nSecondsElapsed = 0; pDlg->SetTimer(MOVE_TIMER_ID, 1000, NULL); CString sMsg; sMsg = "C-STORE-REQUEST sent"; L_INT nRet = SendCStoreRequest(nPresentationID, pDlg->m_uStoreMessageID++, (L_CHAR*)(LPCTSTR) sSOPClassUID, (L_CHAR*)(LPCTSTR) sSOPInstanceUID, COMMAND_PRIORITY_MEDIUM, (L_CHAR*)(LPCTSTR) pDlg->m_pMoveNet->GetAssociate()->GetCalling(), pDlg->m_uMoveMessageID, &DataSet); if (nRet == DICOM_SUCCESS) { theApp.SaveSet(&DataSet, TRUE, FALSE, &sLoggedFile); if (sLoggedFile.GetLength()) { sMsg += " and logged into: " + sLoggedFile; } pDlg->LogEvent(theApp.m_sServerAETitle, sMsg); return TRUE; } if(pFailedSOPInstanceUID) { *pFailedSOPInstanceUID = sSOPInstanceUID; } pDlg->LogEvent(theApp.m_sServerAETitle, sMsg); return FALSE; } L_BOOL CNetClient::ChangeTransferSyntax ( LDicomDS& DataSet, const CString& sDataSetTS, const CString& sAssociatedTS ) { // No need to change the Transfer Syntax if it // is the same as the associated one. if (sDataSetTS == sAssociatedTS) { return TRUE; } // Also, no need to change the Transfer Syntax explicitly // if both belong to this group // of Transfer Syntaxes. if (sAssociatedTS == UID_IMPLICIT_VR_LITTLE_ENDIAN || sAssociatedTS == UID_EXPLICIT_VR_LITTLE_ENDIAN || sAssociatedTS == UID_EXPLICIT_VR_BIG_ENDIAN) { if (sDataSetTS == UID_IMPLICIT_VR_LITTLE_ENDIAN || sDataSetTS == UID_EXPLICIT_VR_LITTLE_ENDIAN || sDataSetTS == UID_EXPLICIT_VR_BIG_ENDIAN) { return TRUE; } } /* We need to change the Transfer Syntax of the Data Set */ if (DataSet.ChangeTransferSyntax((L_CHAR*)(LPCTSTR) sAssociatedTS, 2, 0) != DICOM_SUCCESS) { return FALSE; } // Has the image undergone a lossy compression? if (sDataSetTS == UID_JPEG_BASELINE_1 || sDataSetTS == UID_JPEG_EXTENDED_2_4 || sDataSetTS == UID_JPEG2000) { pDICOMELEMENT pElement = DataSet.FindFirstElement( NULL, TAG_LOSSY_IMAGE_COMPRESSION, FALSE); if (pElement == NULL) { pElement = DataSet.InsertElement(NULL, FALSE, TAG_LOSSY_IMAGE_COMPRESSION, VR_CS, FALSE, 0); } if (pElement) { DataSet.SetStringValue(pElement, "01", 1); } } return TRUE; } L_CHAR *CNetClient::GetTLSErrorString(L_INT nError) { char *pszError = ""; switch (nError) { case DICOM_ERROR_FORMAT: pszError = "Error Format"; break; case DICOM_ERROR_TLS_CLOSE_NOTIFY: pszError = "Error TLS Close Notify"; break; case DICOM_ERROR_TLS_UNEXPECTED_MESSAGE: pszError = "Error TLS Unexpected Message"; break; case DICOM_ERROR_TLS_BAD_RECORD_MAC: pszError = "Error TLS Bad Record MAC"; break; case DICOM_ERROR_TLS_DECRYPT_FAILED: pszError = "Error TLS Decrypt Failed"; break; case DICOM_ERROR_TLS_RECORD_OVERFLOW: pszError = "Error TLS Record Overflow"; break; case DICOM_ERROR_TLS_DECOMPRESSION_FAILURE: pszError = "Error TLS Decompression Failure"; break; case DICOM_ERROR_TLS_HANDSHAKE_FAILURE: pszError = "Error TLS Handshake Failure"; break; case DICOM_ERROR_TLS_BAD_CERTIFICATE: pszError = "Error TLS Bad Certificate"; break; case DICOM_ERROR_TLS_UNSUPPORTED_CERTIFICATE: pszError = "Error TLS Unsupported Certificate"; break; case DICOM_ERROR_TLS_CERTIFICATE_REVOKED: pszError = "Error TLS Certificate Revoked"; break; case DICOM_ERROR_TLS_CERTIFICATE_EXPIRED: pszError = "Error TLS Certificate Expired"; break; case DICOM_ERROR_TLS_CERTIFICATE_UNKNOWN: pszError = "Error TLS Certificate Unknown"; break; case DICOM_ERROR_TLS_ILLEGAL_PARAMETER: pszError = "Error TLS Illegal Parameter"; break; case DICOM_ERROR_TLS_UNKNOWN_CA: pszError = "Error TLS Unknown CA"; break; case DICOM_ERROR_TLS_ACCESS_DENIED: pszError = "Error TLS Access Denied"; break; case DICOM_ERROR_TLS_DECODE_ERROR: pszError = "Error TLS Decode Error"; break; case DICOM_ERROR_TLS_DECRYPT_ERROR: pszError = "Error TLS Decrypt Error"; break; case DICOM_ERROR_TLS_EXPORT_RESTRICTION: pszError = "Error TLS Export Restriction"; break; case DICOM_ERROR_TLS_PROTOCOL_VERSION: pszError = "Error TLS Protocol Version"; break; case DICOM_ERROR_TLS_INSUFFICIENT_SECURITY: pszError = "Error TLS Insufficient Security"; break; case DICOM_ERROR_TLS_INTERNAL_ERROR: pszError = "Error TLS Internal Error"; break; case DICOM_ERROR_TLS_USER_CANCELED: pszError = "Error TLS User Canceled"; break; case DICOM_ERROR_TLS_NO_RENEGOTIATION: pszError = "Error TLS No Renegotiation"; break; case DICOM_ERROR_TLS_NO_KEEPALIVE: pszError = "Error TLS No Keepalive"; break; case DICOM_ERROR_TLS_CLOSED_CONTROLLED: pszError = "Error TLS Closed Controlled"; break; case DICOM_ERROR_TLS_UNABLE_TO_GET_ISSUER_CERT: pszError = "Error TLS UNABLE TO GET ISSUER_CERT"; break; case DICOM_ERROR_TLS_UNABLE_TO_GET_CRL: pszError = "Error TLS UNABLE TO GET CRL"; break; case DICOM_ERROR_TLS_UNABLE_TO_DECRYPT_CERT_SIGNATURE: pszError = "Error TLS UNABLE TO DECRYPT CERT SIGNATURE"; break; case DICOM_ERROR_TLS_UNABLE_TO_DECRYPT_CRL_SIGNATURE: pszError = "Error TLS UNABLE TO DECRYPT CRL SIGNATURE"; break; case DICOM_ERROR_TLS_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: pszError = "Error TLS UNABLE TO DECODE ISSUER PUBLIC KEY"; break; case DICOM_ERROR_TLS_CERT_SIGNATURE_FAILURE: pszError = "Error TLS Cert Signature Failure"; break; case DICOM_ERROR_TLS_CRL_SIGNATURE_FAILURE: pszError = "Error TLS Crl Signature Failure"; break; case DICOM_ERROR_TLS_CERT_NOT_YET_VALID: pszError = "Error TLS CERT NOT YET VALID"; break; case DICOM_ERROR_TLS_CERT_HAS_EXPIRED: pszError = "Error TLS Cert Has Expired"; break; case DICOM_ERROR_TLS_CRL_NOT_YET_VALID: pszError = "Error TLS CRL Not Yet Valid"; break; case DICOM_ERROR_TLS_CRL_HAS_EXPIRED: pszError = "Error TLS CRL Has Expired"; break; case DICOM_ERROR_TLS_ERROR_IN_CERT_NOT_BEFORE_FIELD: pszError = "Error TLS Error In Cert Not Before Field"; break; case DICOM_ERROR_TLS_ERROR_IN_CERT_NOT_AFTER_FIELD: pszError = "Error TLS Error In Cert Not After Field"; break; case DICOM_ERROR_TLS_ERROR_IN_CRL_LAST_UPDATE_FIELD: pszError = "Error TLS Error In Crl Last Update Field"; break; case DICOM_ERROR_TLS_ERROR_IN_CRL_NEXT_UPDATE_FIELD: pszError = "Error TLS Error In Crl Next Update Field"; break; case DICOM_ERROR_TLS_OUT_OF_MEM: pszError = "Error TLS Out Of Memory"; break; case DICOM_ERROR_TLS_DEPTH_ZERO_SELF_SIGNED_CERT: pszError = "Error TLS Depth Zero Self Signed Cert"; break; case DICOM_ERROR_TLS_SELF_SIGNED_CERT_IN_CHAIN: pszError = "Error TLS Self Signed Cert In Chain"; break; case DICOM_ERROR_TLS_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: pszError = "Error TLS Unable To Get Issuer Cert Locally"; break; case DICOM_ERROR_TLS_UNABLE_TO_VERIFY_LEAF_SIGNATURE: pszError = "Error TLS Unable To Verify Leaf Signature"; break; case DICOM_ERROR_TLS_CERT_CHAIN_TOO_LONG: pszError = "Error TLS Cert Chain Too Long"; break; case DICOM_ERROR_TLS_CERT_REVOKED: pszError = "Error TLS Cert Revoked"; break; case DICOM_ERROR_TLS_INVALID_CA: pszError = "Error TLS Invalid CA"; break; case DICOM_ERROR_TLS_PATH_LENGTH_EXCEEDED: pszError = "Error TLS Path Length Exceeded"; break; case DICOM_ERROR_TLS_INVALID_PURPOSE: pszError = "Error TLS Invalid Purpose"; break; case DICOM_ERROR_TLS_CERT_UNTRUSTED: pszError = "Error TLS Cert Untrusted"; break; case DICOM_ERROR_TLS_CERT_REJECTED: pszError = "Error TLS Cert Rejected"; break; case DICOM_ERROR_TLS_SUBJECT_ISSUER_MISMATCH: pszError = "Error TLS Subject Issuer Mismatch"; break; case DICOM_ERROR_TLS_AKID_SKID_MISMATCH: pszError = "Error TLS Akid Skid Mismatch"; break; case DICOM_ERROR_TLS_AKID_ISSUER_SERIAL_MISMATCH: pszError = "Error TLS Akid Issuer Serial Mismatch"; break; case DICOM_ERROR_TLS_KEYUSAGE_NO_CERTSIGN: pszError = "Error TLS Keyusage No Certsign"; break; case DICOM_ERROR_TLS_APPLICATION_VERIFICATION: pszError = "Error TLS Application Verification"; break; case DICOM_ERROR_TLS_INVALID_CTX: pszError = "Error TLS Invalid CTX"; break; case DICOM_ERROR_TLS_INVALID_CTX_VERIFY_DEPTH: pszError = "Error TLS Invalid CTX Verify Depth"; break; case DICOM_ERROR_TLS_INVALID_CTX_VERIFY_MODE: pszError = "Error TLS Invalid CTX Verify Mode"; break; case DICOM_ERROR_TLS_INVALID_CTX_CAFILE: pszError = "Error TLS Invalid CTX Cafile"; break; case DICOM_ERROR_TLS_INVALID_CTX_OPTIONS: pszError = "Error TLS Invalid CTX Options"; break; } return pszError; } L_VOID CNetClient::OnReceive(L_INT nError, L_UCHAR nType, L_CHAR *pBuffer, L_UINT32 nBytes) { L_CHAR *pszError = ""; L_CHAR szMsg[200]; CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); switch(nError) { case DICOM_SUCCESS: // do nothing break; case DICOM_ERROR_FORMAT: case DICOM_ERROR_TLS_CLOSE_NOTIFY: case DICOM_ERROR_TLS_UNEXPECTED_MESSAGE: case DICOM_ERROR_TLS_BAD_RECORD_MAC: case DICOM_ERROR_TLS_DECRYPT_FAILED: case DICOM_ERROR_TLS_RECORD_OVERFLOW: case DICOM_ERROR_TLS_DECOMPRESSION_FAILURE: case DICOM_ERROR_TLS_HANDSHAKE_FAILURE: case DICOM_ERROR_TLS_BAD_CERTIFICATE: case DICOM_ERROR_TLS_UNSUPPORTED_CERTIFICATE: case DICOM_ERROR_TLS_CERTIFICATE_REVOKED: case DICOM_ERROR_TLS_CERTIFICATE_EXPIRED: case DICOM_ERROR_TLS_CERTIFICATE_UNKNOWN: case DICOM_ERROR_TLS_ILLEGAL_PARAMETER: case DICOM_ERROR_TLS_UNKNOWN_CA: case DICOM_ERROR_TLS_ACCESS_DENIED: case DICOM_ERROR_TLS_DECODE_ERROR: case DICOM_ERROR_TLS_DECRYPT_ERROR: case DICOM_ERROR_TLS_EXPORT_RESTRICTION: case DICOM_ERROR_TLS_PROTOCOL_VERSION: case DICOM_ERROR_TLS_INSUFFICIENT_SECURITY: case DICOM_ERROR_TLS_INTERNAL_ERROR: case DICOM_ERROR_TLS_USER_CANCELED: case DICOM_ERROR_TLS_NO_RENEGOTIATION: case DICOM_ERROR_TLS_NO_KEEPALIVE: case DICOM_ERROR_TLS_CLOSED_CONTROLLED: pszError = GetTLSErrorString(nError); // close the connection wsprintf(szMsg, "[%d]%s -- closing connection", nError, pszError); pDlg->LogEvent("", szMsg); Close(); break; default: // Just log a message wsprintf(szMsg, "Error[%d]", nError); pDlg->LogEvent("", szMsg); break; } } L_VOID CNetClient::OnReceiveData(L_UCHAR nID, LDicomDS* pCS, LDicomDS* pDS) { theApp.SaveSet(pCS, FALSE, TRUE, NULL); theApp.SaveSet(pDS, TRUE, TRUE, &sLastReceivedFileName); } L_UINT32 CNetClient::GetChallengeISCL(L_UINT64 *pChallenge, L_UINT64 nParameter) { *pChallenge = 0x0123456789ABCDE1; return 0; } L_UINT32 CNetClient::InternalAuthenticateISCL (L_UINT64 nChallenge, L_UINT64 *pResponse, L_UINT64 nParameter) { *pResponse = nChallenge + 1; return 0; } L_UINT32 CNetClient::ExternalAuthenticateISCL (L_UINT64 nChallenge, L_UINT64 nResponse, L_UINT64 nParameter) { if (nResponse == nChallenge + 1) return 0; else return 1; } L_VOID CNetClient::OnSecureLinkReady(L_INT nError) { CString str; L_CHAR authdata[128]; memset(authdata, 0, 128); L_UINT32 n = 128; GetPeerAuthDataISCL(authdata, &n); str.Format("Connected ISCL with peer identified by %s", authdata); // AfxMessageBox(str); } L_VOID CNetClient::OnReceivedISCLPacket(L_INT nError, L_CHAR *pBuffer, L_UINT32 nBytes) { CString str, total; if(nError != 0) { str.Format("Error is: %d\n", nError); // AfxMessageBox(str); LDicomNet::OnReceivedISCLPacket(nError, pBuffer, nBytes); return; } str.Format("Recv ISCL packet, msg id: 0x%X\n", *((L_UINT32 *)&(pBuffer[4]))); total += str; str.Format("data length: %d\n", *((L_UINT32 *)&(pBuffer[8]))); total += str; str.Format("option: %d", *((L_UINT32 *)&(pBuffer[12]))); total += str; // AfxMessageBox(total); LDicomNet::OnReceivedISCLPacket(nError, pBuffer, nBytes); } L_VOID CNetClient::LoadDefaultSettings() { SetMutualAuthAlgISCL(DICOM_ISCL_MUTUAL_AUTH_3P4W); SetMutualAuthKeyISCL(1, 11619789628100321); SetMutualAuthKeyISCL(2, 34217865672122111); SetMutualAuthKeyISCL(3, 1605935625518899689); SetMutualAuthKeyISCL(4, 138217077775855676); SetMutualAuthKeyISCL(5, 9117318694593010212); SetMutualAuthKeyISCL(6, 3485297985488245687); SetMutualAuthKeyISCL(7, 1533287511573403981); SetMutualAuthKeyISCL(8, 5604839976916070822); SetIndexForMutualAuthISCL(1); SetDefaultEncryptionISCL(DICOM_ISCL_ENCRYPT_DESCBC); SetDefaultSigningISCL(DICOM_ISCL_MAC_MD5); SetEncryptKeyISCL(1, 8079278526052745737); SetEncryptKeyISCL(2, 1312864321990916052); SetEncryptKeyISCL(3, 7190959962252002117); SetEncryptKeyISCL(4, 3619524191167482890); SetEncryptKeyISCL(5, 3466658849848898336); SetEncryptKeyISCL(6, 8474124475946342520); SetEncryptKeyISCL(7, 7725464453540259890); SetEncryptKeyISCL(8, 4320705344832296668); SetIndexForEncryptISCL(1); SetMaxMessageLengthISCL(1024000); SetMaxCommBlockLengthISCL(8129); SetAuthDataISCL(SERVER_AUTH_DATA, SERVER_AUTH_DATA_LEN); } L_INT CNetClient::LoadFromFileSettings() { CStdioFile f; CString szData; L_INT i; L_UINT64 nISCLAuthKeys[8]; L_UINT nISCLAuthCurrentKey; L_UINT32 nISCLAuthAlg; L_UINT64 nISCLEncKeys[8]; L_UINT nISCLEncCurrentKey; L_UINT32 nISCLEncAlg; L_UINT32 nISCLEncSign; if (f.Open("secur.ini", CFile::modeRead|CFile::typeText)) { // ISCL Auth if (f.ReadString(szData) == NULL) { return 0; } else { nISCLAuthAlg = (L_UINT32)_atoi64(szData); } for (i = 0; i < 8; i++) { if (f.ReadString(szData) == NULL) { return 0; } else { nISCLAuthKeys[i] = _atoi64(szData); } } if (f.ReadString(szData) == NULL) { return 0; } else { nISCLAuthCurrentKey = (L_UINT32)_atoi64(szData); } //ISCL Encrypt if (f.ReadString(szData) == NULL) { return 0; } else { nISCLEncAlg = (L_UINT32)_atoi64(szData); } if (f.ReadString(szData) == NULL) { return 0; } else { nISCLEncSign = (L_UINT32)_atoi64(szData); } for (i = 0; i < 8; i++) { if (f.ReadString(szData) == NULL) { return 0; } else { nISCLEncKeys[i] = _atoi64(szData); } } if (f.ReadString(szData) == NULL) { return 0; } else { nISCLEncCurrentKey = (L_UINT32)_atoi64(szData); } f.Close(); } else { return 0; } SetMutualAuthAlgISCL(nISCLAuthAlg); for (i = 0; i < 8; i++) { if (nISCLAuthKeys[i] != 0) { SetMutualAuthKeyISCL(i+1, nISCLAuthKeys[i]); } } SetIndexForMutualAuthISCL(nISCLAuthCurrentKey+1); SetDefaultEncryptionISCL(nISCLEncAlg); SetDefaultSigningISCL(nISCLEncSign); for (i = 0; i < 8; i++) { if (nISCLEncKeys[i] != 0) { SetEncryptKeyISCL(i+1, nISCLEncKeys[i]); } } SetIndexForEncryptISCL(nISCLEncCurrentKey+1); SetMaxMessageLengthISCL(1024000); SetMaxCommBlockLengthISCL(8129); SetAuthDataISCL(SERVER_AUTH_DATA, SERVER_AUTH_DATA_LEN); return 1; } L_VOID CNetClient::FindPatients(LDicomDS* pReqIdentifier, L_UCHAR nPresentationID, L_UINT16 uMessageID, L_CHAR* pszClass, CString& sUser) { if (!pReqIdentifier) return; CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDICOMELEMENT pElement; // The Unique Key: Patient ID pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_ID, FALSE); if (!pElement) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_MISSING_ATTRIBUTE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } CString sPatientID = pReqIdentifier->GetStringValue(pElement, 0, 1); // Required Keys: // Patient's Name CString sPatientName; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_NAME, FALSE); if (pElement) { sPatientName = pReqIdentifier->GetStringValue(pElement, 0, 1); } CString sQueryString; // Form the query string sQueryString = "SELECT * FROM Patients"; if (sPatientID.GetLength() || sPatientName.GetLength()) { sQueryString += " WHERE "; if (sPatientID.GetLength()) { sQueryString += "PatientID LIKE '" + PrepareForWCM(sPatientID) + "'"; } if (sPatientName.GetLength()) { if (sPatientID.GetLength()) { sQueryString += " AND "; } sQueryString += "PatientName LIKE '" + PrepareForWCM(sPatientName) + "'"; } } // The response Identifier LDicomDS RspIdentifier((L_CHAR*)(LPCTSTR) theApp.m_sTempFilesFolder); PrepareRspIdentifier(RspIdentifier, *pReqIdentifier, 1, TRUE); // Connect to the database CDatabase Database; try { Database.OpenEx(theApp.m_sConnectionString, CDatabase::noOdbcDialog); } catch(...) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } CString sCount, sLoggedFile, sMsg; int nRelatedStudiesCount, nRelatedSeriesCount, nRelatedInstancesCount; L_INT nRet; // Look for matches CPatientRecordset PatientRS(&Database); try { PatientRS.Open(CRecordset::dynaset, sQueryString); while (!PatientRS.IsEOF()) { // The Unique Key SetKeyElement(RspIdentifier, PatientRS.m_PatientID, TAG_PATIENT_ID); // Required Keys: SetKeyElement(RspIdentifier, PatientRS.m_PatientName, TAG_PATIENT_NAME); // Optional Keys: SetTimeDateKeyElement(RspIdentifier, PatientRS.m_PatientBirthDate, TAG_PATIENT_BIRTH_DATE, FALSE); SetTimeDateKeyElement(RspIdentifier, PatientRS.m_PatientBirthTime, TAG_PATIENT_BIRTH_TIME, TRUE); SetKeyElement(RspIdentifier, PatientRS.m_PatientSex, TAG_PATIENT_SEX); //SetKeyElement(RspIdentifier, PatientRS., TAG_OTHER_PATIENT_IDS); //SetKeyElement(RspIdentifier, PatientRS., TAG_OTHER_PATIENT_NAMES); SetKeyElement(RspIdentifier, PatientRS.m_EthnicGroup, TAG_ETHNIC_GROUP); SetKeyElement(RspIdentifier, PatientRS.m_PatientComments, TAG_PATIENT_COMMENTS); // The number of related studies, series, and instances nRelatedStudiesCount = nRelatedSeriesCount = nRelatedInstancesCount = 0; sQueryString = "SELECT * FROM Studies WHERE PatientID='" + PatientRS.m_PatientID + "'"; CStudyRecordset StudyRS(&Database); StudyRS.Open(CRecordset::dynaset, sQueryString); while (!StudyRS.IsEOF()) { sQueryString = "SELECT * FROM Series WHERE StudyInstanceUID='" + StudyRS.m_StudyInstanceUID + "'"; CSeriesRecordset SeriesRS(&Database); SeriesRS.Open(CRecordset::dynaset, sQueryString); while (!SeriesRS.IsEOF()) { sQueryString = "SELECT * FROM Images WHERE SeriesInstanceUID='" + SeriesRS.m_SeriesInstanceUID + "'"; CImageRecordset ImageRS(&Database); ImageRS.Open(CRecordset::dynaset, sQueryString); while (!ImageRS.IsEOF()) { nRelatedInstancesCount++; ImageRS.MoveNext(); } ImageRS.Close(); nRelatedSeriesCount++; SeriesRS.MoveNext(); } SeriesRS.Close(); nRelatedStudiesCount++; StudyRS.MoveNext(); } StudyRS.Close(); sCount.Format("%d", nRelatedStudiesCount); SetKeyElement(RspIdentifier, sCount, TAG_NUMBER_OF_PATIENT_RELATED_STUDIES); sCount.Format("%d", nRelatedSeriesCount); SetKeyElement(RspIdentifier, sCount, TAG_NUMBER_OF_PATIENT_RELATED_SERIES); sCount.Format("%d", nRelatedInstancesCount); SetKeyElement(RspIdentifier, sCount, TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES); // Here is a match nRet = SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PENDING, &RspIdentifier); if (nRet == DICOM_SUCCESS) { theApp.SaveSet(&RspIdentifier, TRUE, FALSE, &sLoggedFile); sMsg = "C-FIND-RESPONSE sent"; if (sLoggedFile.GetLength()) { sMsg += " and logged into: " + sLoggedFile; } pDlg->LogEvent(sUser, sMsg); } PatientRS.MoveNext(); } PatientRS.Close(); } catch(...) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } Database.Close(); // The final C-FIND-RSP SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_SUCCESS, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); } L_VOID CNetClient::FindStudies(LDicomDS* pReqIdentifier, L_UCHAR nPresentationID, L_UINT16 uMessageID, L_CHAR* pszClass, CString& sUser, BOOL bPatientRoot) { if (!pReqIdentifier) return; L_BOOL bTryDateRange = FALSE; CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDICOMELEMENT pElement; // Patient ID // (Unique Key of the above level if Patient Root Query SOP Class) // (Required Key if Study Root Query SOP Class) CString sPatientID; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_ID, FALSE); if (pElement) { sPatientID = pReqIdentifier->GetStringValue(pElement, 0, 1); } if (bPatientRoot && sPatientID.IsEmpty()) { SendCFindResponse(nPresentationID, uMessageID, pszClass, (pElement == NULL) ? COMMAND_STATUS_MISSING_ATTRIBUTE : COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } // The Unique Key: Study Instance UID pElement = pReqIdentifier->FindFirstElement(NULL, TAG_STUDY_INSTANCE_UID, FALSE); if (!pElement) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_MISSING_ATTRIBUTE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } L_CHAR* pszStudyInstanceUID = pReqIdentifier->GetStringValue(pElement, 0, 1); // Required Keys: // Study Date pVALUEDATE pvdStudyDate = NULL; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_STUDY_DATE, FALSE); if (pElement) { pvdStudyDate = pReqIdentifier->GetDateValue(pElement, 0, 1); if(pElement->nLength > 0) { bTryDateRange = TRUE; } } // Study Time pVALUETIME pvtStudyTime = NULL; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_STUDY_TIME, FALSE); if (pElement) { pvtStudyTime = pReqIdentifier->GetTimeValue(pElement, 0, 1); } // Accession Number CString sAccessionNumber; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_ACCESSION_NUMBER, FALSE); if (pElement) { sAccessionNumber = pReqIdentifier->GetStringValue(pElement, 0, 1); } // Study ID CString sStudyID; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_STUDY_ID, FALSE); if (pElement) { sStudyID = pReqIdentifier->GetStringValue(pElement, 0, 1); } // If Study Root Query SOP Class: Patient Name and Patient ID (obtained above) CString sPatientName; if (!bPatientRoot) { pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_NAME, FALSE); if (pElement) { sPatientName = pReqIdentifier->GetStringValue(pElement, 0, 1); } } CString sQueryString; // Form the query string sQueryString = "SELECT * FROM Studies"; if (bPatientRoot) { sQueryString += " WHERE PatientID='" + sPatientID + "'"; } if (pszStudyInstanceUID || pvdStudyDate ||bTryDateRange || pvtStudyTime || sAccessionNumber.GetLength() ||sStudyID.GetLength() || sPatientID.GetLength() || sPatientName.GetLength()) { BOOL bUseAND = FALSE; if (!bPatientRoot) { sQueryString += " WHERE "; } else { bUseAND = TRUE; } if (pszStudyInstanceUID) { if (bUseAND) sQueryString += " AND "; sQueryString += GetQueryStringForUIDs(*pReqIdentifier, TAG_STUDY_INSTANCE_UID, "StudyInstanceUID"); bUseAND = TRUE; } if (pvdStudyDate||bTryDateRange) { if (bUseAND) sQueryString += " AND "; sQueryString += GetQueryStringForDateRange(*pReqIdentifier,TAG_STUDY_DATE,"StudyDate",0); bUseAND = TRUE; } if (pvtStudyTime) { if (bUseAND) sQueryString += " AND "; CString sQuery; sQuery.Format("(HOUR(StudyTime)=%u AND MINUTE(StudyTime)=%u AND SECOND(StudyTime)=%u)", pvtStudyTime->nHours, pvtStudyTime->nMinutes, pvtStudyTime->nSeconds); sQueryString += sQuery; bUseAND = TRUE; } if (sAccessionNumber.GetLength()) { if (bUseAND) sQueryString += " AND "; sQueryString += "AccessionNumber LIKE '" + PrepareForWCM(sAccessionNumber) + "'"; bUseAND = TRUE; } if (sStudyID.GetLength()) { if (bUseAND) sQueryString += " AND "; sQueryString += "StudyID LIKE '" + PrepareForWCM(sStudyID) + "'"; bUseAND = TRUE; } if (!bPatientRoot) { if (sPatientID.GetLength()) { if (bUseAND) sQueryString += " AND "; sQueryString += "PatientID LIKE '" + PrepareForWCM(sPatientID) + "'"; bUseAND = TRUE; } if (sPatientName.GetLength()) { if (bUseAND) sQueryString += " AND "; sQueryString += "PatientName LIKE '" + PrepareForWCM(sPatientName) + "'"; } } } // The response Identifier LDicomDS RspIdentifier((L_CHAR*)(LPCTSTR) theApp.m_sTempFilesFolder); PrepareRspIdentifier(RspIdentifier, *pReqIdentifier, 2, bPatientRoot); if (bPatientRoot) { SetKeyElement(RspIdentifier, sPatientID, TAG_PATIENT_ID); } // Connect to the database CDatabase Database; try { Database.OpenEx(theApp.m_sConnectionString, CDatabase::noOdbcDialog); } catch(...) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } CString sCount, sLoggedFile, sMsg; int nRelatedSeriesCount, nRelatedInstancesCount; L_INT nRet; // Look for matches CStudyRecordset StudyRS(&Database); try { StudyRS.Open(CRecordset::dynaset, sQueryString); while (!StudyRS.IsEOF()) { // The Unique Key SetKeyElement(RspIdentifier, StudyRS.m_StudyInstanceUID, TAG_STUDY_INSTANCE_UID); // Required Keys: SetTimeDateKeyElement(RspIdentifier, StudyRS.m_StudyDate, TAG_STUDY_DATE, FALSE); SetTimeDateKeyElement(RspIdentifier, StudyRS.m_StudyTime, TAG_STUDY_TIME, TRUE); SetKeyElement(RspIdentifier, StudyRS.m_AccessionNumber, TAG_ACCESSION_NUMBER); SetKeyElement(RspIdentifier, StudyRS.m_StudyID, TAG_STUDY_ID); if (!bPatientRoot) { SetKeyElement(RspIdentifier, StudyRS.m_PatientName, TAG_PATIENT_NAME); SetKeyElement(RspIdentifier, StudyRS.m_PatientID, TAG_PATIENT_ID); } // Optional Keys: SetKeyElement(RspIdentifier, StudyRS.m_StudyDescription, TAG_STUDY_DESCRIPTION); // The number of related series and instances nRelatedSeriesCount = nRelatedInstancesCount = 0; sQueryString = "SELECT * FROM Series WHERE StudyInstanceUID='" + StudyRS.m_StudyInstanceUID + "'"; CSeriesRecordset SeriesRS(&Database); SeriesRS.Open(CRecordset::dynaset, sQueryString); while (!SeriesRS.IsEOF()) { sQueryString = "SELECT * FROM Images WHERE SeriesInstanceUID='" + SeriesRS.m_SeriesInstanceUID + "'"; CImageRecordset ImageRS(&Database); ImageRS.Open(CRecordset::dynaset, sQueryString); while (!ImageRS.IsEOF()) { nRelatedInstancesCount++; ImageRS.MoveNext(); } ImageRS.Close(); nRelatedSeriesCount++; SeriesRS.MoveNext(); } SeriesRS.Close(); sCount.Format("%d", nRelatedSeriesCount); SetKeyElement(RspIdentifier, sCount, TAG_NUMBER_OF_STUDY_RELATED_SERIES); sCount.Format("%d", nRelatedInstancesCount); SetKeyElement(RspIdentifier, sCount, TAG_NUMBER_OF_STUDY_RELATED_INSTANCES); // Here is a match nRet = SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PENDING, &RspIdentifier); if (nRet == DICOM_SUCCESS) { theApp.SaveSet(&RspIdentifier, TRUE, FALSE, &sLoggedFile); sMsg = "C-FIND-RESPONSE sent"; if (sLoggedFile.GetLength()) { sMsg += " and logged into: " + sLoggedFile; } pDlg->LogEvent(sUser, sMsg); } StudyRS.MoveNext(); } StudyRS.Close(); } catch(...) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } Database.Close(); // The final C-FIND-RSP SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_SUCCESS, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); } L_VOID CNetClient::FindSeries(LDicomDS* pReqIdentifier, L_UCHAR nPresentationID, L_UINT16 uMessageID, L_CHAR* pszClass, CString& sUser, BOOL bPatientRoot) { if (!pReqIdentifier) return; CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDICOMELEMENT pElement; // Unique Keys of the above levels: // Patient ID (if Patient Root Query SOP Class) CString sPatientID; if (bPatientRoot) { pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_ID, FALSE); if (pElement) { sPatientID = pReqIdentifier->GetStringValue(pElement, 0, 1); } if (sPatientID.IsEmpty()) { SendCFindResponse(nPresentationID, uMessageID, pszClass, (pElement == NULL) ? COMMAND_STATUS_MISSING_ATTRIBUTE : COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } } // Study Instance UID CString sStudyInstanceUID; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_STUDY_INSTANCE_UID, FALSE); if (pElement) { sStudyInstanceUID = pReqIdentifier->GetStringValue(pElement, 0, 1); } if (sStudyInstanceUID.IsEmpty()) { SendCFindResponse(nPresentationID, uMessageID, pszClass, (pElement == NULL) ? COMMAND_STATUS_MISSING_ATTRIBUTE : COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } // The Unique Key: Series Instance UID pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SERIES_INSTANCE_UID, FALSE); if (!pElement) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_MISSING_ATTRIBUTE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } L_CHAR* pszSeriesInstanceUID = pReqIdentifier->GetStringValue(pElement, 0, 1); // Required Keys: // Modality CString sModality; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_MODALITY, FALSE); if (pElement) { sModality = pReqIdentifier->GetStringValue(pElement, 0, 1); } // Series Number CString sSeriesNumber; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SERIES_NUMBER, FALSE); if (pElement) { L_INT32* pnValue = pReqIdentifier->GetLongValue(pElement, 0, 1); if (pnValue) { sSeriesNumber.Format("%i", *pnValue); } } CString sQueryString; // Form the query string sQueryString = "SELECT * FROM Series WHERE "; if (bPatientRoot) { sQueryString += "PatientID='" + sPatientID + "' AND "; } sQueryString += "StudyInstanceUID='" + sStudyInstanceUID + "'"; if (pszSeriesInstanceUID || sModality.GetLength() || sSeriesNumber.GetLength()) { if (pszSeriesInstanceUID) { sQueryString += " AND " + GetQueryStringForUIDs(*pReqIdentifier, TAG_SERIES_INSTANCE_UID, "SeriesInstanceUID"); } if (sModality.GetLength()) { sQueryString += " AND Modality LIKE '" + PrepareForWCM(sModality) + "'"; } if (sSeriesNumber.GetLength()) { sQueryString += " AND SeriesNumber=" + sSeriesNumber; } } // The response Identifier LDicomDS RspIdentifier((L_CHAR*)(LPCTSTR) theApp.m_sTempFilesFolder); PrepareRspIdentifier(RspIdentifier, *pReqIdentifier, 3, bPatientRoot); if (bPatientRoot) { SetKeyElement(RspIdentifier, sPatientID, TAG_PATIENT_ID); } SetKeyElement(RspIdentifier, sStudyInstanceUID, TAG_STUDY_INSTANCE_UID); // Connect to the database CDatabase Database; try { Database.OpenEx(theApp.m_sConnectionString, CDatabase::noOdbcDialog); } catch(...) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } CString sValue, sLoggedFile, sMsg; int nRelatedInstancesCount; L_INT nRet; // Look for matches CSeriesRecordset SeriesRS(&Database); try { SeriesRS.Open(CRecordset::dynaset, sQueryString); while (!SeriesRS.IsEOF()) { // The Unique Key SetKeyElement(RspIdentifier, SeriesRS.m_SeriesInstanceUID, TAG_SERIES_INSTANCE_UID); // Required Keys: SetKeyElement(RspIdentifier, SeriesRS.m_Modality, TAG_MODALITY); sValue.Format("%d", SeriesRS.m_SeriesNumber); SetKeyElement(RspIdentifier, sValue, TAG_SERIES_NUMBER); // Optional Keys: // The number of related instances nRelatedInstancesCount = 0; sQueryString = "SELECT * FROM Images WHERE SeriesInstanceUID='" + SeriesRS.m_SeriesInstanceUID + "'"; CImageRecordset ImageRS(&Database); ImageRS.Open(CRecordset::dynaset, sQueryString); while (!ImageRS.IsEOF()) { nRelatedInstancesCount++; ImageRS.MoveNext(); } ImageRS.Close(); sValue.Format("%d", nRelatedInstancesCount); SetKeyElement(RspIdentifier, sValue, TAG_NUMBER_OF_SERIES_RELATED_INSTANCES); // Here is a match nRet = SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PENDING, &RspIdentifier); if (nRet == DICOM_SUCCESS) { theApp.SaveSet(&RspIdentifier, TRUE, FALSE, &sLoggedFile); sMsg = "C-FIND-RESPONSE sent"; if (sLoggedFile.GetLength()) { sMsg += " and logged into: " + sLoggedFile; } pDlg->LogEvent(sUser, sMsg); } SeriesRS.MoveNext(); } SeriesRS.Close(); } catch(...) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } Database.Close(); // The final C-FIND-RSP SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_SUCCESS, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); } L_VOID CNetClient::FindImages(LDicomDS* pReqIdentifier, L_UCHAR nPresentationID, L_UINT16 uMessageID, L_CHAR* pszClass, CString& sUser, BOOL bPatientRoot) { if (!pReqIdentifier) return; CDICOMSRVDlg* pDlg = (CDICOMSRVDlg*) AfxGetMainWnd(); pDICOMELEMENT pElement; // Unique Keys of the above levels: // Patient ID (if Patient Root Query SOP Class) CString sPatientID; if (bPatientRoot) { pElement = pReqIdentifier->FindFirstElement(NULL, TAG_PATIENT_ID, FALSE); if (pElement) { sPatientID = pReqIdentifier->GetStringValue(pElement, 0, 1); } if (sPatientID.IsEmpty()) { SendCFindResponse(nPresentationID, uMessageID, pszClass, (pElement == NULL) ? COMMAND_STATUS_MISSING_ATTRIBUTE : COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } } // Study Instance UID CString sStudyInstanceUID; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_STUDY_INSTANCE_UID, FALSE); if (pElement) { sStudyInstanceUID = pReqIdentifier->GetStringValue(pElement, 0, 1); } if (sStudyInstanceUID.IsEmpty()) { SendCFindResponse(nPresentationID, uMessageID, pszClass, (pElement == NULL) ? COMMAND_STATUS_MISSING_ATTRIBUTE : COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } // Series Instance UID CString sSeriesInstanceUID; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SERIES_INSTANCE_UID, FALSE); if (pElement) { sSeriesInstanceUID = pReqIdentifier->GetStringValue(pElement, 0, 1); } if (sSeriesInstanceUID.IsEmpty()) { SendCFindResponse(nPresentationID, uMessageID, pszClass, (pElement == NULL) ? COMMAND_STATUS_MISSING_ATTRIBUTE : COMMAND_STATUS_MISSING_ATTRIBUTE_VALUE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } // The Unique Key: SOP Instance UID pElement = pReqIdentifier->FindFirstElement(NULL, TAG_SOP_INSTANCE_UID, FALSE); if (!pElement) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_MISSING_ATTRIBUTE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } L_CHAR* pszSOPInstanceUID = pReqIdentifier->GetStringValue(pElement, 0, 1); // Required Keys: // Instance Number CString sInstanceNumber; pElement = pReqIdentifier->FindFirstElement(NULL, TAG_INSTANCE_NUMBER, FALSE); if (pElement) { L_INT32* pnValue = pReqIdentifier->GetLongValue(pElement, 0, 1); if (pnValue) { sInstanceNumber.Format("%i", *pnValue); } } CString sQueryString; // Form the query string sQueryString = "SELECT * FROM Images WHERE "; if (bPatientRoot) { sQueryString += "PatientID='" + sPatientID + "' AND "; } sQueryString += "StudyInstanceUID='" + sStudyInstanceUID + "' AND "; sQueryString += "SeriesInstanceUID='" + sSeriesInstanceUID + "'"; if (pszSOPInstanceUID || sInstanceNumber.GetLength()) { if (pszSOPInstanceUID) { sQueryString += " AND " + GetQueryStringForUIDs(*pReqIdentifier, TAG_SOP_INSTANCE_UID, "SOPInstanceUID"); } if (sInstanceNumber.GetLength()) { sQueryString += " AND InstanceNumber=" + sInstanceNumber; } } // The response Identifier LDicomDS RspIdentifier((L_CHAR*)(LPCTSTR) theApp.m_sTempFilesFolder); PrepareRspIdentifier(RspIdentifier, *pReqIdentifier, 4, bPatientRoot); if (bPatientRoot) { SetKeyElement(RspIdentifier, sPatientID, TAG_PATIENT_ID); } SetKeyElement(RspIdentifier, sStudyInstanceUID, TAG_STUDY_INSTANCE_UID); SetKeyElement(RspIdentifier, sSeriesInstanceUID, TAG_SERIES_INSTANCE_UID); // Connect to the database CDatabase Database; try { Database.OpenEx(theApp.m_sConnectionString, CDatabase::noOdbcDialog); } catch(...) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } CString sValue, sLoggedFile, sMsg; L_INT nRet; // Look for matches CImageRecordset ImageRS(&Database); try { ImageRS.Open(CRecordset::dynaset, sQueryString); while (!ImageRS.IsEOF()) { // The Unique Key SetKeyElement(RspIdentifier, ImageRS.m_SOPInstanceUID, TAG_SOP_INSTANCE_UID); // Required Keys: sValue.Format("%d", ImageRS.m_InstanceNumber); SetKeyElement(RspIdentifier, sValue, TAG_INSTANCE_NUMBER); // Here is a match nRet = SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PENDING, &RspIdentifier); if (nRet == DICOM_SUCCESS) { theApp.SaveSet(&RspIdentifier, TRUE, FALSE, &sLoggedFile); sMsg = "C-FIND-RESPONSE sent"; if (sLoggedFile.GetLength()) { sMsg += " and logged into: " + sLoggedFile; } pDlg->LogEvent(sUser, sMsg); } ImageRS.MoveNext(); } ImageRS.Close(); } catch(...) { SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_PROCESSING_FAILURE, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); return; } Database.Close(); // The final C-FIND-RSP SendCFindResponse(nPresentationID, uMessageID, pszClass, COMMAND_STATUS_SUCCESS, NULL); pDlg->LogEvent(sUser, "C-FIND-RESPONSE sent"); } // For the Wild Card Matching: Replaces every '*' with '%' and every '?' with '_'. CString CNetClient::PrepareForWCM(const CString& sValue) const { CString sPreparedValue = sValue; int iLength = sPreparedValue.GetLength(); for (int i = 0; i < iLength; i++) { if (sPreparedValue.GetAt(i) == '*') { sPreparedValue.SetAt(i, '%'); } else if (sPreparedValue.GetAt(i) == '?') { sPreparedValue.SetAt(i, '_'); } } return sPreparedValue; } // iQRLevel: (1: Ptn), (2: Std), (3: Srs), (4: Img) L_VOID CNetClient::PrepareRspIdentifier(LDicomDS& RspIdentifier, LDicomDS& ReqIdentifier, int iQRLevel, BOOL bPatientRoot) const { RspIdentifier.InitDS(CLASS_UNKNOWN, DS_METAHEADER_ABSENT | DS_LITTLE_ENDIAN | DS_EXPLICIT_VR); // The Query/Retrieve Level switch (iQRLevel) { case 1: SetElement(&RspIdentifier, TAG_QUERY_RETRIEVE_LEVEL, VR_CS, "PATIENT"); break; case 2: SetElement(&RspIdentifier, TAG_QUERY_RETRIEVE_LEVEL, VR_CS, "STUDY"); break; case 3: SetElement(&RspIdentifier, TAG_QUERY_RETRIEVE_LEVEL, VR_CS, "SERIES"); break; case 4: SetElement(&RspIdentifier, TAG_QUERY_RETRIEVE_LEVEL, VR_CS, "IMAGE"); break; } // The Unique Keys if (bPatientRoot) { RspIdentifier.InsertElement(NULL, FALSE, TAG_PATIENT_ID, VR_LO, FALSE, 0); } if (iQRLevel >= 2) { RspIdentifier.InsertElement(NULL, FALSE, TAG_STUDY_INSTANCE_UID, VR_UI, FALSE, 0); } if (iQRLevel >= 3) { RspIdentifier.InsertElement(NULL, FALSE, TAG_SERIES_INSTANCE_UID, VR_UI, FALSE, 0); } if (iQRLevel >= 4) { RspIdentifier.InsertElement(NULL, FALSE, TAG_SOP_INSTANCE_UID, VR_UI, FALSE, 0); } // The Required and Optional Keys // (Note: This demo supports only the existence of some Optional Keys) switch (iQRLevel) { case 1: // Required Keys: InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_PATIENT_NAME, VR_PN); // Optional Keys: InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_PATIENT_BIRTH_DATE, VR_DA); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_PATIENT_BIRTH_TIME, VR_TM); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_PATIENT_SEX, VR_CS); //InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_OTHER_PATIENT_IDS, VR_LO); //InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_OTHER_PATIENT_NAMES, VR_PN); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_ETHNIC_GROUP, VR_SH); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_PATIENT_COMMENTS, VR_LT); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_NUMBER_OF_PATIENT_RELATED_STUDIES, VR_IS); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_NUMBER_OF_PATIENT_RELATED_SERIES, VR_IS); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES, VR_IS); break; case 2: // Required Keys: InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_STUDY_DATE, VR_DA); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_STUDY_TIME, VR_TM); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_ACCESSION_NUMBER, VR_SH); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_STUDY_ID, VR_SH); if (!bPatientRoot) { InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_PATIENT_NAME, VR_PN); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_PATIENT_ID, VR_LO); } // Optional Keys: InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_STUDY_DESCRIPTION, VR_LO); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_NUMBER_OF_STUDY_RELATED_SERIES, VR_IS); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_NUMBER_OF_STUDY_RELATED_INSTANCES, VR_IS); break; case 3: // Required Keys: InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_MODALITY, VR_CS); InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_SERIES_NUMBER, VR_IS); // Optional Keys: InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_NUMBER_OF_SERIES_RELATED_INSTANCES, VR_IS); break; case 4: // Required Keys: InsertKeyElement(RspIdentifier, ReqIdentifier, TAG_INSTANCE_NUMBER, VR_IS); break; } } L_VOID CNetClient::InsertKeyElement(LDicomDS& RspIdentifier, LDicomDS& ReqIdentifier, L_UINT32 uTag, L_UINT16 uVR) const { pDICOMELEMENT pElement = ReqIdentifier.FindFirstElement(NULL, uTag, FALSE); if (pElement) { RspIdentifier.InsertElement(NULL, FALSE, uTag, uVR, FALSE, 0); } } L_VOID CNetClient::SetKeyElement(LDicomDS& RspIdentifier, CString& sValue, L_UINT32 uTag) const { pDICOMELEMENT pElement = RspIdentifier.FindFirstElement(NULL, uTag, FALSE); if (pElement) { RspIdentifier.SetConvertValue(pElement, (L_CHAR*)(LPCTSTR) sValue, 1); } } L_VOID CNetClient::SetTimeDateKeyElement(LDicomDS& RspIdentifier, TIMESTAMP_STRUCT& ts, L_UINT32 uTag, L_BOOL bTimeValue) const { pDICOMELEMENT pElement = RspIdentifier.FindFirstElement(NULL, uTag, FALSE); if (pElement) { if (bTimeValue) { VALUETIME vt; vt.nHours = ts.hour; vt.nMinutes = ts.minute; vt.nSeconds = ts.second; vt.nFractions = ts.fraction; RspIdentifier.SetTimeValue(pElement, &vt, 1); } else { VALUEDATE vd; vd.nYear = ts.year; vd.nMonth = ts.month; vd.nDay = ts.day; RspIdentifier.SetDateValue(pElement, &vd, 1); } } } static L_BOOL IsRange(L_CHAR * pszValue) { L_CHAR * pTemp = pszValue; if(pTemp) { while(*pTemp) { if(*pTemp == '-') { return TRUE; } pTemp ++; } } return FALSE; } void ConvertDicomDateToAccessDate(L_CHAR * pszDicomDate , CString & AccessDate) { char szTemp[32]; AccessDate.Empty(); if (lstrlen(pszDicomDate) < 8) { return ; } L_INT nOffset = 0; AccessDate += "#"; strncpy(szTemp,pszDicomDate+nOffset,4);//year szTemp[4]=0; nOffset+=4; AccessDate += szTemp; AccessDate += "/"; strncpy(szTemp,pszDicomDate+nOffset,2);//month szTemp[2]=0; nOffset+=2; AccessDate += szTemp; AccessDate += "/"; strncpy(szTemp,pszDicomDate+nOffset,2);//day szTemp[2]=0; AccessDate += szTemp; AccessDate += "#"; } CString CNetClient:: GetQueryStringForDateRange ( LDicomDS& ReqIdentifier, L_UINT32 uTag, const CString& sFieldName, L_UINT uIndex ) { L_UINT32 nCount=1; L_CHAR pSeps[] = "\\"; L_CHAR *pToken; L_CHAR *pBuf=NULL; L_UINT uLocalIndex = 0 ; CString sQueryString; pDICOMELEMENT pElement = ReqIdentifier.FindFirstElement(NULL, uTag, FALSE); if (!pElement) return sQueryString; pBuf = (L_CHAR*)malloc(pElement->nLength +1); if(!pBuf) return sQueryString; ReqIdentifier.GetBinaryValue(pElement, pBuf, pElement->nLength); pBuf[pElement->nLength] = 0 ; pToken = strtok( pBuf, pSeps ); while( pToken != NULL ) { // Is this the value we are looking for ? if(uLocalIndex == uIndex) { L_CHAR *p = pToken; CString strAccessDate; if(IsRange(p)) { /* These are the rules: 1. The date inside DICOM is formatted as yyyymmdd so "19930822" would represent August 22,1993. 2. A string of the form " - " shall match all occurrences of dates which fall between and inclusive. 3. A string of the form "- " shall match all occurrences of dates prior to and including 4. A string of the form " -" shall match all occurrences of and subsequent dates */ // If it starts with a '-' then it's an upper range if(p [0] == '-' ) { ConvertDicomDateToAccessDate(p+1/*skip '-'*/, strAccessDate); sQueryString = "("; sQueryString += sFieldName + " <= "; sQueryString += strAccessDate; sQueryString += " )"; } // If it ends with a '-' then it's an lower range else if (p [strlen(p)-1] == '-') { ConvertDicomDateToAccessDate(p, strAccessDate); sQueryString = "("; sQueryString += sFieldName + " >= "; sQueryString += strAccessDate; sQueryString += " )"; } // It's a full range else { sQueryString = "( "; sQueryString += sFieldName + " BETWEEN "; ConvertDicomDateToAccessDate(p, strAccessDate); sQueryString += strAccessDate; sQueryString += " AND "; ConvertDicomDateToAccessDate(p+8+1, strAccessDate); sQueryString += strAccessDate; sQueryString += " )"; } } // No range ! else { ConvertDicomDateToAccessDate(p, strAccessDate); sQueryString = "("; sQueryString += sFieldName + " = "; sQueryString += strAccessDate; sQueryString += " )"; } break; } uLocalIndex ++ ; pToken = strtok( NULL, pSeps ); } if(pBuf) { free(pBuf); } return sQueryString; } // For the List of UID Matching CString CNetClient::GetQueryStringForUIDs ( LDicomDS& ReqIdentifier, L_UINT32 uTag, const CString& sFieldName ) { CString sQueryString; pDICOMELEMENT pElement = ReqIdentifier.FindFirstElement(NULL, uTag, FALSE); if (!pElement) return sQueryString; CString sUID; L_UINT32 uUIDsCount = ReqIdentifier.GetCountValue(pElement); if (uUIDsCount > 1) { sQueryString = "("; } for (L_UINT32 i = 0; i < uUIDsCount; i++) { sUID = ReqIdentifier.GetStringValue(pElement, i, 1); sQueryString += sFieldName + "='" + sUID + "'"; if (i != uUIDsCount - 1) { sQueryString += " OR "; } } if (uUIDsCount > 1) { sQueryString += ")"; } return sQueryString; } L_BOOL CNetClient::SaveDataSet(LDicomDS* pDataSet, CString& sFilename) { if (!pDataSet) return FALSE; // File Meta Information Version SetElement(pDataSet, TAG_FILE_META_INFORMATION_VERSION, VR_OB, "01"); pDICOMELEMENT pElement; // Media Storage SOP Class UID L_CHAR* pszSOPClassUID = NULL; pElement = pDataSet->FindFirstElement(NULL, TAG_SOP_CLASS_UID, FALSE); if (pElement) { pszSOPClassUID = pDataSet->GetStringValue(pElement, 0, 1); } SetElement(pDataSet, TAG_MEDIA_STORAGE_SOP_CLASS_UID, VR_UI, pszSOPClassUID); pDataSet->FreeValue(pElement); // Media Storage SOP Instance UID L_CHAR* pszSOPInstanceUID = NULL; pElement = pDataSet->FindFirstElement(NULL, TAG_SOP_INSTANCE_UID, FALSE); if (pElement) { pszSOPInstanceUID = pDataSet->GetStringValue(pElement, 0, 1); } SetElement(pDataSet, TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, VR_UI, pszSOPInstanceUID); pDataSet->FreeValue(pElement); LDicomAssociate* pAssociate = GetAssociate(); // Implementation Class UID L_CHAR* pszImplementationClassUID = NULL; if (pAssociate) { pszImplementationClassUID = pAssociate->GetImplementClass(); } SetElement(pDataSet, TAG_IMPLEMENTATION_CLASS_UID, VR_UI, pszImplementationClassUID); // Implementation Version Name L_CHAR* pszImplementationVersionName = NULL; if (pAssociate) { pszImplementationVersionName = pAssociate->GetImplementVersion(); } SetElement(pDataSet, TAG_IMPLEMENTATION_VERSION_NAME, VR_SH, pszImplementationVersionName); L_UINT16 uRet = pDataSet->SaveDS((L_CHAR*)(LPCTSTR) sFilename, DS_METAHEADER_PRESENT | DS_GROUP_LENGTHS); return (uRet == DICOM_SUCCESS); } L_VOID CNetClient::SetElement(LDicomDS* pDataSet, L_UINT32 uTag, L_UINT16 uVR, L_CHAR* pszValue) const { if (pDataSet == NULL) return; pDICOMELEMENT pElement = pDataSet->FindFirstElement(NULL, uTag, FALSE); if (pElement == NULL) { pElement = pDataSet->InsertElement(NULL, FALSE, uTag, uVR, FALSE, 0); } if (pElement && pszValue) { pDataSet->SetConvertValue(pElement, pszValue, 1); } } L_INT CNetClient::OnPrivateKeyPassword(L_CHAR *pszPassword, L_INT nSize, L_INT uFlag) { strcpy(pszPassword, (LPSTR)(LPCSTR)theApp.m_strPrivateKeyPassword); return theApp.m_strPrivateKeyPassword.GetLength(); }