File: dimstore.cc

package info (click to toggle)
dcmtk 3.6.7-9~deb12u3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 60,000 kB
  • sloc: cpp: 298,501; ansic: 47,533; makefile: 5,556; sh: 4,341; xml: 482; perl: 277; lex: 103
file content (521 lines) | stat: -rw-r--r-- 23,314 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
/*
 *
 *  Copyright (C) 1994-2021, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were partly developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *  For further copyrights, see the following paragraphs.
 *
 */

/*
**  Copyright (C) 1993/1994, OFFIS, Oldenburg University and CERIUM
**
**  This software and supporting documentation were
**  developed by
**
**    Institut OFFIS
**    Bereich Kommunikationssysteme
**    Westerstr. 10-12
**    26121 Oldenburg, Germany
**
**    Fachbereich Informatik
**    Abteilung Prozessinformatik
**    Carl von Ossietzky Universitaet Oldenburg
**    Ammerlaender Heerstr. 114-118
**    26111 Oldenburg, Germany
**
**    CERIUM
**    Laboratoire SIM
**    Faculte de Medecine
**    2 Avenue du Pr. Leon Bernard
**    35043 Rennes Cedex, France
**
**  for CEN/TC251/WG4 as a contribution to the Radiological
**  Society of North America (RSNA) 1993 Digital Imaging and
**  Communications in Medicine (DICOM) Demonstration.
**
**  THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND NEITHER OFFIS,
**  OLDENBURG UNIVERSITY NOR CERIUM MAKE ANY WARRANTY REGARDING
**  THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY OR
**  FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER
**  DISEASES OR ITS CONFORMITY TO ANY SPECIFICATION.  THE
**  ENTIRE RISK AS TO QUALITY AND PERFORMANCE OF THE SOFTWARE
**  IS WITH THE USER.
**
**  Copyright of the software and supporting documentation
**  is, unless otherwise stated, jointly owned by OFFIS,
**  Oldenburg University and CERIUM and free access is hereby
**  granted as a license to use this software, copy this
**  software and prepare derivative works based upon this
**  software. However, any distribution of this software
**  source code or supporting documentation or derivative
**  works (source code and supporting documentation) must
**  include the three paragraphs of this copyright notice.
**
*/

/*
**
** Author: Andrew Hewett                Created: 03-06-93
**
** Module: dimstore
**
** Purpose:
**      This file contains the routines which help with
**      storage services.
**
** Module Prefix: DIMSE_
*/


/*
** Include Files
*/

#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#include "dcmtk/dcmnet/diutil.h"
#include "dcmtk/dcmnet/dimse.h"        /* always include the module header */
#include "dcmtk/dcmnet/cond.h"
#include "dcmtk/dcmdata/dcostrmf.h"    /* for class DcmOutputFileStream */
#include "dcmtk/ofstd/ofstd.h"         /* for OFStandard::getFileSize() */


/* Global flag to enable/disable workaround code for some buggy Store SCUs
 * in DIMSE_storeProvider().  If enabled, an illegal space-padding in the
 * Affected SOP Instance UID field of the C-STORE-RQ message is retained
 * in the corresponding C-STORE-RSP message.
 * To enable the workaround, this flag must be set to OFTrue and
 * dcmEnableAutomaticInputDataCorrection must be set to OFFalse.
 * (see declaration in dcmdata/include/dcobject.h)
 */
OFGlobal<OFBool> dcmPeerRequiresExactUIDCopy(OFFalse);


/*
**
*/

typedef struct {
    void *callbackData;
    T_DIMSE_StoreProgress *progress;
    T_DIMSE_C_StoreRQ *request;
    DIMSE_StoreUserCallback callback;
} DIMSE_PrivateUserContext;


static void
privateUserCallback(void *callbackData, unsigned long bytes)
{
    DIMSE_PrivateUserContext *ctx;
    ctx = (DIMSE_PrivateUserContext*)callbackData;
    ctx->progress->state = DIMSE_StoreProgressing;
    ctx->progress->progressBytes = bytes;
    ctx->progress->callbackCount++;
    if (ctx->callback) {
        ctx->callback(ctx->callbackData, ctx->progress, ctx->request);
    }
}

