Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
342 changes: 342 additions & 0 deletions docs/dcm4che5-migration-guide.md

Large diffs are not rendered by default.

34 changes: 30 additions & 4 deletions server/build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,39 @@
<include name="com/mirth/connect/connectors/dimse/DICOMReceiverProperties.class" />
<include name="com/mirth/connect/connectors/dimse/DICOMDispatcherProperties.class" />
</jar>
<jar destfile="${connectors.dicom}/dicom-server.jar" modificationTime="${archive.entry.date}">
<!-- dicom-server.jar: version-neutral connector code (always loaded) -->
<jar destfile="${connectors.dicom}/dicom-server.jar" basedir="${classes}" modificationTime="${archive.entry.date}">
<include name="com/mirth/connect/connectors/dimse/**" />
<exclude name="com/mirth/connect/connectors/dimse/dicom/dcm2/**" />
<exclude name="com/mirth/connect/connectors/dimse/dicom/dcm5/**" />
<exclude name="com/mirth/connect/connectors/dimse/DefaultDICOMConfiguration.class" />
<exclude name="com/mirth/connect/connectors/dimse/DefaultDcm5DICOMConfiguration.class" />
<exclude name="com/mirth/connect/connectors/dimse/DICOMReceiverProperties.class" />
<exclude name="com/mirth/connect/connectors/dimse/DICOMDispatcherProperties.class" />
</jar>

<!-- dicom-server-dcm2.jar: dcm4che2-specific code (variant-loaded when dicom.library=dcm4che2) -->
<!-- Bundles stock tool classes from dcm4che-tool-*.jar to avoid signed/unsigned package split.
Patched classes from ${classes} listed first; duplicate="preserve" keeps first entry. -->
<jar destfile="${connectors.dicom}/dicom-server-dcm2.jar" modificationTime="${archive.entry.date}"
duplicate="preserve">
<fileset dir="${classes}">
<include name="com/mirth/connect/connectors/dimse/**" />
<include name="com/mirth/connect/connectors/dimse/dicom/dcm2/**" />
<include name="com/mirth/connect/connectors/dimse/DefaultDICOMConfiguration.class" />
<include name="org/dcm4che2/**" />
<exclude name="com/mirth/connect/connectors/dimse/DICOMReceiverProperties.class" />
<exclude name="com/mirth/connect/connectors/dimse/DICOMDispatcherProperties.class" />
</fileset>
<zipfileset src="${lib.extensions}/dimse/dcm4che-tool-dcmrcv-2.0.29.jar">
<include name="org/dcm4che2/**" />
</zipfileset>
<zipfileset src="${lib.extensions}/dimse/dcm4che-tool-dcmsnd-2.0.29.jar">
<include name="org/dcm4che2/**" />
</zipfileset>
</jar>

<!-- dicom-server-dcm5.jar: dcm4che5-specific code (variant-loaded when dicom.library=dcm4che5) -->
<jar destfile="${connectors.dicom}/dicom-server-dcm5.jar" basedir="${classes}" modificationTime="${archive.entry.date}">
<include name="com/mirth/connect/connectors/dimse/dicom/dcm5/**" />
<include name="com/mirth/connect/connectors/dimse/DefaultDcm5DICOMConfiguration.class" />
</jar>

<!-- connectors.doc -->
Expand Down
4 changes: 4 additions & 0 deletions server/conf/mirth.properties
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,7 @@ database.connection.retrywaitinmilliseconds = 10000
# database-readonly.url = jdbc:...
#
database.enable-read-write-split = true

# DICOM library backend. Supported values: dcm4che2 (default), dcm4che5.
# Changing this value requires a server restart.
# dicom.library = dcm4che2
4 changes: 3 additions & 1 deletion server/docs/thirdparty/THIRD-PARTY-README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ terms.
DcmRcv and DcmSnd were modified to allow overriding of the network
connections.

