1 /*
2  *
3  *  Copyright (C) 2008-2019, 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: Base class for Service Class Users (SCUs)
19  *
20  */
21 
22 #ifndef SCU_H
23 #define SCU_H
24 
25 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
26 
27 #include "dcmtk/dcmdata/dctk.h"    /* covers most common dcmdata classes */
28 #include "dcmtk/dcmnet/dcasccff.h" /* for reading a association config file */
29 #include "dcmtk/dcmnet/dcasccfg.h" /* for holding association config file infos */
30 #include "dcmtk/dcmnet/dcompat.h"
31 #include "dcmtk/dcmnet/dimse.h" /* DIMSE network layer */
32 #include "dcmtk/ofstd/oflist.h"
33 
34 // include this file in doxygen documentation
35 
36 /** @file scu.h
37  *  @brief general Service Class User (SCU) class
38  */
39 
40 /** Different types of closing an association
41  */
42 enum DcmCloseAssociationType
43 {
44     /// Release the current association
45     DCMSCU_RELEASE_ASSOCIATION,
46     /// Abort the current association
47     DCMSCU_ABORT_ASSOCIATION,
48     /// Peer requested release (Aborting)
49     DCMSCU_PEER_REQUESTED_RELEASE,
50     /// Peer aborted the association
51     DCMSCU_PEER_ABORTED_ASSOCIATION
52 };
53 
54 /** Storage mode used for DICOM objects received via C-STORE
55  */
56 enum DcmStorageMode
57 {
58     /// Ignore any objects received via C-STORE
59     DCMSCU_STORAGE_IGNORE,
60     /// Try to store the objects to disk
61     DCMSCU_STORAGE_DISK,
62     /// Try to store to disk in bit-preserving mode. This is especially useful
63     /// for huge files that cannot fully be received in memory since the data
64     /// is directly streamed to disk. Originally, this was introduced for DICOM
65     /// signatures which can be kept valid this way.
66     DCMSCU_STORAGE_BIT_PRESERVING
67 };
68 
69 /** Base class for C-FIND, C-MOVE and C-GET responses
70  */
71 class DCMTK_DCMNET_EXPORT QRResponse
72 {
73 public:
74     /** Standard constructor.
75      */
QRResponse()76     QRResponse()
77         : m_messageIDRespondedTo(0)
78         , m_affectedSOPClassUID()
79         , m_dataset(NULL)
80         , m_status(0)
81         , m_statusDetail(NULL)
82     {
83     }
84 
85     /** Destructor, cleans up internal memory (dataset if present).
86      */
~QRResponse()87     virtual ~QRResponse()
88     {
89         delete m_dataset;
90         delete m_statusDetail;
91     }
92 
93     /// The message ID responded to (mandatory response field,
94     /// equals message ID from request)
95     Uint16 m_messageIDRespondedTo;
96 
97     /// Optional response field according to part 7 of the standard.
98     /// If present, equals SOP Class UID from request.
99     OFString m_affectedSOPClassUID;
100 
101     /// Conditional response field (NULL if absent). From the standard (2009,
102     /// part 4, C.4.2.1.4.2), for C-MOVE: In Q/R if no C-STORE sub-operation
103     /// failed, Failed SOP Instance UID List (0008,0058) is absent and
104     /// therefore no Data Set shall be sent in the C-MOVE response. Further
105     /// rules: Statuses of Canceled, Failure, Refused, or Warning shall
106     /// contain the Failed SOP Instance UID List Attribute; status of
107     /// Pending shall not.
108     DcmDataset* m_dataset;
109 
110     /// The returned DIMSE status (mandatory Response Field)
111     Uint16 m_status;
112 
113     /// Status detail (NULL if absent). For some DIMSE return status codes,
114     /// an additional dataset is sent which gives further information (i.e.
115     /// in case of warnings or errors).
116     DcmDataset* m_statusDetail;
117 
118 private:
119     /** Private undefined copy constructor.
120      * @param other The find response to copy from
121      */
122     QRResponse(const QRResponse& other);
123 
124     /** Private undefined assignment operator.
125      *  @param other The find response that should be assigned from
126      *  @return Reference to this
127      */
128     QRResponse& operator=(const QRResponse& other);
129 };
130 
131 /// Base class representing for single C-GET or C-MOVE response
132 class DCMTK_DCMNET_EXPORT RetrieveResponse : public QRResponse
133 {
134 public:
135     /** Standard constructor
136      */
RetrieveResponse()137     RetrieveResponse()
138         : m_numberOfRemainingSubops(0)
139         , m_numberOfCompletedSubops(0)
140         , m_numberOfFailedSubops(0)
141         , m_numberOfWarningSubops(0)
142     {
143     }
144 
145     /** Destructor, cleans up internal memory
146      */
~RetrieveResponse()147     virtual ~RetrieveResponse()
148     {
149     }
150 
151     /** Prints response to INFO log level.
152      */
153     void print();
154 
155     /// Number of remaining sub operations (in Q/R: C-STORE calls).
156     /// For Q/R MOVE and GET, for status of pending this field shall be filled.
157     /// For others, the field may be filled.
158     Uint16 m_numberOfRemainingSubops;
159 
160     /// Number of successfully completed sub operations (in Q/R: C-STORE calls).
161     /// For Q/R MOVE and GET, for status of pending this field shall be filled.
162     /// For others, the field may be filled.
163     Uint16 m_numberOfCompletedSubops;
164 
165     /// Number of failed sub operations (in Q/R: C-STORE calls).
166     /// For Q/R MOVE and GET, for status of pending this field shall be filled.
167     /// For others, the field may be filled.
168     Uint16 m_numberOfFailedSubops;
169 
170     /// Number generated warnings generated by sub operations (in Q/R: C-STORE calls).
171     /// For Q/R MOVE and GET, for status of pending this field shall be filled.
172     /// For others, the field may be filled.
173     Uint16 m_numberOfWarningSubops;
174 
175 private:
176     /** Private undefined copy constructor
177      *  @param other Response to copy from
178      */
179     RetrieveResponse(const RetrieveResponse& other);
180 
181     /** Private undefined assignment operator
182      *  @param other Response that should be assigned from
183      *  @return Reference to this
184      */
185     RetrieveResponse& operator=(const RetrieveResponse& other);
186 };
187 
188 /** Base class for implementing DICOM Service Class User functionality. The class offers
189  *  support for negotiating associations and sending and receiving arbitrary DIMSE messages
190  *  on that connection. DcmSCU has built-in C-ECHO support so derived classes do not have to
191  *  implement that capability on their own.
192  *  @warning This class is EXPERIMENTAL. Be careful to use it in production environment.
193  */
194 class DCMTK_DCMNET_EXPORT DcmSCU
195 {
196 
197 public:
198     /** Constructor, just initializes internal class members
199      */
200     DcmSCU();
201 
202     /** Virtual destructor
203      */
204     virtual ~DcmSCU();
205 
206     /** Add presentation context to be used for association negotiation
207      *  @param abstractSyntax [in] Abstract syntax name in UID format
208      *  @param xferSyntaxes   [in] List of transfer syntaxes to be added for the given abstract
209      *                             syntax
210      *  @param role           [in] The role to be negotiated
211      *  @return EC_Normal if adding was successful, otherwise error code
212      */
213     OFCondition addPresentationContext(const OFString& abstractSyntax,
214                                        const OFList<OFString>& xferSyntaxes,
215                                        const T_ASC_SC_ROLE role = ASC_SC_ROLE_DEFAULT);
216 
217     /** Initialize network, i.e.\ prepare for association negotiation. If the SCU is already
218      *  connected, the call will not be successful and the old connection keeps open.
219      *  @return EC_Normal if initialization was successful, otherwise error code.
220      *          NET_EC_AlreadyConnected if SCU is already connected.
221      */
222     virtual OFCondition initNetwork();
223 
224     /** Negotiate association by using presentation contexts and parameters as defined by
225      *  earlier function calls. If negotiation fails, there is no need to close the association
226      *  or to do anything else with this class.
227      *  @return EC_Normal if negotiation was successful, otherwise error code.
228      *          NET_EC_AlreadyConnected if SCU is already connected.
229      */
230     virtual OFCondition negotiateAssociation();
231 
232     /** After negotiation association, this call returns the first usable presentation context
233      *  given the desired abstract syntax and transfer syntax
234      *  @param abstractSyntax [in] The abstract syntax (UID) to look for
235      *  @param transferSyntax [in] The transfer syntax (UID) to look for. If empty, the transfer
236      *                             syntax is not checked.
237      *  @param requestorRole  [in] The role to look for (denoting the role the association
238      *                             requestor plays)
239      *  @return Adequate Presentation context ID that can be used. 0 if none found.
240      */
241     T_ASC_PresentationContextID findPresentationContextID(const OFString& abstractSyntax,
242                                                           const OFString& transferSyntax,
243                                                           const T_ASC_SC_ROLE requestorRole = ASC_SC_ROLE_DEFAULT);
244 
245     /** After a successful association negotiation, this function is called to return the
246      *  presentation context ID that best matches the desired abstract syntax and transfer
247      *  syntax (TS). The function tries to do the following:
248      *  - If possible finds a presentation context with matching TS
249      *  - Else then tries to find an explicit VR uncompressed TS presentation context
250      *  - Else then tries to find an implicit VR uncompressed TS presentation context
251      *  - Else finally accepts each matching presentation ctx independent of TS.
252      *  @warning This method does not support filtering for a specific role, yet.
253      *  @param abstractSyntax [in] The abstract syntax (UID) to look for
254      *  @param transferSyntax [in] The transfer syntax (UID) to look for. If empty, the transfer
255      *                             syntax is not checked.
256      *  @return Adequate Presentation context ID that can be used. 0 if no appropriate
257      *  presentation context could be found at all.
258      */
259     T_ASC_PresentationContextID findAnyPresentationContextID(const OFString& abstractSyntax,
260                                                              const OFString& transferSyntax);
261 
262     /** This function sends a C-ECHO command via network to another DICOM application
263      *  @param presID [in] Presentation context ID to use. A value of 0 lets SCP class tries
264      *                     to choose one on its own.
265      *  @return EC_Normal if echo was successful, an error code otherwise
266      *
267      */
268     virtual OFCondition sendECHORequest(const T_ASC_PresentationContextID presID);
269 
270     /** This function sends a C-STORE request on the currently opened association and receives
271      *  the corresponding response then. If required and supported, the dataset of the SOP
272      *  instance can be converted automatically to the network transfer syntax that was
273      *  negotiated (and is specified by the parameter 'presID'). However, this feature is
274      *  disabled by default. See setDatasetConversionMode() on how to enable it.
275      *  @param presID        [in]  Contains in the end the ID of the presentation context which
276      *                             was specified in the DIMSE command. If 0 is given, the
277      *                             function tries to find an appropriate presentation context
278      *                             itself (based on SOP class and original transfer syntax of
279      *                             the 'dicomFile' or 'dataset').
280      *  @param dicomFile     [in]  The filename of the DICOM file to be sent. Alternatively, a
281      *                             dataset can be given in the next parameter. If both are given
282      *                             the dataset from the file name is used.
283      *  @param dataset       [in]  The dataset to be sent. Alternatively, a filename can be
284      *                             specified in the previous parameter. If both are given the
285      *                             dataset from the filename is used.
286      *  @param rspStatusCode [out] The response status code received. 0 means success, others
287      *                             can be found in the DICOM standard.
288      *  @param moveOriginatorAETitle [in] If this C-STORE is started due to a C-MOVE request,
289      *                               this parameter informs the C-STORE SCP about the C-MOVE
290      *                               client's AE title.
291      *  @param moveOriginatorMsgID   [in] If this C-STORE is started due to a C-MOVE request,
292      *                               this parameter informs the C-STORE SCP about the C-MOVE
293      *                               message ID.
294      *  @return EC_Normal if request could be issued and response was received successfully,
295      *          error code otherwise. That means that if the receiver sends a response denoting
296      *          failure of the storage request, EC_Normal will be returned.
297      */
298     virtual OFCondition sendSTORERequest(const T_ASC_PresentationContextID presID,
299                                          const OFFilename& dicomFile,
300                                          DcmDataset* dataset,
301                                          Uint16& rspStatusCode,
302                                          const OFString& moveOriginatorAETitle = "",
303                                          const Uint16 moveOriginatorMsgID      = 0);
304 
305     /** Sends a C-MOVE Request on given presentation context and receives list of responses.
306      *  The function receives the first response and then calls the function handleMOVEResponse()
307      *  which gets the relevant presentation context together with the response dataset and
308      *  status information. Then it waits again for the next response, if there are more to
309      *  come (i.e. response status is PENDING). In the end, after receiving all responses, the
310      *  full list of responses is returned to the caller. If he is not interested, he just sets
311      *  responses=NULL when calling the function.
312      *  This function can be overwritten by actual SCU implementations but just should work fine
313      *  for most people.
314      *  @param presID                 [in]  The presentation context ID that should be used.
315      *                                      Must be an odd number.
316      *  @param moveDestinationAETitle [in]  The move destination's AE title, i.e.\ the one that
317      *                                      is used for connection to the storage server.
318      *  @param dataset                [in]  The dataset containing the information about the
319      *                                      object(s) to be retrieved.
320      *  @param responses              [out] The incoming C-MOVE responses for this request.
321      *                                      The caller is responsible for providing a non-NULL
322      *                                      pointer for this case. After receiving the results,
323      *                                      the caller is responsible for freeing the memory of
324      *                                      this variable. If NULL is specified, the responses
325      *                                      will not be returned to the caller.
326      *  @return EC_Normal if everything went fine, i.e.\ if request could be send and responses
327      *          (with whatever status) could be received.
328      */
329     virtual OFCondition sendMOVERequest(const T_ASC_PresentationContextID presID,
330                                         const OFString& moveDestinationAETitle,
331                                         DcmDataset* dataset,
332                                         OFList<RetrieveResponse*>* responses);
333 
334     /** This is the standard handler for C-MOVE message responses: It just adds up all responses
335      *  it receives and prints a DEBUG message. Therefore, it is called for each response
336      *  received in sendMOVERequest(). The idea is of course to overwrite this function in a
337      *  derived, actual SCU implementation if required. Thus, after each response, the caller of
338      *  sendMOVERequest() can decide on its own whether he wants to cancel the C-MOVE session,
339      *  terminate the association, do something useful or whatever. Thus this function is a more
340      *  object-oriented kind of callback.
341      *  @param presID              [in]  The presentation context ID where the response was
342      *                                   received on.
343      *  @param response            [in]  The C-MOVE response received.
344      *  @param waitForNextResponse [out] Denotes whether SCU should try to receive another
345      *                                   response. If set to OFTrue, then sendMOVERequest() will
346      *                                   continue waiting for responses. The current
347      *                                   implementation does that for all responses do not have
348      *                                   status Failed, Warning, Success or unknown. If set to
349      *                                   OFFalse, sendMOVERequest() will return control to the
350      *                                   caller.
351      *  @return EC_Normal, if response could be handled. Error code otherwise.
352      *          The current implementation always returns EC_Normal.
353      */
354     virtual OFCondition handleMOVEResponse(const T_ASC_PresentationContextID presID,
355                                            RetrieveResponse* response,
356                                            OFBool& waitForNextResponse);
357 
358     /** Sends a C-GET Request on given presentation context and receives list of responses. It
359      *  then switches control to the function handleCGETSession().
360      *  The full list of responses is returned to the caller. If he is not interested, he can
361      *  set responses=NULL when calling the function.
362      *  This function can be overwritten by actual SCU implementations but just should work fine
363      *  for most people.
364      *  @param presID    [in]  The presentation context ID that should be used. Must be an odd
365      *                         number.
366      *  @param dataset   [in]  The dataset containing the information about the
367      *                         object(s) to be retrieved
368      *  @param responses [out] The incoming C-GET responses for this request. If the caller
369      *                         specifies NULL, no responses will be returned; otherwise there
370      *                         should be at least one final C-GET response (mandatory). C-GET
371      *                         responses after each DICOM object received are optional and may
372      *                         have been omitted by the server.
373      *  @return EC_Normal if everything went fine, i.e.\ if request could be sent and expected
374      *          responses (with whatever status) could be received.
375      */
376     virtual OFCondition sendCGETRequest(const T_ASC_PresentationContextID presID,
377                                         DcmDataset* dataset,
378                                         OFList<RetrieveResponse*>* responses);
379 
380     /** Does the logic for switching between C-GET Response and C-STORE Requests. Sends a C-GET
381      *  Request on given presentation context and receives list of responses. The full list of
382      *  responses is returned to the caller. If he is not interested, he can set responses=NULL
383      *  when calling the function. After sending a C-GET Request, there might be two different
384      *  responses coming in: C-GET-RSP (optional after each received object and mandatory after
385      *  the last object) or a mandatory C-STORE for each incoming object that is received due to
386      *  the request. This function therefore either calls handleCGETResponse() or
387      *  handleSTORERequest() in order to deal with the incoming message. All other messages lead
388      *  to an error within this handler.
389      *  This function can be overwritten by actual SCU implementations but just should work fine
390      *  for most people.
391      *  @param presID    [in]  The presentation context ID that should be used. Must be an odd
392      *                         number.
393      *  @param dataset   [in]  The dataset containing the information about the object(s) to be
394      *                         retrieved
395      *  @param responses [out] The incoming C-GET responses for this request. If the caller
396      *                         specifies NULL, no responses will be returned; otherwise there
397      *                         should be at least one final C-GET response (mandatory). C-GET
398      *                         responses after each DICOM object received are optional and may
399      *                         have been omitted by the server.
400      *  @return EC_Normal if everything went fine, i.e.\ if request could be send
401      *          and expected responses (with whatever status) could be received.
402      */
403     virtual OFCondition handleCGETSession(const T_ASC_PresentationContextID presID,
404                                           DcmDataset* dataset,
405                                           OFList<RetrieveResponse*>* responses);
406 
407     /** Function handling a single C-GET Response. This standard handler reads the status of the
408      *  response and decides whether to receive any further messages related to the original
409      *  C-GET Request or whether the last response was received or an error occurred.
410      *  @param presID              [in]  The presentation context the C-GET Response was
411      *                                   received on.
412      *  @param response            [in]  The response received
413      *  @param continueCGETSession [out] Defines whether it is decided to wait for further C-GET
414      *                                   Responses/C-STORE Requests within this C-GET session
415      *  @return If no errors occur (dataset response NULL, SCU not connected), this method will
416      *          return EC_Normal, otherwise error code.
417      */
418     virtual OFCondition handleCGETResponse(const T_ASC_PresentationContextID presID,
419                                            RetrieveResponse* response,
420                                            OFBool& continueCGETSession);
421 
422     /** Function handling a single C-STORE Request. If storage mode is set to disk (default),
423      *  this function is called and the incoming object stored to disk.
424      *  @param presID              [in]  The presentation context the C-STORE Request was
425      *                                   received on.
426      *  @param incomingObject      [in]  The dataset (the object) received. The function
427      *                                   takes ownership of the dataset, i.e. deletes it
428      *                                   after processing.
429      *  @param continueCGETSession [out] Defines whether it is decided to wait for further
430      *                                   C-GET Responses/C-STORE requests within this C-GET
431      *                                   session.
432      *  @param cStoreReturnStatus  [out] Denotes the desired C-STORE return status.
433      *  @return If errors occur (incomingObject NULL or SCU not connected or file could not be
434      *          stored), this method will return an error code otherwise EC_Normal.
435      */
436     virtual OFCondition handleSTORERequest(const T_ASC_PresentationContextID presID,
437                                            DcmDataset* incomingObject,
438                                            OFBool& continueCGETSession,
439                                            Uint16& cStoreReturnStatus);
440 
441     /** Function handling a single C-STORE Request. If storage mode is set to bit preserving,
442      *  this function is called and the incoming object stored directly to disk, i.e. not stored
443      *  fully in memory.
444      *  @param presID   [in] The presentation context the C-STORE Response was received on.
445      *  @param filename [in] The filename to store to
446      *  @param request  [in] The incoming C-STORE request command set
447      *  @return If errors occur (incomingObject NULL or SCU not connected filename not
448      *          specified), this method will return an error code otherwise EC_Normal.
449      */
450     virtual OFCondition
451     handleSTORERequestFile(T_ASC_PresentationContextID* presID, const OFString& filename, T_DIMSE_C_StoreRQ* request);
452 
453     /** Sends a C-FIND Request on given presentation context and receives list of responses.
454      *  The function receives the first response and then calls the function handleFINDResponse()
455      *  which gets the relevant presentation context together with the response dataset and
456      *  status information. Then it waits again for the next response, if there are more to
457      *  come (i.e. response status is PENDING). In the end, after receiving all responses, the
458      *  full list of responses is returned to the caller. If he is not interested, he just sets
459      *  responses=NULL when calling the function.
460      *  This function can be overwritten by actual SCU implementations but just should work fine
461      *  for most people.
462      *  @param presID    [in]  The presentation context ID that should be used. Must be an odd
463      *                         number.
464      *  @param queryKeys [in]  The dataset containing the query keys to be searched for on the
465      *                         server (SCP).
466      *  @param responses [out] The incoming C-FIND responses for this request. The caller is
467      *                         responsible for providing a non-NULL pointer for this case.
468      *                         After receiving the results, the caller is responsible for
469      *                         freeing the memory of this variable. If NULL is specified, the
470      *                         responses will be not returned to the caller.
471      *  @return EC_Normal if everything went fine, i.e.\ if request could be send and responses
472      *          (with whatever status) could be received.
473      */
474     virtual OFCondition
475     sendFINDRequest(const T_ASC_PresentationContextID presID, DcmDataset* queryKeys, OFList<QRResponse*>* responses);
476 
477     /** This is the standard handler for C-FIND message responses: It just adds up all responses
478      *  it receives and prints a DEBUG message. Therefore, it is called for each response
479      *  received in sendFINDRequest(). The idea is of course to overwrite this function in a
480      *  derived, actual SCU implementation if required. Thus, after each response, the caller of
481      *  sendFINDRequest() can decide on its own whether he wants to cancel the C-FIND session,
482      *  terminate the association, do something useful or whatever. That way this is a more
483      *  object-oriented kind of callback.
484      *  @param presID              [in]  The presentation context ID where the response was
485      *                                   received on.
486      *  @param response            [in]  The C-FIND response received.
487      *  @param waitForNextResponse [out] Denotes whether SCU should try to receive another
488      *                                   response. If set to OFTrue, then sendFINDRequest()
489      *                                   will continue waiting for responses. The current
490      *                                   implementation does that for all responses do not have
491      *                                   status SUCCESS. If set to OFFalse, sendFINDRequest()
492      *                                   will return control to the caller.
493      *  @return EC_Normal, if response could be handled. Error code otherwise.
494      *          The current implementation always returns EC_Normal.
495      */
496     virtual OFCondition
497     handleFINDResponse(const T_ASC_PresentationContextID presID, QRResponse* response, OFBool& waitForNextResponse);
498 
499     /** Send C-CANCEL and, therefore, ends the C-FIND -GET or -MOVE session, i.e.\ no further
500      *  responses will be handled. A call to this function only makes sense if an association
501      *  is open, the given presentation context represents a valid C-FIND/GET/MOVE-enabled SOP
502      *  class and usually only, if the last command send on that presentation context was a
503      *  C-FIND message.
504      *  @param presID [in] The presentation context ID where the C-CANCEL should be sent on.
505      *  @return The current implementation always returns EC_Normal.
506      */
507     virtual OFCondition sendCANCELRequest(const T_ASC_PresentationContextID presID);
508 
509     /** This function sends a N-ACTION request on the currently opened association and receives
510      *  the corresponding response then
511      *  @param presID         [in]  The ID of the presentation context to be used for sending
512      *                              the request message. Should not be 0.
513      *  @param sopInstanceUID [in]  The requested SOP Instance UID
514      *  @param actionTypeID   [in]  The action type ID to be used
515      *  @param reqDataset     [in]  The dataset to be sent
516      *  @param rspStatusCode  [out] The response status code received. 0 means success,
517      *                              others can be found in the DICOM standard.
518      *  @return EC_Normal if request could be issued and response was received successfully,
519      *          an error code otherwise
520      */
521     virtual OFCondition sendACTIONRequest(const T_ASC_PresentationContextID presID,
522                                           const OFString& sopInstanceUID,
523                                           const Uint16 actionTypeID,
524                                           DcmDataset* reqDataset,
525                                           Uint16& rspStatusCode);
526 
527     /** This function sends N-EVENT-REPORT request and receives the corresponding response
528      *  @param presID         [in]  The ID of the presentation context to be used for sending
529      *                              the request message. Should not be 0.
530      *  @param sopInstanceUID [in]  The requested SOP Instance UID
531      *  @param eventTypeID    [in]  The event type ID to be used
532      *  @param reqDataset     [in]  The request dataset to be sent
533      *  @param rspStatusCode  [out] The response status code received. 0 means success,
534      *                              others can be found in the DICOM standard.
535      *  @return EC_Normal if request could be issued and response was received successfully,
536      *          an error code otherwise
537      */
538     virtual OFCondition sendEVENTREPORTRequest(const T_ASC_PresentationContextID presID,
539                                                const OFString& sopInstanceUID,
540                                                const Uint16 eventTypeID,
541                                                DcmDataset* reqDataset,
542                                                Uint16& rspStatusCode);
543 
544     /** Receives N-EVENT-REPORT request on the currently opened association and sends a
545      *  corresponding response. Calls checkEVENTREPORTRequest() in order to determine the
546      *  DIMSE status code to be used for the N-EVENT-REPORT response.
547      *  @param reqDataset  [out] Pointer to the dataset received
548      *  @param eventTypeID [out] Event Type ID from the command set received
549      *  @param timeout     [in]  Optional timeout in seconds for receiving data. This value
550      *                           (if not 0) overwrites the standard DIMSE timeout and also
551      *                           enables the non-blocking mode for receiving the request.
552      *  @return status, EC_Normal if successful, an error code otherwise
553      */
554     virtual OFCondition handleEVENTREPORTRequest(DcmDataset*& reqDataset, Uint16& eventTypeID, const int timeout = 0);
555 
556     /** Function handling a single C-GET, C-FIND or C-MOVE Response, used by
557      *  handleCGETResponse(), handleFINDResponse() and handleMOVEResponse().
558      *  It prints a message to the logger (warning, error or debug level related
559      *  to the status code provided) and sets waitForNextResponse if the status
560      *  provided is a DIMSE pending status.
561      *  @param dimseStatus         [in]  The DIMSE status code to handle
562      *  @param message             [in]  The message to be printed
563      *  @param waitForNextResponse [out] Defines whether it is decided to wait for further
564      *                                   Responses with the C-GET/FIND/MOVE session.
565      *  @return If no processing errors occurs this method will return EC_Normal,
566      *          otherwise an error code.
567      */
568     virtual OFCondition
569     handleSessionResponseDefault(const Uint16 dimseStatus, const OFString& message, OFBool& waitForNextResponse);
570 
571     /** Closes the association created by this SCU. Also resets the current association.
572      *  @deprecated The use of this method is deprecated. Please use releaseAssociation()
573      *    or abortAssociation() instead.
574      *  @param closeType [in] Define whether to release or abort the association
575      */
576     virtual void closeAssociation(const DcmCloseAssociationType closeType);
577 
578     /** Releases the current association by sending an A-RELEASE request to the SCP.
579      *  Please note that this release only applies to associations that have been
580      *  created by calling initNetwork() and negotiateAssociation().
581      *  @return status, EC_Normal if successful, an error code otherwise
582      */
583     virtual OFCondition releaseAssociation();
584 
585     /** Aborts the current association by sending an A-ABORT request to the SCP.
586      *  Please note that this abort only applies to associations that have been
587      *  created by calling initNetwork() and negotiateAssociation().
588      *  @return status, EC_Normal if successful, an error code otherwise
589      */
590     virtual OFCondition abortAssociation();
591 
592     /* Set methods */
593 
594     /** Set maximum PDU length (to be received by SCU)
595      *  @param maxRecPDU [in] The maximum PDU size to use in bytes
596      */
597     void setMaxReceivePDULength(const Uint32 maxRecPDU);
598 
599     /** Set whether to send in DIMSE blocking or non-blocking mode
600      *  @param blockingMode [in] Either blocking or non-blocking mode
601      */
602     void setDIMSEBlockingMode(const T_DIMSE_BlockingMode blockingMode);
603 
604     /** Set SCU's AE title to be used in association negotiation
605      *  @param myAETtitle [in] The SCU's AE title to be used
606      */
607     void setAETitle(const OFString& myAETtitle);
608 
609     /** Set SCP's host (host name or IP address) to talk to in association negotiation
610      *  @param peerHostName [in] The SCP's hostname or IP address to be used
611      */
612     void setPeerHostName(const OFString& peerHostName);
613 
614     /** Set SCP's AE title to talk to in association negotiation
615      *  @param peerAETitle [in] The SCP's AE title to be used
616      */
617     void setPeerAETitle(const OFString& peerAETitle);
618 
619     /** Set SCP's port number to connect to for association negotiation
620      *  @param peerPort [in] The SCP's port number
621      */
622     void setPeerPort(const Uint16 peerPort);
623 
624     /** Set timeout for receiving DIMSE messages
625      *  @param dimseTimeout [in] DIMSE timeout in seconds for receiving data.
626      *                           If the blocking mode is DIMSE_NONBLOCKING the SCU
627      *                           will try to read data from the incoming socket stream
628      *                           for the number of seconds configured.
629      */
630     void setDIMSETimeout(const Uint32 dimseTimeout);
631 
632     /** Set timeout for receiving ACSE messages
633      *  @param acseTimeout [in] ACSE timeout in seconds used by timer for message timeouts
634      *                          during association negotiation
635      */
636     void setACSETimeout(const Uint32 acseTimeout);
637 
638     /** Set global timeout for connecting to the SCP. Note that this is a global
639      *  DCMTK setting i.e. it affects all code that uses dcmnet to start a DICOM
640      *  association to another host. Setting the timeout to -1 sets an infinite timeout,
641      *  i.e. any association request will wait forever (blocking) until the SCP accepts
642      *  the connection request. A value of 0 lets the SCU return immediately if the SCP
643      *  is not reachable at the first attempt.
644      *  @param connectionTimeout [in] Connection Timeout in seconds when connecting
645      *                                to SCPs. -1 will wait forever (blocking mode).
646      */
647     void setConnectionTimeout(const Sint32 connectionTimeout);
648 
649     /** Set an association configuration file and profile to be used
650      *  @param filename [in] File name of the association configuration file
651      *  @param profile  [in] Profile inside the association negotiation file
652      */
653     void setAssocConfigFileAndProfile(const OFString& filename, const OFString& profile);
654 
655     /** Set the directory that should be used by the standard C-GET handler to store objects
656      *  that come in with the corresponding C-STORE requests
657      *  @param storeDir [in] The directory to store to. It is checked in handleSTORERequest()
658      *                       whether the directory is writeable and readable. By default, the
659      *                       received objects are stored in the current working directory.
660      */
661     void setStorageDir(const OFString& storeDir);
662 
663     /** Set the storage mode to be used. Default is DCMSCU_STORAGE_DISK.
664      *  @param storageMode The storage mode to be used.
665      */
666     void setStorageMode(const DcmStorageMode storageMode);
667 
668     /** Set whether to show presentation contexts in verbose or debug mode
669      *  @param mode [in] Show presentation contexts in verbose mode if OFTrue. By default,
670      *                   the presentation contexts are shown in debug mode.
671      */
672     void setVerbosePCMode(const OFBool mode);
673 
674     /** Set the mode that specifies whether the transfer syntax of the dataset can be changed
675      *  for network transmission. This mainly covers the compression/decompression of datasets,
676      *  which is disabled by default.
677      *  @param mode [in] Allow dataset conversion if OFTrue
678      */
679     void setDatasetConversionMode(const OFBool mode);
680 
681     /** Set the mode that specifies whether the progress of sending and receiving DIMSE
682      *  messages is notified by calling notifySENDProgress() and notifyRECEIVEProgress(),
683      *  respectively. The progress notification is enabled by default.
684      *  @param mode [in] Disable progress notification if OFFalse
685      */
686     void setProgressNotificationMode(const OFBool mode);
687 
688     /* Get methods */
689 
690     /** Get current connection status
691      *  @return OFTrue if SCU is currently connected, OFFalse otherwise
692      */
693     OFBool isConnected() const;
694 
695     /** Returns maximum PDU length configured to be received by SCU
696      *  @return Maximum PDU length in bytes
697      */
698     Uint32 getMaxReceivePDULength() const;
699 
700     /** Returns whether DIMSE messaging is configured to be blocking or unblocking
701      *  @return The blocking mode configured
702      */
703     T_DIMSE_BlockingMode getDIMSEBlockingMode() const;
704 
705     /** Returns the SCU's own configured AE title
706      *  @return The AE title configured for this SCU
707      */
708     const OFString& getAETitle() const;
709 
710     /** Returns the SCP's (peer's) host configured
711      *  @return The host name (or IP) configured to be talked to
712      */
713     const OFString& getPeerHostName() const;
714 
715     /** Returns the SCP's (peer's) AE title configured
716      *  @return The AE title configured to be talked to
717      */
718     const OFString& getPeerAETitle() const;
719 
720     /** Returns the SCP's (peer's) TCP/IP port configured
721      *  @return The port configured to talked to
722      */
723     Uint16 getPeerPort() const;
724 
725     /** Returns DIMSE timeout in seconds for receiving data. If the blocking
726      *  mode is DIMSE_NONBLOCKING the SCU will try to read data from
727      *  the incoming socket stream for the number of seconds configured.
728      *  @return The DIMSE timeout (in seconds) configured
729      */
730     Uint32 getDIMSETimeout() const;
731 
732     /** Returns ACSE timeout in seconds used by timer for message timeouts during
733      *  association negotiation.
734      *  @return The ACSE timeout (in seconds) configured
735      */
736     Uint32 getACSETimeout() const;
737 
738     /** Returns the timeout configured defining how long SCU will wait for the
739      *  SCP when requesting an association. -1 means infinite waiting (blocking),
740      *  0 means no waiting at all. Note that this is currently a global DCMTK setting.
741      *  @return The connection timeout (in seconds)
742      */
743     Sint32 getConnectionTimeout() const;
744 
745     /** Returns the storage directory used for storing objects received with C-STORE requests
746      *  in the context of C-GET sessions. Default is empty string which refers to the current
747      *  working directory.
748      *  @return The storage directory
749      */
750     OFString getStorageDir() const;
751 
752     /** Returns the storage mode enabled
753      *  @return The storage mode enabled
754      */
755     DcmStorageMode getStorageMode() const;
756 
757     /** Returns the verbose presentation context mode configured specifying whether details
758      *  on the presentation contexts (negotiated during association setup) should be shown in
759      *  verbose or debug mode. The latter is the default.
760      *  @return The current verbose presentation context mode. Show details on the
761      *          presentation contexts on INFO log level (verbose) if OFTrue and on DEBUG
762      *          level if OFFalse.
763      */
764     OFBool getVerbosePCMode() const;
765 
766     /** Returns the mode that specifies whether the transfer syntax of the dataset can be
767      *  changed for network transmission. This mainly covers the compression/decompression
768      *  of datasets, which is disabled by default.
769      *  @return The current dataset conversion mode, enabled if OFTrue
770      */
771     OFBool getDatasetConversionMode() const;
772 
773     /** Returns the mode that specifies whether the progress of sending and receiving DIMSE
774      *  messages is notified by calling notifySENDProgress() and notifyRECEIVEProgress(),
775      *  respectively. The progress notification is enabled by default.
776      *  @return The current progress notification mode, enabled if OFTrue
777      */
778     OFBool getProgressNotificationMode() const;
779 
780     /** Returns whether SCU is configured to create a TLS connection with the SCP
781      *  @return OFFalse for this class but may be overridden by derived classes
782      */
783     OFBool getTLSEnabled() const;
784 
785     /** Deletes internal networking structures from memory */
786     void freeNetwork();
787 
788 protected:
789     /** Sends a DIMSE command and possibly also a dataset from a data object via network to
790      *  another DICOM application
791      *  @param presID     [in]  Presentation context ID to be used for message
792      *  @param msg        [in]  Structure that represents a certain DIMSE command which
793      *                          shall be sent
794      *  @param dataObject [in]  The instance data which shall be sent to the other DICOM
795      *                          application; NULL, if there is none
796      *  @param commandSet [out] If this parameter is not NULL it will return a copy of the
797      *                          DIMSE command which is sent to the other DICOM application
798      *  @return EC_Normal if sending request was successful, an error code otherwise
799      */
800     OFCondition sendDIMSEMessage(const T_ASC_PresentationContextID presID,
801                                  T_DIMSE_Message* msg,
802                                  DcmDataset* dataObject,
803                                  DcmDataset** commandSet = NULL);
804 
805     /** Returns SOP Class UID, SOP Instance UID and original transfer syntax for a given dataset.
806      *  If the dataset is NULL, all returned values will be undefined (i.e. empty or EXS_Unknown).
807      *  @param dataset        [in]  The dataset to read from
808      *  @param sopClassUID    [out] The value of attribute SOP Class UID if present
809      *  @param sopInstanceUID [out] The value of attribute SOP Instance UID if present
810      *  @param transferSyntax [out] The value of transfer syntax that originally was read from
811      *                              disk. Will be unknown if the dataset was created in memory.
812      *  @return EC_Normal if all information could be retrieved and is valid, an error code
813      *    otherwise
814      */
815     OFCondition getDatasetInfo(DcmDataset* dataset,
816                                OFString& sopClassUID,
817                                OFString& sopInstanceUID,
818                                E_TransferSyntax& transferSyntax);
819 
820     /** Tells DcmSCU to use a secure TLS connection described by the given TLS layer
821      *  @param tlayer [in] The TLS transport layer including all TLS parameters
822      *  @return EC_Normal if given transport layer is ok, an error code otherwise
823      */
824     OFCondition useSecureConnection(DcmTransportLayer* tlayer);
825 
826     /** Receive DIMSE command (excluding dataset!) over the currently open association
827      *  @param presID       [out] Contains in the end the ID of the presentation context
828      *                            which was specified in the DIMSE command received
829      *  @param msg          [out] The message received
830      *  @param statusDetail [out] If a non-NULL value is passed this variable will in the end
831      *                            contain detailed information with regard to the status
832      *                            information which is captured in the status element
833      *                            (0000,0900). Note that the value for element (0000,0900) is
834      *                            not contained in this return value but in internal msg. For
835      *                            details on the structure of this object, see DICOM standard
836      *                            part 7, annex C).
837      *  @param commandSet   [out] If this parameter is not NULL, it will return a copy of the
838      *                            DIMSE command which was received from the other DICOM
839      *                            application. The caller is responsible to de-allocate the
840      *                            returned object!
841      *  @param timeout      [in]  If this parameter is not 0, it specifies the timeout (in
842      *                            seconds) to be used for receiving the DIMSE command.
843      *                            Otherwise, the default timeout value is used (see
844      *                            setDIMSETimeout()).
845      *  @return EC_Normal if command could be received successfully, an error code otherwise
846      */
847     OFCondition receiveDIMSECommand(T_ASC_PresentationContextID* presID,
848                                     T_DIMSE_Message* msg,
849                                     DcmDataset** statusDetail,
850                                     DcmDataset** commandSet = NULL,
851                                     const Uint32 timeout    = 0);
852 
853     /** Receives one dataset (of instance data) via network from another DICOM application
854      *  @param presID     [out] Contains in the end the ID of the presentation context which
855      *                          was used in the PDVs that were received on the network. If the
856      *                          PDVs show different presentation context IDs, this function
857      *                          will return an error.
858      *  @param dataObject [out] Contains in the end the information which was received over
859      *                          the network
860      *  @return EC_Normal if dataset could be received successfully, an error code otherwise
861      */
862     OFCondition receiveDIMSEDataset(T_ASC_PresentationContextID* presID, DcmDataset** dataObject);
863 
864     /** clear list of presentation contexts. In addition, any currently selected association
865      *  configuration file is disabled.
866      */
867     void clearPresentationContexts();
868 
869     /** After negotiation association, this call returns the presentation context belonging
870      *  to the given presentation context ID
871      *  @param presID         [in]  The presentation context ID to look for
872      *  @param abstractSyntax [out] The abstract syntax (UID) for that ID.
873      *                              Empty, if such a presentation context does not exist.
874      *  @param transferSyntax [out] The transfer syntax (UID) for that ID.
875      *                              Empty, if such a presentation context does not exist.
876      */
877     void findPresentationContext(const T_ASC_PresentationContextID presID,
878                                  OFString& abstractSyntax,
879                                  OFString& transferSyntax);
880 
881     /* ***********************************************************************
882      *  Functions particularly interesting for overwriting in derived classes
883      * *********************************************************************** */
884 
885     /** This function is called if an object was received due to a C-GET request and can be
886      *  overwritten by a user in order to be informed about such an event. The default
887      *  implementation just prints a DEBUG message. Note that this function is not called if
888      *  the SCU is in storage mode DCMSCU_STORAGE_IGNORE.
889      *  @param filename       [in] The filename written
890      *  @param sopClassUID    [in] The SOP Class UID of the object written
891      *  @param sopInstanceUID [in] The SOP Instance UID of the object written
892      */
893     virtual void
894     notifyInstanceStored(const OFString& filename, const OFString& sopClassUID, const OFString& sopInstanceUID) const;
895 
896     /** This function is called while sending DIMSE messages, i.e.\ on each PDV of a dataset.
897      *  The default implementation just prints a TRACE message on the number of bytes sent so
898      *  far. By overwriting this method, the progress of the send process can be shown to the
899      *  user in a more appropriate way. The progress notification can also be disabled
900      *  completely by calling setProgressNotificationMode().
901      *  @param byteCount [in] Number of bytes sent so far
902      */
903     virtual void notifySENDProgress(const unsigned long byteCount);
904 
905     /** This function is called while receiving DIMSE messages, i.e.\ on each PDV of a dataset.
906      *  The default implementation just prints a TRACE message on the number of bytes received
907      *  so far. By overwriting this method, the progress of the receive process can be shown to
908      *  the user in a more appropriate way. The progress notification can also be disabled
909      *  completely by calling setProgressNotificationMode().
910      *  @param byteCount [in] Number of bytes received so far
911      */
912     virtual void notifyRECEIVEProgress(const unsigned long byteCount);
913 
914     /** Check given N-EVENT-REPORT request and dataset for validity. This method is called by
915      *  handleEVENTREPORTRequest() before sending the response in order to determine the
916      *  DIMSE status code to be used for the response message.
917      *  @param request    [in] The N-EVENT-REPORT request message data structure
918      *  @param reqDataset [in] The N-EVENT-REPORT request dataset received. Might be NULL.
919      *  @return DIMSE status code to be used for the N-EVENT-REPORT response.
920      *          Always returns STATUS_Success (0). Derived classes should, therefore,
921      *          overwrite this method and return a more appropriate value based on the
922      *          result of the checks performed.
923      */
924     virtual Uint16 checkEVENTREPORTRequest(T_DIMSE_N_EventReportRQ& request, DcmDataset* reqDataset);
925 
926     /** Sends back a C-STORE response on the given presentation context, with the designated
927      *  status, fitting the corresponding C-STORE request.
928      *  @param presID  [in] The presentation context ID to be used.
929      *  @param status  [in] The storage DIMSE status to be used.
930      *  @param request [in] The C-STORE request that should be responded to.
931      *  @result EC_Normal if the response could be sent, error otherwise.
932      */
933     virtual OFCondition
934     sendSTOREResponse(T_ASC_PresentationContextID presID, Uint16 status, const T_DIMSE_C_StoreRQ& request);
935 
936     /** Helper function that generates a storage filename by extracting SOP Class and SOP
937      *  Instance UID from a dataset and combining that with the configured storage directory.
938      *  The SOP class is used to create an initial two letter abbreviation for the
939      *  corresponding modality, e.g. CT. An example for a storage filename created by this
940      *  function is "/storage_dir/CT.1.2.3.4.5" for a CT with SOP Instance UID "1.2.3.4.5".
941      *  This function might be overwritten to change the filename behaviour completely. This
942      *  function is only called if the SCU is in DCMSCU_STORAGE_DISK mode.
943      *  @param dataset [in] The dataset that should be stored to disk
944      *  @result Non-empty string if successful, otherwise empty string.
945      */
946     virtual OFString createStorageFilename(DcmDataset* dataset);
947 
948     /** Receives a DICOM dataset on a given presentation context ID but does not store it in
949      *  memory or disk, thus ignoring it.
950      *  @param presID  [in] The presentation context to be used
951      *  @param request [in] The corresponding C-STORE request
952      *  @return EC_Normal if ignoring worked, error code otherwise.
953      */
954     virtual OFCondition ignoreSTORERequest(T_ASC_PresentationContextID presID, const T_DIMSE_C_StoreRQ& request);
955 
956     /* Callback functions (static) */
957 
958     /** Callback function used for sending DIMSE messages.
959      *  @param callbackContext [in] The desired user callback data
960      *  @param byteCount       [in] Progress bytes count
961      */
962     static void callbackSENDProgress(void* callbackContext, unsigned long byteCount);
963 
964     /** Callback function used for receiving DIMSE messages.
965      *  @param callbackContext [in] The desired user callback data
966      *  @param byteCount       [in] Progress bytes count
967      */
968     static void callbackRECEIVEProgress(void* callbackContext, unsigned long byteCount);
969 
970 private:
971     /** Private undefined copy-constructor. Shall never be called.
972      *  @param src Source object
973      */
974     DcmSCU(const DcmSCU& src);
975 
976     /** Private undefined operator=. Shall never be called.
977      *  @param src Source object
978      *  @return Reference to this
979      */
980     DcmSCU& operator=(const DcmSCU& src);
981 
982     /// Association of this SCU. This class only handles 1 association at a time.
983     T_ASC_Association* m_assoc;
984 
985     /// The DICOM network the association is based on
986     T_ASC_Network* m_net;
987 
988     /// Association parameters
989     T_ASC_Parameters* m_params;
990 
991     /// Configuration file for presentation contexts (optional)
992     OFString m_assocConfigFilename;
993 
994     /// Profile in configuration file that should be used (optional)
995     OFString m_assocConfigProfile;
996 
997     /// Defines presentation context, consisting of one abstract syntax name
998     /// and a list of transfer syntaxes for this abstract syntax
999     struct DCMTK_DCMNET_EXPORT DcmSCUPresContext
1000     {
1001         /** Default constructor
1002          */
DcmSCUPresContextDcmSCUPresContext1003         DcmSCUPresContext()
1004             : abstractSyntaxName()
1005             , transferSyntaxes()
1006             , roleSelect(ASC_SC_ROLE_DEFAULT)
1007         {
1008         }
1009         /// Abstract Syntax Name of Presentation Context
1010         OFString abstractSyntaxName;
1011         /// List of Transfer Syntaxes for Presentation Context
1012         OFList<OFString> transferSyntaxes;
1013         /// Role Selection
1014         T_ASC_SC_ROLE roleSelect;
1015     };
1016 
1017     /// List of presentation contexts that should be negotiated
1018     OFList<DcmSCUPresContext> m_presContexts;
1019 
1020     /// Configuration file containing association parameters
1021     OFString m_assocConfigFile;
1022 
1023     /// The last DIMSE successfully sent, unresponded DIMSE request
1024     T_DIMSE_Message* m_openDIMSERequest;
1025 
1026     /// Maximum PDU size (default: 16384 bytes)
1027     Uint32 m_maxReceivePDULength;
1028 
1029     /// DIMSE blocking mode (default: blocking)
1030     T_DIMSE_BlockingMode m_blockMode;
1031 
1032     /// AE title of this application (default: ANY-SCU)
1033     OFString m_ourAETitle;
1034 
1035     /// Peer host (IP or host name)
1036     OFString m_peer;
1037 
1038     /// AE title of remote application (default: ANY-SCP)
1039     OFString m_peerAETitle;
1040 
1041     /// Port of remote application entity (default: 104)
1042     Uint16 m_peerPort;
1043 
1044     /// DIMSE timeout (default: unlimited)
1045     Uint32 m_dimseTimeout;
1046 
1047     /// ACSE timeout (default: 30 seconds)
1048     Uint32 m_acseTimeout;
1049 
1050     /// Storage directory for objects received with C-STORE due to a running
1051     /// C-GET session. By default, the received objects are stored in the current
1052     /// working directory.
1053     OFString m_storageDir;
1054 
1055     /// Set whether bit preserving storage should be enabled, i.e.\ any objects
1056     /// retrieved via C-STORE should be written directly to disk without any data
1057     /// correction/re-computation (e.g.\ group length calculations, padding, etc.).
1058     /// This is especially interesting for retaining valid signatures, and also to
1059     /// receive huge files which cannot be fully received in memory.
1060     /// Default value: DCMSCU_STORAGE_DISK
1061     DcmStorageMode m_storageMode;
1062 
1063     /// Verbose PC mode (default: disabled)
1064     OFBool m_verbosePCMode;
1065 
1066     /// Dataset conversion mode (default: disabled)
1067     OFBool m_datasetConversionMode;
1068 
1069     /// Progress notification mode (default: enabled)
1070     OFBool m_progressNotificationMode;
1071 
1072     /** Returns next available message ID free to be used by SCU
1073      *  @return Next free message ID
1074      */
1075     Uint16 nextMessageID();
1076 };
1077 
1078 #endif // SCU_H
1079