OFCondition
DIMSE_storeUser(
    T_ASC_Association *assoc, T_ASC_PresentationContextID presId,
    T_DIMSE_C_StoreRQ *request,
    const char *imageFileName, DcmDataset *imageDataSet,
    DIMSE_StoreUserCallback callback, void *callbackData,
    T_DIMSE_BlockingMode blockMode, int timeout,
    T_DIMSE_C_StoreRSP *response,
    DcmDataset **statusDetail,
    T_DIMSE_DetectedCancelParameters *checkForCancelParams,
    long imageFileTotalBytes)
    /*
     * This function transmits data from a file or a dataset to an SCP. The transmission is
     * conducted via network and using DIMSE C-STORE messages. Additionally, this function
     * evaluates C-STORE-Response messages which were received from the SCP.
     *
     * Parameters:
     *   assoc                - [in] The association (network connection to SCP).
     *   presId               - [in] The ID of the presentation context which shall be used
     *   request              - [in] Represents a DIMSE C-Store Request Message. Contains corresponding
     *                               information, e.g. message ID, affected SOP class UID, etc.
     *   imageFileName        - [in] The name of the file which is currently processed.
     *   imageDataSet         - [in] The data set which is currently processed.
     *   callback             - [in] Pointer to a function which shall be called to indicate progress.
     *   callbackData         - [in] Pointer to data which shall be passed to the progress indicating function
     *   blockMode            - [in] The blocking mode for receiving data (either DIMSE_BLOCKING or DIMSE_NONBLOCKING)
     *   timeout              - [in] Timeout interval for receiving data. If the blocking mode is DIMSE_NONBLOCKING
     *   response             - [out] Represents a DIMSE C-Store Response Message. Contains corresponding
     *                                information, e.g. message ID being responded to, affected SOP class UID, etc.
     *                                This variable contains in the end the C-STORE-RSP command which was received
     *                                as a response to the C-STORE-RQ which was sent.
     *   statusDetail         - [out] If a non-NULL value is passed this variable will in the end contain detailed
     *                                information with regard to the status information which is captured in the status
     *                                element (0000,0900) of the response message. Note that the value for element (0000,0900)
     *                                is not contained in this return value but in response.
     *   checkForCancelParams - [out] Indicates, if a C-Cancel (Request) Message was encountered. Contains corresponding
     *                                information, e.g. a boolean value if a corresponding message was encountered and the
     *                                C-Cancel (Request) Message itself (in case it actually was encountered).
     *   imageFileTotalBytes  - [in] The size of the file which is currently processed in bytes.
     */
{
    OFCondition cond = EC_Normal;
    T_DIMSE_Message req, rsp;
    DIMSE_PrivateUserContext callbackCtx;
    DIMSE_ProgressCallback privCallback = NULL;
    T_DIMSE_StoreProgress progress;
    progress.state = DIMSE_StoreBegin;
    progress.callbackCount = 0;
    progress.progressBytes = 0;
    progress.totalBytes = 0;

    /* if there is no image file or no data set, no data can be sent */
    if (imageFileName == NULL && imageDataSet == NULL) return DIMSE_NULLKEY;

    /* initialize the variables which represent DIMSE C-STORE request and DIMSE C-STORE response messages */
    memset((char*)&req, 0, sizeof(req));
    memset((char*)&rsp, 0, sizeof(rsp));

    /* set corresponding values in the request message variable */
    req.CommandField = DIMSE_C_STORE_RQ;
    request->DataSetType = DIMSE_DATASET_PRESENT;
    req.msg.CStoreRQ = *request;

    /* set up callback routine which is used to indicate progress */
    if (callback != NULL) {
        /* in case the caller indicated that he has his own progress indicating */
        /* function set some variables correspondingly so that this particular */
        /* function will be called whenever progress shall be indicated. */
        privCallback = privateUserCallback;     /* function defined above */
        callbackCtx.callbackData = callbackData;
        progress.state = DIMSE_StoreBegin;
        progress.callbackCount = 1;
        progress.progressBytes = 0;
        if (imageFileTotalBytes > 0) progress.totalBytes = imageFileTotalBytes;
        else
        {
          if (imageFileName != NULL) progress.totalBytes = OFstatic_cast(long, OFStandard::getFileSize(imageFileName));
          else progress.totalBytes = dcmGuessModalityBytes(request->AffectedSOPClassUID);
        }
        callbackCtx.progress = &progress;
        callbackCtx.request = request;
        callbackCtx.callback = callback;
        /* execute initial callback */
        callback(callbackData, &progress, request);
    } else {
        /* in case the caller does not have his own progress indicating function no */
        /* corresponding function will be called when progress shall be indicated. */
        privCallback = NULL;
    }

    /* send C-STORE-RQ message and instance data using file data or data set */
    if (imageFileName != NULL) {
        cond = DIMSE_sendMessageUsingFileData(assoc, presId, &req,
            NULL, imageFileName, privCallback, &callbackCtx);
    } else {
        cond = DIMSE_sendMessageUsingMemoryData(assoc, presId, &req,
            NULL, imageDataSet, privCallback, &callbackCtx);
    }

    if (cond != EC_Normal) {
        return cond;
    }

    /* execute final callback */
    if (callback) {
        progress.state = DIMSE_StoreEnd;
        progress.callbackCount++;
        /* execute final callback */
        callback(callbackData, &progress, request);
    }

    /* check if a C-CANCEL-RQ message was encountered earlier */
    if (checkForCancelParams != NULL) {
        checkForCancelParams->cancelEncountered = OFFalse;
    }

    /* try to receive C-STORE-RSP */
    do
    {
        /* remember the ID of the presentation context in a local variable */
        T_ASC_PresentationContextID thisPresId = presId;

        /* try to receive a C-STORE-RSP over the network. */
        cond = DIMSE_receiveCommand(assoc, blockMode, timeout,
            &thisPresId, &rsp, statusDetail);
        if (cond != EC_Normal) return cond;

        /* if everything was successful so far, the rsp variable contains the command which */
        /* was received check if we encountered a C-CANCEL-RQ; if so, set some variables */
        if (checkForCancelParams != NULL && rsp.CommandField == DIMSE_C_CANCEL_RQ)
        {
            checkForCancelParams->cancelEncountered = OFTrue;
            checkForCancelParams->req = rsp.msg.CCancelRQ;
            checkForCancelParams->presId = thisPresId;
        } else {
        /* if we did not receive a C-CANCEL-RQ */

            /* if we did also not encounter a C-STORE-RSP, something is wrong */
            if (rsp.CommandField != DIMSE_C_STORE_RSP)
            {
              char buf[256];
              sprintf(buf, "DIMSE: Unexpected Response Command Field: 0x%x", (unsigned)rsp.CommandField);
              return makeDcmnetCondition(DIMSEC_UNEXPECTEDRESPONSE, OF_error, buf);
            }

            /* if we get to here, we received a C-STORE-RSP; store this message in the reference parameter */
            *response = rsp.msg.CStoreRSP;          // BoundsChecker warning !?

            /* check if the response relates to the request which was sent earlier; if not, return an error */
            if (response->MessageIDBeingRespondedTo != request->MessageID)
            {
              char buf2[256];
              sprintf(buf2, "DIMSE: Unexpected Response MsgId: %d (expected: %d)", response->MessageIDBeingRespondedTo, request->MessageID);
              return makeDcmnetCondition(DIMSEC_UNEXPECTEDRESPONSE, OF_error, buf2);
            }
        }
    } while (checkForCancelParams != NULL && rsp.CommandField == DIMSE_C_CANCEL_RQ);

    /* return result value */
    return EC_Normal;
}



