/******************************************************************************* * Copyright (c) 2009-2019 Weasis Team and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v20.html * * Contributors: * Nicolas Roduit - initial API and implementation *******************************************************************************/ package org.weasis.dicom.util; import java.io.IOException; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.Set; import org.dcm4che3.data.Attributes; import org.dcm4che3.data.Tag; import org.dcm4che3.data.VR; import org.dcm4che3.net.ApplicationEntity; import org.dcm4che3.net.Association; import org.dcm4che3.net.Connection; import org.dcm4che3.net.Device; import org.dcm4che3.net.PDVInputStream; import org.dcm4che3.net.Status; import org.dcm4che3.net.TransferCapability; import org.dcm4che3.net.pdu.PresentationContext; import org.dcm4che3.net.service.BasicCEchoSCP; import org.dcm4che3.net.service.BasicCStoreSCP; import org.dcm4che3.net.service.DicomServiceException; import org.dcm4che3.net.service.DicomServiceRegistry; import org.dcm4che3.tool.common.CLIUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.weasis.dicom.param.AdvancedParams; import org.weasis.dicom.param.AttributeEditor; import org.weasis.dicom.param.DicomForwardDestination; import org.weasis.dicom.param.DicomNode; import org.weasis.dicom.param.ForwardDestination; import org.weasis.dicom.param.ForwardDicomNode; import org.weasis.dicom.util.ForwardUtil.Params; public class StoreScpForward { private static final Logger LOGGER = LoggerFactory.getLogger(StoreScpForward.class); private final Device device = new Device("storescp"); private final ApplicationEntity ae = new ApplicationEntity("*"); private final Connection conn = new Connection(); private volatile int priority; private volatile int status = 0; private final Map<ForwardDicomNode, List<ForwardDestination>> destinations; private final BasicCStoreSCP cstoreSCP = new BasicCStoreSCP("*") { @Override protected void store(Association as, PresentationContext pc, Attributes rq, PDVInputStream data, Attributes rsp) throws IOException { Optional<ForwardDicomNode> sourceNode = destinations.keySet().stream().filter(n -> n.getForwardAETitle().equals(as.getCalledAET())).findFirst(); if (!sourceNode.isPresent()) { throw new IllegalStateException("Cannot find the forward AeTitle " + as.getCalledAET()); } ForwardDicomNode fwdNode = sourceNode.get(); List<ForwardDestination> destList = destinations.get(fwdNode); if (destList == null || destList.isEmpty()) { throw new IllegalStateException("No DICOM destinations for " + fwdNode.toString()); } DicomNode callingNode = DicomNode.buildRemoteDicomNode(as); Set<DicomNode> srcNodes = fwdNode.getAcceptedSourceNodes(); boolean valid = srcNodes.isEmpty() || srcNodes.stream().anyMatch(n -> n.getAet().equals(callingNode.getAet()) && (!n.isValidateHostname() || n.equalsHostname(callingNode.getHostname()))); if (!valid) { rsp.setInt(Tag.Status, VR.US, Status.NotAuthorized); LOGGER.error("Refused: not authorized (124H). Source node: {}. SopUID: {}", callingNode, rq.getString(Tag.AffectedSOPInstanceUID)); return; } rsp.setInt(Tag.Status, VR.US, status); try { Params p = new Params(rq.getString(Tag.AffectedSOPInstanceUID), rq.getString(Tag.AffectedSOPClassUID), pc.getTransferSyntax(), priority, data, as); ForwardUtil.storeMulitpleDestination(fwdNode, destList, p); } catch (Exception e) { throw new DicomServiceException(Status.ProcessingFailure, e); } } }; public StoreScpForward(ForwardDicomNode fwdNode, DicomNode destinationNode) throws IOException { this(null, fwdNode, destinationNode, null); } public StoreScpForward(AdvancedParams forwardParams, ForwardDicomNode callingNode, DicomNode destinationNode) throws IOException { this(forwardParams, callingNode, destinationNode, null); } /** * @param forwardParams * the optional advanced parameters (proxy, authentication, connection and TLS) for the final destination * @param fwdNode * the calling DICOM node configuration * @param destinationNode * the final DICOM node configuration * @param attributesEditor * the editor for modifying attributes on the fly (can be Null) * @throws IOException */ public StoreScpForward(AdvancedParams forwardParams, ForwardDicomNode fwdNode, DicomNode destinationNode, AttributeEditor attributesEditor) throws IOException { device.setDimseRQHandler(createServiceRegistry()); device.addConnection(conn); device.addApplicationEntity(ae); ae.setAssociationAcceptor(true); ae.addConnection(conn); DicomForwardDestination uniqueDestination = new DicomForwardDestination(forwardParams, fwdNode, destinationNode, attributesEditor); this.destinations = new HashMap<>(); destinations.put(fwdNode, Collections.singletonList(uniqueDestination)); } public StoreScpForward(Map<ForwardDicomNode, List<ForwardDestination>> destinations) { device.setDimseRQHandler(createServiceRegistry()); device.addConnection(conn); device.addApplicationEntity(ae); ae.setAssociationAcceptor(true); ae.addConnection(conn); this.destinations = Objects.requireNonNull(destinations); } public final void setPriority(int priority) { this.priority = priority; } private DicomServiceRegistry createServiceRegistry() { DicomServiceRegistry serviceRegistry = new DicomServiceRegistry(); serviceRegistry.addDicomService(new BasicCEchoSCP()); serviceRegistry.addDicomService(cstoreSCP); return serviceRegistry; } public void setStatus(int status) { this.status = status; } public void loadDefaultTransferCapability(URL transferCapabilityFile) { Properties p = new Properties(); try { if (transferCapabilityFile != null) { p.load(transferCapabilityFile.openStream()); } else { p.load(this.getClass().getResourceAsStream("sop-classes.properties")); } } catch (IOException e) { LOGGER.error("Cannot read sop-classes", e); } for (String cuid : p.stringPropertyNames()) { String ts = p.getProperty(cuid); TransferCapability tc = new TransferCapability(null, CLIUtils.toUID(cuid), TransferCapability.Role.SCP, CLIUtils.toUIDs(ts)); ae.addTransferCapability(tc); } } public ApplicationEntity getApplicationEntity() { return ae; } public Connection getConnection() { return conn; } public Device getDevice() { return device; } public void stop() { destinations.values().forEach(l -> l.forEach(ForwardDestination::stop)); } }