HAPI 2.3 (source code can be downloaded at:
dcm4che 5.34.3 (source code can be downloaded at: http://www.dcm4che.org/)

HAPI 2.3 (source code can be downloaded at:
https://github.com/hapifhir/hapi-hl7v2)

iText, a free Java-PDF library version 2.1.7 (source code can be downloaded
Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*
* Copyright (c) Mirth Corporation. All rights reserved.
*
*
* http://www.mirthcorp.com
*
*
* The software in this package is published under the terms of the MPL license a copy of which has
* been included with this distribution in the LICENSE.txt file.
*/
Expand All @@ -11,22 +11,51 @@

import java.util.Map;

import org.dcm4che2.net.Association;
import org.dcm4che2.net.NetworkConnection;
import org.dcm4che2.tool.dcmrcv.MirthDcmRcv;
import org.dcm4che2.tool.dcmsnd.MirthDcmSnd;

import com.mirth.connect.connectors.dimse.dicom.OieDicomReceiver;
import com.mirth.connect.connectors.dimse.dicom.OieDicomSender;
import com.mirth.connect.donkey.server.channel.Connector;

/**
* Version-neutral interface for DICOM connector configuration. Implementations
* configure the DICOM sender and receiver without direct dcm4che library dependencies.
*
* <p>Custom implementations can cast the sender/receiver to the dcm4che-specific
* type (e.g., Dcm2DicomSender) and call {@code unwrap()} to access the underlying
* MirthDcmSnd/MirthDcmRcv if needed.
*/
public interface DICOMConfiguration {

public void configureConnectorDeploy(Connector connector) throws Exception;

public NetworkConnection createNetworkConnection();

public void configureDcmRcv(MirthDcmRcv dcmrcv, DICOMReceiver connector, DICOMReceiverProperties connectorProperties) throws Exception;

public void configureDcmSnd(MirthDcmSnd dcmsnd, DICOMDispatcher connector, DICOMDispatcherProperties connectorProperties) throws Exception;

public Map<String, Object> getCStoreRequestInformation(Association as);
}
void configureConnectorDeploy(Connector connector) throws Exception;

void configureReceiver(OieDicomReceiver receiver, DICOMReceiver connector, DICOMReceiverProperties connectorProperties) throws Exception;

void configureSender(OieDicomSender sender, DICOMDispatcher connector, DICOMDispatcherProperties connectorProperties) throws Exception;

/**
* Extracts additional information from a DICOM C-STORE association request.
* The association parameter is the library-specific association object.
*
* <p>For the dcm4che2 backend the runtime type is
* {@code org.dcm4che2.net.Association}. Custom implementations should cast
* accordingly:
* <pre>{@code
* Association as = (Association) association;
* map.put("calledAET", as.getCalledAET());
* }</pre>
*
* @param association The library-specific association object
* @return Additional key-value pairs to add to the source map
*/
Map<String, Object> getCStoreRequestInformation(Object association);

/**
* Optional factory method for creating a custom {@code NetworkConnection}.
* The returned object must be an instance of the library-specific
* NetworkConnection class (e.g., {@code org.dcm4che2.net.NetworkConnection}
* for the dcm4che2 backend). If {@code null} is returned, the default
* NetworkConnection is used.
*
* @return A library-specific NetworkConnection, or {@code null} for the default
*/
default Object createNetworkConnection() { return null; }
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*
* Copyright (c) Mirth Corporation. All rights reserved.
*
*
* http://www.mirthcorp.com
*
*
* The software in this package is published under the terms of the MPL license a copy of which has
* been included with this distribution in the LICENSE.txt file.
*/
Expand All @@ -15,49 +15,58 @@

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.dcm4che2.tool.dcmrcv.MirthDcmRcv;
import org.dcm4che2.tool.dcmsnd.MirthDcmSnd;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.mirth.connect.connectors.dimse.dicom.OieDicomReceiver;
import com.mirth.connect.connectors.dimse.dicom.OieDicomSender;
import com.mirth.connect.util.MirthSSLUtil;

public class DICOMConfigurationUtil {

public static void configureDcmRcv(MirthDcmRcv dcmrcv, DICOMReceiver connector, DICOMReceiverProperties connectorProperties, String[] protocols) throws Exception {
private static final Logger logger = LogManager.getLogger(DICOMConfigurationUtil.class);

public static void configureReceiver(OieDicomReceiver receiver, DICOMReceiver connector, DICOMReceiverProperties connectorProperties, String[] protocols) throws Exception {
if (!StringUtils.equals(connectorProperties.getTls(), "notls")) {
if (connectorProperties.getTls().equals("without")) {
dcmrcv.setTlsWithoutEncyrption();
logger.warn("DICOM receiver configured with TLS NULL encryption (SSL_RSA_WITH_NULL_SHA). "
+ "This provides authentication only — data is sent in cleartext. "
+ "Consider using AES encryption instead.");
receiver.setTlsWithoutEncryption();
} else if (connectorProperties.getTls().equals("3des")) {
dcmrcv.setTls3DES_EDE_CBC();
logger.warn("DICOM receiver configured with deprecated 3DES cipher suite. "
+ "Consider using AES encryption instead.");
receiver.setTls3DES_EDE_CBC();
} else if (connectorProperties.getTls().equals("aes")) {
dcmrcv.setTlsAES_128_CBC();
receiver.setTlsAES_128_CBC();
}

String trustStore = connector.getReplacer().replaceValues(connectorProperties.getTrustStore(), connector.getChannelId(), connector.getChannel().getName());
if (StringUtils.isNotBlank(trustStore)) {
dcmrcv.setTrustStoreURL(trustStore);
receiver.setTrustStoreURL(trustStore);
}

String trustStorePW = connector.getReplacer().replaceValues(connectorProperties.getTrustStorePW(), connector.getChannelId(), connector.getChannel().getName());
if (StringUtils.isNotBlank(trustStorePW)) {
dcmrcv.setTrustStorePassword(trustStorePW);
receiver.setTrustStorePassword(trustStorePW);
}

String keyPW = connector.getReplacer().replaceValues(connectorProperties.getKeyPW(), connector.getChannelId(), connector.getChannel().getName());
if (StringUtils.isNotBlank(keyPW)) {
dcmrcv.setKeyPassword(keyPW);
receiver.setKeyPassword(keyPW);
}

String keyStore = connector.getReplacer().replaceValues(connectorProperties.getKeyStore(), connector.getChannelId(), connector.getChannel().getName());
if (StringUtils.isNotBlank(keyStore)) {
dcmrcv.setKeyStoreURL(keyStore);
receiver.setKeyStoreURL(keyStore);
}

String keyStorePW = connector.getReplacer().replaceValues(connectorProperties.getKeyStorePW(), connector.getChannelId(), connector.getChannel().getName());
if (StringUtils.isNotBlank(keyStorePW)) {
dcmrcv.setKeyStorePassword(keyStorePW);
receiver.setKeyStorePassword(keyStorePW);
}

dcmrcv.setTlsNeedClientAuth(connectorProperties.isNoClientAuth());
receiver.setTlsNeedClientAuth(connectorProperties.isNoClientAuth());

protocols = ArrayUtils.clone(protocols);

Expand All @@ -73,31 +82,38 @@ public static void configureDcmRcv(MirthDcmRcv dcmrcv, DICOMReceiver connector,
protocols = protocolsList.toArray(new String[protocolsList.size()]);
}

dcmrcv.setTlsProtocol(MirthSSLUtil.getEnabledHttpsProtocols(protocols));
receiver.setTlsProtocol(MirthSSLUtil.getEnabledHttpsProtocols(protocols));

dcmrcv.initTLS();
receiver.initTLS();
}
}

public static void configureDcmSnd(MirthDcmSnd dcmsnd, DICOMDispatcher connector, DICOMDispatcherProperties connectorProperties, String[] protocols) throws Exception {
public static void configureSender(OieDicomSender sender, DICOMDispatcher connector, DICOMDispatcherProperties connectorProperties, String[] protocols) throws Exception {
if (connectorProperties.getTls() != null && !connectorProperties.getTls().equals("notls")) {
if (connectorProperties.getTls().equals("without"))
dcmsnd.setTlsWithoutEncyrption();
if (connectorProperties.getTls().equals("3des"))
dcmsnd.setTls3DES_EDE_CBC();
if (connectorProperties.getTls().equals("without")) {
logger.warn("DICOM sender configured with TLS NULL encryption (SSL_RSA_WITH_NULL_SHA). "
+ "This provides authentication only — data is sent in cleartext. "
+ "Consider using AES encryption instead.");
sender.setTlsWithoutEncryption();
}
if (connectorProperties.getTls().equals("3des")) {
logger.warn("DICOM sender configured with deprecated 3DES cipher suite. "
+ "Consider using AES encryption instead.");
sender.setTls3DES_EDE_CBC();
}
if (connectorProperties.getTls().equals("aes"))
dcmsnd.setTlsAES_128_CBC();
if (connectorProperties.getTrustStore() != null && !connectorProperties.getTrustStore().equals(""))
dcmsnd.setTrustStoreURL(connectorProperties.getTrustStore());
if (connectorProperties.getTrustStorePW() != null && !connectorProperties.getTrustStorePW().equals(""))
dcmsnd.setTrustStorePassword(connectorProperties.getTrustStorePW());
if (connectorProperties.getKeyPW() != null && !connectorProperties.getKeyPW().equals(""))
dcmsnd.setKeyPassword(connectorProperties.getKeyPW());
if (connectorProperties.getKeyStore() != null && !connectorProperties.getKeyStore().equals(""))
dcmsnd.setKeyStoreURL(connectorProperties.getKeyStore());
if (connectorProperties.getKeyStorePW() != null && !connectorProperties.getKeyStorePW().equals(""))
dcmsnd.setKeyStorePassword(connectorProperties.getKeyStorePW());
dcmsnd.setTlsNeedClientAuth(connectorProperties.isNoClientAuth());
sender.setTlsAES_128_CBC();
if (StringUtils.isNotBlank(connectorProperties.getTrustStore()))
sender.setTrustStoreURL(connectorProperties.getTrustStore());
if (StringUtils.isNotBlank(connectorProperties.getTrustStorePW()))
sender.setTrustStorePassword(connectorProperties.getTrustStorePW());
if (StringUtils.isNotBlank(connectorProperties.getKeyPW()))
sender.setKeyPassword(connectorProperties.getKeyPW());
if (StringUtils.isNotBlank(connectorProperties.getKeyStore()))
sender.setKeyStoreURL(connectorProperties.getKeyStore());
if (StringUtils.isNotBlank(connectorProperties.getKeyStorePW()))
sender.setKeyStorePassword(connectorProperties.getKeyStorePW());
sender.setTlsNeedClientAuth(connectorProperties.isNoClientAuth());

protocols = ArrayUtils.clone(protocols);

Expand All @@ -115,9 +131,9 @@ public static void configureDcmSnd(MirthDcmSnd dcmsnd, DICOMDispatcher connector

protocols = MirthSSLUtil.getEnabledHttpsProtocols(protocols);

dcmsnd.setTlsProtocol(protocols);
sender.setTlsProtocol(protocols);

dcmsnd.initTLS();
sender.initTLS();
}
}
}
}
Loading
Loading