OFCondition
DIMSE_sendStoreResponse(T_ASC_Association * assoc,
    T_ASC_PresentationContextID presID,
    const T_DIMSE_C_StoreRQ *request,
    T_DIMSE_C_StoreRSP *response,
    DcmDataset *statusDetail)
    /*
     * This function takes care of sending a C-STORE-RSP message over the network to the DICOM
     * application this application is connected with.
     *
     * Parameters:
     *   assoc        - [in] The association (network connection to another DICOM application).
     *   presID       - [in] The ID of the presentation context which was specified in the PDV
     *                       which contained the DIMSE C-STORE-RQ command.
     *   request      - [in] The DIMSE C-STORE-RQ command which was received earlier.
     *   response     - [inout] The C-STORE-RSP command which shall be sent. Might be modified.
     *   statusDetail - [in] Detailed information with regard to the status information which is captured
     *                       in the status element (0000,0900). Note that the value for element (0000,0900)
     *                       is contained in this variable.
     */
{
    OFCondition         cond = EC_Normal;
    T_DIMSE_Message     rsp;

    /* create response message */
    memset((char*)&rsp, 0, sizeof(rsp));
    rsp.CommandField = DIMSE_C_STORE_RSP;
    response->MessageIDBeingRespondedTo = request->MessageID;
    OFStandard::strlcpy(response->AffectedSOPClassUID, request->AffectedSOPClassUID, sizeof(response->AffectedSOPClassUID));
    OFStandard::strlcpy(response->AffectedSOPInstanceUID, request->AffectedSOPInstanceUID, sizeof(response->AffectedSOPInstanceUID));
    response->opts = (O_STORE_AFFECTEDSOPCLASSUID | O_STORE_AFFECTEDSOPINSTANCEUID);
    response->DataSetType = DIMSE_DATASET_NULL;
    rsp.msg.CStoreRSP = *response;

    /* send response message over the network */
    cond = DIMSE_sendMessageUsingMemoryData(assoc, presID, &rsp, statusDetail, NULL, NULL, NULL);

    /* return result value */
    return cond;
}


