1 /*
2  *
3  *  Copyright (C) 1994-2019, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were partly developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *  For further copyrights, see the following paragraphs.
14  *
15  */
16 
17 /*
18 **  Copyright (C) 1993/1994, OFFIS, Oldenburg University and CERIUM
19 **
20 **  This software and supporting documentation were
21 **  developed by
22 **
23 **    Institut OFFIS
24 **    Bereich Kommunikationssysteme
25 **    Westerstr. 10-12
26 **    26121 Oldenburg, Germany
27 **
28 **    Fachbereich Informatik
29 **    Abteilung Prozessinformatik
30 **    Carl von Ossietzky Universitaet Oldenburg
31 **    Ammerlaender Heerstr. 114-118
32 **    26111 Oldenburg, Germany
33 **
34 **    CERIUM
35 **    Laboratoire SIM
36 **    Faculte de Medecine
37 **    2 Avenue du Pr. Leon Bernard
38 **    35043 Rennes Cedex, France
39 **
40 **  for CEN/TC251/WG4 as a contribution to the Radiological
41 **  Society of North America (RSNA) 1993 Digital Imaging and
42 **  Communications in Medicine (DICOM) Demonstration.
43 **
44 **  THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND NEITHER OFFIS,
45 **  OLDENBURG UNIVERSITY NOR CERIUM MAKE ANY WARRANTY REGARDING
46 **  THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY OR
47 **  FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER
48 **  DISEASES OR ITS CONFORMITY TO ANY SPECIFICATION.  THE
49 **  ENTIRE RISK AS TO QUALITY AND PERFORMANCE OF THE SOFTWARE
50 **  IS WITH THE USER.
51 **
52 **  Copyright of the software and supporting documentation
53 **  is, unless otherwise stated, jointly owned by OFFIS,
54 **  Oldenburg University and CERIUM and free access is hereby
55 **  granted as a license to use this software, copy this
56 **  software and prepare derivative works based upon this
57 **  software. However, any distribution of this software
58 **  source code or supporting documentation or derivative
59 **  works (source code and supporting documentation) must
60 **  include the three paragraphs of this copyright notice.
61 **
62 */
63 
64 /*
65 **
66 ** Author: Andrew Hewett                Created: 03-06-93
67 **
68 ** Module: dimse
69 **
70 ** Purpose:
71 **      This file contains the routines which provide dimse layer services
72 **      for DICOM V.3 applications.
73 **
74 ** Module Prefix: DIMSE_
75 */
76 
77 /*
78 ** Include Files
79 */
80 
81 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
82 
83 #define INCLUDE_CSTDLIB
84 #define INCLUDE_CSTDIO
85 #define INCLUDE_CSTRING
86 #define INCLUDE_CSTDARG
87 #define INCLUDE_UNISTD
88 #include "dcmtk/ofstd/ofstdinc.h"
89 
90 #ifdef HAVE_UNIX_H
91 #if defined(macintosh) && defined (HAVE_WINSOCK_H)
92 /* unix.h defines timeval incompatible with winsock.h */
93 #define timeval _UNWANTED_timeval
94 #endif
95 #include <unix.h>       /* for unlink() under Metrowerks C++ (Macintosh) */
96 #undef timeval
97 #endif
98 #ifdef HAVE_FCNTL_H
99 #include <fcntl.h>
100 #endif
101 
102 #include "dcmtk/dcmnet/diutil.h"
103 #include "dcmtk/dcmnet/dimse.h"        /* always include the module header */
104 #include "dcmtk/dcmnet/cond.h"
105 #include "dimcmd.h"
106 #include "dcmtk/dcmdata/dcdeftag.h"    /* for tag names */
107 #include "dcmtk/dcmdata/dcdict.h"      /* for dcmDataDict */
108 #include "dcmtk/dcmdata/dcfilefo.h"    /* for class DcmFileFormat */
109 #include "dcmtk/dcmdata/dcmetinf.h"    /* for class DcmMetaInfo */
110 #include "dcmtk/dcmdata/dcistrmb.h"    /* for class DcmInputBufferStream */
111 #include "dcmtk/dcmdata/dcostrmb.h"    /* for class DcmOutputBufferStream */
112 #include "dcmtk/dcmdata/dcostrmf.h"    /* for class DcmOutputFileStream */
113 #include "dcmtk/dcmdata/dcvrul.h"      /* for class DcmUnsignedLong */
114 #include "dcmtk/dcmdata/dcvrobow.h"    /* for class DcmOtherByteOtherWord */
115 #include "dcmtk/dcmdata/dcvrsh.h"      /* for class DcmShortString */
116 #include "dcmtk/dcmdata/dcvrae.h"      /* for class DcmApplicationEntity */
117 #include "dcmtk/dcmdata/dcdicent.h"    /* for class DcmDictEntry, needed for MSVC5 */
118 #include "dcmtk/dcmdata/dcwcache.h"    /* for class DcmWriteCache */
119 #include "dcmtk/dcmdata/dcvrui.h"      /* for class DcmUniqueIdentifier */
120 
121 
122 /*
123  * Global variables, mutex protected
124  */
125 
126 /*  global flag allowing to restrict the maximum size of outgoing
127  *  P-DATA PDUs to a value less than the maximum supported by the
128  *  remote application entity or this library.  May be useful
129  *  if there is an interaction between PDU size and other network
130  *  layers, e. g. TLS, IP or below.
131  */
132 OFGlobal<Uint32> dcmMaxOutgoingPDUSize((Uint32) -1);
133 
134 /*
135  * Other global variables (should be used very, very rarely).
136  * Modification of this variables is THREAD UNSAFE.
137  */
138 
139 /*
140  * Define global defaults for data encoding when sending out data-sets.
141  * These can be adjusted to allow variants to be tested.
142  */
143 
144 E_GrpLenEncoding  g_dimse_send_groupLength_encoding = EGL_recalcGL;
145 E_EncodingType    g_dimse_send_sequenceType_encoding = EET_ExplicitLength;
146 
147 /*
148  * These globals control the options for saving all DIMSE messages
149  */
150 
151 OFBool g_dimse_save_dimse_data = OFFalse;
152 static unsigned long g_dimse_commandCounter = 0;
153 static unsigned long g_dimse_dataCounter = 0;
154 
155 
156 /*
157 ** Private Functions Bodies
158 */
159 
saveDimseFragment(DcmDataset * dset,OFBool isCommand,OFBool isReceive)160 static void saveDimseFragment(
161         DcmDataset *dset,
162         OFBool isCommand,
163         OFBool isReceive)
164     /*
165      * This function saves the information which is contained in dset to a file
166      *
167      * Parameters:
168      *   dset      - [in] Contains the information which shall be written to a file.
169      *   isCommand - [in] Specifies if dset's information refers to a DIMSE command (as for
170      *                    example C-STORE) (OFTrue) or if it refers to instance data (OFFalse).
171      *   isReceive - [in] Specifies if this application received (OFTrue) or sent (OFFalse)
172      *                    the information in dset.
173      */
174 {
175   /* depending on if the information in dset refers to a DIMSE command or not, set some global variables */
176   if (isCommand)
177   {
178     g_dimse_commandCounter++;
179     g_dimse_dataCounter = 0;
180   } else g_dimse_dataCounter++;
181 
182   /* if there is no information exit this function */
183   if (dset==NULL) return;
184 
185   /* create a string which will contain the name of the file we are about to write to */
186   /* filenames are supposed to look like "dimse-cmd-[rcv|snd]-[counter].dcm" */
187   const char *transmission = (isReceive ? "rcv" : "snd");
188   char filename[2048];
189   if (isCommand)
190   {
191     sprintf(filename, "dimse-cmd-%s-%04lu.dcm", transmission, g_dimse_commandCounter);
192   } else {
193     if (g_dimse_dataCounter < 2)
194     {
195       sprintf(filename, "dimse-dat-%s-%04lu.dcm", transmission, g_dimse_commandCounter);
196     } else {
197       sprintf(filename, "dimse-dat-%s-%04lu-%02lu.dcm", transmission, g_dimse_commandCounter, g_dimse_dataCounter);
198     }
199   }
200 
201   /* write data to file */
202   dset->saveFile(filename, EXS_LittleEndianImplicit, EET_ExplicitLength, EGL_recalcGL, EPD_withoutPadding);
203   return;
204 }
205 
206 static OFBool
isDataDictPresent()207 isDataDictPresent()
208 {
209     /* is a data dictionary present */
210     return dcmDataDict.isDictionaryLoaded();
211 }
212 
213 /*
214  * PDV Reading
215  */
216 
217 static OFCondition
DIMSE_readNextPDV(T_ASC_Association * assoc,T_DIMSE_BlockingMode blocking,int timeout,DUL_PDV * pdv)218 DIMSE_readNextPDV(
219         T_ASC_Association *assoc,
220         T_DIMSE_BlockingMode blocking,
221         int timeout,
222         DUL_PDV *pdv)
223     /*
224      * This function returns the next PDV which was (earlier or just now) received on the incoming
225      * socket stream. If there are no PDVs (which were already received earlier) waiting to be picked
226      * up, this function will go ahead and read a new PDU (containing one or more new PDVs) from the
227      * incoming socket stream.
228      *
229      * Parameters:
230      *   assoc    - [in] The association (network connection to another DICOM application).
231      *   blocking - [in] The blocking mode for reading data (either DIMSE_BLOCKING or DIMSE_NONBLOCKING)
232      *   timeout  - [in] Timeout interval for receiving data. If the blocking mode is DIMSE_NONBLOCKING
233      *                   and we are trying to read data from the incoming socket stream and no data has
234      *                   been received after timeout seconds, an error will be reported.
235      *   pdv      - [out] Contains in the end the next PDV which was (earlier or just now) received on
236      *                    the incoming socket stream.
237      *
238      */
239 {
240     DUL_BLOCKOPTIONS blk;
241 
242     /*
243      * NOTE: DUL currently ignores blocking and timeout so do it here!
244      */
245 
246     /* determine the DUL blocking option */
247     blk = (blocking == DIMSE_BLOCKING) ? (DUL_BLOCK) : (DUL_NOBLOCK);
248 
249     /* get the next PDV from the association, in case there are still some PDVs waiting to be picked up */
250     OFCondition cond = DUL_NextPDV(&assoc->DULassociation, pdv);
251 
252     if (cond.bad())
253     {
254         /* in case DUL_NextPDV(...) did not return DUL_NORMAL, the association */
255         /* did not contain any more PDVs that are waiting to be picked up. Hence, */
256         /* we need to read new PDVs from the incoming socket stream. */
257 
258         /* if the blocking mode is DIMSE_NONBLOCKING and there is no data waiting after timeout seconds, report an error */
259         if (blocking == DIMSE_NONBLOCKING)
260         {
261             if (!ASC_dataWaiting(assoc, timeout)) return DIMSE_NODATAAVAILABLE;
262         }
263 
264         /* try to receive new PDVs on the incoming socket stream (in detail, try to receive one PDU) */
265         cond = DUL_ReadPDVs(&assoc->DULassociation, NULL, blk, timeout);
266 
267         /* check return value, if it is different from DUL_PDATAPDUARRIVED, an error occurred */
268         if (cond != DUL_PDATAPDUARRIVED)
269         {
270             if (cond == DUL_NULLKEY || cond == DUL_ILLEGALKEY) return DIMSE_ILLEGALASSOCIATION;
271             else if (cond == DUL_PEERREQUESTEDRELEASE ||
272                      cond == DUL_PEERABORTEDASSOCIATION) return cond;
273             else return makeDcmnetSubCondition(DIMSEC_READPDVFAILED, OF_error, "DIMSE Read PDV failed", cond);
274         }
275 
276         /* get the next PDV, assign it to pdv */
277         cond = DUL_NextPDV(&assoc->DULassociation, pdv);
278         if (cond.bad())
279         {
280             return makeDcmnetSubCondition(DIMSEC_READPDVFAILED, OF_error, "DIMSE Read PDV failed", cond);
281         }
282     }
283 
284     /* return ok */
285     return EC_Normal;
286 }
287 
288 /*
289  * Checking and Validation
290  */
291 
292 
293 static OFCondition
getTransferSyntax(T_ASC_Association * assoc,T_ASC_PresentationContextID pid,E_TransferSyntax * xferSyntax)294 getTransferSyntax(
295         T_ASC_Association *assoc,
296         T_ASC_PresentationContextID pid,
297         E_TransferSyntax *xferSyntax)
298     /*
299      * This function checks if the presentation context id which was passed refers to a valid presentation
300      * context. If this is the case, this function determines the transfer syntax the presentation context ID
301      * refers to (will be returned to the user) and also checks if dcmtk supports this transfer syntax.
302      *
303      * Parameters:
304      *   assoc      - [in] The association (network connection to another DICOM application).
305      *   pid        - [in] The id of the presentation context which shall be checked regarding validity.
306      *   xferSyntax - [out] If pid refers to a valid presentation context, this variable contains in the
307      *                     end the transfer syntax which is specified in the presentation context.
308      */
309 {
310     T_ASC_PresentationContext pc;
311     char *ts = NULL;
312 
313     /* figure out if is this a valid presentation context */
314     OFCondition cond = ASC_findAcceptedPresentationContext(assoc->params, pid, &pc);
315     if (cond.bad())
316     {
317         return makeDcmnetSubCondition(DIMSEC_RECEIVEFAILED, OF_error, "DIMSE Failed to receive message", cond);
318     }
319 
320     /* determine the transfer syntax which is specified in the presentation context */
321     ts = pc.acceptedTransferSyntax;
322 
323     /* create a DcmXfer object on the basis of the transfer syntax which was determined above */
324     DcmXfer xfer(ts);
325 
326     /* check if the transfer syntax is supported by dcmtk */
327     *xferSyntax = xfer.getXfer();
328     switch (*xferSyntax)
329     {
330         case EXS_LittleEndianImplicit:
331         case EXS_LittleEndianExplicit:
332         case EXS_BigEndianExplicit:
333         case EXS_JPEGProcess1:
334         case EXS_JPEGProcess2_4:
335         case EXS_JPEGProcess3_5:
336         case EXS_JPEGProcess6_8:
337         case EXS_JPEGProcess7_9:
338         case EXS_JPEGProcess10_12:
339         case EXS_JPEGProcess11_13:
340         case EXS_JPEGProcess14:
341         case EXS_JPEGProcess15:
342         case EXS_JPEGProcess16_18:
343         case EXS_JPEGProcess17_19:
344         case EXS_JPEGProcess20_22:
345         case EXS_JPEGProcess21_23:
346         case EXS_JPEGProcess24_26:
347         case EXS_JPEGProcess25_27:
348         case EXS_JPEGProcess28:
349         case EXS_JPEGProcess29:
350         case EXS_JPEGProcess14SV1:
351         case EXS_RLELossless:
352         case EXS_JPEGLSLossless:
353         case EXS_JPEGLSLossy:
354         case EXS_JPEG2000LosslessOnly:
355         case EXS_JPEG2000:
356         case EXS_JPEG2000MulticomponentLosslessOnly:
357         case EXS_JPEG2000Multicomponent:
358         case EXS_MPEG2MainProfileAtMainLevel:
359         case EXS_MPEG2MainProfileAtHighLevel:
360         case EXS_MPEG4HighProfileLevel4_1:
361         case EXS_MPEG4BDcompatibleHighProfileLevel4_1:
362         case EXS_MPEG4HighProfileLevel4_2_For2DVideo:
363         case EXS_MPEG4HighProfileLevel4_2_For3DVideo:
364         case EXS_MPEG4StereoHighProfileLevel4_2:
365         case EXS_HEVCMainProfileLevel5_1:
366         case EXS_HEVCMain10ProfileLevel5_1:
367 #ifdef WITH_ZLIB
368         case EXS_DeflatedLittleEndianExplicit:
369 #endif
370         /* OK, these can be supported */
371         break;
372     default:
373         /* all other transfer syntaxes are not supported; hence, set the error indicator variable */
374         {
375           char buf[256];
376           sprintf(buf, "DIMSE Unsupported transfer syntax: %s", ts);
377           OFCondition subCond = makeDcmnetCondition(DIMSEC_UNSUPPORTEDTRANSFERSYNTAX, OF_error, buf);
378           cond = makeDcmnetSubCondition(DIMSEC_RECEIVEFAILED, OF_error, "DIMSE Failed to receive message", subCond);
379         }
380         break;
381     }
382 
383     /* return result value */
384     return cond;
385 }
386 
387 
388 static OFCondition
checkPresentationContextForMessage(T_ASC_Association * assoc,T_DIMSE_Message * msg,T_ASC_PresentationContextID presID,E_TransferSyntax * xferSyntax)389 checkPresentationContextForMessage(
390         T_ASC_Association *assoc,
391         T_DIMSE_Message *msg,
392         T_ASC_PresentationContextID presID,
393         E_TransferSyntax *xferSyntax)
394     /*
395      * This function checks if the presentation context id refers to a valid presentation context and
396      * determines the transfer syntax which is specified for this presentation context. Additionally,
397      * the function checks if the specified transfer syntax is supported at all. If any of the checks
398      * returns an error, return a corresponding error..
399      *
400      * Parameters:
401      *   assoc                - [in] The association (network connection to another DICOM application).
402      *   msg                  - [in] Structure that represents a certain DIMSE message.
403      *   presID               - [in] The ID of the presentation context which shall be used.
404      *   xferSyntax           - [out] Contains in the end the transfer syntax which is specified
405      *                                in the presentation context with the corresponding ID.
406      */
407 {
408     /* char *as; */     /* abstract syntax */
409     /* char *ts; */     /* transfer syntax */
410     T_ASC_PresentationContext pc;
411 
412     /* try to find the accepted presentation context on the basis of the ID which was passed */
413     OFCondition cond = ASC_findAcceptedPresentationContext(assoc->params, presID, &pc);
414 
415     /*
416      * as = pc.abstractSyntax;
417      * ts = pc.acceptedTransferSyntax;
418      */
419 
420     /* if the accepted presentation context was found, check if the message type is supported */
421     if (cond.good())
422     {
423       switch (msg->CommandField)
424       {
425         case DIMSE_C_ECHO_RQ:
426         case DIMSE_C_ECHO_RSP:
427         case DIMSE_C_STORE_RQ:
428         case DIMSE_C_STORE_RSP:
429         case DIMSE_C_GET_RQ:
430         case DIMSE_C_GET_RSP:
431         case DIMSE_C_FIND_RQ:
432         case DIMSE_C_FIND_RSP:
433         case DIMSE_C_MOVE_RQ:
434         case DIMSE_C_MOVE_RSP:
435         case DIMSE_C_CANCEL_RQ:
436         case DIMSE_N_EVENT_REPORT_RQ:
437         case DIMSE_N_EVENT_REPORT_RSP:
438         case DIMSE_N_GET_RQ:
439         case DIMSE_N_GET_RSP:
440         case DIMSE_N_SET_RQ:
441         case DIMSE_N_SET_RSP:
442         case DIMSE_N_ACTION_RQ:
443         case DIMSE_N_ACTION_RSP:
444         case DIMSE_N_CREATE_RQ:
445         case DIMSE_N_CREATE_RSP:
446         case DIMSE_N_DELETE_RQ:
447         case DIMSE_N_DELETE_RSP:
448             break;
449 
450         default:
451             cond = DIMSE_BADCOMMANDTYPE;
452             break;
453       }
454     }
455 
456     /* if everything was ok so far determine the transfer syntax */
457     /* which is specified in the accepted presentation context */
458     if (cond.good()) cond = getTransferSyntax(assoc, presID, xferSyntax);
459 
460     /* otherwise return ok */
461     return cond;
462 }
463 
464 static OFCondition
validateMessage(T_ASC_Association * assoc,T_DIMSE_Message * msg)465 validateMessage(
466         T_ASC_Association *assoc,
467         T_DIMSE_Message *msg)
468     /*
469      * This function checks if the information which is contained in msg meets certain conditions.
470      * For example, if msg represents a C-ECHO-RQ, then there is not supposed to be a corresponding
471      * data set. If the specified conditions are not met, return an error.
472      *
473      * Parameters:
474      *   assoc - [in] The association (network connection to another DICOM application).
475      *   msg   - [in] Structure that represents a certain DIMSE message.
476      */
477 {
478     OFCondition cond = EC_Normal;
479 
480     /* check certain conditions depending on the message type */
481     switch (msg->CommandField) {
482     case DIMSE_C_ECHO_RQ:
483         if (msg->msg.CEchoRQ.DataSetType != DIMSE_DATASET_NULL) {
484             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Echo RQ: DataSetType != NULL");
485             cond = DIMSE_BADMESSAGE;
486         }
487         break;
488     case DIMSE_C_ECHO_RSP:
489         if (msg->msg.CEchoRSP.DataSetType != DIMSE_DATASET_NULL) {
490             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Echo RSP: DataSetType != NULL");
491             cond = DIMSE_BADMESSAGE;
492         }
493         break;
494     case DIMSE_C_STORE_RQ:
495         if (msg->msg.CStoreRQ.DataSetType == DIMSE_DATASET_NULL) {
496             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Store RQ: DataSetType == NULL");
497             cond = DIMSE_BADMESSAGE;
498         }
499         if (! IN_RANGE(strlen(msg->msg.CStoreRQ.AffectedSOPInstanceUID), 1, DIC_UI_LEN)) {
500             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Store RQ: AffectedSOPInstanceUID: bad size");
501             cond = DIMSE_BADMESSAGE;
502         }
503         break;
504     case DIMSE_C_STORE_RSP:
505         if (msg->msg.CStoreRSP.DataSetType != DIMSE_DATASET_NULL) {
506             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Store RSP: DataSetType != NULL");
507             cond = DIMSE_BADMESSAGE;
508         }
509         if ((msg->msg.CStoreRSP.opts & O_STORE_AFFECTEDSOPINSTANCEUID) &&
510             (! IN_RANGE(strlen(msg->msg.CStoreRSP.AffectedSOPInstanceUID), 1, DIC_UI_LEN))) {
511             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Store RSP: AffectedSOPInstanceUID: bad size");
512             cond = DIMSE_BADMESSAGE;
513         }
514         break;
515     case DIMSE_C_GET_RQ:
516         if (msg->msg.CGetRQ.DataSetType == DIMSE_DATASET_NULL) {
517             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Get RQ: DataSetType == NULL");
518             cond = DIMSE_BADMESSAGE;
519         }
520         break;
521     case DIMSE_C_GET_RSP:
522         /* data set can be empty or present */
523         break;
524     case DIMSE_C_FIND_RQ:
525         if (msg->msg.CFindRQ.DataSetType == DIMSE_DATASET_NULL) {
526             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Find RQ: DataSetType == NULL");
527             cond = DIMSE_BADMESSAGE;
528         }
529         break;
530     case DIMSE_C_FIND_RSP:
531         /* data set can be empty or present */
532         break;
533     case DIMSE_C_MOVE_RQ:
534         if (msg->msg.CMoveRQ.DataSetType == DIMSE_DATASET_NULL) {
535             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Move RQ: DataSetType == NULL");
536             cond = DIMSE_BADMESSAGE;
537         }
538         break;
539     case DIMSE_C_MOVE_RSP:
540         /* data set can be empty or present */
541         break;
542     case DIMSE_C_CANCEL_RQ:
543         if (msg->msg.CCancelRQ.DataSetType != DIMSE_DATASET_NULL) {
544             DCMNET_WARN(DIMSE_warn_str(assoc) << "C-Cancel RQ: DataSetType != NULL");
545             cond = DIMSE_BADMESSAGE;
546         }
547         break;
548 
549     case DIMSE_N_EVENT_REPORT_RQ:
550     case DIMSE_N_EVENT_REPORT_RSP:
551     case DIMSE_N_GET_RQ:
552     case DIMSE_N_GET_RSP:
553     case DIMSE_N_SET_RQ:
554     case DIMSE_N_SET_RSP:
555     case DIMSE_N_ACTION_RQ:
556     case DIMSE_N_ACTION_RSP:
557     case DIMSE_N_CREATE_RQ:
558     case DIMSE_N_CREATE_RSP:
559     case DIMSE_N_DELETE_RQ:
560     case DIMSE_N_DELETE_RSP:
561         /* No checking on the normalized messages yet, assume ok */
562         break;
563 
564     default:
565         cond = DIMSE_BADCOMMANDTYPE;
566         break;
567     }
568 
569     return cond;
570 }
571 
572 
573 /*
574  * Message sending support routines
575  */
576 
577 #if 0
578 /*** Currently Unused */
579 /*
580 ** SendStraightFileData does not work for files with a Metaheader.
581 ** It would need to be rewritten to read data after the Metaheader.
582 ** However, finding out where the Metaheader ends means parsing the
583 ** whole file in the current implementation.  It is thus not worth
584 ** the effort!
585 */
586 static OFCondition
587 sendStraightFileData(
588         T_ASC_Association *assoc,
589         const char *dataFileName,
590         T_ASC_PresentationContextID presID,
591         E_TransferSyntax /* xferSyntax */,
592         DIMSE_ProgressCallback callback,
593         void *callbackContext)
594 
595 {
596     /* we assume that the file contains transfer syntax compatible data */
597     unsigned char *buf;
598     unsigned long bufLen;
599     FILE *f;
600     long nbytes;
601     OFBool last;
602     unsigned long bytesTransmitted = 0;
603     OFCondition dulCond = EC_Normal;
604     DUL_PDVLIST pdvList;
605     DUL_PDV pdv;
606     /* the following variable is currently unused, leave it for future use */
607     unsigned long pdvCount = 0;
608 
609     buf = assoc->sendPDVBuffer;
610     bufLen = assoc->sendPDVLength;
611 
612     OFCondition cond = EC_Normal;
613 
614     f = fopen(dataFileName, "rb");
615     if (f == NULL) {
616         char buf[256];
617         DCMNET_WARN(DIMSE_warn_str(assoc) << "sendStraightFileData: cannot open DICOM file ("
618             << dataFileName << "): " << OFStandard::strerror(errno, buf, sizeof(buf)));
619         cond = DIMSE_SENDFAILED;
620     }
621 
622     while (cond.good() && ((nbytes = fread(buf, 1, bufLen, f)) > 0)) {
623         last = ((unsigned long)nbytes != bufLen);
624         pdv.fragmentLength = nbytes;
625         pdv.presentationContextID = presID;
626         pdv.pdvType = DUL_DATASETPDV;
627         pdv.lastPDV = last;
628         pdv.data = buf;
629 
630         pdvList.count = 1;
631         pdvList.pdv = &pdv;
632 
633         DCMNET_TRACE("DIMSE sendStraightFileData: sending " << pdv.fragmentLength << " bytes (last: "
634             << ((last)?("YES"):("NO")) << ")");
635 
636         dulCond = DUL_WritePDVs(&assoc->DULassociation, &pdvList);
637         if (dulCond.bad())
638         {
639             cond = makeDcmnetSubCondition(DIMSEC_SENDFAILED, OF_error, "DIMSE Failed to send message", dulCond);
640         }
641 
642         bytesTransmitted += nbytes;
643         pdvCount += pdvList.count;
644 
645         if (callback) { /* execute callback function */
646             callback(callbackContext, bytesTransmitted);
647         }
648     }
649 
650     fclose(f);
651 
652     return cond;
653 }
654 #endif
655 
656 static OFCondition
sendDcmDataset(T_ASC_Association * assoc,DcmDataset * obj,T_ASC_PresentationContextID presID,E_TransferSyntax xferSyntax,DUL_DATAPDV pdvType,DIMSE_ProgressCallback callback,void * callbackContext)657 sendDcmDataset(
658         T_ASC_Association *assoc,
659         DcmDataset *obj,
660         T_ASC_PresentationContextID presID,
661         E_TransferSyntax xferSyntax,
662         DUL_DATAPDV pdvType,
663         DIMSE_ProgressCallback callback,
664         void *callbackContext)
665     /*
666      * This function sends all information which is included in a DcmDataset object over
667      * the network which is provided in assoc.
668      *
669      * Parameters:
670      *   assoc           - [in] The association (network connection to another DICOM application).
671      *   obj             - [in] Contains the information which shall be sent over the network.
672      *   presId          - [in] The ID of the presentation context which shall be used
673      *   xferSyntax      - [in] The transfer syntax which shall be used.
674      *   pdvType         - [in] Specifies if the information in this DcmDataset object belongs to
675      *                          a DIMSE command (as for example C-STORE) (DUL_COMMANDPDV) or if
676      *                          the information is actual instance information (DUL_DATASETPDV).
677      *   callback        - [in] Pointer to a function which shall be called to indicate progress.
678      *   callbackContext - []
679      */
680 {
681     OFCondition dulCond = EC_Normal;
682     OFCondition econd = EC_Normal;
683     unsigned char *buf;
684     unsigned long bufLen;
685     OFBool last = OFFalse;
686     OFBool written = OFFalse;
687     offile_off_t rtnLength;
688     Uint32 bytesTransmitted = 0;
689     DUL_PDVLIST pdvList;
690     DUL_PDV pdv;
691     /* the following variable is currently unused, leave it for future use */
692     unsigned long pdvCount = 0;
693     DcmWriteCache wcache;
694 
695     /* initialize some local variables (we want to use the association's send buffer */
696     /* to store data) this buffer can only take a certain number of elements */
697     buf = assoc->sendPDVBuffer;
698     bufLen = assoc->sendPDVLength;
699 
700     /* we may wish to restrict output PDU size */
701     Uint32 maxpdulen = dcmMaxOutgoingPDUSize.get();
702 
703     /* max PDV size is max PDU size minus 12 bytes PDU/PDV header */
704     if (bufLen + 12 > maxpdulen)
705     {
706       bufLen = maxpdulen - 12;
707     }
708 
709     /* on the basis of the association's buffer, create a buffer variable that we can write to */
710     DcmOutputBufferStream outBuf(buf, bufLen);
711 
712     /* prepare all elements in the DcmDataset variable for transfer */
713     obj->transferInit();
714 
715     /* initialize two more variables: groupLength_encoding specifies what will be done concerning */
716     /* group length tags, sequenceType_encoding specifies how sequences will be handled */
717     E_GrpLenEncoding groupLength_encoding = g_dimse_send_groupLength_encoding;
718     E_EncodingType sequenceType_encoding = g_dimse_send_sequenceType_encoding;
719 
720     /* Mind that commands must always include group length (0000,0000) and */
721     /* that commands do not contain sequences, yet */
722     if (pdvType == DUL_COMMANDPDV)
723         groupLength_encoding = EGL_withGL;
724 
725     /* start a loop: in each iteration information from the DcmDataset object (i.e. infor- */
726     /* mation which shall be sent) will be set in the buffer (we need more than one itera- */
727     /* tion if there is more information than the buffer can take at a time), a PDV object */
728     /* with the buffer's data will be created and assigned to a list, and finally the */
729     /* list's information will be sent over the network to the other DICOM application. */
730     while (!last)
731     {
732         /* write data values which are contained in the DcmDataSet variable to the above created */
733         /* buffer. Mind the transfer syntax, the sequence type encoding, the group length encoding */
734         /* and remove all padding data elements. Depending on whether all information has been written */
735         /* to the buffer, update the variable that determines the end of the while loop. (Note that */
736         /* DcmDataset stores information about what of its content has already been sent to the buffer.) */
737         if (! written)
738         {
739           econd = obj->write(outBuf, xferSyntax, sequenceType_encoding, &wcache,
740                              groupLength_encoding, EPD_withoutPadding);
741           if (econd == EC_Normal)                   /* all contents have been written to the buffer */
742           {
743               written = OFTrue;
744           }
745           else if (econd == EC_StreamNotifyClient)  /* no more space in buffer, _not_ all elements have been written to it */
746           {
747               // nothing to do
748           }
749           else                                      /* some error has occurred */
750           {
751               DCMNET_WARN(DIMSE_warn_str(assoc) << "writeBlock Failed (" << econd.text() << ")");
752               return DIMSE_SENDFAILED;
753           }
754         }
755 
756         if (written) outBuf.flush(); // flush stream including embedded compression codec.
757 
758         /* get buffer and its length, assign to local variable */
759         void *fullBuf = NULL;
760         outBuf.flushBuffer(fullBuf, rtnLength);
761 
762         last = written && outBuf.isFlushed();
763 
764         /* if the buffer is not empty, do something with its contents */
765         if (rtnLength > 0)
766         {
767             /* rtnLength could be odd */
768             if (rtnLength & 1)
769             {
770               /* this should only happen if we use a stream compressed transfer
771                * syntax and then only at the very end of the stream. Everything
772                * else is a failure.
773                */
774               if (!last)
775               {
776                 return makeDcmnetCondition(DIMSEC_SENDFAILED, OF_error,
777                   "DIMSE Failed to send message: odd block length encountered");
778               }
779 
780               /* since the block size is always even, block size must be larger
781                * than rtnLength, so we can safely add a pad byte (and hope that
782                * the pad byte will not confuse the receiver's decompressor).
783                */
784               unsigned char *cbuf = (unsigned char *)fullBuf;
785               cbuf[rtnLength++] = 0; // add zero pad byte
786             }
787 
788             /* initialize a DUL_PDV variable with the buffer's data */
789             pdv.fragmentLength = OFstatic_cast(unsigned long, rtnLength);
790             pdv.presentationContextID = presID;
791             pdv.pdvType = pdvType;
792             pdv.lastPDV = last;
793             pdv.data = fullBuf;
794 
795             /* append this PDV to a PDV list structure, set the counter variable */
796             /* to 1 since this structure contains only 1 element */
797             pdvList.count = 1;
798             pdvList.pdv = &pdv;
799 
800             /* dump some information if required */
801             DCMNET_TRACE("DIMSE sendDcmDataset: sending " << pdv.fragmentLength << " bytes");
802 
803             /* send information over the network to the other DICOM application */
804             dulCond = DUL_WritePDVs(&assoc->DULassociation, &pdvList);
805             if (dulCond.bad())
806                 return makeDcmnetSubCondition(DIMSEC_SENDFAILED, OF_error, "DIMSE Failed to send message", dulCond);
807 
808             /* count the bytes and the amount of PDVs which were transmitted */
809             bytesTransmitted += OFstatic_cast(Uint32, rtnLength);
810             pdvCount += pdvList.count;
811 
812             /* execute callback function to indicate progress */
813             if (callback) {
814                 callback(callbackContext, bytesTransmitted);
815             }
816         }
817     }
818 
819     /* indicate the end of the transfer */
820     obj->transferEnd();
821 
822     return EC_Normal;
823 }
824 
825 /*
826 ** Public Functions Bodies
827 */
828 
829 /*
830  * DIMSE Messaging
831  */
832 
833 /*
834  * Message Send
835  */
836 
837 static OFCondition
DIMSE_sendMessage(T_ASC_Association * assoc,T_ASC_PresentationContextID presID,T_DIMSE_Message * msg,DcmDataset * statusDetail,DcmDataset * dataObject,const char * dataFileName,DIMSE_ProgressCallback callback,void * callbackContext,DcmDataset ** commandSet)838 DIMSE_sendMessage(
839         T_ASC_Association *assoc,
840         T_ASC_PresentationContextID presID,
841         T_DIMSE_Message *msg,
842         DcmDataset *statusDetail,
843         DcmDataset *dataObject,
844         const char *dataFileName,
845         DIMSE_ProgressCallback callback,
846         void *callbackContext,
847         DcmDataset **commandSet)
848     /*
849      * This function sends a DIMSE command and possibly also instance data from a file or from a given
850      * data object via network to another DICOM application.
851      *
852      * Parameters:
853      *   assoc           - [in] The association (network connection to another DICOM application).
854      *   presId          - [in] The ID of the presentation context which shall be used
855      *   msg             - [in] Structure that represents a certain DIMSE command which shall be sent.
856      *   statusDetail    - [in] Detailed information with regard to the status information which is captured
857      *                          in the status element (0000,0900). Note that the value for element (0000,0900)
858      *                          is contained in this variable.
859      *   dataObject      - [in] The instance data which shall be sent to the other DICOM application,
860      *                          NULL, if there is none
861      *   dataFileName    - [in] The name of the file that contains the instance data which shall be sent to
862      *                          the other DICOM application, NULL; if there is none.
863      *   callback        - [in] Pointer to a function which shall be called to indicate progress.
864      *   callbackContext - []
865      *   commandSet      - [out] [optional parameter, default = NULL] If this parameter is not NULL
866      *                           it will return a copy of the DIMSE command which is sent to the other
867      *                           DICOM application.
868      */
869 {
870     E_TransferSyntax xferSyntax;
871     DcmDataset *cmdObj = NULL;
872     DcmFileFormat dcmff;
873     int fromFile = 0;
874     OFCondition cond = EC_Normal;
875 
876     if (commandSet) *commandSet = NULL;
877 
878     /* check if the data dictionary is available. If not return an error */
879     if (!isDataDictPresent()) return DIMSE_NODATADICT;
880 
881     /* validate DIMSE command information, i.e. check if the information which is */
882     /* contained in msg meets certain conditions. For example, if msg represents a */
883     /* C-ECHO-RQ, then there is not supposed to be a corresponding data set. If the */
884     /* specified conditions are not met, return an error. */
885     if (EC_Normal != (cond = validateMessage(assoc, msg))) return cond;
886 
887     /* check if the presentation context id refers to a valid presentation context and determine the */
888     /* transfer syntax which is specified for this presentation context. Additionally, check if the specified */
889     /* transfer syntax is supported at all. If any of the checks returns an error, return this error. */
890     if (EC_Normal != (cond = checkPresentationContextForMessage(assoc, msg, presID, &xferSyntax))) return cond;
891 
892     /* create a DcmDataset object ("command object") based on the information in the DIMSE command */
893     /* variable (remember that all DICOM commands are - in the end - particular data sets). The */
894     /* information which will shortly be set in this object will be sent over the network later. */
895     cond = DIMSE_buildCmdObject(msg, &cmdObj);
896 
897     /* if the command object has been created successfully and there is status detail */
898     /* information move the status detail information to the command object. */
899     if (cond.good() && statusDetail != NULL)
900     {
901       /* move the status detail to the command */
902       DcmElement* e;
903       while ((e = statusDetail->remove((unsigned long)0)) != NULL) cmdObj->insert(e, OFTrue);
904     }
905 
906     /* if the command object has been created successfully and the data set is present */
907     /* according to the information in the DIMSE command information variable, do something */
908     if (cond.good() && DIMSE_isDataSetPresent(msg))
909     {
910       /* if a data object and a file name were passed, something is fishy */
911       if ((dataObject != NULL)&&(dataFileName != NULL))
912       {
913         DCMNET_WARN(DIMSE_warn_str(assoc) << "sendData: both object and file specified (sending object only)");
914       }
915       /* if there is no data object but a file name, we need to read data from the specified file */
916       /* to create a data object with the actual instance data that shall be sent */
917       else if ((dataObject == NULL)&&(dataFileName != NULL))
918       {
919         if (! dcmff.loadFile(dataFileName, EXS_Unknown).good())
920         {
921           DCMNET_WARN(DIMSE_warn_str(assoc) << "sendMessage: cannot open DICOM file ("
922             << dataFileName << "): " << OFStandard::getLastSystemErrorCode().message());
923           cond = DIMSE_SENDFAILED;
924         } else {
925           dataObject = dcmff.getDataset();
926           fromFile = 1;
927         }
928       }
929 
930       /* if we have a data object now, check if we can write the data object's elements in the required  */
931       /* transfer syntax. In detail, every single item of the data object will be checked. */
932       if (dataObject)
933       {
934         if (dataObject->isEmpty())
935         {
936           /* if dataset is empty, i.e. it contains no data elements, create a warning. */
937           DCMNET_WARN(DIMSE_warn_str(assoc) << "sendMessage: dataset is empty");
938           cond = DIMSE_SENDFAILED;
939         }
940         else if (! dataObject->canWriteXfer(xferSyntax))
941         {
942           /* if we cannot write all elements in the required transfer syntax, create a warning. */
943           DcmXfer writeXferSyntax(xferSyntax);
944           DcmXfer originalXferSyntax(dataObject->getOriginalXfer());
945           if (fromFile && dataFileName)
946           {
947             DCMNET_WARN(DIMSE_warn_str(assoc) << "sendMessage: unable to convert DICOM file '"
948               << dataFileName << "' from '" << originalXferSyntax.getXferName()
949               << "' transfer syntax to '" << writeXferSyntax.getXferName() << "'");
950           } else {
951             DCMNET_WARN(DIMSE_warn_str(assoc) << "sendMessage: unable to convert dataset from '"
952               << originalXferSyntax.getXferName() << "' transfer syntax to '"
953               << writeXferSyntax.getXferName() << "'");
954           }
955           cond = DIMSE_SENDFAILED;
956         }
957       } else {
958         /* if there is neither a data object nor a file name, create a warning, since */
959         /* the information in msg specified that instance data should be present. */
960         DCMNET_WARN(DIMSE_warn_str(assoc) << "sendMessage: no dataset to send");
961         cond = DIMSE_SENDFAILED;
962       }
963     }
964 
965     /* if all previous calls were successful, go ahead and send the */
966     /* specified DIMSE command to the other DICOM application */
967     if (cond.good())
968     {
969       /* if the global variable says so, we want to save the */
970       /* DIMSE command's information to a file */
971       if (g_dimse_save_dimse_data) saveDimseFragment(cmdObj, OFTrue, OFFalse);
972 
973       /* return a copy of the DIMSE command if required */
974       if (commandSet) *commandSet = new DcmDataset(*cmdObj);
975 
976       /* dump information if required */
977       DCMNET_TRACE("DIMSE Command to be sent on Presentation Context ID: " << OFstatic_cast(Uint16, presID));
978       DCMNET_TRACE("DIMSE Command to send:" << OFendl << DcmObject::PrintHelper(*cmdObj));
979 
980       /* Send the DIMSE command. DIMSE commands are always little endian implicit. */
981       cond = sendDcmDataset(assoc, cmdObj, presID, EXS_LittleEndianImplicit, DUL_COMMANDPDV, NULL, NULL);
982     }
983 
984     /* Then we still have to send the actual instance data if the DIMSE command information variable */
985     /* says that instance data is present and there actually is a corresponding data object */
986     if (cond.good() && DIMSE_isDataSetPresent(msg) && (dataObject))
987     {
988       /* again, if the global variable says so, we want to save the instance data to a file */
989       if (g_dimse_save_dimse_data) saveDimseFragment(dataObject, OFFalse, OFFalse);
990 
991       /* Send the instance data set using the corresponding transfer syntax */
992       cond = sendDcmDataset(assoc, dataObject, presID, xferSyntax,
993           DUL_DATASETPDV, callback, callbackContext);
994     }
995 
996     /* clean up some memory */
997     delete cmdObj;
998 
999     /* return result value */
1000     return cond;
1001 }
1002 
1003 OFCondition
DIMSE_sendMessageUsingFileData(T_ASC_Association * assoc,T_ASC_PresentationContextID presID,T_DIMSE_Message * msg,DcmDataset * statusDetail,const char * dataFileName,DIMSE_ProgressCallback callback,void * callbackContext,DcmDataset ** commandSet)1004 DIMSE_sendMessageUsingFileData(
1005         T_ASC_Association *assoc,
1006         T_ASC_PresentationContextID presID,
1007         T_DIMSE_Message *msg,
1008         DcmDataset *statusDetail,
1009         const char *dataFileName,
1010         DIMSE_ProgressCallback callback,
1011         void *callbackContext,
1012         DcmDataset **commandSet)
1013 {
1014     /* simply call DIMSE_sendMessage to accomplish this task */
1015     return DIMSE_sendMessage(assoc, presID, msg, statusDetail, NULL, dataFileName, callback, callbackContext, commandSet);
1016 }
1017 
1018 OFCondition
DIMSE_sendMessageUsingMemoryData(T_ASC_Association * assoc,T_ASC_PresentationContextID presID,T_DIMSE_Message * msg,DcmDataset * statusDetail,DcmDataset * dataObject,DIMSE_ProgressCallback callback,void * callbackContext,DcmDataset ** commandSet)1019 DIMSE_sendMessageUsingMemoryData(
1020         T_ASC_Association *assoc,
1021         T_ASC_PresentationContextID presID,
1022         T_DIMSE_Message *msg,
1023         DcmDataset *statusDetail,
1024         DcmDataset *dataObject,
1025         DIMSE_ProgressCallback callback,
1026         void *callbackContext,
1027         DcmDataset **commandSet)
1028 {
1029     /* simply call DIMSE_sendMessage to accomplish this task */
1030     return DIMSE_sendMessage(assoc, presID, msg, statusDetail, dataObject, NULL, callback, callbackContext, commandSet);
1031 }
1032 
1033 /*
1034  * Message Receive
1035  */
1036 
DIMSE_ignoreDataSet(T_ASC_Association * assoc,T_DIMSE_BlockingMode blocking,int timeout,DIC_UL * bytesRead,DIC_UL * pdvCount)1037 OFCondition DIMSE_ignoreDataSet(
1038         T_ASC_Association *assoc,
1039         T_DIMSE_BlockingMode blocking,
1040         int timeout,
1041         DIC_UL *bytesRead,
1042         DIC_UL *pdvCount)
1043 {
1044     OFCondition cond = EC_Normal;
1045     DUL_PDV pdv;
1046     OFBool last = OFFalse;
1047 
1048     while (!last) {
1049         cond = DIMSE_readNextPDV(assoc, blocking, timeout, &pdv);
1050         if (cond.bad()) {
1051             break;
1052         }
1053         if (pdv.pdvType != DUL_DATASETPDV) {
1054             cond = DIMSE_UNEXPECTEDPDVTYPE;
1055             break;
1056         }
1057         *bytesRead += pdv.fragmentLength;
1058         (*pdvCount)++;
1059         last = pdv.lastPDV;
1060     }
1061     return cond;
1062 }
1063 
1064 
1065 OFCondition
DIMSE_receiveCommand(T_ASC_Association * assoc,T_DIMSE_BlockingMode blocking,int timeout,T_ASC_PresentationContextID * presID,T_DIMSE_Message * msg,DcmDataset ** statusDetail,DcmDataset ** commandSet)1066 DIMSE_receiveCommand(
1067         T_ASC_Association *assoc,
1068         T_DIMSE_BlockingMode blocking,
1069         int timeout,
1070         T_ASC_PresentationContextID*presID,
1071         T_DIMSE_Message *msg,
1072         DcmDataset **statusDetail,
1073         DcmDataset **commandSet)
1074 {
1075     OFCondition cond = EC_Normal;
1076     unsigned long bytesRead;
1077     unsigned long pdvCount;
1078 
1079     DUL_DATAPDV type;
1080     OFBool last;
1081     DUL_PDV pdv;
1082 
1083     DIC_UL elemsLeft;
1084     T_ASC_PresentationContextID pid = 0;
1085     E_TransferSyntax xferSyntax;
1086     DcmDataset *cmdSet;
1087     OFCondition econd = EC_Normal;
1088 
1089     if (statusDetail) *statusDetail = NULL;
1090     if (commandSet) *commandSet = NULL;
1091 
1092     /* dump some information if required */
1093     DCMNET_TRACE("DIMSE receiveCommand");
1094 
1095     /* check if the data dictionary is available. If not return an error */
1096     if (!isDataDictPresent()) return DIMSE_NODATADICT;
1097 
1098     /* set PDV counter to 0 */
1099     pdvCount = 0;
1100 
1101     /* create a new DcmDataset variable to capture the DIMSE command which we are about to receive */
1102     cmdSet = new DcmDataset();
1103     if (cmdSet == NULL) return EC_MemoryExhausted;
1104 
1105     /* prepare the DcmDataset variable for transfer of data */
1106     cmdSet->transferInit();
1107 
1108     /* create a buffer variable which can be used to store the received information */
1109     DcmInputBufferStream cmdBuf;
1110     if (! cmdBuf.good())
1111     {
1112         delete cmdSet;
1113         return makeDcmnetCondition(DIMSEC_PARSEFAILED, OF_error, "DIMSE: receiveCommand: Failed to initialize cmdBuf");
1114     }
1115 
1116     /* start a loop in which we want to read a DIMSE command from the incoming socket stream. */
1117     /* Since the command could stretch over more than one PDU, the use of a loop is mandatory. */
1118     for (last = OFFalse, bytesRead = 0, type = DUL_COMMANDPDV;
1119          type == DUL_COMMANDPDV && !last;)
1120     {
1121         /* make the stream remember any unread bytes */
1122         cmdBuf.releaseBuffer();
1123 
1124         /* get next PDV (in detail, in order to get this PDV, a */
1125         /* PDU has to be read from the incoming socket stream) */
1126         cond = DIMSE_readNextPDV(assoc, blocking, timeout, &pdv);
1127         if (cond.bad() || (cond == DUL_PEERREQUESTEDRELEASE))
1128         {
1129             delete cmdSet;
1130             if (cond == DIMSE_READPDVFAILED)
1131                 return makeDcmnetSubCondition(DIMSEC_RECEIVEFAILED, OF_error, "DIMSE Failed to receive message", cond);
1132             else return cond; /* it was an abort or release request */
1133         }
1134 
1135         /* if this is the first loop iteration, get the presentation context ID which is captured in the */
1136         /* current PDV. If this is not the first loop iteration, check if the presentation context IDs in */
1137         /* the current PDV and in the last PDV are identical. If they are not, return an error. */
1138         if (pdvCount == 0)
1139         {
1140             pid = pdv.presentationContextID;
1141         }
1142         else if (pdv.presentationContextID != pid)
1143         {
1144             delete cmdSet;
1145             char buf1[256];
1146             sprintf(buf1, "DIMSE: Different PresIDs inside Command Set: %d != %d", pid, pdv.presentationContextID);
1147             OFCondition subCond = makeDcmnetCondition(DIMSEC_INVALIDPRESENTATIONCONTEXTID, OF_error, buf1);
1148             return makeDcmnetSubCondition(DIMSEC_RECEIVEFAILED, OF_error, "DIMSE Failed to receive message", subCond);
1149         }
1150 
1151         /* check if the fragment length of the current PDV is odd. This should */
1152         /* never happen (see DICOM standard (year 2000) part 7, annex F) (or */
1153         /* the corresponding section in a later version of the standard.) */
1154         if ((pdv.fragmentLength % 2) != 0)
1155         {
1156             /* This should NEVER happen.  See Part 7, Annex F. */
1157             char buf2[256];
1158             sprintf(buf2, "DIMSE: Odd Fragment Length: %lu", pdv.fragmentLength);
1159             cond = makeDcmnetCondition(DIMSEC_RECEIVEFAILED, OF_error, buf2);
1160             break;
1161         }
1162 
1163         /* if information is contained the PDVs fragment, we want to insert this information into the buffer */
1164         if (pdv.fragmentLength > 0) {
1165             cmdBuf.setBuffer(pdv.data, pdv.fragmentLength);
1166         }
1167 
1168         /* if this fragment contains the last fragment of the DIMSE command, set the end of the stream */
1169         if (pdv.lastPDV) {
1170             cmdBuf.setEos();
1171         }
1172 
1173         /* insert the information which is contained in the buffer into the DcmDataset */
1174         /* variable. Mind that DIMSE commands are always specified in the little endian */
1175         /* implicit transfer syntax. Additionally, we want to remove group length tags. */
1176         econd = cmdSet->read(cmdBuf, EXS_LittleEndianImplicit, EGL_withoutGL);
1177         if (econd != EC_Normal && econd != EC_StreamNotifyClient)
1178         {
1179           delete cmdSet;
1180           return makeDcmnetSubCondition(DIMSEC_RECEIVEFAILED, OF_error, "DIMSE: receiveCommand: cmdSet->read() Failed", econd);
1181         }
1182 
1183         /* update the counter that counts how many bytes were read from the incoming socket */
1184         /* stream. This variable will only be used for dumping general information. */
1185         bytesRead += pdv.fragmentLength;
1186 
1187         /* update the following variables which will be evaluated at the beginning of each loop iteration. */
1188         last = pdv.lastPDV;
1189         type = pdv.pdvType;
1190 
1191         /* update the counter that counts how many PDVs were received on the incoming */
1192         /* socket stream. This variable will be used for determining the first */
1193         /* loop iteration and dumping general information. */
1194         pdvCount++;
1195     }
1196 
1197     /* indicate the end of the transfer */
1198     cmdSet->transferEnd();
1199 
1200     /* dump information if required */
1201     DCMNET_TRACE("DIMSE receiveCommand: " << pdvCount << " PDVs (" << bytesRead << " bytes), PresID=" << (int) pid);
1202 
1203     /* check if this is a valid presentation context */
1204     cond = getTransferSyntax(assoc, pid, &xferSyntax);
1205     if (cond.bad())
1206     {
1207         delete cmdSet;
1208         return cond;
1209     }
1210 
1211     /* check if the PDVs which were read actually do belong */
1212     /* to a DIMSE command. If not, return an error. */
1213     if (type != DUL_COMMANDPDV)
1214     {
1215         delete cmdSet;
1216         OFCondition subCond = makeDcmnetCondition(DIMSEC_UNEXPECTEDPDVTYPE, OF_error, "DIMSE: Command PDV Expected");
1217         return makeDcmnetSubCondition(DIMSEC_RECEIVEFAILED, OF_error, "DIMSE Failed to receive message", subCond);
1218     }
1219 
1220     /* if the global variable says so, we want to save the */
1221     /* DIMSE command's information to a file */
1222     if (g_dimse_save_dimse_data) saveDimseFragment(cmdSet, OFTrue, OFTrue);
1223 
1224     /* return a copy of the DIMSE command if required */
1225     if (commandSet) *commandSet = new DcmDataset(*cmdSet);
1226 
1227     /* dump some more information if required */
1228     DCMNET_TRACE("DIMSE Command Received:" << OFendl << DcmObject::PrintHelper(*cmdSet));
1229 
1230     /* parse the information in cmdSet and create a corresponding T_DIMSE_Message */
1231     /* structure which represents the the DIMSE message which was received */
1232     cond = DIMSE_parseCmdObject(msg, cmdSet);
1233 
1234     /* if the T_DIMSE_Message structure was created successfully, validate the message, i.e. */
1235     /* check if the information which is contained in msg meets certain conditions */
1236     if (cond == EC_Normal) {
1237         cond = validateMessage(assoc, msg);
1238     }
1239 
1240     /* Whatever is left in the cmdSet object should be status detail */
1241     /* information. Return this information to the caller if required. */
1242     if (cond == EC_Normal) {
1243         /* Count the elements that are still existent in cmdSet */
1244         elemsLeft = DIMSE_countElements(cmdSet);
1245 
1246         /* if the caller requires status Detail information and there is some */
1247         if ((statusDetail != NULL) && (elemsLeft > 0)) {
1248             /* return it to the caller in the reference parameter */
1249             *statusDetail = cmdSet;
1250         } else {
1251             /* either nothing is left or the caller does not want status */
1252             /* detail information; hence, delete the cmdSet object */
1253             delete cmdSet;
1254         }
1255     }
1256     /* if some error occurred before, delete cmdSet */
1257     else
1258         delete cmdSet;
1259 
1260     /* set the Presentation Context ID we received (out parameter) */
1261     *presID = pid;
1262 
1263     /* return result value */
1264     return cond;
1265 }
1266 
1267 
DIMSE_createFilestream(const OFFilename & filename,const T_DIMSE_C_StoreRQ * request,const T_ASC_Association * assoc,T_ASC_PresentationContextID presIdCmd,int writeMetaheader,DcmOutputFileStream ** filestream)1268 OFCondition DIMSE_createFilestream(
1269         const OFFilename &filename,
1270         const T_DIMSE_C_StoreRQ *request,
1271         const T_ASC_Association *assoc,
1272         T_ASC_PresentationContextID presIdCmd,
1273         int writeMetaheader,
1274         DcmOutputFileStream **filestream)
1275 {
1276   OFCondition cond = EC_Normal;
1277   DcmElement *elem=NULL;
1278   DcmMetaInfo *metainfo=NULL;
1279   DcmTag metaElementGroupLength(DCM_FileMetaInformationGroupLength);
1280   DcmTag fileMetaInformationVersion(DCM_FileMetaInformationVersion);
1281   DcmTag mediaStorageSOPClassUID(DCM_MediaStorageSOPClassUID);
1282   DcmTag mediaStorageSOPInstanceUID(DCM_MediaStorageSOPInstanceUID);
1283   DcmTag transferSyntaxUID(DCM_TransferSyntaxUID);
1284   DcmTag implementationClassUID(DCM_ImplementationClassUID);
1285   DcmTag implementationVersionName(DCM_ImplementationVersionName);
1286   DcmTag sourceApplicationEntityTitle(DCM_SourceApplicationEntityTitle);
1287   T_ASC_PresentationContext presentationContext;
1288 
1289   if (filename.isEmpty() || (request==NULL) || (assoc==NULL) ||
1290       (assoc->params==NULL) || (filestream==NULL))
1291   {
1292     return DIMSE_NULLKEY;
1293   }
1294 
1295   cond = ASC_findAcceptedPresentationContext(assoc->params, presIdCmd, &presentationContext);
1296   if (cond.bad()) return cond;
1297 
1298   if (writeMetaheader)
1299   {
1300     if (NULL == (metainfo = new DcmMetaInfo())) return EC_MemoryExhausted;
1301     if (NULL != (elem = new DcmUnsignedLong(metaElementGroupLength)))
1302     {
1303       metainfo->insert(elem, OFTrue);
1304       Uint32 temp = 0;
1305       ((DcmUnsignedLong*)elem)->putUint32Array(&temp, 1);
1306     } else cond = EC_MemoryExhausted;
1307     if (NULL != (elem = new DcmOtherByteOtherWord(fileMetaInformationVersion)))
1308     {
1309       metainfo->insert(elem, OFTrue);
1310       Uint8 version[2] = {0,1};
1311       ((DcmOtherByteOtherWord*)elem)->putUint8Array(version, 2);
1312     } else cond = EC_MemoryExhausted;
1313     if (NULL != (elem = new DcmUniqueIdentifier(mediaStorageSOPClassUID)))
1314     {
1315       metainfo->insert(elem, OFTrue);
1316       ((DcmUniqueIdentifier*)elem)->putString(request->AffectedSOPClassUID);
1317     } else cond = EC_MemoryExhausted;
1318     if (NULL != (elem = new DcmUniqueIdentifier(mediaStorageSOPInstanceUID)))
1319     {
1320       metainfo->insert(elem, OFTrue);
1321       ((DcmUniqueIdentifier*)elem)->putString(request->AffectedSOPInstanceUID);
1322     } else cond = EC_MemoryExhausted;
1323     if (NULL != (elem = new DcmUniqueIdentifier(transferSyntaxUID)))
1324     {
1325       metainfo->insert(elem, OFTrue);
1326       elem->putString(presentationContext.acceptedTransferSyntax);
1327     } else cond = EC_MemoryExhausted;
1328     if (NULL != (elem = new DcmUniqueIdentifier(implementationClassUID)))
1329     {
1330       metainfo->insert(elem, OFTrue);
1331       const char *uid = OFFIS_IMPLEMENTATION_CLASS_UID;
1332       ((DcmUniqueIdentifier*)elem)->putString(uid);
1333     } else cond = EC_MemoryExhausted;
1334     if (NULL != (elem = new DcmShortString(implementationVersionName)))
1335     {
1336       metainfo->insert(elem, OFTrue);
1337       const char *version = OFFIS_DTK_IMPLEMENTATION_VERSION_NAME2;
1338       ((DcmShortString*)elem)->putString(version);
1339 
1340       if (strlen(OFFIS_DTK_IMPLEMENTATION_VERSION_NAME2) > 16)
1341       {
1342         DCMNET_WARN("DICOM implementation version name too long: " << OFFIS_DTK_IMPLEMENTATION_VERSION_NAME2);
1343       }
1344     } else cond = EC_MemoryExhausted;
1345     if (NULL != (elem = new DcmApplicationEntity(sourceApplicationEntityTitle)))
1346     {
1347       metainfo->insert(elem, OFTrue);
1348       const char *aet = assoc->params->DULparams.callingAPTitle;
1349       if (aet) ((DcmApplicationEntity*)elem)->putString(aet);
1350     } else cond = EC_MemoryExhausted;
1351 
1352     if (cond == EC_MemoryExhausted)
1353     {
1354       delete metainfo;
1355       return cond;
1356     }
1357 
1358     cond = metainfo->computeGroupLengthAndPadding(EGL_withGL, EPD_noChange,
1359       META_HEADER_DEFAULT_TRANSFERSYNTAX, EET_UndefinedLength);
1360     if (cond.bad())
1361     {
1362       delete metainfo;
1363       return cond;
1364     }
1365   }
1366 
1367   *filestream = new DcmOutputFileStream(filename);
1368   if ((*filestream == NULL)||(! (*filestream)->good()))
1369   {
1370      if (metainfo) delete metainfo;
1371      if (*filestream)
1372      {
1373        delete *filestream;
1374        *filestream = NULL;
1375      }
1376      OFOStringStream stream;
1377      stream << "DIMSE createFilestream: cannot create file '" << filename << "'" << OFStringStream_ends;
1378      OFSTRINGSTREAM_GETOFSTRING(stream, msg)
1379      return makeDcmnetCondition(DIMSEC_OUTOFRESOURCES, OF_error, msg.c_str());
1380   }
1381 
1382   if (metainfo)
1383   {
1384     metainfo->transferInit();
1385     if (EC_Normal != metainfo->write(**filestream, META_HEADER_DEFAULT_TRANSFERSYNTAX, EET_ExplicitLength, NULL))
1386     {
1387       OFOStringStream stream;
1388       stream << "DIMSE createFilestream: cannot write metaheader to file '" << filename << "'" << OFStringStream_ends;
1389       OFSTRINGSTREAM_GETOFSTRING(stream, msg)
1390       cond = makeDcmnetCondition(DIMSEC_OUTOFRESOURCES, OF_error, msg.c_str());
1391     }
1392     metainfo->transferEnd();
1393     delete metainfo;
1394   }
1395 
1396   return cond;
1397 }
1398 
1399 
1400 OFCondition
DIMSE_receiveDataSetInFile(T_ASC_Association * assoc,T_DIMSE_BlockingMode blocking,int timeout,T_ASC_PresentationContextID * presID,DcmOutputStream * filestream,DIMSE_ProgressCallback callback,void * callbackData)1401 DIMSE_receiveDataSetInFile(
1402         T_ASC_Association *assoc,
1403         T_DIMSE_BlockingMode blocking,
1404         int timeout,
1405         T_ASC_PresentationContextID *presID,
1406         DcmOutputStream *filestream,
1407         DIMSE_ProgressCallback callback,
1408         void *callbackData)
1409 {
1410     OFCondition cond = EC_Normal;
1411     DUL_PDV pdv;
1412     T_ASC_PresentationContextID pid = 0;
1413     E_TransferSyntax xferSyntax;
1414     OFBool last = OFFalse;
1415     DIC_UL pdvCount = 0;
1416     DIC_UL bytesRead = 0;
1417 
1418     if ((assoc == NULL) || (presID==NULL) || (filestream==NULL)) return DIMSE_NULLKEY;
1419 
1420     *presID = 0;        /* invalid value */
1421     offile_off_t written = 0;
1422     while (!last)
1423     {
1424         cond = DIMSE_readNextPDV(assoc, blocking, timeout, &pdv);
1425         if (cond != EC_Normal) last = OFTrue; // terminate loop
1426 
1427         if (!last)
1428         {
1429           if (pdv.pdvType != DUL_DATASETPDV)
1430           {
1431             cond = DIMSE_UNEXPECTEDPDVTYPE;
1432             last = OFTrue;
1433           }
1434         }
1435 
1436         if (!last)
1437         {
1438           if (pdvCount == 0)
1439           {
1440             pid = pdv.presentationContextID;
1441             /* is this a valid presentation context ? */
1442             cond = getTransferSyntax(assoc, pid, &xferSyntax);
1443             if (cond.bad()) last = OFTrue; // terminate loop
1444           }
1445           else if (pdv.presentationContextID != pid)
1446           {
1447             char buf1[256];
1448             sprintf(buf1, "DIMSE: Different PresIDs inside data set: %d != %d", pid, pdv.presentationContextID);
1449             OFCondition subCond = makeDcmnetCondition(DIMSEC_INVALIDPRESENTATIONCONTEXTID, OF_error, buf1);
1450             cond = makeDcmnetSubCondition(DIMSEC_RECEIVEFAILED, OF_error, "DIMSE Failed to receive message", subCond);
1451             last = OFTrue; // terminate loop
1452           }
1453         }
1454 
1455         if (!last)
1456         {
1457           if ((pdv.fragmentLength % 2) != 0)
1458           {
1459             /* This should NEVER happen.  See Part 7, Annex F. */
1460             char buf2[256];
1461             sprintf(buf2, "DIMSE: Odd Fragment Length: %lu", pdv.fragmentLength);
1462             cond = makeDcmnetCondition(DIMSEC_RECEIVEFAILED, OF_error, buf2);
1463             last = OFTrue; // terminate loop
1464           }
1465         }
1466 
1467         if (!last)
1468         {
1469           written = filestream->write((void *)(pdv.data), (Uint32)(pdv.fragmentLength));
1470           if ((! filestream->good()) || (written != (Uint32)(pdv.fragmentLength)))
1471           {
1472               cond = DIMSE_ignoreDataSet(assoc, blocking, timeout, &bytesRead, &pdvCount);
1473               if (cond == EC_Normal)
1474               {
1475                 cond = makeDcmnetCondition(DIMSEC_OUTOFRESOURCES, OF_error, "DIMSE receiveDataSetInFile: Cannot write to file");
1476               }
1477               last = OFTrue; // terminate loop
1478           }
1479         }
1480 
1481         if (!last)
1482         {
1483           bytesRead += pdv.fragmentLength;
1484           pdvCount++;
1485           last = pdv.lastPDV;
1486 
1487           DCMNET_TRACE("DIMSE receiveDataSetInFile: " << pdv.fragmentLength
1488               << " bytes read (last: " << ((last)?("YES"):("NO")) << ")");
1489 
1490           if (callback)
1491           { /* execute callback function */
1492             callback(callbackData, bytesRead);
1493           }
1494         }
1495     }
1496 
1497     /* set the Presentation Context ID we received */
1498     *presID = pid;
1499     return cond;
1500 }
1501 
1502 
1503 OFCondition
DIMSE_receiveDataSetInMemory(T_ASC_Association * assoc,T_DIMSE_BlockingMode blocking,int timeout,T_ASC_PresentationContextID * presID,DcmDataset ** dataObject,DIMSE_ProgressCallback callback,void * callbackData)1504 DIMSE_receiveDataSetInMemory(
1505         T_ASC_Association *assoc,
1506         T_DIMSE_BlockingMode blocking,
1507         int timeout,
1508         T_ASC_PresentationContextID *presID,
1509         DcmDataset **dataObject,
1510         DIMSE_ProgressCallback callback,
1511         void *callbackData)
1512 {
1513     OFCondition cond = EC_Normal;
1514     OFCondition econd = EC_Normal;
1515     DcmDataset *dset = NULL;
1516     DUL_PDV pdv;
1517     T_ASC_PresentationContextID pid = 0;
1518     E_TransferSyntax xferSyntax;
1519     OFBool last = OFFalse;
1520     DIC_UL pdvCount = 0;
1521     DIC_UL bytesRead = 0;
1522 
1523     /* check if the caller provided an address where the data set can be stored. If not return an error */
1524     if (dataObject == NULL) return DIMSE_NULLKEY;
1525 
1526     /* check if the data dictionary is available. If not return an error */
1527     if (!isDataDictPresent()) return DIMSE_NODATADICT;
1528 
1529     /* if we need to create a DcmDataset object at the given address, do so */
1530     if (*dataObject == NULL) {
1531         dset = new DcmDataset();
1532     } else {
1533         /* if not, use the one which was passed */
1534         dset = *dataObject;
1535     }
1536 
1537     /* check if there is still no DcmDataset object which can be used to store the data set. */
1538     if (dset == NULL)
1539     {
1540         /* if this is the case, just go ahead an receive data, but do not store it anywhere */
1541         cond = DIMSE_ignoreDataSet(assoc, blocking, timeout, &bytesRead, &pdvCount);
1542 
1543         /* if receiving was successful, let the caller know though that no DcmDataset variable could be created */
1544         if (cond == EC_Normal)
1545         {
1546             return makeDcmnetCondition(DIMSEC_OUTOFRESOURCES, OF_error, "DIMSE receiveDataSetInMemory: Cannot create DcmDataset");
1547         }
1548 
1549         /* if we get to here, receiving was not successful; there must have */
1550         /* been a network problem. Just pass the result on to the caller. */
1551         return cond;
1552     }
1553 
1554     /* create a buffer variable which can be used to store the received information */
1555     DcmInputBufferStream dataBuf;
1556 
1557     /* prepare the DcmDataset variable for transfer of data */
1558     dset->transferInit();
1559 
1560     /* start a loop in which we want to read the data set from the incoming socket stream. */
1561     /* Since the data set could stretch over more than one PDU, the use of a loop is mandatory. */
1562     while (!last && cond == EC_Normal)
1563     {
1564 
1565         /* make the stream remember any unread bytes */
1566         dataBuf.releaseBuffer();
1567 
1568         /* get next PDV (in detail, in order to get this PDV, a */
1569         /* PDU has to be read from the incoming socket stream) */
1570         cond = DIMSE_readNextPDV(assoc, blocking, timeout, &pdv);
1571 
1572         /* if some error occurred, end the loop */
1573         if (cond != EC_Normal) last = OFTrue;
1574 
1575         /* we are expecting to see a data set PDV; if the PDV which was received does not refer */
1576         /* to a data set, set the error indicating variable correspondingly and end the loop. */
1577         if (!last)
1578         {
1579           if (pdv.pdvType != DUL_DATASETPDV)
1580           {
1581             cond = DIMSE_UNEXPECTEDPDVTYPE;
1582             last = OFTrue;
1583           }
1584         }
1585 
1586         /* if this is the first loop iteration, get the presentation context ID which is captured */
1587         /* in the current PDV and determine what transfer syntax the presentation context ID refers */
1588         /* to (this is also a check concerning the question if the presentation context ID is valid). */
1589         /* If this is not the first loop iteration, check if the presentation context IDs in the */
1590         /* current PDV and in the last PDV are identical. If they are not, return an error. */
1591         if (!last)
1592         {
1593             if (pdvCount == 0)
1594             {
1595                 pid = pdv.presentationContextID;
1596 
1597                 cond = getTransferSyntax(assoc, pid, &xferSyntax);
1598                 if (cond.bad()) last = OFTrue;
1599             }
1600             else if (pdv.presentationContextID != pid)
1601             {
1602                 char buf1[256];
1603                 sprintf(buf1, "DIMSE: Different PresIDs inside data set: %d != %d", pid, pdv.presentationContextID);
1604                 OFCondition subCond = makeDcmnetCondition(DIMSEC_INVALIDPRESENTATIONCONTEXTID, OF_error, buf1);
1605                 cond = makeDcmnetSubCondition(DIMSEC_RECEIVEFAILED, OF_error, "DIMSE Failed to receive message", subCond);
1606                 last = OFTrue;
1607             }
1608         }
1609 
1610         if (!last)
1611         {
1612             /* check if the fragment length of the current PDV is odd. This should */
1613             /* never happen (see DICOM standard (year 2000) part 7, annex F) (or */
1614             /* the corresponding section in a later version of the standard.) */
1615             if ((pdv.fragmentLength % 2) != 0)
1616             {
1617                 char buf2[256];
1618                 sprintf(buf2, "DIMSE: Odd Fragment Length: %lu", pdv.fragmentLength);
1619                 cond = makeDcmnetCondition(DIMSEC_RECEIVEFAILED, OF_error, buf2);
1620                 last = OFTrue;
1621             }
1622         }
1623 
1624         if (!last)
1625         {
1626             /* if information is contained the PDVs fragment, we want to insert this information into the buffer */
1627             if (pdv.fragmentLength > 0)
1628             {
1629                 dataBuf.setBuffer(pdv.data, pdv.fragmentLength);
1630             }
1631 
1632             /* if this fragment contains the last fragment of the data set, set the end of the stream */
1633             if (pdv.lastPDV)
1634             {
1635                 dataBuf.setEos();
1636             }
1637 
1638             /* insert the information which is contained in the buffer into the DcmDataset variable. Mind the */
1639             /* transfer syntax which was specified through the presentation context ID of the first PDV. */
1640             econd = dset->read(dataBuf, xferSyntax);
1641             if (econd != EC_Normal && econd != EC_StreamNotifyClient)
1642             {
1643                 DCMNET_WARN(DIMSE_warn_str(assoc) << "DIMSE receiveDataSetInMemory: dset->read() Failed ("
1644                     << econd.text() << ")");
1645                 cond = DIMSE_RECEIVEFAILED;
1646                 last = OFTrue;
1647             }
1648         }
1649 
1650         if (!last)
1651         {
1652             /* update the counter that counts how many bytes were read from the incoming socket */
1653             /* stream. This variable will only be used for dumping general information. */
1654             bytesRead += pdv.fragmentLength;
1655 
1656             /* update the counter that counts how many PDVs were received on the incoming */
1657             /* socket stream. This variable will be used for determining the first */
1658             /* loop iteration and dumping general information. */
1659             pdvCount++;
1660 
1661             /* update the variable which will be evaluated at the beginning of each loop iteration. */
1662             last = pdv.lastPDV;
1663 
1664             /* dump information if required */
1665             DCMNET_TRACE("DIMSE receiveDataSetInMemory: " << pdv.fragmentLength
1666                 << " bytes read (last: " << ((last)?("YES"):("NO")) << ")");
1667 
1668             /* execute callback function after each received PDV */
1669             if (callback)
1670             {
1671                 callback(callbackData, bytesRead);
1672             }
1673         }
1674     }
1675 
1676     /* indicate the end of the transfer */
1677     dset->transferEnd();
1678 
1679     /* in case an error occurred, return this error */
1680     if (cond.bad())
1681     {
1682         if (*dataObject == NULL)
1683         {
1684             delete dset;
1685         }
1686         return cond;
1687     }
1688 
1689     /* if the global variable says so, we want to save the */
1690     /* DIMSE command's information to a file */
1691     if (g_dimse_save_dimse_data) saveDimseFragment(dset, OFFalse, OFTrue);
1692 
1693     /* set the Presentation Context ID we received */
1694     *presID = pid;
1695 
1696     /* set the object we received */
1697     *dataObject = dset;
1698 
1699     /* return result value */
1700     return cond;
1701 }
1702 
1703 
1704 /*
1705  * Misc functions
1706  */
1707 
DIMSE_warn_str(T_ASC_Association * assoc)1708 OFString DIMSE_warn_str(T_ASC_Association *assoc)
1709 {
1710     return OFString("DIMSE Warning: (") + assoc->params->DULparams.callingAPTitle
1711         + "," + assoc->params->DULparams.calledAPTitle + "): ";
1712 }
1713