1 /*
2  *
3  *  Copyright (C) 2009-2020, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module:  dcmnet
15  *
16  *  Author:  Michael Onken
17  *
18  *  Purpose: General SCP class that can be used to implement derived SCP
19  *           applications.
20  *
21  */
22 
23 #ifndef SCP_H
24 #define SCP_H
25 
26 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
27 
28 #include "dcmtk/dcmdata/dctk.h" /* Covers most common dcmdata classes */
29 #include "dcmtk/dcmnet/assoc.h"
30 #include "dcmtk/dcmnet/dimse.h"  /* DIMSE network layer */
31 #include "dcmtk/dcmnet/diutil.h" /* for DCMNET_WARN() */
32 #include "dcmtk/dcmnet/scpcfg.h"
33 #include "dcmtk/oflog/oflog.h"
34 
35 // include this file in doxygen documentation
36 
37 /** @file scp.h
38  *  @brief general Service Class Provider (SCP) class
39  */
40 
41 /** Structure representing single process in multi-process mode
42  */
43 struct DCMTK_DCMNET_EXPORT DcmProcessSlotType
44 {
45     /// Name of peer
46     DIC_NODENAME peerName;
47     /// Calling AE title
48     DIC_AE callingAETitle;
49     /// Called AE title
50     DIC_AE calledAETitle;
51     /// Process ID
52     int processId;
53     /// Start time
54     time_t startTime;
55     /// Indicator if process has storage ability
56     OFBool hasStorageAbility;
57 };
58 
59 /** Action codes that can be given to DcmSCP to control behavior during SCP's operation.
60  *  Different hooks permit jumping into different phases of SCP operation.
61  */
62 enum DcmSCPActionType
63 {
64     /// No action defined
65     DCMSCP_ACTION_UNDEFINED,
66     /// Tell SCP to refuse association
67     DCMSCP_ACTION_REFUSE_ASSOCIATION
68 };
69 
70 /** Codes denoting a reason for refusing an association
71  */
72 enum DcmRefuseReasonType
73 {
74     /// Too many associations (SCP cannot handle a further association)
75     DCMSCP_TOO_MANY_ASSOCIATIONS,
76     /// Forking a new SCP process failed
77     DCMSCP_CANNOT_FORK,
78     /// Refusing association because of bad application context name
79     DCMSCP_BAD_APPLICATION_CONTEXT_NAME,
80     /// Refusing association because of disallowed connecting host
81     DCMSCP_CALLING_HOST_NOT_ALLOWED,
82     /// Refusing association because of unaccepted called AE title
83     DCMSCP_CALLED_AE_TITLE_NOT_RECOGNIZED,
84     /// Refusing association because of unaccepted calling AE title
85     DCMSCP_CALLING_AE_TITLE_NOT_RECOGNIZED,
86     /// Refusing association because SCP was forced to do so
87     DCMSCP_FORCED,
88     /// Refusing association because of missing Implementation Class UID
89     DCMSCP_NO_IMPLEMENTATION_CLASS_UID,
90     /// Refusing association because of no acceptable Presentation Contexts
91     DCMSCP_NO_PRESENTATION_CONTEXTS,
92     /// Refusing association because of internal error
93     DCMSCP_INTERNAL_ERROR
94 };
95 
96 /** Structure representing a single Presentation Context. Fields "reserved" and "result"
97  *  not included from DUL_PRESENTATIONCONTEXT, which served as the blueprint for this
98  *  structure.
99  */
100 struct DCMTK_DCMNET_EXPORT DcmPresentationContextInfo
101 {
DcmPresentationContextInfoDcmPresentationContextInfo102     DcmPresentationContextInfo()
103         : presentationContextID(0)
104         , abstractSyntax()
105         , proposedSCRole(0)
106         , acceptedSCRole(0)
107         , acceptedTransferSyntax()
108     {
109     }
110 
111     /// Presentation Context ID as proposed by SCU
112     Uint8 presentationContextID;
113     /// Abstract Syntax name (UID) as proposed by SCU
114     OFString abstractSyntax;
115     /// SCP role as proposed from SCU
116     Uint8 proposedSCRole;
117     /// Role accepted by SCP for this Presentation Context
118     Uint8 acceptedSCRole;
119     /// Transfer Syntax accepted for this Presentation Context (UID)
120     OFString acceptedTransferSyntax;
121     // Fields "reserved" and "result" not included from DUL_PRESENTATIONCONTEXT
122 };
123 
124 /** Base class for implementing a DICOM Service Class Provider (SCP). Derived classes can
125  *  add the presentation contexts they want to support, set further parameters (port, peer
126  *  host name, etc. as desired) and then call DcmSCP's listen() method to start the server.
127  *  For incoming associations and DIMSE messages, a derived class can define the behavior
128  *  of the server.
129  *  The DcmSCP base class does not support any presentation contexts per default.
130  *  In particular the Verification SOP class which every SCP must support,
131  *  is not added automatically in order to give the user full control over the
132  *  supported list of presentation contexts. However, if this class should negotiate
133  *  Verification, call setEnableVerification(). In that case DcmSCP will also
134  *  respond to related C-ECHO requests. Note that this cannot be reverted.
135  *  @warning This class is EXPERIMENTAL. Be careful to use it in production environment.
136  */
137 class DCMTK_DCMNET_EXPORT DcmSCP
138 {
139 
140 public:
141     /** Constructor. Initializes internal member variables.
142      */
143     DcmSCP();
144 
145     /** Virtual destructor, frees internal memory.
146      */
147     virtual ~DcmSCP();
148 
149     /** Starts providing the implemented services to SCUs.
150      *  After calling this method the SCP is listening for connection requests.
151      *  @return The result. Per default, the method only returns in case of fatal errors.
152      *          However, there are ways to stop listening in a controlled way:
153      *          <ul>
154      *          <li>In non-blocking mode, use stopAfterConnectionTimeout() in order
155      *          shut down after the TCP timeout set with setConnectionTimeout() has
156      *          occurred. In that case, the method returns with NET_EC_StopAfterConnectionTimeout.</li>
157      *          <li>In non-blocking and blocking mode, stopAfterCurrentAssociation() can
158      *          be used to return after an association has been handled and ended.
159      *          In that case, NET_EC_StopAfterAssociation is returned.</li>
160      *          </ul>
161      *         Other error codes include
162      *          <ul>
163      *          <li>NET_EC_InvalidSCPAssociationProfile: Returned if the SCP's presentation
164      *          context information is invalid (e.g. no presentation contexts have
165      *          been added).
166      *          </li>
167      *          <li>NET_EC_InsufficientPortPrivileges: Returned if the SCP is not
168      *          allowed to open the specified TCP port for listening. The reason
169      *          may be that you try to open a port number below 1024 on a Unix-like
170      *          system as non-root user.
171      *          <li>EC_setuidFailed: Returned (on Unix-like systems) if the DcmSCP
172      *          was not able to drop root privileges.
173      *          </ul>
174      */
175     virtual OFCondition listen();
176 
177     /** Alternative interface to providing the implemented services to SCUs.
178      *  This method opens the TCP port for incoming connections, drops root privileges
179      *  if necessary and then returns. The caller can then perform short other operations
180      *  and finally call acceptAssociations(). If any SCU tries to connect between
181      *  this method and the call to acceptAssociations(), they are placed on the
182      *  TCP listen backlog and will be handled by acceptAssociations() unless they
183      *  time out or the backlog overflows (the size of the backlog is defined by the
184      *  PRV_LISTENBACKLOG macro).
185      *  @return EC_Normal if successful, an error code otherwise.
186      */
187     virtual OFCondition openListenPort();
188 
189     /** Alternative interface to providing the implemented services to SCUs.
190      *  This method should be called after a call to openListenPort().
191      *  @return The result. Per default, the method only returns in case of fatal errors.
192      *    See documentation of listen() method on how to stop listening in a controlled way.
193      */
194     virtual OFCondition acceptAssociations();
195 
196     /* ************************************************************* */
197     /*             Set methods for configuring SCP behavior          */
198     /* ************************************************************* */
199 
200     /** Enables negotiation of the Verification SOP Class. It adds the Verification
201      *  SOP Class to the list of supported abstract syntaxes for the given profile.
202      *  All uncompressed transfer syntaxes are supported. If Verification SOP
203      *  class is added here, DcmSCP will respond to related C-ECHO requests. Note
204      *  that this cannot be reverted.
205      *  The default behavior of DcmSCP is not to support any SOP Class at all.
206      *  @param profile [in] The profile Verification SOP Class should
207      *                 be added to. The default is to add it to the
208      *                 DcmSCP's internal standard profile called
209      *                 "DEFAULT".
210      *  @return EC_Normal if Verification SOP Class could be added,
211      *          error otherwise.
212      */
213     OFCondition setEnableVerification(const OFString& profile = "DEFAULT");
214 
215     /** Add abstract syntax to presentation contexts the SCP is able to negotiate with SCUs.
216      *  @param abstractSyntax [in] The UID of the abstract syntax (e.g.\ SOP class) to add
217      *  @param xferSyntaxes   [in] List of transfer syntaxes (UIDs) that should be supported
218      *                             for the given abstract syntax name
219      *  @param requestorRole  [in] The role to be negotiated. This denotes the role of the
220      *                        the association requestor that this instance should accept, i.e. if
221      *                        set to ASC_SC_ROLE_SCP it means that the association requestor
222      *                        is allowed to negotiate the SCP role, thus, that this DcmSCP instance
223      *                        will be playing the SCU role for this abstract syntax. The default
224      *                        role (ASC_SC_ROLE_DEFAULT) implicates that this DcmSCP instance
225      *                        will be allowed to play the SCP role only, i.e. it will acknowledge
226      *                        such an explicit SCU role request, but also it will accept a proposal
227      *                        for the abstract syntax with no explicit role being proposed
228      *                        at all (since per default the requestor is SCU and the acceptor SCP).
229      *  @param profile        [in] The profile the abstract syntax should be added to. The
230      *                             default is to add it to the DcmSCP's internal standard
231      *                             profile called "DEFAULT".
232      *  @return EC_Normal if adding was successful, an error code otherwise
233      */
234     virtual OFCondition addPresentationContext(const OFString& abstractSyntax,
235                                                const OFList<OFString>& xferSyntaxes,
236                                                const T_ASC_SC_ROLE requestorRole = ASC_SC_ROLE_DEFAULT,
237                                                const OFString& profile           = "DEFAULT");
238 
239     /** Set SCP's TCP/IP listening port
240      *  @param port [in] The port number to listen on. Note that usually on Unix-like systems
241      *                   only root user is permitted to open ports below 1024.
242      */
243     void setPort(const Uint16 port);
244 
245     /** Set AE title of the server
246      *  @param aetitle [in] The AE title of the server. By default, all SCU association requests
247      *                      calling another AE title will be rejected. This behavior can be
248      *                      changed by using the setRespondWithCalledAETitle() method.
249      */
250     void setAETitle(const OFString& aetitle);
251 
252     /** Set SCP to use the called AE title from the SCU request for the response, i.e.\ the SCP
253      *  will always respond with setting it's own name to the one the SCU used for calling.
254      *  Overrides any AE title eventually set with setAETitle().
255      *  @param useCalled [in] If OFTrue, the SCP will use the called AE title from the request
256      *                        for responding. DcmSCP's default is OFFalse.
257      */
258     void setRespondWithCalledAETitle(const OFBool useCalled);
259 
260     /** Loads association configuration file
261      *  @param assocFile [in] The filename of the association configuration to be loaded. The
262      *                        association configuration file must be valid for an SCP.
263      *  @return EC_Normal if loading was successful, error otherwise
264      */
265     virtual OFCondition loadAssociationCfgFile(const OFString& assocFile);
266 
267     /** If an association profile should be selected, either by loading an association
268      *  configuration file or using the addPresentationContext() function, one of those can
269      *  be selected and checked for validity using this method.
270      *  @param profileName [in] The name of the association profile which must be configured
271      *                          before being selected here
272      *  @return EC_Normal if selecting/checking was successful, an error code otherwise
273      */
274     virtual OFCondition setAndCheckAssociationProfile(const OFString& profileName);
275 
276     /** Force every association request to be refused by SCP, no matter what the SCU is
277      *  offering
278      *  @param doRefuse [in] If OFTrue, every association is being refused. DcmSCP's default
279      *                       is not to refuse every association.
280      */
281     void forceAssociationRefuse(const OFBool doRefuse);
282 
283     /** Set maximum PDU size the SCP is able to receive. This size is sent in association
284      *  response message to SCU.
285      *  @param maxRecPDU [in] The maximum PDU size to use in bytes
286      */
287     void setMaxReceivePDULength(const Uint32 maxRecPDU);
288 
289     /** Set whether waiting for a TCP/IP connection should be blocking or non-blocking.
290      *  In non-blocking mode, the networking routines will wait for specified connection
291      *  timeout, see setConnectionTimeout() function. In blocking mode, no timeout is set
292      *  but the operating system's network routines will be used to read from the socket
293      *  for incoming data. In the worst case, this may be a long time until that call
294      *  returns. The default of DcmSCP is blocking mode.
295      *  @param blockingMode [in] Either DUL_BLOCK for blocking mode or DUL_NOBLOCK
296      *                           for non-blocking mode
297      */
298     void setConnectionBlockingMode(const DUL_BLOCKOPTIONS blockingMode);
299 
300     /** Set whether DIMSE messaging should be blocking or non-blocking. In non-blocking mode,
301      *  the networking routines will wait for DIMSE messages for the specified DIMSE timeout
302      *  time, see setDIMSETimeout() function. In blocking mode, no timeout is set but the
303      *  operating system's network routines will be used to read from the socket for new data.
304      *  In the worst case, this may be a long time until that call returns. The default of
305      *  DcmSCP is blocking mode.
306      *  @param blockingMode [in] Either DIMSE_BLOCKING for blocking mode or DIMSE_NONBLOCKING
307      *                           for non-blocking mode
308      */
309     void setDIMSEBlockingMode(const T_DIMSE_BlockingMode blockingMode);
310 
311     /** Set the timeout to be waited for incoming DIMSE message packets. This is only relevant
312      *  for DIMSE blocking mode messaging (see also setDIMSEBlockingMode()).
313      *  @param dimseTimeout [in] DIMSE receive timeout in seconds
314      */
315     void setDIMSETimeout(const Uint32 dimseTimeout);
316 
317     /** Set the timeout used during ACSE messaging protocol.
318      *  @param acseTimeout [in] ACSE timeout in seconds.
319      */
320     void setACSETimeout(const Uint32 acseTimeout);
321 
322     /** Set the timeout that should be waited for connection requests.
323      *  Only relevant in non-blocking mode (default).
324      *  @param timeout [in] TCP/IP connection timeout in seconds.
325      */
326     void setConnectionTimeout(const Uint32 timeout);
327 
328     /** Set whether to show presentation contexts in verbose or debug mode
329      *  @param mode [in] Show presentation contexts in verbose mode if OFTrue. By default, the
330      *                   presentation contexts are shown in debug mode.
331      */
332     void setVerbosePCMode(const OFBool mode);
333 
334     /** Enables or disables looking up the host name from a connecting system.
335      *  Note that this sets a GLOBAL flag in DCMTK, i.e. the behavior changes
336      *  for all servers. This should be changed in the future.
337      *  @param mode [in] OFTrue, if host name lookup should be enabled, OFFalse for disabling it.
338      */
339     void setHostLookupEnabled(const OFBool mode);
340 
341     /** Set the mode that specifies whether the progress of sending and receiving DIMSE messages
342      *  is notified by calling notifySENDProgress() and notifyRECEIVEProgress(), respectively.
343      *  The progress notification is enabled by default.
344      *  @param mode [in] Disable progress notification if OFFalse
345      */
346     void setProgressNotificationMode(const OFBool mode);
347 
348     /** Option to always accept a default role as association acceptor.
349      *  If OFFalse (default) the acceptor will reject a presentation context proposed
350      *  with Default role (no role selection at all) when it is configured for role
351      *  SCP only. If this option is set to OFTrue then such presentation contexts will
352      *  be accepted in Default role (i.e. acceptor does not return role selection for
353      *  this presentation context at all). Overall, if set to OFTrue, there are no
354      *  requestor proposals possible that lead to a complete rejection of a presentation
355      *  context. See also role documentation in dul.h.
356      *  @param  enabled If OFTrue, do not reject Default role proposals when configured
357      *          for SCP role. OFFalse (default behaviour): Reject such proposals.
358      */
359     void setAlwaysAcceptDefaultRole(const OFBool enabled);
360 
361     /* Get methods for SCP settings */
362 
363     /** Returns TCP/IP port number SCP listens for new connection requests
364      *  @return The port number
365      */
366     Uint16 getPort() const;
367 
368     /** Returns SCP's own AE title. Only used if the SCP is not configured to respond with the
369      *  called AE title the SCU uses for association negotiation, see setRespondWithCalledAETitle().
370      *  @return The configured AE title
371      */
372     const OFString& getAETitle() const;
373 
374     /** Returns whether SCP uses the called AE title from SCU requests to respond to connection
375      *  requests instead of a configured AE title
376      *  @return OFTrue, if the SCU's calling AE title is utilized, OFFalse otherwise
377      */
378     OFBool getRespondWithCalledAETitle() const;
379 
380     /** Returns whether SCP should refuse any association request no matter what the SCU proposes
381      *  @return OFTrue, if SCP is configured to refuse every association
382      */
383     OFBool getRefuseAssociation() const;
384 
385     /** Returns maximum PDU length configured to be received by SCP
386      *  @return Maximum PDU length in bytes
387      */
388     Uint32 getMaxReceivePDULength() const;
389 
390     /** Returns whether receiving of TCP/IP connection requests is done in blocking or
391      *  unblocking mode
392      *  @return DUL_BLOCK if in blocking mode, otherwise DUL_NOBLOCK
393      */
394     DUL_BLOCKOPTIONS getConnectionBlockingMode() const;
395 
396     /** Returns whether receiving of DIMSE messages is done in blocking or unblocking mode
397      *  @return DIMSE_BLOCKING if in blocking mode, otherwise DIMSE_NONBLOCKING
398      */
399     T_DIMSE_BlockingMode getDIMSEBlockingMode() const;
400 
401     /** Returns DIMSE timeout (only applicable in blocking mode)
402      *  @return DIMSE timeout in seconds
403      */
404     Uint32 getDIMSETimeout() const;
405 
406     /** Returns ACSE timeout
407      *  @return ACSE timeout in seconds
408      */
409     Uint32 getACSETimeout() const;
410 
411     /** Returns connection timeout
412      *  @return TCP/IP connection timeout in seconds
413      */
414     Uint32 getConnectionTimeout() const;
415 
416     /** Returns the verbose presentation context mode configured specifying whether details on
417      *  the presentation contexts (negotiated during association setup) should be shown in
418      *  verbose or debug mode. The latter is the default.
419      *  @return The verbose presentation context mode configured
420      */
421     OFBool getVerbosePCMode() const;
422 
423     /** Returns whether a connecting system's host name is looked up.
424      *  @return OFTrue, if host name lookup is enabled, OFFalse otherwise
425      */
426     OFBool getHostLookupEnabled() const;
427 
428     /** Returns the mode that specifies whether the progress of sending and receiving DIMSE
429      *  messages is notified by calling notifySENDProgress() and notifyRECEIVEProgress(),
430      *  respectively. The progress notification is enabled by default.
431      *  @return The current progress notification mode, enabled if OFTrue
432      */
433     OFBool getProgressNotificationMode() const;
434 
435     /** Get access to the configuration of the SCP. Note that the functionality
436      *  on the configuration object is shadowed by other API functions of DcmSCP.
437      *  The existing functions are provided in order to not break users of this
438      *  "older" API where no configuration object existed.
439      *  @return a reference to the DcmSCPConfig object used by this DcmSCP object.
440      */
441     virtual DcmSCPConfig& getConfig();
442 
443     /** Set the DcmSCPConfig object to use for configuring this DcmSCP object.
444      *  A deep copy is performed.
445      *  @param  config The configuration to use.
446      *  @return EC_Normal if configuration can be used. The configuration can
447      *          only be changed if the SCP is not yet connected, otherwise
448      *          NET_EC_AlreadyConnected is returned.
449      *
450      */
451     virtual OFCondition setConfig(const DcmSCPConfig& config);
452 
453     /* ************************************************************* */
454     /*  Methods for receiving runtime (i.e. connection time) infos   */
455     /* ************************************************************* */
456 
457     /** Returns whether SCP is currently connected. If in multi-process mode, the "father"
458      *  process should always return false here, because connection is always handled by child
459      *  process.
460      *  @return OFTrue, if SCP is currently connected to calling SCU
461      */
462     OFBool isConnected() const;
463 
464     /** Returns number of associations currently running. Only applicable in Unix-like
465      *  operating systems. Can only be greater than one when running in multi-process mode.
466      *  @return Number of currently running associations
467      */
468     Uint16 numAssociations() const;
469 
470     /** Returns AE title the SCU used as called AE title in association request
471      *  @return AE title the SCP was called with. Empty string if SCP is currently not
472      *          connected.
473      */
474     OFString getCalledAETitle() const;
475 
476     /** Returns AE title (calling AE title) the SCU used for association request
477      *  @return Calling AE title of SCU. Empty string if SCP is currently not connected.
478      */
479     OFString getPeerAETitle() const;
480 
481     /** Returns IP address of connected SCU
482      *  @return IP address of connected SCU. Empty string if SCP is currently not connected.
483      */
484     OFString getPeerIP() const;
485 
486     /** Returns maximum PDU size the communication peer (i.e.\ the SCU) is able to receive
487      *  @return Maximum PDU size the SCU is able to receive. Returns zero if SCP is currently
488      *          not connected.
489      */
490     Uint32 getPeerMaxPDULength() const;
491 
492     /// DcmThreadSCP needs access to configuration (m_cfg), at least
493     friend class DcmThreadSCP;
494 
495 protected:
496     /* ********************************************* */
497     /*  Functions available to derived classes only  */
498     /* ********************************************* */
499 
500     /** This call returns the presentation context belonging to the given
501      *  presentation context ID.
502      *  @param presID         [in]  The presentation context ID to look for
503      *  @param abstractSyntax [out] The abstract syntax (UID) for that ID.
504      *                              Empty, if such a presentation context does not exist.
505      *  @param transferSyntax [out] The transfer syntax (UID) for that ID.
506      *                              Empty, if such a presentation context does not exist.
507      */
508     void findPresentationContext(const T_ASC_PresentationContextID presID,
509                                  OFString& abstractSyntax,
510                                  OFString& transferSyntax);
511 
512     /** Aborts the current association by sending an A-ABORT request to the SCU.
513      *  This method allows derived classes to abort an association in case of severe errors.
514      *  @return status, EC_Normal if successful, an error code otherwise
515      */
516     virtual OFCondition abortAssociation();
517 
518     /* *********************************************************************** */
519     /*  Functions particularly interesting for overwriting in derived classes  */
520     /* *********************************************************************** */
521 
522     /** Handle incoming command set and react accordingly, e.g.\ sending response via
523      *  DIMSE_sendXXXResponse(). The standard handler only knows how to handle
524      *  a C-ECHO request message (by calling handleEchoRequest()) if it is sent on a
525      *  presentation context configured for the Verification SOP Class.
526      *  This function is most likely to be implemented by a derived class
527      *  implementing a specific SCP behavior.
528      *  @param incomingMsg The DIMSE message received
529      *  @param presInfo Additional information on the Presentation Context used
530      *  @return EC_Normal if the message could be handled, error if not. Especially
531      *          DIMSE_BADCOMMANDTYPE should be returned if there is no handler for
532      *          this particular type of DIMSE message. E.g. the default handler in
533      *          DcmSCP only handles C-ECHO requests and, therefore, returns
534      *          DIMSE_BADCOMMANDTYPE otherwise.
535      */
536     virtual OFCondition handleIncomingCommand(T_DIMSE_Message* incomingMsg, const DcmPresentationContextInfo& presInfo);
537 
538     /** Overwrite this function to be notified about an incoming association request.
539      *  The standard handler only outputs some information to the logger.
540      *  @param params The association parameters that were received.
541      *  @param desiredAction The desired action how to handle this association request.
542      */
543     virtual void notifyAssociationRequest(const T_ASC_Parameters& params, DcmSCPActionType& desiredAction);
544 
545     /** Overwrite this function if called AE title should undergo checking. If
546      *  OFTrue is returned, the AE title is accepted and processing is continued.
547      *  In case of OFFalse, the SCP will refuse the incoming association with
548      *  error "Called Application Entity Title Not Recognized".
549      *  The standard handler always returns OFTrue.
550      *  @param calledAE The called AE title the SCU used that should be checked
551      *  @return OFTrue, if AE title is accepted, OFFalse otherwise
552      */
553     virtual OFBool checkCalledAETitleAccepted(const OFString& calledAE);
554 
555     /** Overwrite this function if calling AE title should undergo checking. If
556      *  OFTrue is returned, the AE title is accepted and processing is continued.
557      *  In case of OFFalse, the SCP will refuse the incoming association with
558      *  error "Calling Application Entity Title Not Recognized".
559      *  The standard handler always returns OFTrue.
560      *  @param callingAE The calling AE title the SCU used that should be checked
561      *  @return OFTrue, if AE title is accepted, OFFalse otherwise
562      */
563     virtual OFBool checkCallingAETitleAccepted(const OFString& callingAE);
564 
565     /** Overwrite this function if calling IP / host name should undergo checking.
566      *  If OFTrue is returned, the host is accepted and processing is continued.
567      *  In case of OFFalse, the SCP will refuse the incoming association with
568      *  an error. The standard handler always returns OFTrue.
569      *  @param hostOrIP The IP of the client to check.
570      *  @return OFTrue, if IP/host is accepted, OFFalse otherwise
571      */
572     virtual OFBool checkCallingHostAccepted(const OFString& hostOrIP);
573 
574     /** Overwrite this function to be notified about an incoming association request.
575      *  The standard handler only outputs some information to the logger.
576      */
577     virtual void notifyAssociationAcknowledge();
578 
579     /** Overwrite this function to be notified about an incoming association release request.
580      *  The standard handler only outputs some information to the logger.
581      */
582     virtual void notifyReleaseRequest();
583 
584     /** Overwrite this function to be notified about an incoming association abort request.
585      *  The standard handler only outputs some information to the logger.
586      */
587     virtual void notifyAbortRequest();
588 
589     /** Overwrite this function to be notified when an association is terminated.
590      *  The standard handler only outputs some information to the logger.
591      */
592     virtual void notifyAssociationTermination();
593 
594     /** Overwrite this function to be notified about a connection timeout in
595      *  non-blocking mode (see setConnectionBlockingMode() and setConnectionTimeout()
596      *  methods). In blocking mode, this method has no effect since it's never called.
597      *  The standard handler only outputs some information to the TRACE logger.
598      */
599     virtual void notifyConnectionTimeout();
600 
601     /** Overwrite this function to be notified when a DIMSE error occurs.
602      *  The standard handler only outputs error information to the logger.
603      *  @param cond [in] The DIMSE error occurred.
604      */
605     virtual void notifyDIMSEError(const OFCondition& cond);
606 
607     /** This function is called while sending DIMSE messages, i.e.\ on each PDV of a dataset.
608      *  The default implementation just prints a TRACE message on the number of bytes sent so
609      *  far. By overwriting this method, the progress of the send process can be shown to the
610      *  user in a more appropriate way. The progress notification can also be disabled
611      *  completely by calling setProgressNotificationMode().
612      *  @param byteCount [in] Number of bytes sent so far
613      */
614     virtual void notifySENDProgress(const unsigned long byteCount);
615 
616     /** This function is called while receiving DIMSE messages, i.e.\ on each PDV of a dataset.
617      *  The default implementation just prints a TRACE message on the number of bytes received
618      *  so far. By overwriting this method, the progress of the receive process can be shown to
619      *  the user in a more appropriate way. The progress notification can also be disabled
620      *  completely by calling setProgressNotificationMode().
621      *  @param byteCount [in] Number of bytes received so far
622      */
623     virtual void notifyRECEIVEProgress(const unsigned long byteCount);
624 
625     /** This method can be used to return from the listen() loop in a controlled way.
626      *  In order to use it, it must be overwritten in a derived class. As long as no
627      *  severe error occurs and this method returns OFFalse, the listen() method will wait
628      *  for incoming associations in an infinite loop.
629      *  @return The standard handler always returns OFFalse
630      */
631     virtual OFBool stopAfterCurrentAssociation();
632 
633     /** This method can be used to return from the listen() loop in a controlled way.
634      *  In order to use it, it must be overwritten in a derived class. As long as no
635      *  severe error occurs and this method returns OFFalse, the listen() method will wait
636      *  for incoming associations in an infinite loop. If this method returns OFTrue, the
637      *  SCP will return from the listen() loop after a connection timeout occurs (see
638      *  setConnectionTimeout() method). In blocking mode (see setConnectionBlockingMode()
639      *  method), this method has no effect (it's never called) since the underlying
640      *  routines will wait forever for an incoming TCP connection.
641      *  @return The standard handler always returns OFFalse
642      */
643     virtual OFBool stopAfterConnectionTimeout();
644 
645     // -- C-ECHO --
646 
647     /** Standard handler for Verification Service Class (DICOM Echo). Returns echo response
648      *  (i.e. whether C-ECHO could be responded to with status success).
649      *  @param reqMessage [in] The C-ECHO request message that was received
650      *  @param presID     [in] The presentation context to be used. By default, the
651      *                         presentation context of the request is used.
652      *  @return status, EC_Normal if successful, an error code otherwise
653      */
654     virtual OFCondition handleECHORequest(T_DIMSE_C_EchoRQ& reqMessage, const T_ASC_PresentationContextID presID);
655 
656     // --- C-STORE --
657 
658     /** Receive C-STORE request on the currently opened association, store the
659      *  accompanying dataset in memory and send a corresponding response. Calls
660      *  checkSTORERequest() in order to determine the DIMSE status code to be used for
661      *  the C-STORE response.
662      *  @note This handler receives the dataset belonging the C-STORE request completely
663      *    in memory. If very large datasets are expected, another handler should be
664      *    implemented that calls the receiveSTORERequest() method with a filename.
665      *  @param reqMessage [in]    The C-STORE request message that was received
666      *  @param presID     [in]    The presentation context to be used. By default, the
667      *                            presentation context of the request is used.
668      *  @param reqDataset [inout] Pointer to data structure where the received dataset
669      *                            should be stored. If NULL, a new dataset is created,
670      *                            which has to be deleted by the caller.
671      *  @return status, EC_Normal if successful, an error code otherwise
672      */
673     virtual OFCondition handleSTORERequest(T_DIMSE_C_StoreRQ& reqMessage,
674                                            const T_ASC_PresentationContextID presID,
675                                            DcmDataset*& reqDataset);
676 
677     /** Receive C-STORE request (and store accompanying dataset in memory).
678      *  For very large datasets, the other receiveSTORERequest() method should be used
679      *  because it stores the received dataset directly to file.
680      *  @param reqMessage [in]    The C-STORE request message that was received
681      *  @param presID     [in]    The presentation context to be used. By default, the
682      *                            presentation context of the request is used.
683      *  @param reqDataset [inout] Pointer to data structure where the received dataset
684      *                            should be stored. If NULL, a new dataset is created,
685      *                            which has to be deleted by the caller.
686      *  @return status, EC_Normal if successful, an error code otherwise
687      */
688     virtual OFCondition receiveSTORERequest(T_DIMSE_C_StoreRQ& reqMessage,
689                                             const T_ASC_PresentationContextID presID,
690                                             DcmDataset*& reqDataset);
691 
692     /** Receive C-STORE request (and store accompanying dataset directly to file).
693      *  The dataset is stored exactly as received, i.e. without any conversions.
694      *  @param reqMessage [in] The C-STORE request message that was received
695      *  @param presID     [in] The presentation context to be used. By default, the
696      *                         presentation context of the request is used.
697      *  @param filename   [in] The filename used to store the received dataset
698      *  @return status, EC_Normal if successful, an error code otherwise
699      */
700     virtual OFCondition receiveSTORERequest(T_DIMSE_C_StoreRQ& reqMessage,
701                                             const T_ASC_PresentationContextID presID,
702                                             const OFString& filename);
703 
704     /** Respond to the C-STORE request (with details from the request message)
705      *  @param presID        [in] The presentation context ID to respond to
706      *  @param reqMessage    [in] The C-STORE request that should be responded to
707      *  @param rspStatusCode [in] The response status code. 0 means success,
708      *                            others can found in the DICOM standard.
709      *  @return EC_Normal, if responding was successful, an error code otherwise
710      */
711     virtual OFCondition sendSTOREResponse(const T_ASC_PresentationContextID presID,
712                                           const T_DIMSE_C_StoreRQ& reqMessage,
713                                           const Uint16 rspStatusCode);
714 
715     /** Respond to the C-STORE request (with given details)
716      *  @param presID         [in] The presentation context ID to respond to
717      *  @param messageID      [in] The message ID being responded to
718      *  @param sopClassUID    [in] The affected SOP class UID
719      *  @param sopInstanceUID [in] The affected SOP instance UID
720      *  @param rspStatusCode  [in] The response status code. 0 means success,
721      *                             others can found in the DICOM standard.
722      *  @param statusDetail   [in] The status detail of the response (if desired).
723      *  @return EC_Normal, if responding was successful, an error code otherwise
724      */
725     virtual OFCondition sendSTOREResponse(const T_ASC_PresentationContextID presID,
726                                           const Uint16 messageID,
727                                           const OFString& sopClassUID,
728                                           const OFString& sopInstanceUID,
729                                           const Uint16 rspStatusCode,
730                                           DcmDataset* statusDetail = NULL);
731 
732     /** Check given C-STORE request and dataset for validity. This method is called by
733      *  handleSTORERequest() before sending the response in order to determine the DIMSE
734      *  status code to be used for the response message.
735      *  @param reqMessage [in] The C-STORE request message data structure
736      *  @param reqDataset [in] The C-STORE request dataset received. Might be NULL.
737      *  @return DIMSE status code to be used for the C-STORE response.
738      *          Always returns STATUS_Success (0). Derived classes should, therefore,
739      *          overwrite this method and return a more appropriate value based on the
740      *          result of the checks performed.
741      */
742     virtual Uint16 checkSTORERequest(T_DIMSE_C_StoreRQ& reqMessage, DcmDataset* reqDataset);
743 
744     // -- C-FIND --
745 
746     /** Receive C-FIND request
747      *  @param reqMessage [in]  The C-FIND request message that was received
748      *  @param presID     [in]  The presentation context to be used. By default, the
749      *                          presentation context of the request is used.
750      *  @param reqDataset [out] Pointer to the dataset received
751      *  @return status, EC_Normal if successful, an error code otherwise
752      */
753     virtual OFCondition
754     receiveFINDRequest(T_DIMSE_C_FindRQ& reqMessage, const T_ASC_PresentationContextID presID, DcmDataset*& reqDataset);
755 
756     /** Handle C-FIND request. This function is deprecated and will be removed in
757      *  the future. For now it calls receiveFINDRequest() which should be used
758      *  instead.
759      *  @param reqMessage [in]  The C-FIND request message that was received
760      *  @param presID     [in]  The presentation context to be used. By default, the
761      *                          presentation context of the request is used.
762      *  @param reqDataset [out] Pointer to the dataset received
763      *  @return status, EC_Normal if successful, an error code otherwise
764      */
765     virtual OFCondition
handleFINDRequest(T_DIMSE_C_FindRQ & reqMessage,const T_ASC_PresentationContextID presID,DcmDataset * & reqDataset)766     handleFINDRequest(T_DIMSE_C_FindRQ& reqMessage, const T_ASC_PresentationContextID presID, DcmDataset*& reqDataset)
767     {
768         DCMNET_WARN("handleFINDRequest() is deprecated, use receiveFINDRequest() instead");
769         return receiveFINDRequest(reqMessage, presID, reqDataset);
770     }
771 
772     /** Respond to the C-FIND request
773      *  @param presID        [in] The presentation context ID to respond to
774      *  @param messageID     [in] The message ID being responded to
775      *  @param sopClassUID   [in] The affected SOP class UID
776      *  @param rspDataset    [in] The response dataset
777      *  @param rspStatusCode [in] The response status code. 0 means success,
778      *                            others can found in the DICOM standard.
779      *  @param statusDetail  [in] Any status (must fit response code), if desired
780      *  @return EC_Normal, if responding was successful, an error code otherwise
781      */
782     virtual OFCondition sendFINDResponse(const T_ASC_PresentationContextID presID,
783                                          const Uint16 messageID,
784                                          const OFString& sopClassUID,
785                                          DcmDataset* rspDataset,
786                                          const Uint16 rspStatusCode,
787                                          DcmDataset* statusDetail = NULL);
788 
789     /** Check for C-CANCEL. This is needed for example for a Query/Retrieve
790      *  server that is in the middle of returning C-FIND responses to a
791      *  client and has to perform a regular check whether the client sent a
792      *  C-CANCEL in order to stop receiving C-FIND responses.
793      *  @param presID    [in] The presentation context ID where C-CANCEL is
794      *                        expected.
795      *  @param messageID [in] The "message ID responded to" that the client
796      *                        is expected to use (usually this is the message
797      *                        ID used in the original FIND/GET/MOVE request).
798      *  @return EC_Normal, if C-CANCEL was received. DIMSE_NODATAAVAILABLE if no
799      *          DIMSE message (or anything) was received from the client.
800      *          DIMSEC_UNEXPECTEDREQUEST if command is received but it is not
801      *          a C-CANCEL message, or the message ID used by client is wrong
802      *          (message ID must be the one from the original FIND/MOVE/GET
803      *          request).
804      *          DIMSEC_INVALIDPRESENTATIONCONTEXTID if the wrong presentation
805      *          context (ID) was used for sending. Other low level errors
806      *          (e.g. DIMSEC_UNEXPECTEDPDVTYPE) could be returned, too.
807      */
808     virtual OFCondition checkForCANCEL(T_ASC_PresentationContextID presID, const Uint16 messageID);
809 
810     // --- C-MOVE --
811 
812     /** Receive C-MOVE request on the currently active association.
813      *  @param reqMessage [in]  The C-MOVE request message that was received
814      *  @param presID     [in]  The presentation context to be used. By default, the
815      *                          presentation context of the request is used.
816      *  @param reqDataset [out] Pointer to the dataset received
817      *  @param moveDest   [out] The move destination where to send the instances
818      *  @return status, EC_Normal if successful, an error code otherwise
819      */
820     virtual OFCondition receiveMOVERequest(T_DIMSE_C_MoveRQ& reqMessage,
821                                            const T_ASC_PresentationContextID presID,
822                                            DcmDataset*& reqDataset,
823                                            OFString& moveDest);
824 
825     /** Receive C-MOVE request on the currently active association. This function
826      *  is deprecated and will be removed in the future. For now it calls
827      *  receiveMOVERequest() which should be used instead.
828      *  @param reqMessage [in]  The C-MOVE request message that was received
829      *  @param presID     [in]  The presentation context to be used. By default, the
830      *                          presentation context of the request is used.
831      *  @param reqDataset [out] Pointer to the dataset received
832      *  @param moveDest   [out] The move destination where to send the instances
833      *  @return status, EC_Normal if successful, an error code otherwise
834      */
handleMOVERequest(T_DIMSE_C_MoveRQ & reqMessage,const T_ASC_PresentationContextID presID,DcmDataset * & reqDataset,OFString & moveDest)835     virtual OFCondition handleMOVERequest(T_DIMSE_C_MoveRQ& reqMessage,
836                                           const T_ASC_PresentationContextID presID,
837                                           DcmDataset*& reqDataset,
838                                           OFString& moveDest)
839     {
840         DCMNET_WARN("handleMOVERequest() is deprecated, use receiveMOVERequest() instead");
841         return receiveMOVERequest(reqMessage, presID, reqDataset, moveDest);
842     }
843 
844     /** Respond to the C-MOVE request
845      *  @param presID        [in] The presentation context ID to respond to
846      *  @param messageID     [in] The message ID being responded to
847      *  @param sopClassUID   [in] The affected SOP class UID
848      *  @param rspDataset    [in] The response dataset
849      *  @param rspStatusCode [in] The status code of the response. 0 means success,
850      *                            others can found in the DICOM standard.
851      *  @param statusDetail  [in] The status detail of the response (if desired).
852      *  @param numRemain     [in] Number of remaining sub-operations.
853      *                            Required for Pending status codes, often optional otherwise.
854      *                            Sent if one of the num parameters is not 0.
855      *  @param numComplete   [in] Number of completed sub-operations.
856      *                            Required for Pending status codes, often optional otherwise.
857      *                            Sent if one of the num parameters is not 0.
858      *  @param numFail       [in] Number of failed sub-operations.
859      *                            Required for Pending status codes, often optional otherwise.
860      *                            Sent if one of the num parameters is not 0.
861      *  @param numWarn       [in] Number of warning sub-operations.
862      *                            Required for Pending status codes, often optional otherwise.
863      *                            Sent if one of the num parameters is not 0.
864      *  @return EC_Normal, if responding was successful, an error code otherwise
865      */
866     virtual OFCondition sendMOVEResponse(const T_ASC_PresentationContextID presID,
867                                          const Uint16 messageID,
868                                          const OFString& sopClassUID,
869                                          DcmDataset* rspDataset,
870                                          const Uint16 rspStatusCode,
871                                          DcmDataset* statusDetail = NULL,
872                                          const Uint16 numRemain   = 0,
873                                          const Uint16 numComplete = 0,
874                                          const Uint16 numFail     = 0,
875                                          const Uint16 numWarn     = 0);
876 
877     // -- N-ACTION --
878 
879     /** Receive N-ACTION request on the currently opened association.
880      *  @param reqMessage   [in]  The N-ACTION request message that was received
881      *  @param presID       [in]  The presentation context to be used. By default, the
882      *                            presentation context of the request is used.
883      *  @param reqDataset   [out] Pointer to the dataset received
884      *  @param actionTypeID [out] Action Type ID from the command set received
885      *  @return status, EC_Normal if successful, an error code otherwise
886      */
887     virtual OFCondition receiveACTIONRequest(T_DIMSE_N_ActionRQ& reqMessage,
888                                              const T_ASC_PresentationContextID presID,
889                                              DcmDataset*& reqDataset,
890                                              Uint16& actionTypeID);
891 
892     /** Receive N-ACTION request on the currently opened association. This
893      *  function is deprecated and will be removed in the future. For now it calls
894      *  receiveACTIONRequest() which should be used instead.
895      *  @param reqMessage   [in]  The N-ACTION request message that was received
896      *  @param presID       [in]  The presentation context to be used. By default, the
897      *                            presentation context of the request is used.
898      *  @param reqDataset   [out] Pointer to the dataset received
899      *  @param actionTypeID [out] Action Type ID from the command set received
900      *  @return status, EC_Normal if successful, an error code otherwise
901      */
handleACTIONRequest(T_DIMSE_N_ActionRQ & reqMessage,const T_ASC_PresentationContextID presID,DcmDataset * & reqDataset,Uint16 & actionTypeID)902     virtual OFCondition handleACTIONRequest(T_DIMSE_N_ActionRQ& reqMessage,
903                                             const T_ASC_PresentationContextID presID,
904                                             DcmDataset*& reqDataset,
905                                             Uint16& actionTypeID)
906     {
907         DCMNET_WARN("handleACTIONRequest() is deprecated, use receiveACTIONRequest() instead");
908         return receiveACTIONRequest(reqMessage, presID, reqDataset, actionTypeID);
909     }
910 
911     /** Respond to the N-ACTION request
912      *  @param presID         [in] The presentation context ID to respond to
913      *  @param messageID      [in] The message ID being responded to
914      *  @param sopClassUID    [in] The affected SOP class UID
915      *  @param sopInstanceUID [in] The affected SOP instance UID
916      *  @param rspStatusCode  [in] The response status code. 0 means success,
917      *                             others can found in the DICOM standard.
918      *  @return EC_Normal, if responding was successful, an error code otherwise
919      */
920     virtual OFCondition sendACTIONResponse(const T_ASC_PresentationContextID presID,
921                                            const Uint16 messageID,
922                                            const OFString& sopClassUID,
923                                            const OFString& sopInstanceUID,
924                                            const Uint16 rspStatusCode);
925 
926     // -- N-EVENT-REPORT --
927 
928     /** Receive N-EVENT-REPORT request on the currently opened association and send a
929      *  corresponding response. Calls checkEVENTREPORTRequest() in order to determine the
930      *  DIMSE status code to be used for the N-EVENT-REPORT response.
931      *  @param reqMessage  [in]  The N-EVENT-REPORT request message that was received
932      *  @param presID      [in]  The presentation context to be used. By default, the
933      *                           presentation context of the request is used.
934      *  @param reqDataset  [out] Pointer to the dataset received
935      *  @param eventTypeID [out] Event Type ID from the command set received
936      *  @return status, EC_Normal if successful, an error code otherwise
937      */
938     virtual OFCondition handleEVENTREPORTRequest(T_DIMSE_N_EventReportRQ& reqMessage,
939                                                  const T_ASC_PresentationContextID presID,
940                                                  DcmDataset*& reqDataset,
941                                                  Uint16& eventTypeID);
942 
943     /** Send N-EVENT-REPORT request on the current association and receive a corresponding
944      *  response.
945      *  @param presID         [in]  The ID of the presentation context to be used for sending
946      *                              the request message. Should not be 0.
947      *  @param sopInstanceUID [in]  The requested SOP Instance UID
948      *  @param messageID      [in]  The request message ID
949      *  @param eventTypeID    [in]  The event type ID to be used
950      *  @param reqDataset     [in]  The request dataset to be sent
951      *  @param rspStatusCode  [out] The response status code received. 0 means success,
952      *                              others can be found in the DICOM standard.
953      *  @return EC_Normal if request could be issued and response was received successfully,
954      *          an error code otherwise
955      */
956     virtual OFCondition sendEVENTREPORTRequest(const T_ASC_PresentationContextID presID,
957                                                const OFString& sopInstanceUID,
958                                                const Uint16 messageID,
959                                                const Uint16 eventTypeID,
960                                                DcmDataset* reqDataset,
961                                                Uint16& rspStatusCode);
962 
963     /** Check given N-EVENT-REPORT request and dataset for validity. This method is called by
964      *  handleEVENTREPORTRequest() before sending the response in order to determine the
965      *  DIMSE status code to be used for the response message.
966      *  @param reqMessage [in] The N-EVENT-REPORT request message data structure
967      *  @param reqDataset [in] The N-EVENT-REPORT request dataset received. Might be NULL.
968      *  @return DIMSE status code to be used for the N-EVENT-REPORT response.
969      *          Always returns STATUS_Success (0). Derived classes should, therefore,
970      *          overwrite this method and return a more appropriate value based on the
971      *          result of the checks performed.
972      */
973     virtual Uint16 checkEVENTREPORTRequest(T_DIMSE_N_EventReportRQ& reqMessage, DcmDataset* reqDataset);
974 
975     /* ********************************************************************* */
976     /*  Further functions and member variables                               */
977     /* ********************************************************************* */
978 
979     /** Helper function to return presentation context information by given
980      *  presentation context ID.
981      *  @param head The presentation context list
982      *  @param presentationContextID The presentation context ID
983      *  @return The presentation context information
984      */
985     static DUL_PRESENTATIONCONTEXT* findPresentationContextID(LST_HEAD* head,
986                                                               T_ASC_PresentationContextID presentationContextID);
987 
988     /** Helper function to return presentation context information by given
989      *  presentation context ID.
990      *  @param assoc The association to search
991      *  @param presID The presentation context ID
992      *  @param presInfo The result presentation context information, if found
993      *  @return OFTrue if presentation context with ID could be found, OFFalse
994      *          otherwise
995      */
996     static OFBool getPresentationContextInfo(const T_ASC_Association* assoc,
997                                              const Uint8 presID,
998                                              DcmPresentationContextInfo& presInfo);
999 
1000     /** This function takes care of receiving, negotiating and accepting/refusing an
1001      *  association request. Additionally, if negotiation was successful, it handles any
1002      *  incoming DIMSE commands by calling handleAssociation(). An error is only returned, if
1003      *  something goes wrong. Therefore, refusing an association because of wrong application
1004      *  context name or no common presentation contexts with the SCU does NOT lead to an error.
1005      *  @param network [in] Contains network parameters
1006      *  @return EC_Normal, if everything went fine, DUL_NOASSOCIATIONREQUEST if a timeout
1007      *          occurs in non-blocking mode, DIMSE_ILLEGALASSOCIATION or ASC_NULLKEY if
1008      *          severe internal errors occurred (should not happen)
1009      */
1010     virtual OFCondition waitForAssociationRQ(T_ASC_Network* network);
1011 
1012     /** Actually process association request.
1013      *  @return EC_Normal if association could be processed, ASC_NULLKEY otherwise
1014      *          (only if internal association structure is invalid, should never happen)
1015      */
1016     virtual OFCondition processAssociationRQ();
1017 
1018     /** This function checks all presentation contexts proposed by the SCU whether they are
1019      *  supported or not. It is not an error if no common presentation context could be
1020      *  identified with the SCU; only issues like problems in memory management etc. are
1021      *  reported as an error. This function does not send a response message to the SCU. This
1022      *  is done in other functions.
1023      *  @return EC_Normal if negotiation was successfully done, an error code otherwise
1024      */
1025     virtual OFCondition negotiateAssociation();
1026 
1027     /** This function takes care of refusing an association request
1028      *  @param reason [in] The reason why the association request will be refused and that
1029      *                     will be reported to the SCU.
1030      */
1031     virtual void refuseAssociation(const DcmRefuseReasonType reason);
1032 
1033     /** This function takes care of handling the other DICOM application's request. After
1034      *  having accomplished all necessary steps, the association will be dropped and destroyed.
1035      */
1036     virtual void handleAssociation();
1037 
1038     /** Send a DIMSE command and possibly also a dataset from a data object via network to
1039      *  another DICOM application
1040      *  @param presID          [in]  Presentation context ID to be used for message
1041      *  @param message         [in]  Structure that represents a certain DIMSE command which
1042      *                               shall be sent
1043      *  @param dataObject      [in]  The instance data which shall be sent to the other DICOM
1044      *                               application; NULL, if there is none
1045      *  @param statusDetail    [in]  The status detail of the response (if desired).
1046      *  @param commandSet      [out] If this parameter is not NULL it will return a copy of the
1047      *                               DIMSE command which is sent to the other DICOM application
1048      *  @return Returns EC_Normal if sending request was successful, an error code otherwise
1049      */
1050     OFCondition sendDIMSEMessage(const T_ASC_PresentationContextID presID,
1051                                  T_DIMSE_Message* message,
1052                                  DcmDataset* dataObject,
1053                                  DcmDataset* statusDetail = NULL,
1054                                  DcmDataset** commandSet  = NULL);
1055 
1056     /** Receive DIMSE command (excluding dataset!) over the currently open association
1057      *  @param presID       [out] Contains in the end the ID of the presentation context
1058      *                            which was specified in the DIMSE command received
1059      *  @param message      [out] The message received
1060      *  @param statusDetail [out] If a non-NULL value is passed this variable will in the end
1061      *                            contain detailed information with regard to the status
1062      *                            information which is captured in the status element
1063      *                            (0000,0900). Note that the value for element (0000,0900) is
1064      *                            not contained in this return value but in internal message.
1065      *                            For details on the structure of this object, see DICOM
1066      *                            standard part 7, annex C).
1067      *  @param commandSet   [out] If this parameter is not NULL, it will return a copy of the
1068      *                            DIMSE command which was received from the other DICOM
1069      *                            application. The caller is responsible to de-allocate the
1070      *                            returned object!
1071      *  @param timeout      [in]  If this parameter is not 0, it specifies the timeout (in
1072      *                            seconds) to be used for receiving the DIMSE command.
1073      *                            Otherwise, the default timeout value is used (see
1074      *                            setDIMSETimeout()).
1075      *  @return EC_Normal if command could be received successfully, an error code otherwise
1076      */
1077     OFCondition receiveDIMSECommand(T_ASC_PresentationContextID* presID,
1078                                     T_DIMSE_Message* message,
1079                                     DcmDataset** statusDetail,
1080                                     DcmDataset** commandSet = NULL,
1081                                     const Uint32 timeout    = 0);
1082 
1083     /** Receive one dataset (of instance data) via network from another DICOM application
1084      *  @param presID     [out]   Contains in the end the ID of the presentation context
1085      *                            which was used in the PDVs that were received on the
1086      *                            network. If the PDVs show different presentation context
1087      *                            IDs, this function will return an error.
1088      *  @param dataObject [inout] Contains in the end the information that was received
1089      *                            over the network. If this parameter points to NULL, a new
1090      *                            dataset will be created by the underlying routines, which
1091      *                            has to be deleted by the caller.
1092      *  @return EC_Normal if dataset could be received successfully, an error code otherwise
1093      */
1094     OFCondition receiveDIMSEDataset(T_ASC_PresentationContextID* presID, DcmDataset** dataObject);
1095 
1096     /** Receive one C-STORE request dataset via network from another DICOM application and
1097      *  store it directly to file (i.e.\ exactly as received without any conversions)
1098      *  @param presID      [inout] Initially, the presentation context the C-STORE request was
1099      *                             received on. Contains in the end the ID of the presentation
1100      *                             context which was used in the PDVs that were received on the
1101      *                             network. If the PDVs show different presentation context
1102      *                             IDs, this function will return an error.
1103      *  @param reqMessage  [in]    The C-STORE request message that was received
1104      *  @param filename    [in]    Name of the file that is created to store the received dataset
1105      *  @return EC_Normal if dataset could be received successfully, an error code otherwise
1106      */
1107     OFCondition receiveSTORERequestDataset(T_ASC_PresentationContextID* presID,
1108                                            T_DIMSE_C_StoreRQ& reqMessage,
1109                                            const OFString& filename);
1110 
1111     /** Add given element to existing status detail object or create new one.
1112      *  @param statusDetail  The status detail to add the element to. Status detail
1113      *           is information additional to the DIMSE status code which can be
1114      *           provided for some DIMSE messages. If NULL is provided,
1115      *           a new status detail object is created and returned. All status
1116      *           detail attributes need to have the VR AT or LO which is also
1117      *           checked by the underlying routine.
1118      *  @param elem The element to be copied into the status detail.
1119      *  @return OFTrue if status detail was successfully added,
1120      *          OFFalse otherwise.
1121      */
1122     static OFBool addStatusDetail(DcmDataset** statusDetail, const DcmElement* elem);
1123 
1124     /* Callback functions (static) */
1125 
1126     /** Callback function used for sending DIMSE messages.
1127      *  @param callbackContext [in] The desired user callback data
1128      *  @param byteCount       [in] Progress bytes count
1129      */
1130     static void callbackSENDProgress(void* callbackContext, unsigned long byteCount);
1131 
1132     /** Callback function used for receiving DIMSE messages.
1133      *  @param callbackContext [in] The desired user callback data
1134      *  @param byteCount       [in] Progress bytes count
1135      */
1136     static void callbackRECEIVEProgress(void* callbackContext, unsigned long byteCount);
1137 
1138 private:
1139     /// Network instance run by this SCP
1140     T_ASC_Network* m_network;
1141 
1142     /// Current association run by this SCP
1143     T_ASC_Association* m_assoc;
1144 
1145     /// SCP configuration. The configuration is a shared object since in some scenarios one
1146     /// might like to share a single configuration instance with multiple SCPs without copying
1147     /// it, e.g. in the context of the DcmSCPPool class.
1148     DcmSharedSCPConfig m_cfg;
1149 
1150     /** Drops association and clears internal structures to free memory
1151      */
1152     void dropAndDestroyAssociation();
1153 
1154     /** Private undefined copy constructor. Shall never be called.
1155      *  @param src Source object
1156      */
1157     DcmSCP(const DcmSCP& src);
1158 
1159     /** Private undefined assignment operator. Shall never be called.
1160      *  @param src Source object
1161      *  @return Reference to this
1162      */
1163     DcmSCP& operator=(const DcmSCP& src);
1164 };
1165 
1166 #endif // SCP_H
1167