typedef struct {
    void *callbackData;
    T_DIMSE_StoreProgress *progress;
    T_DIMSE_C_StoreRQ *request;
    char *imageFileName;
    DcmDataset **imageDataSet;
    T_DIMSE_C_StoreRSP *response;
    DcmDataset **statusDetail;
    DIMSE_StoreProviderCallback callback;
} DIMSE_PrivateProviderContext;

static void
privateProviderCallback(void *callbackData, unsigned long bytes)
{
    DIMSE_PrivateProviderContext *ctx;
    ctx = (DIMSE_PrivateProviderContext*)callbackData;
    ctx->progress->state = DIMSE_StoreProgressing;
    ctx->progress->progressBytes = bytes;
    ctx->progress->callbackCount++;
    if (ctx->callback) {
        ctx->callback(ctx->callbackData, ctx->progress, ctx->request,
            ctx->imageFileName, ctx->imageDataSet, ctx->response,
            ctx->statusDetail);
    }
}


OFCondition
DIMSE_storeProvider( T_ASC_Association *assoc,
    T_ASC_PresentationContextID presIdCmd,
    T_DIMSE_C_StoreRQ *request,
    const char* imageFileName, int writeMetaheader,
    DcmDataset **imageDataSet,
    DIMSE_StoreProviderCallback callback, void *callbackData,
    T_DIMSE_BlockingMode blockMode, int timeout)
    /*
     * This function receives a data set over the network and either stores this data in a file (exactly as it was
     * received) or it stores this data in memory. Before, during and after the process of receiving data, the callback
     * function which was provided by the caller (if it was provided) will be called to indicate progress.
     *
     * Parameters:
     *   assoc           - [in] The association (network connection to another DICOM application).
     *   presIDCmd       - [in] The ID of the presentation context which was specified in the PDV which contained
     *                          the DIMSE command.
     *   request         - [in] The DIMSE C-STORE-RQ message that was received.
     *   imageFileName   - [in] If this variable does not equal NULL, the information (which was received over the network)
     *                          will be written to a file exactly as it was received over the network. In such a case, this
     *                          this variable contains the name of the file the information shall be written to.
     *   writeMetaheader - [in] Specifies if the resulting file shall only contain the dataset which was received
     *                          (OFFalse) or if it shall contain both metaheader and dataset information (OFTrue)
     *                          (i.e if the file will be written according to the DICOM file format).
     *   imageDataSet    - [inout] If this variable does not equal NULL, and at the same time imageFileName equals NULL,
     *                          this variable will in the end contain the information which was received over the network.
     *                          Note that this function assumes that either imageFileName or imageDataSet does not equal NULL.
     *   callback        - [in] Pointer to a function which shall be called to indicate progress.
     *   callbackData    - [in] Pointer to data which shall be passed to the progress indicating function
     *   blockMode       - [in] The blocking mode for receiving data (either DIMSE_BLOCKING or DIMSE_NONBLOCKING)
     *   timeout         - [in] Timeout interval for receiving data (if the blocking mode is DIMSE_NONBLOCKING).
     */
{
    OFCondition cond = EC_Normal;
    DIMSE_PrivateProviderContext callbackCtx;
    DIMSE_ProgressCallback privCallback = NULL;
    T_ASC_PresentationContextID presIdData = 0;
    T_DIMSE_C_StoreRSP response;
    DcmDataset *statusDetail = NULL;
    T_DIMSE_StoreProgress progress;

    /* initialize progress struct */
    progress.state = DIMSE_StoreBegin;
    progress.callbackCount = 1;
    progress.progressBytes = 0;
    progress.totalBytes = 0;

    /* initialize the C-STORE-RSP message variable */
    memset((char*)&response, 0, sizeof(response));
    response.DimseStatus = STATUS_STORE_Success;      /* assume */
    response.MessageIDBeingRespondedTo = request->MessageID;
    response.DataSetType = DIMSE_DATASET_NULL;  /* always for C-STORE-RSP */
    OFStandard::strlcpy(response.AffectedSOPClassUID, request->AffectedSOPClassUID, sizeof(response.AffectedSOPClassUID));
    OFStandard::strlcpy(response.AffectedSOPInstanceUID, request->AffectedSOPInstanceUID, sizeof(response.AffectedSOPInstanceUID));
    response.opts = (O_STORE_AFFECTEDSOPCLASSUID | O_STORE_AFFECTEDSOPINSTANCEUID);
    if (request->opts & O_STORE_RQ_BLANK_PADDING) response.opts |= O_STORE_RSP_BLANK_PADDING;
    if (dcmPeerRequiresExactUIDCopy.get()) response.opts |= O_STORE_PEER_REQUIRES_EXACT_UID_COPY;

    /* set up callback routine */
    if (callback != NULL) {
        /* only if caller requires */
        privCallback = privateProviderCallback; /* function defined above */
        callbackCtx.callbackData = callbackData;
        progress.totalBytes = dcmGuessModalityBytes(request->AffectedSOPClassUID);
        callbackCtx.progress = &progress;
        callbackCtx.request = request;
        callbackCtx.imageFileName = (char*)imageFileName;
        callbackCtx.imageDataSet = imageDataSet;
        callbackCtx.response = &response;
        callbackCtx.statusDetail = &statusDetail;
        callbackCtx.callback = callback;
        /* execute initial callback */
        callback(callbackData, &progress, request,
            (char*)imageFileName, imageDataSet,
            &response, &statusDetail);
    } else {
        privCallback = NULL;
    }

    /* in the following, we want to receive data over the network and do something with this data. If the */
    /* imageFileName does not equal NULL, the caller required that the data shall be written to a file */
    /* exactly the way it was received over the network. Hence, a filestream will be created and the data */
    /* set will be received and written to the file through the call to DIMSE_receiveDataSetInFile(...).*/
    /* If the imageFileName does equal NULL but at the same time imageDataSet does not equal NULL, the */
    /* data shall be received and stored in memory. This will be handled through the call to function */
    /* DIMSE_receiveDataSetInMemory(...). The case in which both variables are NULL is considered to */
    /* be an error and will be handled correspondingly. */
    if (imageFileName != NULL) {
        /* create filestream */
        DcmOutputFileStream *filestream = NULL;
        cond = DIMSE_createFilestream(imageFileName, request, assoc, presIdCmd, writeMetaheader, &filestream);
        if (cond.bad())
        {
          /* We cannot create the filestream, so ignore the incoming dataset and return an out-of-resources error to the SCU */
          DIC_UL bytesRead = 0;
          DIC_UL pdvCount=0;
          cond = DIMSE_ignoreDataSet(assoc, blockMode, timeout, &bytesRead, &pdvCount);
          if (cond.good())
          {
            OFString s = "DIMSE_storeProvider: Cannot create file: ";
            s += imageFileName;
            cond = makeDcmnetCondition(DIMSEC_OUTOFRESOURCES, OF_error, s.c_str());
          }
        } else {
          /* if no error occurred, receive data and write it to the file */
          cond = DIMSE_receiveDataSetInFile(assoc, blockMode, timeout, &presIdData, filestream, privCallback, &callbackCtx);
          delete filestream;
          if (cond != EC_Normal)
          {
            if (strcmp(imageFileName, NULL_DEVICE_NAME) != 0) OFStandard::deleteFile(imageFileName);
          }
        }
    }
    else if (imageDataSet != NULL)
    {
        /* receive data and store it in memory */
        cond = DIMSE_receiveDataSetInMemory(assoc, blockMode, timeout,
            &presIdData, imageDataSet, privCallback, &callbackCtx);
    } else {
        /* if both variables are set to NULL, report an error */
        return DIMSE_BADDATA;
    }

    /* check if presentation context IDs of the command (which was received earlier) and of the data */
    /* set (which was received just now) differ from each other. If this is the case, return an error. */
    if (cond.good() && (presIdData != presIdCmd))
    {
        cond = makeDcmnetCondition(DIMSEC_INVALIDPRESENTATIONCONTEXTID, OF_error, "DIMSE: Presentation Contexts of Command and Data Differ");
    }

    /* depending on the error status, set the success indicating flag in the response message */
    if (cond == EC_Normal) {
        response.DimseStatus = STATUS_STORE_Success;
    } else if (cond == DIMSE_OUTOFRESOURCES) {
        response.DimseStatus = STATUS_STORE_Refused_OutOfResources;
    } else {
        return cond;
    }

    /* execute final callback (user does not have to provide callback) */
    if (callback) {
        progress.state = DIMSE_StoreEnd;
        progress.callbackCount++;
        /* execute final callback */
        callback(callbackData, &progress, request,
            (char*)imageFileName, imageDataSet,
            &response, &statusDetail);
    }

    /* send a C-STORE-RSP message over the network to the other DICOM application */
    OFCondition cond2 = DIMSE_sendStoreResponse(assoc, presIdCmd, request, &response, statusDetail);

    /* if we already had an error condition, don't overwrite */
    if (cond.good()) cond = cond2;

    /* return result value */
    return cond;
}