1 /* ----------------------------- MNI Header -----------------------------------
2 @NAME : dicom_network.c
3 @DESCRIPTION: Routines for doing dicom network communications
4 @METHOD :
5 @GLOBALS :
6 @CREATED : February 10, 1997 (Peter Neelin)
7 @MODIFIED :
8 * $Log: dicom_network.c,v $
9 * Revision 6.12 2008-08-12 05:00:22 rotor
10 * * large number of changes from Claude (64 bit and updates)
11 *
12 * Revision 6.11 2004/10/29 13:08:41 rotor
13 * * rewrote Makefile with no dependency on a minc distribution
14 * * removed all references to the abominable minc_def.h
15 * * I should autoconf this really, but this is old code that
16 * is now replaced by Jon Harlaps PERL version..
17 *
18 * Revision 6.10 2001/03/19 18:30:32 neelin
19 * Added function to set implementation uid and changed name of function
20 * that gets it.
21 *
22 * Revision 6.9 2000/05/17 20:17:47 neelin
23 * Added mechanism to allow testing of input streams for more data through
24 * function acr_file_ismore.
25 * This is used in dicom_client_routines to allow asynchronous transfer
26 * of data, with testing for more input done before sending new messages.
27 * Previous use of select for this was misguided, since select may report that
28 * no data is waiting on the file descriptor while data is store in the file
29 * pointer buffer (or Acr file pointer buffer).
30 *
31 * Revision 6.8 2000/02/03 13:30:30 neelin
32 * Changed initial value of counter for acr_create_uid so that uid does not ever
33 * contain a zero.
34 *
35 * Revision 6.7 1999/10/29 17:51:51 neelin
36 * Fixed Log keyword
37 *
38 * Revision 6.6 1998/11/11 17:47:38 neelin
39 * Fixed up freeing of data pointers on file close.
40 *
41 * Revision 6.5 1998/11/11 17:05:03 neelin
42 * Added pointer for client data to dicom structure.
43 *
44 * Revision 6.4 1998/03/23 20:22:37 neelin
45 * Added includes for new functions.
46 *
47 * Revision 6.3 1998/03/23 20:16:16 neelin
48 * Moved some general-purpose functions from dicom_client_routines and
49 * added one for implementation uid.
50 *
51 * Revision 6.2 1998/03/10 17:05:30 neelin
52 * Fixed handling of PDV control header last fragment bit (it should be
53 * set for both command and data parts of the message). Re-organized code
54 * to use watchpoints differently: put PDU watchpoint on real afp
55 * everywhere and store PDV watchpoint in dicom io structure.
56 *
57 * Revision 6.1 1997/10/20 22:52:46 neelin
58 * Added support for implementation user information in association request.
59 *
60 * Revision 6.0 1997/09/12 13:23:59 neelin
61 * Release of minc version 0.6
62 *
63 * Revision 5.0 1997/08/21 13:25:00 neelin
64 * Release of minc version 0.5
65 *
66 * Revision 4.3 1997/08/21 13:24:56 neelin
67 * Pre-release
68 *
69 * Revision 4.2 1997/07/10 17:14:38 neelin
70 * Added more status codes and function to return status string.
71 *
72 * Revision 4.1 1997/07/09 17:38:55 neelin
73 * Added function acr_dicom_get_io_data.
74 *
75 * Revision 4.0 1997/05/07 20:01:23 neelin
76 * Release of minc version 0.4
77 *
78 * Revision 1.2 1997/04/21 20:21:09 neelin
79 * Updated the library to handle dicom messages.
80 *
81 * Revision 1.1 1997/02/20 16:38:17 neelin
82 * Initial revision
83 *
84 @COPYRIGHT :
85 Copyright 1997 Peter Neelin, McConnell Brain Imaging Centre,
86 Montreal Neurological Institute, McGill University.
87 Permission to use, copy, modify, and distribute this
88 software and its documentation for any purpose and without
89 fee is hereby granted, provided that the above copyright
90 notice appear in all copies. The author and McGill University
91 make no representations about the suitability of this
92 software for any purpose. It is provided "as is" without
93 express or implied warranty.
94 ---------------------------------------------------------------------------- */
95
96 #include <stdlib.h>
97 #include <stdio.h>
98 #include <unistd.h>
99 #include <limits.h>
100 #include <ctype.h>
101 #include <string.h>
102 #include <time.h>
103 #include <acr_nema.h>
104
105 /* Constants */
106 #define ACR_COMMAND_GRPID 0x0
107 #define PDU_HEADER_LEN (2+ACR_SIZEOF_LONG)
108 #define PDU_ITEM_HEADER_LEN (2+ACR_SIZEOF_SHORT)
109 #define ASSOC_RQ_LEN 74
110 #define ASSOC_RJ_LEN 10
111 #define ABORT_RQ_LEN 10
112 #define DATA_TF_LEN 6
113 #define MAX_PDU_STRING_LENGTH 1024
114 #define DICOM_NETWORK_BYTE_ORDER ACR_BIG_ENDIAN
115
116 /* PDU item types */
117 #define PDU_ITEM_APPLICATION_CONTEXT 0x10
118 #define PDU_ITEM_PRESENTATION_CONTEXT 0x20
119 #define PDU_ITEM_PRES_CONTEXT_REPLY 0x21
120 #define PDU_ITEM_ABSTRACT_SYNTAX 0x30
121 #define PDU_ITEM_TRANSFER_SYNTAX 0x40
122 #define PDU_ITEM_USER_INFORMATION 0x50
123 #define PDU_ITEM_MAXIMUM_LENGTH 0x51
124 #define PDU_ITEM_IMPLEMENTATION_CLASS_UID 0x52
125 #define PDU_ITEM_IMPLEMENTATION_VERSION_NAME 0x55
126
127 /* Mask for getting info out of PDV message control header */
128 #define PDV_COMMAND_PDV_MASK 0x1
129 #define PDV_LAST_FRAGMENT_MASK 0x2
130
131 /* Types to allow 2-level hierarchy of i/o streams */
132 typedef enum {DICOM_INPUT=0xbead, DICOM_OUTPUT} Dicom_IO_stream_type;
133 typedef struct {
134 Dicom_IO_stream_type stream_type;
135 Acr_File *real_afp; /* Pointer to real input stream */
136 Acr_File *virtual_afp; /* Pointer to message stream to which this
137 io data is attached */
138 int presentation_context_id;
139 long pdv_watchpoint; /* Distance from end of current PDV
140 to PDU watchpoint on real input stream.
141 This should be a positive number. */
142 long maximum_length; /* Maximum PDU length (excluding header) */
143 int writing_command; /* True if writing command portion */
144 long data_length; /* Length of data portion of message */
145 void *client_data; /* Pointer to client data, if any */
146 } Acr_Dicom_IO;
147
148 /* Private functions */
149 private Acr_Status read_pdu_header(Acr_File *afp, int *pdu_type,
150 Acr_Long *pdu_length);
151 private Acr_Status read_assoc_rq(Acr_File *afp, Acr_Group group);
152 private Acr_Status read_assoc_rq_ac(Acr_File *afp, Acr_Group group,
153 int is_request);
154 private Acr_Status read_data_tf(Acr_File *dicom_afp, Acr_Group group);
155 private Acr_Status read_rel_rq(Acr_File *afp, Acr_Group group);
156 private Acr_Status read_abort_rq(Acr_File *afp, Acr_Group group);
157 private Acr_Status read_assoc_ac(Acr_File *afp, Acr_Group group);
158 private Acr_Status read_assoc_rj(Acr_File *afp, Acr_Group group);
159 private Acr_Status read_rel_rp(Acr_File *afp, Acr_Group group);
160 private Acr_Status read_pdu_item(Acr_File *afp, Acr_Element *item);
161 private Acr_Status read_uid_item(Acr_File *afp, Acr_Element_Id elid,
162 Acr_Element *item);
163 private Acr_Status read_long_item(Acr_File *afp, Acr_Element_Id elid,
164 Acr_Element *item);
165 private Acr_Status read_unknown_item(Acr_File *afp, int item_type,
166 Acr_Element *item);
167 private Acr_Status read_pres_context_item(Acr_File *afp, Acr_Element *item);
168 private Acr_Status read_pres_context_reply_item(Acr_File *afp,
169 Acr_Element *item);
170 private Acr_Status read_user_info_item(Acr_File *afp, Acr_Element *item);
171 private char *get_uid_string(char *buffer, int length);
172 private Acr_Element pdu_create_element_uid(Acr_Element_Id elid,
173 char *value, int length);
174 private Acr_Element pdu_create_element_short(Acr_Element_Id elid,
175 void *input_value);
176 private Acr_Status write_assoc_rq(Acr_File *afp, Acr_Group group);
177 private Acr_Status write_assoc_ac(Acr_File *afp, Acr_Group group);
178 private Acr_Status write_assoc_rq_ac(Acr_File *afp, Acr_Group group,
179 int is_request, long *length);
180 private Acr_Status write_assoc_rj(Acr_File *afp, Acr_Group group_list);
181 private Acr_Status write_rel_rq(Acr_File *afp, Acr_Group group_list);
182 private Acr_Status write_rel_rp(Acr_File *afp, Acr_Group group_list);
183 private Acr_Status write_abort_rq(Acr_File *afp, Acr_Group group_list);
184 private Acr_Status write_fixed_length_pdu(Acr_File *afp, int pdu_type,
185 int result, int source, int reason);
186 private Acr_Status write_data_tf(Acr_File *dicom_afp, Acr_Message message);
187 private Acr_Status write_uid_item(Acr_File *afp, Acr_Element element,
188 int item_type, long *length);
189 private Acr_Status write_pres_context_item(Acr_File *afp, Acr_Element item,
190 int is_request, long *length);
191 private Acr_Status write_user_info_item(Acr_File *afp, Acr_Group group,
192 long *length);
193 private Acr_Status write_unknown_item(Acr_File *afp, int item_type,
194 long data_length, char *data_pointer,
195 long *length);
196 private void pdu_copy_uid(char *string, char *buffer, int length);
197 private Acr_File *initialize_dicom_stream(void *io_data, int maxlength,
198 Acr_Io_Routine io_routine,
199 Dicom_IO_stream_type stream_type);
200 private Acr_Dicom_IO *get_dicom_io_pointer(Acr_File *afp);
201 private Acr_File *get_dicom_real_afp(Acr_File *afp);
202 private long get_dicom_pdv_watchpoint(Acr_File *afp);
203 private void set_dicom_pdv_watchpoint(Acr_File *afp, long pdv_watchpoint);
204 private void dicom_reset(Acr_File *afp);
205 private void dicom_setup_output(Acr_File *afp,
206 long command_length, long data_length);
207 private int dicom_input_routine(void *io_data, void *buffer, int nbytes);
208 private int dicom_output_routine(void *io_data, void *buffer, int nbytes);
209 private int dicom_ismore(void *io_data);
210
211 /* Macros */
212 #define EXTRACT_UID(group, elid, value, length) \
213 acr_group_add_element(group, pdu_create_element_uid(elid, value, (int)length))
214 #define EXTRACT_SHORT(group, elid, value) \
215 acr_group_add_element(group, pdu_create_element_short(elid, value))
216 #define SAVE_SHORT(group, elid, value) \
217 acr_group_add_element(group, \
218 acr_create_element_short(elid, (Acr_Short) (value)))
219 #define GET_SHORT(input, output) \
220 acr_get_short(DICOM_NETWORK_BYTE_ORDER, (long) 1, input, output)
221 #define GET_LONG(input, output) \
222 acr_get_long(DICOM_NETWORK_BYTE_ORDER, (long) 1, input, output)
223 #define PUT_SHORT(input, output) \
224 acr_put_short(DICOM_NETWORK_BYTE_ORDER, (long) 1, input, output)
225 #define PUT_LONG(input, output) \
226 acr_put_long(DICOM_NETWORK_BYTE_ORDER, (long) 1, input, output)
227 #define COPY_UID(group, elid, buffer, length) \
228 pdu_copy_uid(acr_find_string(group, elid, ""), (char *) buffer, length)
229
230
231 /*****************************************************************************/
232 /*************************** UTILITY ROUTINES ********************************/
233 /*****************************************************************************/
234
235 /* ----------------------------- MNI Header -----------------------------------
236 @NAME : acr_uid_equal
237 @INPUT : uid1
238 uid2
239 @OUTPUT : (nothing)
240 @RETURNS : TRUE if uid's are equal, FALSE otherwise
241 @DESCRIPTION: Responds to READYq message
242 @METHOD :
243 @GLOBALS :
244 @CALLS :
245 @CREATED : February 21, 1997 (Peter Neelin)
246 @MODIFIED :
247 ---------------------------------------------------------------------------- */
acr_uid_equal(char * uid1,char * uid2)248 public int acr_uid_equal(char *uid1, char *uid2)
249 {
250 int len1, len2, i;
251
252 /* Skip leading blanks */
253 while (isspace(*uid1)) {uid1++;}
254 while (isspace(*uid2)) {uid2++;}
255
256 /* Skip trailing blanks */
257 len1 = strlen(uid1);
258 for (i=len1-1; (i >= 0) && isspace(uid1[i]); i--) {}
259 if (isspace(uid1[i+1])) uid1[i+1] = '\0';
260 len2 = strlen(uid2);
261 for (i=len2-1; (i >= 0) && isspace(uid1[i]); i--) {}
262 if (isspace(uid1[i+1])) uid1[i+1] = '\0';
263
264 /* Compare the strings */
265 return (strcmp(uid1, uid2) == 0);
266 }
267
268 /* ----------------------------- MNI Header -----------------------------------
269 @NAME : acr_create_uid
270 @INPUT : (none)
271 @OUTPUT : (none)
272 @RETURNS : pointer to internal buffer containing uid
273 @DESCRIPTION: Routine to generate a unique uid. The string is returned in
274 an internal buffer space and will be overwritten by
275 subsequent calls.
276 @METHOD :
277 @GLOBALS :
278 @CALLS :
279 @CREATED : August 25, 1997 (Peter Neelin)
280 @MODIFIED :
281 ---------------------------------------------------------------------------- */
acr_create_uid(void)282 public char *acr_create_uid(void)
283 {
284 static char uid[64];
285 time_t current_time;
286 int offset;
287 int maxlen;
288 union {
289 unsigned char ch[4];
290 int ul;
291 } host_id;
292 static int counter = 1;
293
294 /* Make up a new UID */
295 host_id.ul = gethostid();
296 current_time = time(NULL);
297 (void) sprintf(uid, "1.%d.%d.%d.%d.%d.%d.%d",
298 (int) 'I',(int) 'P',
299 (int) host_id.ch[0], (int) host_id.ch[1],
300 (int) host_id.ch[2], (int) host_id.ch[3],
301 (int) getpid());
302 offset = strlen(uid);
303 maxlen = sizeof(uid) - 1 - offset;
304 (void) strftime(&uid[offset], (size_t) maxlen, ".%Y%m%d%H%M%S",
305 localtime(¤t_time));
306 (void) sprintf(&uid[strlen(uid)], ".%08d", counter++);
307
308 return uid;
309 }
310
311 /* Space for storing the implementation class uid. Should only be used by
312 acr_set_implementation_uid and acr_get_implementation_uid */
313 static char Implementation_class_uid[65] = "";
314
315 /* ----------------------------- MNI Header -----------------------------------
316 @NAME : acr_set_implementation_uid
317 @INPUT : uid to set for implementation class
318 @OUTPUT : (none)
319 @RETURNS : (nothing)
320 @DESCRIPTION: Routine to set a uid for this software implementation. This
321 string is returned by acr_get_implementation_uid which is used
322 by client routines setting up associations. A static space is
323 used to store the uid string.
324 @METHOD :
325 @GLOBALS :
326 @CALLS :
327 @CREATED : March 7, 2000 (Peter Neelin)
328 @MODIFIED :
329 ---------------------------------------------------------------------------- */
acr_set_implementation_uid(char * uid)330 public void acr_set_implementation_uid(char *uid)
331 {
332 (void) strncpy(Implementation_class_uid, uid,
333 sizeof(Implementation_class_uid));
334 Implementation_class_uid[sizeof(Implementation_class_uid)-1] = '\0';
335 }
336
337 /* ----------------------------- MNI Header -----------------------------------
338 @NAME : acr_get_implementation_uid
339 @INPUT : (none)
340 @OUTPUT : (none)
341 @RETURNS : pointer to internal buffer containing uid
342 @DESCRIPTION: Routine to generate a uid for this software implementation.
343 The string is returned in an internal buffer space and will
344 be overwritten by subsequent calls.
345 @METHOD :
346 @GLOBALS :
347 @CALLS :
348 @CREATED : March 23, 1998 (Peter Neelin)
349 @MODIFIED : March 7, 2001 (P.N.)
350 - changed name from acr_implementation_uid
351 ---------------------------------------------------------------------------- */
acr_get_implementation_uid(void)352 public char *acr_get_implementation_uid(void)
353 {
354
355 /* Set the uid if it is not already set */
356 if (Implementation_class_uid[0] == '\0') {
357 (void) sprintf(Implementation_class_uid,
358 "1.%d.%d.%d.%d.%d", (int) 'I', (int) 'P',
359 (int) 'M', (int) 'N', (int) 'I');
360 }
361
362 return Implementation_class_uid;
363 }
364
365 /*****************************************************************************/
366 /*************************** INPUT ROUTINES **********************************/
367 /*****************************************************************************/
368
369 /* ----------------------------- MNI Header -----------------------------------
370 @NAME : acr_input_dicom_message
371 @INPUT : dicom_afp - acr file pointer for dicom input
372 @OUTPUT : message - NULL if no message is read
373 @RETURNS : status of input
374 @DESCRIPTION: Reads in a dicom message and returns it in a message structure.
375 @METHOD : Although dicom connection, etc. syntaxes differ from the
376 message syntax, this routine and the ones following put it all
377 in the same form.
378 @GLOBALS :
379 @CALLS :
380 @CREATED : February 10, 1997 (Peter Neelin)
381 @MODIFIED :
382 ---------------------------------------------------------------------------- */
acr_input_dicom_message(Acr_File * dicom_afp,Acr_Message * message)383 public Acr_Status acr_input_dicom_message(Acr_File *dicom_afp,
384 Acr_Message *message)
385 {
386 int pdu_type;
387 Acr_Long pdu_length;
388 long old_watchpoint;
389 Acr_File *afp;
390 Acr_Group group;
391 Acr_Status status;
392
393 /* Set message to NULL in case of error */
394 *message = NULL;
395
396 /* Get real afp */
397 afp = get_dicom_real_afp(dicom_afp);
398 if (afp == NULL) {
399 (void) fprintf(stderr, "Bad dicom file pointer\n");
400 exit(EXIT_FAILURE);
401 }
402
403 /* Set a watchpoint before we do anything so that we don't get stuck
404 reading too much */
405 old_watchpoint = acr_get_io_watchpoint(afp);
406 acr_set_io_watchpoint(afp, PDU_HEADER_LEN);
407
408 /* Read in PDU type and length */
409 status = read_pdu_header(afp, &pdu_type, &pdu_length);
410 if (old_watchpoint != ACR_NO_WATCHPOINT)
411 old_watchpoint -= PDU_HEADER_LEN - acr_get_io_watchpoint(afp);
412 if (status != ACR_OK) {
413 acr_set_io_watchpoint(afp, old_watchpoint);
414 return status;
415 }
416
417 /* Create the message and add the PDU type */
418 group = acr_create_group(DCM_PDU_GRPID);
419 SAVE_SHORT(group, DCM_PDU_Type, pdu_type);
420
421 /* Set a watchpoint for reading */
422 acr_set_io_watchpoint(afp, (long)pdu_length);
423
424 /* Call the appropriate routine */
425 switch (pdu_type) {
426 case ACR_PDU_ASSOC_RQ:
427 status = read_assoc_rq(afp, group);
428 break;
429 case ACR_PDU_ASSOC_AC:
430 status = read_assoc_ac(afp, group);
431 break;
432 case ACR_PDU_ASSOC_RJ:
433 status = read_assoc_rj(afp, group);
434 break;
435 case ACR_PDU_DATA_TF:
436 status = read_data_tf(dicom_afp, group);
437 break;
438 case ACR_PDU_REL_RQ:
439 status = read_rel_rq(afp, group);
440 break;
441 case ACR_PDU_REL_RP:
442 status = read_rel_rp(afp, group);
443 break;
444 case ACR_PDU_ABORT_RQ:
445 status = read_abort_rq(afp, group);
446 break;
447 default:
448 status = acr_skip_input_data(afp, pdu_length);
449 break;
450 }
451
452 /* Check that we read exactly up to the watchpoint */
453 if (acr_get_io_watchpoint(afp) != 0) {
454 switch (status) {
455 case ACR_OK:
456 status = ACR_PROTOCOL_ERROR; break;
457 case ACR_END_OF_INPUT:
458 status = ACR_ABNORMAL_END_OF_INPUT; break;
459 }
460 }
461 if (old_watchpoint != ACR_NO_WATCHPOINT)
462 old_watchpoint -= pdu_length - acr_get_io_watchpoint(afp);
463 acr_set_io_watchpoint(afp, old_watchpoint);
464
465 /* Create the message */
466 *message = acr_create_message();
467 acr_message_add_group_list(*message, group);
468
469 return status;
470 }
471
472 /* ----------------------------- MNI Header -----------------------------------
473 @NAME : read_pdu_header
474 @INPUT : afp - acr file pointer
475 @OUTPUT : pdu_type
476 pdu_length
477 @RETURNS : status of input
478 @DESCRIPTION: Reads in the header of a dicom PDU and returns the appropriate
479 values.
480 @METHOD :
481 @GLOBALS :
482 @CALLS :
483 @CREATED : February 10, 1997 (Peter Neelin)
484 @MODIFIED :
485 ---------------------------------------------------------------------------- */
read_pdu_header(Acr_File * afp,int * pdu_type,Acr_Long * pdu_length)486 private Acr_Status read_pdu_header(Acr_File *afp, int *pdu_type,
487 Acr_Long *pdu_length)
488 {
489 unsigned char buffer[PDU_HEADER_LEN];
490 Acr_Status status;
491
492 /* Read in PDU type and length */
493 status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL);
494 if (status != ACR_OK) return status;
495 *pdu_type = (int) buffer[0];
496 GET_LONG(&buffer[2], pdu_length);
497
498 return status;
499 }
500
501 /* ----------------------------- MNI Header -----------------------------------
502 @NAME : read_assoc_rq
503 @INPUT : afp - acr file pointer
504 @OUTPUT : group - group to which pdu information should be added
505 @RETURNS : status of input
506 @DESCRIPTION: Reads in an association request
507 @METHOD :
508 @GLOBALS :
509 @CALLS :
510 @CREATED : February 10, 1997 (Peter Neelin)
511 @MODIFIED :
512 ---------------------------------------------------------------------------- */
read_assoc_rq(Acr_File * afp,Acr_Group group)513 private Acr_Status read_assoc_rq(Acr_File *afp, Acr_Group group)
514 {
515 return read_assoc_rq_ac(afp, group, TRUE);
516 }
517
518 /* ----------------------------- MNI Header -----------------------------------
519 @NAME : read_assoc_rq_ac
520 @INPUT : afp - acr file pointer
521 is_request - TRUE if we should handle request type PDU, FALSE if
522 we should handle accept type PDU
523 @OUTPUT : group - group to which pdu information should be added
524 @RETURNS : status of input
525 @DESCRIPTION: Reads in an association request or accept PDU
526 @METHOD :
527 @GLOBALS :
528 @CALLS :
529 @CREATED : February 10, 1997 (Peter Neelin)
530 @MODIFIED :
531 ---------------------------------------------------------------------------- */
read_assoc_rq_ac(Acr_File * afp,Acr_Group group,int is_request)532 private Acr_Status read_assoc_rq_ac(Acr_File *afp, Acr_Group group,
533 int is_request)
534 {
535 unsigned char buffer[ASSOC_RQ_LEN - PDU_HEADER_LEN];
536 Acr_Element item, presentation_context_list, nextitem, followingitem;
537 int have_pres_context_request;
538 Acr_Status status;
539
540 /* Read in header */
541 status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL);
542 if (status != ACR_OK) return status;
543
544 /* Extract appropriate info and add it to the group */
545 EXTRACT_SHORT(group, DCM_PDU_Protocol_Version, (Acr_Short*)&buffer[6-PDU_HEADER_LEN]);
546 if (is_request) {
547 EXTRACT_UID(group, DCM_PDU_Called_Ap_title,
548 (char *) &buffer[10-PDU_HEADER_LEN], 16);
549 EXTRACT_UID(group, DCM_PDU_Calling_Ap_title,
550 (char *) &buffer[26-PDU_HEADER_LEN], 16);
551 }
552
553 /* Loop, reading items, until the watchpoint is reached */
554 presentation_context_list = NULL;
555 while (acr_get_io_watchpoint(afp) > 0) {
556
557 /* Read in the item (or item list) */
558 status = read_pdu_item(afp, &item);
559 if (status != ACR_OK) {
560 acr_delete_element_list(item);
561 acr_delete_element_list(presentation_context_list);
562 return status;
563 }
564
565 /* Loop over items, checking that we got an acceptable one and
566 add it to the appropriate thing */
567 for (nextitem = item; nextitem != NULL; nextitem = followingitem) {
568
569 /* We have to get the next item before adding the current one
570 to the list, otherwise we lose the link, since adding sets
571 the next pointer to NULL */
572 followingitem = acr_get_element_next(nextitem);
573
574 /* Put presentation context items in a list */
575 if (acr_match_element_id(DCM_PDU_Presentation_context, nextitem) ||
576 acr_match_element_id(DCM_PDU_Presentation_context_reply,
577 nextitem)) {
578 presentation_context_list =
579 acr_element_list_add(presentation_context_list, nextitem);
580 have_pres_context_request =
581 acr_match_element_id(DCM_PDU_Presentation_context, nextitem);
582 }
583 else {
584 acr_group_add_element(group, nextitem);
585 }
586 }
587 }
588
589 /* Add the presentation context list to the group */
590 if (have_pres_context_request) {
591 acr_group_add_element(group,
592 acr_create_element_sequence(DCM_PDU_Presentation_context_list,
593 presentation_context_list));
594 }
595 else {
596 acr_group_add_element(group,
597 acr_create_element_sequence(DCM_PDU_Presentation_context_reply_list,
598 presentation_context_list));
599 }
600
601 return status;
602 }
603
604 /* ----------------------------- MNI Header -----------------------------------
605 @NAME : read_data_tf
606 @INPUT : dicom_afp - acr file pointer for DICOM message level
607 @OUTPUT : group - group to which pdu information should be added
608 @RETURNS : status of input
609 @DESCRIPTION: Reads in a data PDU
610 @METHOD :
611 @GLOBALS :
612 @CALLS :
613 @CREATED : February 10, 1997 (Peter Neelin)
614 @MODIFIED :
615 ---------------------------------------------------------------------------- */
read_data_tf(Acr_File * dicom_afp,Acr_Group group)616 private Acr_Status read_data_tf(Acr_File *dicom_afp, Acr_Group group)
617 {
618 Acr_Status status;
619 Acr_Message message;
620 Acr_Group cur, new;
621 Acr_File *real_afp;
622
623 /* Reset the virtual input stream */
624 dicom_reset(dicom_afp);
625
626 /* Set the watchpoint to some very large number (other than
627 ACR_NO_WATCHPOINT) so that acr_input_message will be happy knowing
628 that there is a watchpoint, even though we don't know what it really
629 is until we get close to it. */
630 acr_set_io_watchpoint(dicom_afp, LONG_MAX-1);
631
632 /* Get the real afp */
633 real_afp = get_dicom_real_afp(dicom_afp);
634 if (real_afp == NULL) {
635 (void) fprintf(stderr, "Bad dicom file pointer\n");
636 exit(EXIT_FAILURE);
637 }
638
639 /* Set a watchpoint to force the read routine to look for a PDV header */
640 set_dicom_pdv_watchpoint(dicom_afp, (long) 0);
641
642 /* Read in the message */
643 status = acr_input_message(dicom_afp, &message);
644
645 /* Add the presentation context id to the pdu group */
646 SAVE_SHORT(group, DCM_PDU_Presentation_context_id,
647 (Acr_Short) acr_get_dicom_pres_context_id(dicom_afp));
648
649 /* Get groups and tack them onto the group list that we already have */
650 if (message != NULL) {
651 new = acr_get_message_group_list(message);
652 cur = group;
653 while (acr_get_group_next(cur) != NULL) {
654 cur = acr_get_group_next(cur);
655 }
656 acr_set_group_next(cur, new);
657 acr_message_reset(message);
658 acr_delete_message(message);
659 }
660
661 /* Check that we read exactly up to the watchpoint */
662 if (acr_get_io_watchpoint(real_afp) != 0) {
663 switch (status) {
664 case ACR_OK:
665 status = ACR_PROTOCOL_ERROR; break;
666 case ACR_END_OF_INPUT:
667 status = ACR_ABNORMAL_END_OF_INPUT; break;
668 }
669 }
670
671 return status;
672 }
673
674 /* ----------------------------- MNI Header -----------------------------------
675 @NAME : read_rel_rq
676 @INPUT : afp - acr file pointer
677 @OUTPUT : group - group to which pdu information should be added
678 @RETURNS : status of input
679 @DESCRIPTION: Reads in a release request
680 @METHOD :
681 @GLOBALS :
682 @CALLS :
683 @CREATED : February 10, 1997 (Peter Neelin)
684 @MODIFIED :
685 ---------------------------------------------------------------------------- */
686 /* ARGSUSED */
read_rel_rq(Acr_File * afp,Acr_Group group)687 private Acr_Status read_rel_rq(Acr_File *afp, Acr_Group group)
688 {
689 return acr_skip_input_data(afp, (long) ACR_SIZEOF_LONG);
690 }
691
692 /* ----------------------------- MNI Header -----------------------------------
693 @NAME : read_abort_rq
694 @INPUT : afp - acr file pointer
695 @OUTPUT : group - group to which pdu information should be added
696 @RETURNS : status of input
697 @DESCRIPTION: Reads in an abort request
698 @METHOD :
699 @GLOBALS :
700 @CALLS :
701 @CREATED : February 10, 1997 (Peter Neelin)
702 @MODIFIED :
703 ---------------------------------------------------------------------------- */
read_abort_rq(Acr_File * afp,Acr_Group group)704 private Acr_Status read_abort_rq(Acr_File *afp, Acr_Group group)
705 {
706 unsigned char buffer[ABORT_RQ_LEN - PDU_HEADER_LEN];
707 Acr_Status status;
708
709 /* Read in request header */
710 status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL);
711 if (status != ACR_OK) return status;
712
713 /* Get values */
714 SAVE_SHORT(group, DCM_PDU_Source, buffer[8 - PDU_HEADER_LEN]);
715 SAVE_SHORT(group, DCM_PDU_Reason, buffer[9 - PDU_HEADER_LEN]);
716
717 return status;
718 }
719
720 /* ----------------------------- MNI Header -----------------------------------
721 @NAME : read_assoc_ac
722 @INPUT : afp - acr file pointer
723 @OUTPUT : group - group to which pdu information should be added
724 @RETURNS : status of input
725 @DESCRIPTION: Reads in an association accept PDU
726 @METHOD :
727 @GLOBALS :
728 @CALLS :
729 @CREATED : February 10, 1997 (Peter Neelin)
730 @MODIFIED :
731 ---------------------------------------------------------------------------- */
read_assoc_ac(Acr_File * afp,Acr_Group group)732 private Acr_Status read_assoc_ac(Acr_File *afp, Acr_Group group)
733 {
734 return read_assoc_rq_ac(afp, group, FALSE);
735 }
736
737 /* ----------------------------- MNI Header -----------------------------------
738 @NAME : read_assoc_rj
739 @INPUT : afp - acr file pointer
740 @OUTPUT : group - group to which pdu information should be added
741 @RETURNS : status of input
742 @DESCRIPTION: Reads in an association reject PDU
743 @METHOD :
744 @GLOBALS :
745 @CALLS :
746 @CREATED : February 10, 1997 (Peter Neelin)
747 @MODIFIED :
748 ---------------------------------------------------------------------------- */
read_assoc_rj(Acr_File * afp,Acr_Group group)749 private Acr_Status read_assoc_rj(Acr_File *afp, Acr_Group group)
750 {
751 unsigned char buffer[ASSOC_RJ_LEN - PDU_HEADER_LEN];
752 Acr_Status status;
753
754 /* Read in request header */
755 status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL);
756 if (status != ACR_OK) return status;
757
758 /* Get values */
759 SAVE_SHORT(group, DCM_PDU_Result, buffer[7 - PDU_HEADER_LEN]);
760 SAVE_SHORT(group, DCM_PDU_Source, buffer[8 - PDU_HEADER_LEN]);
761 SAVE_SHORT(group, DCM_PDU_Reason, buffer[9 - PDU_HEADER_LEN]);
762
763 return status;
764 }
765
766 /* ----------------------------- MNI Header -----------------------------------
767 @NAME : read_rel_rp
768 @INPUT : afp - acr file pointer
769 @OUTPUT : group - group to which pdu information should be added
770 @RETURNS : status of input
771 @DESCRIPTION: Reads in a release reply
772 @METHOD :
773 @GLOBALS :
774 @CALLS :
775 @CREATED : February 10, 1997 (Peter Neelin)
776 @MODIFIED :
777 ---------------------------------------------------------------------------- */
778 /* ARGSUSED */
read_rel_rp(Acr_File * afp,Acr_Group group)779 private Acr_Status read_rel_rp(Acr_File *afp, Acr_Group group)
780 {
781 return acr_skip_input_data(afp, (long) ACR_SIZEOF_LONG);
782 }
783
784 /* ----------------------------- MNI Header -----------------------------------
785 @NAME : read_pdu_item
786 @INPUT : afp - acr file pointer
787 @OUTPUT : item - element representing value read in
788 @RETURNS : status of input
789 @DESCRIPTION: Reads in any type of PDU item and returns it as an element.
790 The element may either by a real DICOM-type element or may
791 be a sequence item.
792 @METHOD :
793 @GLOBALS :
794 @CALLS :
795 @CREATED : February 12, 1997 (Peter Neelin)
796 @MODIFIED :
797 ---------------------------------------------------------------------------- */
read_pdu_item(Acr_File * afp,Acr_Element * item)798 private Acr_Status read_pdu_item(Acr_File *afp, Acr_Element *item)
799 {
800 unsigned char buffer[PDU_ITEM_HEADER_LEN];
801 Acr_Short item_length;
802 int item_type;
803 long old_watchpoint;
804 Acr_Status status;
805
806 /* Read in PDU item type and length */
807 status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL);
808 if (status != ACR_OK) return status;
809 item_type = (int) buffer[0];
810 GET_SHORT(&buffer[2], &item_length);
811
812 /* Set a watchpoint for reading after saving the previous watchpoint */
813 old_watchpoint = acr_get_io_watchpoint(afp);
814 acr_set_io_watchpoint(afp, (long) item_length);
815
816 /* Call the appropriate routine */
817 switch (item_type) {
818 case PDU_ITEM_APPLICATION_CONTEXT:
819 status = read_uid_item(afp, DCM_PDU_Application_context, item);
820 break;
821 case PDU_ITEM_PRESENTATION_CONTEXT:
822 status = read_pres_context_item(afp, item);
823 break;
824 case PDU_ITEM_PRES_CONTEXT_REPLY:
825 status = read_pres_context_reply_item(afp, item);
826 break;
827 case PDU_ITEM_ABSTRACT_SYNTAX:
828 status = read_uid_item(afp, DCM_PDU_Abstract_syntax, item);
829 break;
830 case PDU_ITEM_TRANSFER_SYNTAX:
831 status = read_uid_item(afp, DCM_PDU_Transfer_syntax, item);
832 break;
833 case PDU_ITEM_USER_INFORMATION:
834 status = read_user_info_item(afp, item);
835 break;
836 case PDU_ITEM_MAXIMUM_LENGTH:
837 status = read_long_item(afp, DCM_PDU_Maximum_length, item);
838 break;
839 case PDU_ITEM_IMPLEMENTATION_CLASS_UID:
840 status = read_uid_item(afp, DCM_PDU_Implementation_class_uid, item);
841 break;
842 case PDU_ITEM_IMPLEMENTATION_VERSION_NAME:
843 status = read_uid_item(afp, DCM_PDU_Implementation_version_name, item);
844 break;
845 default:
846 status = read_unknown_item(afp, item_type, item);
847 break;
848 }
849
850 /* Make sure that we read the right amount of data */
851 if ((status == ACR_OK) && (acr_get_io_watchpoint(afp) != 0)) {
852 status = ACR_PROTOCOL_ERROR;
853 }
854
855 /* Set the old watchpoint */
856 if (old_watchpoint != ACR_NO_WATCHPOINT)
857 old_watchpoint -= (long) item_length - acr_get_io_watchpoint(afp);
858 acr_set_io_watchpoint(afp, old_watchpoint);
859
860 return status;
861 }
862
863 /* ----------------------------- MNI Header -----------------------------------
864 @NAME : read_uid_item
865 @INPUT : afp - acr file pointer
866 elid - id of element to be created
867 @OUTPUT : item - element representing value read in
868 @RETURNS : status of input
869 @DESCRIPTION: Reads in a UID item and creates the corresponding element.
870 Data is read up to the next watchpoint.
871 @METHOD :
872 @GLOBALS :
873 @CALLS :
874 @CREATED : February 12, 1997 (Peter Neelin)
875 @MODIFIED :
876 ---------------------------------------------------------------------------- */
read_uid_item(Acr_File * afp,Acr_Element_Id elid,Acr_Element * item)877 private Acr_Status read_uid_item(Acr_File *afp, Acr_Element_Id elid,
878 Acr_Element *item)
879 {
880 unsigned char buffer[MAX_PDU_STRING_LENGTH];
881 long length;
882 Acr_Status status;
883
884 /* Set default item */
885 *item = NULL;
886
887 /* Figure out how much data to read */
888 length = acr_get_io_watchpoint(afp);
889 if (length > MAX_PDU_STRING_LENGTH) return ACR_PDU_UID_TOO_LONG;
890
891 /* Read in the buffer */
892 status = acr_read_buffer(afp, buffer, length, NULL);
893 if (status != ACR_OK) return status;
894
895 /* Pull out the uid and create the item */
896 *item = pdu_create_element_uid(elid, (char *) buffer, (int) length);
897
898 return ACR_OK;
899 }
900
901 /* ----------------------------- MNI Header -----------------------------------
902 @NAME : read_long_item
903 @INPUT : afp - acr file pointer
904 elid - id of element to be created
905 @OUTPUT : item - element representing value read in
906 @RETURNS : status of input
907 @DESCRIPTION: Reads in an item of type long and creates the corresponding
908 element.
909 @METHOD :
910 @GLOBALS :
911 @CALLS :
912 @CREATED : February 12, 1997 (Peter Neelin)
913 @MODIFIED :
914 ---------------------------------------------------------------------------- */
read_long_item(Acr_File * afp,Acr_Element_Id elid,Acr_Element * item)915 private Acr_Status read_long_item(Acr_File *afp, Acr_Element_Id elid,
916 Acr_Element *item)
917 {
918 unsigned char buffer[ACR_SIZEOF_LONG];
919 Acr_Long value;
920 Acr_Status status;
921
922 /* Set default item */
923 *item = NULL;
924
925 /* Check that the right amount of data is to be read */
926 if (acr_get_io_watchpoint(afp) != ACR_SIZEOF_LONG)
927 return ACR_PROTOCOL_ERROR;
928
929 /* Read in the buffer and get the value */
930 status = acr_read_buffer(afp, buffer, (long) ACR_SIZEOF_LONG, NULL);
931 if (status != ACR_OK) return status;
932 GET_LONG((void *) buffer, &value);
933
934 /* Create the item */
935 *item = acr_create_element_long(elid, value);
936
937 return ACR_OK;
938
939 }
940
941 /* ----------------------------- MNI Header -----------------------------------
942 @NAME : read_unknown_item
943 @INPUT : afp - acr file pointer
944 item_type - type code for item to be read in (should be
945 a value between 0 and 255)
946 @OUTPUT : item - element representing value read in
947 @RETURNS : status of input
948 @DESCRIPTION: Reads in an item of unknown type and creates an element with
949 element id = item_type + ACR_UNKNOWN_PDU_ITEM_OFFSET
950 @METHOD :
951 @GLOBALS :
952 @CALLS :
953 @CREATED : February 12, 1997 (Peter Neelin)
954 @MODIFIED :
955 ---------------------------------------------------------------------------- */
read_unknown_item(Acr_File * afp,int item_type,Acr_Element * item)956 private Acr_Status read_unknown_item(Acr_File *afp, int item_type,
957 Acr_Element *item)
958 {
959 Acr_Status status;
960 long length;
961 unsigned char *data_pointer;
962
963 /* Set default item */
964 *item = NULL;
965
966 /* Figure out how much data to read */
967 length = acr_get_io_watchpoint(afp);
968
969 /* Get space for the data. Add a NUL character to the end in case it is
970 a string */
971 data_pointer = MALLOC((size_t) length+1);
972 data_pointer[length] = '\0';
973
974 /* Read in the buffer and get the value */
975 status = acr_read_buffer(afp, data_pointer, length, NULL);
976 if (status != ACR_OK) {
977 FREE(data_pointer);
978 return status;
979 }
980
981 /* Figure out the element id */
982 item_type += ACR_UNKNOWN_PDU_ITEM_OFFSET;
983
984 /* Create the item */
985 *item = acr_create_element(DCM_PDU_GRPID, item_type, ACR_VR_UNKNOWN,
986 length, (char *) data_pointer);
987
988 return ACR_OK;
989
990 }
991
992 /* ----------------------------- MNI Header -----------------------------------
993 @NAME : read_pres_context_item
994 @INPUT : afp - acr file pointer
995 @OUTPUT : item - element representing value read in
996 @RETURNS : status of input
997 @DESCRIPTION: Reads in a presentation context item for an associate request
998 and creates the corresponding element. Data is read up to the
999 next watchpoint.
1000 @METHOD :
1001 @GLOBALS :
1002 @CALLS :
1003 @CREATED : February 12, 1997 (Peter Neelin)
1004 @MODIFIED :
1005 ---------------------------------------------------------------------------- */
read_pres_context_item(Acr_File * afp,Acr_Element * item)1006 private Acr_Status read_pres_context_item(Acr_File *afp, Acr_Element *item)
1007 {
1008 unsigned char buffer[4];
1009 Acr_Element itemlist = NULL, newitem;
1010 Acr_Status status;
1011 int nitems;
1012
1013 /* Set default item */
1014 *item = NULL;
1015
1016 /* Read in buffer */
1017 status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL);
1018 if (status != ACR_OK) return status;
1019
1020 /* Get presentation context id and save it in the item list */
1021 newitem = acr_create_element_short(DCM_PDU_Presentation_context_id,
1022 (Acr_Short) buffer[0]);
1023 itemlist = acr_element_list_add(itemlist, newitem);
1024
1025 /* Read in items until we reach watchpoint */
1026 nitems = 0;
1027 while (acr_get_io_watchpoint(afp) > 0) {
1028
1029 /* Read in the item and add it to the list */
1030 status = read_pdu_item(afp, &newitem);
1031 if (newitem != NULL) {
1032 itemlist = acr_element_list_add(itemlist, newitem);
1033 }
1034
1035 /* Check for a read error */
1036 if (status != ACR_OK) {
1037 acr_delete_element_list(itemlist);
1038 return status;
1039 }
1040
1041 /* Make sure that we got the right thing */
1042 if (((nitems == 0) &&
1043 !acr_match_element_id(DCM_PDU_Abstract_syntax, newitem)) ||
1044 ((nitems > 0) &&
1045 !acr_match_element_id(DCM_PDU_Transfer_syntax, newitem))) {
1046 acr_delete_element_list(itemlist);
1047 return ACR_PROTOCOL_ERROR;
1048 }
1049 nitems++;
1050 }
1051
1052 /* Create the item */
1053 *item = acr_create_element_sequence(DCM_PDU_Presentation_context,
1054 itemlist);
1055
1056 return ACR_OK;
1057 }
1058
1059 /* ----------------------------- MNI Header -----------------------------------
1060 @NAME : read_pres_context_reply_item
1061 @INPUT : afp - acr file pointer
1062 @OUTPUT : item - element representing value read in
1063 @RETURNS : status of input
1064 @DESCRIPTION: Reads in a presentation context item for an associate accept
1065 and creates the corresponding element. Data is read up to the
1066 next watchpoint.
1067 @METHOD :
1068 @GLOBALS :
1069 @CALLS :
1070 @CREATED : February 12, 1997 (Peter Neelin)
1071 @MODIFIED :
1072 ---------------------------------------------------------------------------- */
read_pres_context_reply_item(Acr_File * afp,Acr_Element * item)1073 private Acr_Status read_pres_context_reply_item(Acr_File *afp,
1074 Acr_Element *item)
1075 {
1076 unsigned char buffer[4];
1077 Acr_Element itemlist = NULL, newitem;
1078 Acr_Status status;
1079
1080 /* Set default item */
1081 *item = NULL;
1082
1083 /* Read in buffer */
1084 status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL);
1085 if (status != ACR_OK) return status;
1086
1087 /* Get presentation context id and result and save them in the item list */
1088 newitem = acr_create_element_short(DCM_PDU_Presentation_context_id,
1089 (Acr_Short) buffer[0]);
1090 itemlist = acr_element_list_add(itemlist, newitem);
1091 newitem = acr_create_element_short(DCM_PDU_Result,
1092 (Acr_Short) buffer[2]);
1093 itemlist = acr_element_list_add(itemlist, newitem);
1094
1095 /* Read in items until we reach watchpoint */
1096 while (acr_get_io_watchpoint(afp) > 0) {
1097
1098 /* Read in the item and add it to the list */
1099 status = read_pdu_item(afp, &newitem);
1100 if (newitem != NULL) {
1101 itemlist = acr_element_list_add(itemlist, newitem);
1102 }
1103
1104 /* Check for a read error */
1105 if (status != ACR_OK) {
1106 acr_delete_element_list(itemlist);
1107 return status;
1108 }
1109
1110 /* Make sure that we got the right thing */
1111 if (!acr_match_element_id(DCM_PDU_Transfer_syntax, newitem)) {
1112 acr_delete_element_list(itemlist);
1113 return ACR_PROTOCOL_ERROR;
1114 }
1115 }
1116
1117 /* Create the item */
1118 *item = acr_create_element_sequence(DCM_PDU_Presentation_context_reply,
1119 itemlist);
1120
1121 return ACR_OK;
1122 }
1123
1124 /* ----------------------------- MNI Header -----------------------------------
1125 @NAME : read_user_info_item
1126 @INPUT : afp - acr file pointer
1127 @OUTPUT : item - element list representing values read in
1128 @RETURNS : status of input
1129 @DESCRIPTION: Reads in a user info item and creates the corresponding
1130 element list. Data is read up to the next watchpoint.
1131 @METHOD :
1132 @GLOBALS :
1133 @CALLS :
1134 @CREATED : February 12, 1997 (Peter Neelin)
1135 @MODIFIED :
1136 ---------------------------------------------------------------------------- */
read_user_info_item(Acr_File * afp,Acr_Element * item)1137 private Acr_Status read_user_info_item(Acr_File *afp, Acr_Element *item)
1138 {
1139 Acr_Status status;
1140 Acr_Element newitem;
1141
1142 /* Set default item */
1143 *item = NULL;
1144
1145 /* Read in items until we reach watchpoint */
1146 while (acr_get_io_watchpoint(afp) > 0) {
1147
1148 /* Read in the item */
1149 status = read_pdu_item(afp, &newitem);
1150
1151 /* Check for a read error */
1152 if (status != ACR_OK) {
1153 acr_delete_element_list(newitem);
1154 return status;
1155 }
1156
1157 /* Add the item to the list */
1158 *item = acr_element_list_add(*item, newitem);
1159
1160 }
1161
1162 return ACR_OK;
1163 }
1164
1165 /* ----------------------------- MNI Header -----------------------------------
1166 @NAME : get_uid_string
1167 @INPUT : buffer - pointer to start of string
1168 length - length of string
1169 @OUTPUT : (none)
1170 @RETURNS : Pointer to temporary string space containing a copy of the
1171 string without leading and trailing blanks.
1172 @DESCRIPTION: Removes leading and trailing blanks from a string and copies it
1173 into temporary space.
1174 @METHOD :
1175 @GLOBALS :
1176 @CALLS :
1177 @CREATED : February 12, 1997 (Peter Neelin)
1178 @MODIFIED :
1179 ---------------------------------------------------------------------------- */
get_uid_string(char * buffer,int length)1180 private char *get_uid_string(char *buffer, int length)
1181 {
1182 static char string[MAX_PDU_STRING_LENGTH];
1183 int start, end, ichar;
1184
1185 /* Check for maximum string length */
1186 if (length >= MAX_PDU_STRING_LENGTH) length = MAX_PDU_STRING_LENGTH - 1;
1187
1188 /* Skip leading spaces */
1189 for (start=0; (start < length) && isspace((int) buffer[start]); start++) {}
1190
1191 /* Look for the last non-space */
1192 for (end=length-1; (end >= 0) && isspace((int) buffer[end]); end--) {}
1193
1194 /* Copy the string */
1195 for (ichar=start; ichar <= end; ichar++) {
1196 string[ichar-start] = buffer[ichar];
1197 }
1198
1199 /* Finish off the string. Pad the string with a blank if needed to give
1200 an even length */
1201 if (((end-start+1) % 2) != 0) { /* Odd length */
1202 string[++end] = ' ';
1203 }
1204 string[end+1] = '\0';
1205
1206 return string;
1207 }
1208
1209 /* ----------------------------- MNI Header -----------------------------------
1210 @NAME : pdu_create_element_uid
1211 @INPUT : elid - Element id structure
1212 value - pointer to start of string
1213 length - length of string
1214 @OUTPUT : (none)
1215 @RETURNS : Pointer to newly created element
1216 @DESCRIPTION: Creates an element containing a string of fixed length. Leading
1217 and trailing spaces are removed.
1218 @METHOD :
1219 @GLOBALS :
1220 @CALLS :
1221 @CREATED : February 12, 1997 (Peter Neelin)
1222 @MODIFIED :
1223 ---------------------------------------------------------------------------- */
pdu_create_element_uid(Acr_Element_Id elid,char * value,int length)1224 private Acr_Element pdu_create_element_uid(Acr_Element_Id elid,
1225 char *value, int length)
1226 {
1227 Acr_Element item;
1228
1229 item = acr_create_element_string(elid, get_uid_string(value, length));
1230 acr_set_element_vr(item, ACR_VR_UI);
1231
1232 return item;
1233 }
1234
1235 /* ----------------------------- MNI Header -----------------------------------
1236 @NAME : pdu_create_element_short
1237 @INPUT : elid - Element id structure
1238 value - pointer to location containing short
1239 @OUTPUT : (none)
1240 @RETURNS : Pointer to newly created element
1241 @DESCRIPTION: Creates an element containing a short which is converted from
1242 the input format.
1243 @METHOD :
1244 @GLOBALS :
1245 @CALLS :
1246 @CREATED : February 12, 1997 (Peter Neelin)
1247 @MODIFIED :
1248 ---------------------------------------------------------------------------- */
pdu_create_element_short(Acr_Element_Id elid,void * input_value)1249 private Acr_Element pdu_create_element_short(Acr_Element_Id elid,
1250 void *input_value)
1251 {
1252 Acr_Short svalue;
1253
1254 GET_SHORT(input_value, &svalue);
1255 return acr_create_element_short(elid, svalue);
1256 }
1257
1258 /*****************************************************************************/
1259 /*************************** OUTPUT ROUTINES *********************************/
1260 /*****************************************************************************/
1261
1262 /* ----------------------------- MNI Header -----------------------------------
1263 @NAME : acr_output_dicom_message
1264 @INPUT : dicom_afp - acr file pointer for dicom output
1265 message - the message to write out
1266 @OUTPUT : (none)
1267 @RETURNS : status of output
1268 @DESCRIPTION: Writes out a dicom message.
1269 @METHOD : Although dicom connection, etc. syntaxes differ from the
1270 message syntax, this routine and the ones following take a
1271 message in the same form.
1272 @GLOBALS :
1273 @CALLS :
1274 @CREATED : February 10, 1997 (Peter Neelin)
1275 @MODIFIED :
1276 ---------------------------------------------------------------------------- */
acr_output_dicom_message(Acr_File * dicom_afp,Acr_Message message)1277 public Acr_Status acr_output_dicom_message(Acr_File *dicom_afp,
1278 Acr_Message message)
1279 {
1280 int pdu_type;
1281 Acr_Group group_list;
1282 Acr_File *afp;
1283 Acr_Status status;
1284
1285 /* Get real afp */
1286 afp = get_dicom_real_afp(dicom_afp);
1287 if (afp == NULL) {
1288 (void) fprintf(stderr, "Bad dicom file pointer\n");
1289 exit(EXIT_FAILURE);
1290 }
1291
1292 /* Get group list */
1293 group_list = acr_get_message_group_list(message);
1294
1295 /* Switch on pdu type. If the type is not found, then assume that
1296 we are writing data. */
1297 pdu_type = acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF);
1298 switch (pdu_type) {
1299 case ACR_PDU_ASSOC_RQ:
1300 status = write_assoc_rq(afp, group_list);
1301 break;
1302 case ACR_PDU_ASSOC_AC:
1303 status = write_assoc_ac(afp, group_list);
1304 break;
1305 case ACR_PDU_ASSOC_RJ:
1306 status = write_assoc_rj(afp, group_list);
1307 break;
1308 case ACR_PDU_DATA_TF:
1309 status = write_data_tf(dicom_afp, message);
1310 break;
1311 case ACR_PDU_REL_RQ:
1312 status = write_rel_rq(afp, group_list);
1313 break;
1314 case ACR_PDU_REL_RP:
1315 status = write_rel_rp(afp, group_list);
1316 break;
1317 case ACR_PDU_ABORT_RQ:
1318 status = write_abort_rq(afp, group_list);
1319 break;
1320 default:
1321 (void) fprintf(stderr, "Unrecognized pdu type for output (%d)\n",
1322 pdu_type);
1323 status = ACR_PROTOCOL_ERROR;
1324 }
1325
1326 /* Flush the output buffer */
1327 if ((acr_file_flush(afp) == EOF) && (status == ACR_OK)) {
1328 status = ACR_ABNORMAL_END_OF_OUTPUT;
1329 }
1330
1331 return status;
1332 }
1333
1334 /* ----------------------------- MNI Header -----------------------------------
1335 @NAME : write_assoc_rq
1336 @INPUT : afp - acr file pointer
1337 group - info to write
1338 @OUTPUT : (none)
1339 @RETURNS : status of output
1340 @DESCRIPTION: Writes out an association request
1341 @METHOD :
1342 @GLOBALS :
1343 @CALLS :
1344 @CREATED : February 10, 1997 (Peter Neelin)
1345 @MODIFIED :
1346 ---------------------------------------------------------------------------- */
write_assoc_rq(Acr_File * afp,Acr_Group group)1347 private Acr_Status write_assoc_rq(Acr_File *afp, Acr_Group group)
1348 {
1349 return write_assoc_rq_ac(afp, group, TRUE, NULL);
1350 }
1351
1352 /* ----------------------------- MNI Header -----------------------------------
1353 @NAME : write_assoc_ac
1354 @INPUT : afp - acr file pointer
1355 group - info to write
1356 @OUTPUT : (none)
1357 @RETURNS : status of output
1358 @DESCRIPTION: Writes out an association accept
1359 @METHOD :
1360 @GLOBALS :
1361 @CALLS :
1362 @CREATED : February 10, 1997 (Peter Neelin)
1363 @MODIFIED :
1364 ---------------------------------------------------------------------------- */
write_assoc_ac(Acr_File * afp,Acr_Group group)1365 private Acr_Status write_assoc_ac(Acr_File *afp, Acr_Group group)
1366 {
1367 return write_assoc_rq_ac(afp, group, FALSE, NULL);
1368 }
1369
1370 /* ----------------------------- MNI Header -----------------------------------
1371 @NAME : write_assoc_rq_ac
1372 @INPUT : afp - acr file pointer
1373 group - info to write
1374 is_request - TRUE if we should handle request type PDU, FALSE if
1375 we should handle accept type PDU
1376 @OUTPUT : length - if non-NULL, then nothing is written and the value
1377 is incremented by the amount of data written out.
1378 @RETURNS : status of output
1379 @DESCRIPTION: Writes out an association request or accept PDU
1380 @METHOD : Can be used to write out a PDU or calculate its length. If
1381 length is non-NULL, then length calculation is done and nothing
1382 is written out. Otherwise the PDU is written out. This routine
1383 calls itself to work out the PDU length.
1384 @GLOBALS :
1385 @CALLS :
1386 @CREATED : February 10, 1997 (Peter Neelin)
1387 @MODIFIED :
1388 ---------------------------------------------------------------------------- */
write_assoc_rq_ac(Acr_File * afp,Acr_Group group,int is_request,long * length)1389 private Acr_Status write_assoc_rq_ac(Acr_File *afp, Acr_Group group,
1390 int is_request, long *length)
1391 {
1392 unsigned char buffer[ASSOC_RQ_LEN];
1393 long pdu_length;
1394 int do_write;
1395 int offset;
1396 Acr_Short svalue;
1397 Acr_Long lvalue;
1398 Acr_Element element;
1399 Acr_Status status;
1400 Acr_Element_Id elid;
1401
1402 /* Should we write or calculate length */
1403 do_write = (length == NULL);
1404
1405 /* Write out the first part of the PDU */
1406 if (do_write) {
1407
1408 /* Zero the buffer */
1409 for (offset=0; offset<sizeof(buffer); offset++) {
1410 buffer[offset] = 0;
1411 }
1412
1413 /* Put things in the appropriate places */
1414 buffer[0] = acr_find_short(group, DCM_PDU_Type, 0);
1415 pdu_length = 0;
1416 (void) write_assoc_rq_ac(afp, group, is_request, &pdu_length);
1417 pdu_length -= PDU_HEADER_LEN;
1418 lvalue = (Acr_Long)pdu_length;
1419 PUT_LONG(&lvalue, (Acr_Long*)&buffer[2]);
1420 svalue = acr_find_short(group, DCM_PDU_Protocol_Version, 1);
1421 PUT_SHORT(&svalue, (Acr_Short*)&buffer[6]);
1422 COPY_UID(group, DCM_PDU_Called_Ap_title, &buffer[10], 16);
1423 COPY_UID(group, DCM_PDU_Calling_Ap_title, &buffer[26], 16);
1424
1425 /* Write out the first partr */
1426 status = acr_write_buffer(afp, buffer, sizeof(buffer), NULL);
1427 if (status != ACR_OK) return status;
1428 }
1429
1430 /* Add on the length of the first part of the PDU */
1431 else {
1432 *length += sizeof(buffer);
1433 }
1434
1435 /* Write out the application context */
1436 status = write_uid_item(afp,
1437 acr_find_group_element(group, DCM_PDU_Application_context),
1438 PDU_ITEM_APPLICATION_CONTEXT, length);
1439 if (status != ACR_OK) return status;
1440
1441 /* Write presentation context items */
1442 elid = (is_request ? DCM_PDU_Presentation_context_list :
1443 DCM_PDU_Presentation_context_reply_list);
1444 element = acr_find_group_element(group, elid);
1445 if ((element == NULL) || !acr_element_is_sequence(element)) {
1446 return ACR_PROTOCOL_ERROR;
1447 }
1448 element = (Acr_Element) acr_get_element_data(element);
1449 while (element != NULL) {
1450 status = write_pres_context_item(afp, element, is_request, length);
1451 if (status != ACR_OK) return status;
1452 element = acr_get_element_next(element);
1453 }
1454
1455 /* Write the user info item */
1456 status = write_user_info_item(afp, group, length);
1457
1458 return status;
1459 }
1460
1461 /* ----------------------------- MNI Header -----------------------------------
1462 @NAME : write_assoc_rj
1463 @INPUT : afp - acr file pointer
1464 group_list
1465 @OUTPUT : (none)
1466 @RETURNS : status of output
1467 @DESCRIPTION: Writes out an associate rejection
1468 @METHOD :
1469 @GLOBALS :
1470 @CALLS :
1471 @CREATED : February 10, 1997 (Peter Neelin)
1472 @MODIFIED :
1473 ---------------------------------------------------------------------------- */
write_assoc_rj(Acr_File * afp,Acr_Group group_list)1474 private Acr_Status write_assoc_rj(Acr_File *afp, Acr_Group group_list)
1475 {
1476 int result, source, reason;
1477
1478 result = (int)acr_find_short(group_list, DCM_PDU_Result, 1);
1479 source = (int)acr_find_short(group_list, DCM_PDU_Source, 1);
1480 reason = (int)acr_find_short(group_list, DCM_PDU_Reason, 1);
1481
1482 return write_fixed_length_pdu(afp, ACR_PDU_ASSOC_RJ,
1483 result, source, reason);
1484 }
1485
1486 /* ----------------------------- MNI Header -----------------------------------
1487 @NAME : write_rel_rq
1488 @INPUT : afp - acr file pointer
1489 group_list
1490 @OUTPUT : (none)
1491 @RETURNS : status of output
1492 @DESCRIPTION: Writes out a release request
1493 @METHOD :
1494 @GLOBALS :
1495 @CALLS :
1496 @CREATED : February 10, 1997 (Peter Neelin)
1497 @MODIFIED :
1498 ---------------------------------------------------------------------------- */
1499 /* ARGSUSED */
write_rel_rq(Acr_File * afp,Acr_Group group_list)1500 private Acr_Status write_rel_rq(Acr_File *afp, Acr_Group group_list)
1501 {
1502 return write_fixed_length_pdu(afp, ACR_PDU_REL_RQ, 0, 0, 0);
1503 }
1504
1505 /* ----------------------------- MNI Header -----------------------------------
1506 @NAME : write_rel_rp
1507 @INPUT : afp - acr file pointer
1508 group_list
1509 @OUTPUT : (none)
1510 @RETURNS : status of output
1511 @DESCRIPTION: Writes out a release reply
1512 @METHOD :
1513 @GLOBALS :
1514 @CALLS :
1515 @CREATED : February 10, 1997 (Peter Neelin)
1516 @MODIFIED :
1517 ---------------------------------------------------------------------------- */
1518 /* ARGSUSED */
write_rel_rp(Acr_File * afp,Acr_Group group_list)1519 private Acr_Status write_rel_rp(Acr_File *afp, Acr_Group group_list)
1520 {
1521 return write_fixed_length_pdu(afp, ACR_PDU_REL_RP, 0, 0, 0);
1522 }
1523
1524 /* ----------------------------- MNI Header -----------------------------------
1525 @NAME : write_abort_rq
1526 @INPUT : afp - acr file pointer
1527 group_list
1528 @OUTPUT : (none)
1529 @RETURNS : status of output
1530 @DESCRIPTION: Writes out an abort request
1531 @METHOD :
1532 @GLOBALS :
1533 @CALLS :
1534 @CREATED : February 10, 1997 (Peter Neelin)
1535 @MODIFIED :
1536 ---------------------------------------------------------------------------- */
write_abort_rq(Acr_File * afp,Acr_Group group_list)1537 private Acr_Status write_abort_rq(Acr_File *afp, Acr_Group group_list)
1538 {
1539 int source, reason;
1540
1541 source = (int)acr_find_short(group_list, DCM_PDU_Source, 0);
1542 reason = (int)acr_find_short(group_list, DCM_PDU_Reason, 0);
1543
1544 return write_fixed_length_pdu(afp, ACR_PDU_ABORT_RQ, 0, source, reason);
1545 }
1546
1547 /* ----------------------------- MNI Header -----------------------------------
1548 @NAME : write_fixed_length_pdu
1549 @INPUT : afp - acr file pointer
1550 pdu_type
1551 result - value for result field
1552 source - value for source field
1553 reason - value for reason field
1554 @OUTPUT : (none)
1555 @RETURNS : status of output
1556 @DESCRIPTION: Writes out a fixed length PDU (release request or reply
1557 or an abort request or an associate reject)
1558 @METHOD :
1559 @GLOBALS :
1560 @CALLS :
1561 @CREATED : February 10, 1997 (Peter Neelin)
1562 @MODIFIED :
1563 ---------------------------------------------------------------------------- */
write_fixed_length_pdu(Acr_File * afp,int pdu_type,int result,int source,int reason)1564 private Acr_Status write_fixed_length_pdu(Acr_File *afp, int pdu_type,
1565 int result, int source, int reason)
1566 {
1567 unsigned char buffer[ABORT_RQ_LEN];
1568 Acr_Long pdu_length;
1569 int i;
1570
1571 /* Zero the buffer */
1572 for (i=0; i<sizeof(buffer); i++) {
1573 buffer[i] = 0;
1574 }
1575
1576 /* Put things in the appropriate places */
1577 buffer[0] = (unsigned char) pdu_type;
1578 pdu_length = ABORT_RQ_LEN - PDU_HEADER_LEN;
1579 PUT_LONG(&pdu_length, (Acr_Long*)&buffer[2]);
1580 buffer[7] = (unsigned char) result;
1581 buffer[8] = (unsigned char) source;
1582 buffer[9] = (unsigned char) reason;
1583
1584 /* Write it out */
1585 return acr_write_buffer(afp, buffer, sizeof(buffer), NULL);
1586 }
1587
1588 /* ----------------------------- MNI Header -----------------------------------
1589 @NAME : write_data_tf
1590 @INPUT : dicom_afp - acr file pointer for DICOM messages
1591 message - message to write out
1592 @OUTPUT : (none)
1593 @RETURNS : status of output
1594 @DESCRIPTION: Writes out a data PDU
1595 @METHOD :
1596 @GLOBALS :
1597 @CALLS :
1598 @CREATED : February 10, 1997 (Peter Neelin)
1599 @MODIFIED :
1600 ---------------------------------------------------------------------------- */
write_data_tf(Acr_File * dicom_afp,Acr_Message message)1601 private Acr_Status write_data_tf(Acr_File *dicom_afp, Acr_Message message)
1602 {
1603 Acr_Group pdu_group, command_group, data_group;
1604 Acr_Element element;
1605 Acr_Message temp_message;
1606 long pdu_length, command_length, data_length;
1607 Acr_VR_encoding_type vr_encoding;
1608 Acr_Status status;
1609
1610 /* Look for the pdu group, the command group and the first data group */
1611 pdu_group = acr_get_message_group_list(message);
1612 if (pdu_group == NULL) return ACR_PROTOCOL_ERROR;
1613 if (acr_get_group_group(pdu_group) == DCM_PDU_GRPID) {
1614 command_group = acr_get_group_next(pdu_group);
1615 }
1616 else {
1617 command_group = pdu_group;
1618 pdu_group = NULL;
1619 }
1620 if (command_group == NULL) return ACR_PROTOCOL_ERROR;
1621 if (acr_get_group_group(command_group) == ACR_COMMAND_GRPID) {
1622 data_group = acr_get_group_next(command_group);
1623 }
1624 else {
1625 data_group = command_group;
1626 command_group = NULL;
1627 }
1628
1629 /* Check that we have something to write */
1630 if ((command_group == NULL) && (data_group == NULL))
1631 return ACR_PROTOCOL_ERROR;
1632
1633 /* Set the presentation context id for the stream */
1634 if ((pdu_group != NULL)) {
1635 element = acr_find_group_element(pdu_group,
1636 DCM_PDU_Presentation_context_id);
1637 if (element != NULL) {
1638 acr_set_dicom_pres_context_id(dicom_afp,
1639 (int) acr_get_element_short(element));
1640 }
1641 }
1642
1643 /* Set up the virtual output stream for writing */
1644 vr_encoding = acr_get_vr_encoding(dicom_afp);
1645 if (pdu_group != NULL)
1646 pdu_length = acr_get_group_total_length(pdu_group, vr_encoding);
1647 else
1648 pdu_length = 0;
1649 if (command_group != NULL)
1650 command_length = acr_get_group_total_length(command_group, vr_encoding);
1651 else
1652 command_length = 0;
1653 if (data_group != NULL)
1654 data_length = acr_get_message_total_length(message, vr_encoding) -
1655 command_length - pdu_length;
1656 else
1657 data_length = 0;
1658 if ((command_length <= 0) && (data_length <= 0)) {
1659 return ACR_PROTOCOL_ERROR;
1660 }
1661 dicom_setup_output(dicom_afp, command_length, data_length);
1662
1663 /* Create a temporary message without the PDU group */
1664 if (pdu_group != NULL) {
1665 temp_message = acr_create_message();
1666 acr_message_add_group_list(temp_message, ((command_group != NULL) ?
1667 command_group : data_group));
1668 }
1669 else {
1670 temp_message = message;
1671 }
1672
1673 /* Write out the message */
1674 status = acr_output_message(dicom_afp, temp_message);
1675
1676 /* Delete the temporary message without touching the groups (which are
1677 still part of the original message */
1678 if (temp_message != message) {
1679 acr_message_reset(temp_message);
1680 acr_delete_message(temp_message);
1681 }
1682
1683 return status;
1684 }
1685
1686 /* ----------------------------- MNI Header -----------------------------------
1687 @NAME : write_uid_item
1688 @INPUT : afp - acr file pointer
1689 element - element to write out
1690 item_type - code identifying item to be written out
1691 @OUTPUT : length - if non-NULL, then nothing is written and the value
1692 is incremented by the amount of data written out.
1693 @RETURNS : status of output
1694 @DESCRIPTION: Writes out a uid item.
1695 @METHOD :
1696 @GLOBALS :
1697 @CALLS :
1698 @CREATED : February 12, 1997 (Peter Neelin)
1699 @MODIFIED :
1700 ---------------------------------------------------------------------------- */
write_uid_item(Acr_File * afp,Acr_Element element,int item_type,long * length)1701 private Acr_Status write_uid_item(Acr_File *afp, Acr_Element element,
1702 int item_type, long *length)
1703 {
1704 unsigned char buffer[PDU_ITEM_HEADER_LEN];
1705 long item_length;
1706 Acr_Status status;
1707 int uid_length;
1708 int do_write;
1709 Acr_String uid;
1710 Acr_Short svalue;
1711
1712 /* Check element */
1713 if (element == NULL) return ACR_PROTOCOL_ERROR;
1714
1715 /* Check whether we are writing or counting */
1716 do_write = (length == NULL);
1717
1718 /* Get the uid pointer */
1719 uid = acr_get_element_string(element);
1720 if (uid == NULL) return ACR_PROTOCOL_ERROR;
1721
1722 /* Write item header */
1723 if (do_write) {
1724 buffer[0] = (unsigned char) item_type;
1725 buffer[1] = 0;
1726 item_length = 0;
1727 (void) write_uid_item(afp, element, item_type, &item_length);
1728 item_length -= PDU_ITEM_HEADER_LEN;
1729 svalue = (Acr_Short) item_length;
1730 PUT_SHORT(&svalue, &buffer[2]);
1731 status = acr_write_buffer(afp, buffer, sizeof(buffer), NULL);
1732 if (status != ACR_OK) return status;
1733 }
1734 else {
1735 *length += sizeof(buffer);
1736 }
1737
1738 /* Get the uid length, skipping trailing blanks */
1739 uid_length = strlen(uid);
1740 while ((uid_length > 0) && isspace(uid[uid_length-1])) {uid_length--;}
1741
1742 /* Write out the uid */
1743 if (do_write) {
1744 status = acr_write_buffer(afp, (unsigned char *)uid, uid_length, NULL);
1745 if (status != ACR_OK) return status;
1746 }
1747 else {
1748 *length += uid_length;
1749 }
1750
1751 #if 0
1752 /* Check that we wrote an even number of bytes */
1753 if ((uid_length % 2) != 0) {
1754 if (do_write) {
1755 (void) acr_putc((int) ' ', afp);
1756 }
1757 else {
1758 *length += 1;
1759 }
1760 }
1761 #endif
1762
1763 return ACR_OK;
1764 }
1765
1766 /* ----------------------------- MNI Header -----------------------------------
1767 @NAME : write_pres_context_item
1768 @INPUT : afp - acr file pointer
1769 item - information to write out
1770 @OUTPUT : length - if non-NULL, then routine computes length of output
1771 @RETURNS : status of output
1772 @DESCRIPTION: Writes out a presentation context item (or reply item).
1773 @METHOD :
1774 @GLOBALS :
1775 @CALLS :
1776 @CREATED : February 12, 1997 (Peter Neelin)
1777 @MODIFIED :
1778 ---------------------------------------------------------------------------- */
write_pres_context_item(Acr_File * afp,Acr_Element item,int is_request,long * length)1779 private Acr_Status write_pres_context_item(Acr_File *afp, Acr_Element item,
1780 int is_request, long *length)
1781 {
1782 unsigned char buffer[PDU_ITEM_HEADER_LEN+4];
1783 long item_length;
1784 Acr_Status status;
1785 int do_write;
1786 Acr_Element itemlist, subitem;
1787 Acr_Short svalue;
1788
1789 /* Check element */
1790 if ((item == NULL) || (!acr_element_is_sequence(item))) {
1791 return ACR_PROTOCOL_ERROR;
1792 }
1793 itemlist = (Acr_Element) acr_get_element_data(item);
1794
1795 /* Check whether we are writing or counting */
1796 do_write = (length == NULL);
1797
1798 /* Write item header */
1799 if (do_write) {
1800 buffer[0] = (is_request ?
1801 PDU_ITEM_PRESENTATION_CONTEXT :
1802 PDU_ITEM_PRES_CONTEXT_REPLY);
1803 buffer[1] = 0;
1804 item_length = 0;
1805 (void) write_pres_context_item(afp, item, is_request, &item_length);
1806 item_length -= PDU_ITEM_HEADER_LEN;
1807 svalue = (Acr_Short) item_length;
1808 PUT_SHORT(&svalue, (Acr_Short*)&buffer[2]);
1809 subitem = acr_find_element_id(itemlist, DCM_PDU_Presentation_context_id);
1810 if (subitem == NULL) return ACR_PROTOCOL_ERROR;
1811 buffer[4] = (unsigned char) acr_get_element_short(subitem);
1812 buffer[5] = 0;
1813 if (!is_request) {
1814 subitem = acr_find_element_id(itemlist, DCM_PDU_Result);
1815 if (subitem == NULL) return ACR_PROTOCOL_ERROR;
1816 buffer[6] = (unsigned char) acr_get_element_short(subitem);
1817 }
1818 else
1819 buffer[6] = 0;
1820 buffer[7] = 0;
1821 status = acr_write_buffer(afp, buffer, sizeof(buffer), NULL);
1822 if (status != ACR_OK) return status;
1823 }
1824 else {
1825 *length += sizeof(buffer);
1826 }
1827
1828 /* Write out the transfer syntaxes */
1829 if (is_request) {
1830 subitem = acr_find_element_id(itemlist, DCM_PDU_Abstract_syntax);
1831 if (subitem == NULL) return ACR_PROTOCOL_ERROR;
1832 status = write_uid_item(afp, subitem, PDU_ITEM_ABSTRACT_SYNTAX, length);
1833 if (status != ACR_OK) return status;
1834 subitem = itemlist;
1835 while (subitem != NULL) {
1836 if (acr_match_element_id(DCM_PDU_Transfer_syntax, subitem)) {
1837 status = write_uid_item(afp, subitem, PDU_ITEM_TRANSFER_SYNTAX,
1838 length);
1839 if (status != ACR_OK) return status;
1840 }
1841 subitem = acr_get_element_next(subitem);
1842 }
1843 }
1844 else {
1845 subitem = acr_find_element_id(itemlist, DCM_PDU_Transfer_syntax);
1846 if (subitem == NULL) return ACR_PROTOCOL_ERROR;
1847 status = write_uid_item(afp, subitem, PDU_ITEM_TRANSFER_SYNTAX, length);
1848 if (status != ACR_OK) return status;
1849 }
1850
1851 return ACR_OK;
1852 }
1853
1854 /* ----------------------------- MNI Header -----------------------------------
1855 @NAME : write_user_info_item
1856 @INPUT : afp - acr file pointer
1857 group - information to write out
1858 @OUTPUT : length - if non-NULL, then routine computes length of output
1859 @RETURNS : status of output
1860 @DESCRIPTION: Writes out a user info item
1861 @METHOD :
1862 @GLOBALS :
1863 @CALLS :
1864 @CREATED : February 12, 1997 (Peter Neelin)
1865 @MODIFIED :
1866 ---------------------------------------------------------------------------- */
write_user_info_item(Acr_File * afp,Acr_Group group,long * length)1867 private Acr_Status write_user_info_item(Acr_File *afp, Acr_Group group,
1868 long *length)
1869 {
1870 unsigned char buffer[2*PDU_ITEM_HEADER_LEN+ACR_SIZEOF_LONG];
1871 long item_length;
1872 Acr_Long maximum_length;
1873 Acr_Status status;
1874 int do_write;
1875 Acr_Short svalue;
1876 Acr_Element element;
1877 int item_type;
1878
1879 /* Check whether we are writing or counting */
1880 do_write = (length == NULL);
1881
1882 /* Write item header */
1883 if (do_write) {
1884 buffer[0] = PDU_ITEM_USER_INFORMATION;
1885 buffer[1] = 0;
1886 item_length = 0;
1887 (void) write_user_info_item(afp, group, &item_length);
1888 item_length -= PDU_ITEM_HEADER_LEN;
1889 svalue = (Acr_Short) item_length;
1890 PUT_SHORT(&svalue, (Acr_Short*)&buffer[2]);
1891 buffer[4] = PDU_ITEM_MAXIMUM_LENGTH;
1892 buffer[5] = 0;
1893 svalue = ACR_SIZEOF_LONG;
1894 PUT_SHORT(&svalue, (Acr_Short*)&buffer[6]);
1895 maximum_length = acr_find_long(group, DCM_PDU_Maximum_length, 0);
1896 PUT_LONG(&maximum_length, (Acr_Long*)&buffer[8]);
1897 status = acr_write_buffer(afp, buffer, sizeof(buffer), NULL);
1898 if (status != ACR_OK) return status;
1899 }
1900 else {
1901 *length += sizeof(buffer);
1902 }
1903
1904 /* Write out implementation information */
1905 element = acr_find_group_element(group, DCM_PDU_Implementation_class_uid);
1906 if (element != NULL) {
1907 status = write_unknown_item(afp, PDU_ITEM_IMPLEMENTATION_CLASS_UID,
1908 acr_get_element_length(element),
1909 acr_get_element_data(element), length);
1910 if (status != ACR_OK) return status;
1911 }
1912 element = acr_find_group_element(group,
1913 DCM_PDU_Implementation_version_name);
1914 if (element != NULL) {
1915 status = write_unknown_item(afp, PDU_ITEM_IMPLEMENTATION_VERSION_NAME,
1916 acr_get_element_length(element),
1917 acr_get_element_data(element), length);
1918 if (status != ACR_OK) return status;
1919 }
1920
1921 /* Write any unknown items with an item type > PDU_ITEM_USER_INFORMATION */
1922 if (acr_get_group_group(group) == DCM_PDU_GRPID) {
1923
1924 /* Loop over all elements */
1925 for (element = acr_get_group_element_list(group);
1926 element != NULL;
1927 element = acr_get_element_next(element)) {
1928
1929 /* Check that the element is unknown and that it is an user
1930 info item */
1931 item_type = acr_get_element_element(element);
1932 if (item_type < ACR_UNKNOWN_PDU_ITEM_OFFSET)
1933 continue;
1934 item_type -= ACR_UNKNOWN_PDU_ITEM_OFFSET;
1935 if (item_type < PDU_ITEM_USER_INFORMATION)
1936 continue;
1937
1938 /* Write out the value */
1939 status = write_unknown_item(afp, item_type,
1940 acr_get_element_length(element),
1941 acr_get_element_data(element), length);
1942 if (status != ACR_OK) return status;
1943 }
1944 }
1945
1946 return ACR_OK;
1947 }
1948
1949 /* ----------------------------- MNI Header -----------------------------------
1950 @NAME : write_unknown_item
1951 @INPUT : afp - acr file pointer
1952 item_type - code identifying item to be written out
1953 data_length - length of data to be written
1954 data_pointer - pointer to data
1955 @OUTPUT : length - if non-NULL, then nothing is written and the value
1956 is incremented by the amount of data written out.
1957 @RETURNS : status of output
1958 @DESCRIPTION: Writes out an item of unknown type.
1959 @METHOD :
1960 @GLOBALS :
1961 @CALLS :
1962 @CREATED : February 12, 1997 (Peter Neelin)
1963 @MODIFIED :
1964 ---------------------------------------------------------------------------- */
write_unknown_item(Acr_File * afp,int item_type,long data_length,char * data_pointer,long * length)1965 private Acr_Status write_unknown_item(Acr_File *afp, int item_type,
1966 long data_length, char *data_pointer,
1967 long *length)
1968 {
1969 unsigned char buffer[PDU_ITEM_HEADER_LEN];
1970 long item_length;
1971 int do_write;
1972 Acr_Status status;
1973 Acr_Short svalue;
1974
1975 /* Check whether we are writing or counting */
1976 do_write = (length == NULL);
1977
1978 /* Write item header */
1979 if (do_write) {
1980 buffer[0] = (unsigned char) item_type;
1981 buffer[1] = 0;
1982 item_length = 0;
1983 (void) write_unknown_item(afp, item_type, data_length, data_pointer,
1984 &item_length);
1985 item_length -= PDU_ITEM_HEADER_LEN;
1986 svalue = (Acr_Short) item_length;
1987 PUT_SHORT(&svalue, (Acr_Short*)&buffer[2]);
1988 status = acr_write_buffer(afp, buffer, sizeof(buffer), NULL);
1989 if (status != ACR_OK) return status;
1990 }
1991 else {
1992 *length += sizeof(buffer);
1993 }
1994
1995 /* Write out the data */
1996 if (do_write) {
1997 status = acr_write_buffer(afp, (unsigned char *) data_pointer,
1998 data_length, NULL);
1999 if (status != ACR_OK) return status;
2000 }
2001 else {
2002 *length += data_length;
2003 }
2004
2005 return ACR_OK;
2006 }
2007
2008 /* ----------------------------- MNI Header -----------------------------------
2009 @NAME : pdu_copy_uid
2010 @INPUT : string
2011 length - maximum length of uid
2012 @OUTPUT : buffer
2013 @RETURNS : (nothing)
2014 @DESCRIPTION: Copies a uid from string to buffer, padding with trailing blanks
2015 up to length.
2016 @METHOD :
2017 @GLOBALS :
2018 @CALLS :
2019 @CREATED : February 12, 1997 (Peter Neelin)
2020 @MODIFIED :
2021 ---------------------------------------------------------------------------- */
pdu_copy_uid(Acr_String string,char * buffer,int length)2022 private void pdu_copy_uid(Acr_String string, char *buffer, int length)
2023 {
2024 int i;
2025
2026 for (i=0; (i < length) && (string[i] != '\0'); i++) {
2027 buffer[i] = string[i];
2028 }
2029 for (; (i < length); i++) {
2030 buffer[i] = ' ';
2031 }
2032 }
2033
2034 /*****************************************************************************/
2035 /************************** DICOM I/O STREAM *********************************/
2036 /*****************************************************************************/
2037
2038 /* ----------------------------- MNI Header -----------------------------------
2039 @NAME : acr_initialize_dicom_input
2040 @INPUT : io_data - pointer to data for read and write routines
2041 maxlength - maximum length for a single read or write
2042 (zero or negative means use internal maximum).
2043 io_routine - routine to read or write data
2044 @OUTPUT : (none)
2045 @RETURNS : pointer to Acr_File structure created
2046 @DESCRIPTION: Sets up the routines for reading dicom messages from an input
2047 stream.
2048 @METHOD :
2049 @GLOBALS :
2050 @CALLS :
2051 @CREATED : February 18, 1997 (Peter Neelin)
2052 @MODIFIED :
2053 ---------------------------------------------------------------------------- */
acr_initialize_dicom_input(void * io_data,int maxlength,Acr_Io_Routine io_routine)2054 public Acr_File *acr_initialize_dicom_input(void *io_data,
2055 int maxlength,
2056 Acr_Io_Routine io_routine)
2057 {
2058 return initialize_dicom_stream(io_data, maxlength, io_routine,
2059 DICOM_INPUT);
2060 }
2061
2062 /* ----------------------------- MNI Header -----------------------------------
2063 @NAME : acr_dicom_set_ismore_function
2064 @INPUT : afp
2065 ismore_function
2066 @OUTPUT : (none)
2067 @RETURNS : (none)
2068 @DESCRIPTION: Sets the function to be used for testing if there is more
2069 data waiting on the input stream.
2070 stream.
2071 @METHOD :
2072 @GLOBALS :
2073 @CALLS :
2074 @CREATED : May 17, 2000 (Peter Neelin)
2075 @MODIFIED :
2076 ---------------------------------------------------------------------------- */
acr_dicom_set_ismore_function(Acr_File * afp,Acr_Ismore_Function ismore_function)2077 public void acr_dicom_set_ismore_function(Acr_File *afp,
2078 Acr_Ismore_Function ismore_function)
2079 {
2080 Acr_Dicom_IO *stream_data;
2081
2082 /* Get the structure pointer */
2083 stream_data = get_dicom_io_pointer(afp);
2084
2085 /* Set the dicom ismore function on the virtual stream and the
2086 user-supplied one on the real stream */
2087 if (stream_data == NULL) {
2088 acr_file_set_ismore_function(afp, ismore_function);
2089 }
2090 else {
2091 acr_file_set_ismore_function(stream_data->virtual_afp,
2092 dicom_ismore);
2093 acr_file_set_ismore_function(stream_data->real_afp,
2094 ismore_function);
2095 }
2096
2097 }
2098
2099 /* ----------------------------- MNI Header -----------------------------------
2100 @NAME : acr_initialize_dicom_output
2101 @INPUT : io_data - pointer to data for read and write routines
2102 maxlength - maximum length for a single read or write
2103 (zero or negative means use internal maximum).
2104 io_routine - routine to read or write data
2105 @OUTPUT : (none)
2106 @RETURNS : pointer to Acr_File structure created
2107 @DESCRIPTION: Sets up the routines for writing dicom messages to an output
2108 stream.
2109 @METHOD :
2110 @GLOBALS :
2111 @CALLS :
2112 @CREATED : February 18, 1997 (Peter Neelin)
2113 @MODIFIED :
2114 ---------------------------------------------------------------------------- */
acr_initialize_dicom_output(void * io_data,int maxlength,Acr_Io_Routine io_routine)2115 public Acr_File *acr_initialize_dicom_output(void *io_data,
2116 int maxlength,
2117 Acr_Io_Routine io_routine)
2118 {
2119 return initialize_dicom_stream(io_data, maxlength, io_routine,
2120 DICOM_OUTPUT);
2121 }
2122
2123 /* ----------------------------- MNI Header -----------------------------------
2124 @NAME : acr_close_dicom_file
2125 @INPUT : afp
2126 @OUTPUT : (none)
2127 @RETURNS : (nothing)
2128 @DESCRIPTION: Closes a dicom output stream.
2129 @METHOD :
2130 @GLOBALS :
2131 @CALLS :
2132 @CREATED : February 18, 1997 (Peter Neelin)
2133 @MODIFIED :
2134 ---------------------------------------------------------------------------- */
acr_close_dicom_file(Acr_File * afp)2135 public void acr_close_dicom_file(Acr_File *afp)
2136 {
2137 Acr_Dicom_IO *stream_data;
2138
2139 /* Get the structure pointer */
2140 stream_data = get_dicom_io_pointer(afp);
2141
2142 /* Flush the virtual stream */
2143 (void) acr_file_flush(afp);
2144
2145 /* Close the real stream */
2146 if (stream_data != NULL) {
2147 acr_file_free(stream_data->real_afp);
2148 if (stream_data->client_data != NULL) {
2149 FREE(stream_data->client_data);
2150 }
2151 }
2152
2153 /* Free the virtual stream. This will free the stream data structure. */
2154 acr_file_free(afp);
2155
2156 }
2157
2158 /* ----------------------------- MNI Header -----------------------------------
2159 @NAME : acr_dicom_get_io_data
2160 @INPUT : afp
2161 @OUTPUT : (none)
2162 @RETURNS : pointer to io data
2163 @DESCRIPTION: Gets back pointer that was passed in to
2164 acr_initialize_dicom_{input,output}.
2165 @METHOD :
2166 @GLOBALS :
2167 @CALLS :
2168 @CREATED : February 18, 1997 (Peter Neelin)
2169 @MODIFIED :
2170 ---------------------------------------------------------------------------- */
acr_dicom_get_io_data(Acr_File * afp)2171 public void *acr_dicom_get_io_data(Acr_File *afp)
2172 {
2173 Acr_Dicom_IO *stream_data;
2174
2175 /* Get the structure pointer */
2176 stream_data = get_dicom_io_pointer(afp);
2177
2178 /* Get the io pointer for the real stream */
2179 return acr_file_get_io_data(stream_data->real_afp);
2180 }
2181
2182 /* ----------------------------- MNI Header -----------------------------------
2183 @NAME : acr_dicom_enable_trace / acr_dicom_disable_trace
2184 @INPUT : afp
2185 @OUTPUT : (none)
2186 @RETURNS : (nothing)
2187 @DESCRIPTION: Enables / disables trace on a dicom stream (on the real stream)
2188 @METHOD :
2189 @GLOBALS :
2190 @CALLS :
2191 @CREATED : February 18, 1997 (Peter Neelin)
2192 @MODIFIED :
2193 ---------------------------------------------------------------------------- */
acr_dicom_enable_trace(Acr_File * afp)2194 public void acr_dicom_enable_trace(Acr_File *afp)
2195 {
2196 Acr_Dicom_IO *stream_data;
2197
2198 /* Get the structure pointer */
2199 stream_data = get_dicom_io_pointer(afp);
2200 acr_file_enable_trace((stream_data == NULL) ?
2201 afp : stream_data->real_afp);
2202
2203 }
2204
acr_dicom_disable_trace(Acr_File * afp)2205 public void acr_dicom_disable_trace(Acr_File *afp)
2206 {
2207 Acr_Dicom_IO *stream_data;
2208
2209 /* Get the structure pointer */
2210 stream_data = get_dicom_io_pointer(afp);
2211 acr_file_disable_trace((stream_data == NULL) ?
2212 afp : stream_data->real_afp);
2213
2214 }
2215
2216 /* ----------------------------- MNI Header -----------------------------------
2217 @NAME : acr_dicom_set_eof
2218 @INPUT : afp
2219 @OUTPUT : (none)
2220 @RETURNS : (nothing)
2221 @DESCRIPTION: Tells the input or output stream that it has reached the end
2222 of file so that no more data will be read or written. This can
2223 be useful for preventing further reading or writing if an
2224 alarm goes off, for example.
2225 @METHOD :
2226 @GLOBALS :
2227 @CALLS :
2228 @CREATED : February 18, 1997 (Peter Neelin)
2229 @MODIFIED :
2230 ---------------------------------------------------------------------------- */
acr_dicom_set_eof(Acr_File * afp)2231 public void acr_dicom_set_eof(Acr_File *afp)
2232 {
2233 Acr_Dicom_IO *stream_data;
2234
2235 /* Get the structure pointer */
2236 stream_data = get_dicom_io_pointer(afp);
2237
2238 /* Set eof on both afp's */
2239 acr_file_set_eof(afp);
2240 acr_file_set_eof(stream_data->real_afp);
2241
2242 }
2243
2244 /* ----------------------------- MNI Header -----------------------------------
2245 @NAME : initialize_dicom_stream
2246 @INPUT : io_data - pointer to data for read and write routines
2247 maxlength - maximum length for a single read or write
2248 (zero or negative means use internal maximum).
2249 io_routine - routine to read or write data
2250 stream_type - DICOM_INPUT or DICOM_OUTPUT
2251 @OUTPUT : (none)
2252 @RETURNS : pointer to Acr_File structure created
2253 @DESCRIPTION: Sets up the routines for dicom i/o
2254 @METHOD :
2255 @GLOBALS :
2256 @CALLS :
2257 @CREATED : February 18, 1997 (Peter Neelin)
2258 @MODIFIED :
2259 ---------------------------------------------------------------------------- */
initialize_dicom_stream(void * io_data,int maxlength,Acr_Io_Routine io_routine,Dicom_IO_stream_type stream_type)2260 private Acr_File *initialize_dicom_stream(void *io_data, int maxlength,
2261 Acr_Io_Routine io_routine,
2262 Dicom_IO_stream_type stream_type)
2263 {
2264 Acr_File *real_afp, *virtual_afp;
2265 Acr_Dicom_IO *stream_data;
2266 Acr_Io_Routine virtual_io_routine;
2267
2268 /* Create the real input stream */
2269 real_afp = acr_file_initialize(io_data, maxlength, io_routine);
2270
2271 /* Create the virtual afp data */
2272 stream_data = MALLOC(sizeof(*stream_data));
2273 stream_data->stream_type = stream_type;
2274 stream_data->real_afp = real_afp;
2275 stream_data->presentation_context_id = 0;
2276 stream_data->pdv_watchpoint = ACR_NO_WATCHPOINT;
2277 stream_data->maximum_length = LONG_MAX;
2278 stream_data->writing_command = FALSE;
2279 stream_data->data_length = 0;
2280 stream_data->client_data = NULL;
2281
2282 /* Create the virtual input stream */
2283 if (stream_type == DICOM_INPUT) {
2284 virtual_io_routine = dicom_input_routine;
2285 }
2286 else {
2287 virtual_io_routine = dicom_output_routine;
2288 }
2289 virtual_afp = acr_file_initialize((void *) stream_data, 0,
2290 virtual_io_routine);
2291 stream_data->virtual_afp = virtual_afp;
2292
2293 return virtual_afp;
2294 }
2295
2296 /* ----------------------------- MNI Header -----------------------------------
2297 @NAME : get_dicom_io_pointer
2298 @INPUT : afp
2299 @OUTPUT : (none)
2300 @RETURNS : pointer to Acr_Dicom_IO structure or NULL if an error occurs
2301 @DESCRIPTION: Gets the pointer to the Acr_Dicom_IO structure contained within
2302 the afp.
2303 @METHOD :
2304 @GLOBALS :
2305 @CALLS :
2306 @CREATED : February 18, 1997 (Peter Neelin)
2307 @MODIFIED :
2308 ---------------------------------------------------------------------------- */
get_dicom_io_pointer(Acr_File * afp)2309 private Acr_Dicom_IO *get_dicom_io_pointer(Acr_File *afp)
2310 {
2311 Acr_Dicom_IO *stream_data;
2312
2313 stream_data = (Acr_Dicom_IO *) acr_file_get_io_data(afp);
2314 if ((stream_data == NULL) ||
2315 ((stream_data->stream_type != DICOM_INPUT) &&
2316 (stream_data->stream_type != DICOM_OUTPUT))) {
2317 return NULL;
2318 }
2319 return stream_data;
2320 }
2321
2322 /* ----------------------------- MNI Header -----------------------------------
2323 @NAME : get_dicom_real_afp
2324 @INPUT : afp
2325 @OUTPUT : (none)
2326 @RETURNS : pointer to Acr_File for real input or output
2327 @DESCRIPTION: Gets the pointer to the Acr_File structure that is really
2328 connected to a stdio input or output stream.
2329 @METHOD :
2330 @GLOBALS :
2331 @CALLS :
2332 @CREATED : February 18, 1997 (Peter Neelin)
2333 @MODIFIED :
2334 ---------------------------------------------------------------------------- */
get_dicom_real_afp(Acr_File * afp)2335 private Acr_File *get_dicom_real_afp(Acr_File *afp)
2336 {
2337 Acr_Dicom_IO *stream_data;
2338
2339 /* Get the structure pointer */
2340 stream_data = get_dicom_io_pointer(afp);
2341 if (stream_data == NULL) return NULL;
2342
2343 /* Return the afp */
2344 return stream_data->real_afp;
2345 }
2346
2347 /* ----------------------------- MNI Header -----------------------------------
2348 @NAME : get_dicom_pdv_watchpoint
2349 @INPUT : afp
2350 @OUTPUT : (none)
2351 @RETURNS : Distance from current position to pdv watchpoint on real stream
2352 @DESCRIPTION: Returns the distance to the PDV watchpoint on the real i/o
2353 stream.
2354 @METHOD :
2355 @GLOBALS :
2356 @CALLS :
2357 @CREATED : February 18, 1997 (Peter Neelin)
2358 @MODIFIED :
2359 ---------------------------------------------------------------------------- */
get_dicom_pdv_watchpoint(Acr_File * afp)2360 private long get_dicom_pdv_watchpoint(Acr_File *afp)
2361 {
2362 Acr_Dicom_IO *stream_data;
2363 long current_watchpoint;
2364
2365 stream_data = get_dicom_io_pointer(afp);
2366 if (stream_data == NULL) return ACR_NO_WATCHPOINT;
2367
2368 current_watchpoint = acr_get_io_watchpoint(stream_data->real_afp);
2369 if ((stream_data->pdv_watchpoint == ACR_NO_WATCHPOINT) ||
2370 (current_watchpoint == ACR_NO_WATCHPOINT)) {
2371 return ACR_NO_WATCHPOINT;
2372 }
2373 else {
2374 return current_watchpoint - stream_data->pdv_watchpoint;
2375 }
2376 }
2377
2378 /* ----------------------------- MNI Header -----------------------------------
2379 @NAME : set_dicom_pdv_watchpoint
2380 @INPUT : afp
2381 pdv_watchpoint - distance to pdu watchpoint
2382 @OUTPUT : (none)
2383 @RETURNS : (nothing)
2384 @DESCRIPTION: Sets the watchpoint for the PDV relative to the current position.
2385 @METHOD :
2386 @GLOBALS :
2387 @CALLS :
2388 @CREATED : February 18, 1997 (Peter Neelin)
2389 @MODIFIED :
2390 ---------------------------------------------------------------------------- */
set_dicom_pdv_watchpoint(Acr_File * afp,long pdv_watchpoint)2391 private void set_dicom_pdv_watchpoint(Acr_File *afp, long pdv_watchpoint)
2392 {
2393 Acr_Dicom_IO *stream_data;
2394 long current_watchpoint;
2395
2396 stream_data = get_dicom_io_pointer(afp);
2397 if (stream_data == NULL) return;
2398
2399 current_watchpoint = acr_get_io_watchpoint(stream_data->real_afp);
2400 if (pdv_watchpoint == ACR_NO_WATCHPOINT) {
2401 stream_data->pdv_watchpoint = ACR_NO_WATCHPOINT;
2402 }
2403 else if (current_watchpoint == ACR_NO_WATCHPOINT) {
2404 acr_set_io_watchpoint(stream_data->real_afp, pdv_watchpoint);
2405 stream_data->pdv_watchpoint = 0;
2406 }
2407 else {
2408 stream_data->pdv_watchpoint = current_watchpoint - pdv_watchpoint;
2409 }
2410 }
2411
2412 /* ----------------------------- MNI Header -----------------------------------
2413 @NAME : acr_set_dicom_maximum_length
2414 @INPUT : afp
2415 maximum_length - if <= 0, then a very large value is used
2416 @OUTPUT : (none)
2417 @RETURNS : (nothing)
2418 @DESCRIPTION: Sets the maximum PDU length for a dicom stream. If the length
2419 is unreasonably small (<= PDV header length) then a more
2420 reasonable value is set.
2421 @METHOD :
2422 @GLOBALS :
2423 @CALLS :
2424 @CREATED : February 18, 1997 (Peter Neelin)
2425 @MODIFIED :
2426 ---------------------------------------------------------------------------- */
acr_set_dicom_maximum_length(Acr_File * afp,long maximum_length)2427 public void acr_set_dicom_maximum_length(Acr_File *afp,
2428 long maximum_length)
2429 {
2430 Acr_Dicom_IO *stream_data;
2431
2432 /* Get the structure pointer */
2433 stream_data = get_dicom_io_pointer(afp);
2434 if (stream_data == NULL) return;
2435
2436 /* Set the maximum length */
2437 if (maximum_length <= 0)
2438 maximum_length = LONG_MAX;
2439 else if (maximum_length <= DATA_TF_LEN)
2440 maximum_length = DATA_TF_LEN * 2;
2441 stream_data->maximum_length = maximum_length;
2442 }
2443
2444 /* ----------------------------- MNI Header -----------------------------------
2445 @NAME : acr_set_dicom_pres_context_id
2446 @INPUT : afp
2447 presentation_context_id - presentation context id to use
2448 @OUTPUT : (none)
2449 @RETURNS : (nothing)
2450 @DESCRIPTION: Sets the presentation context id for an i/o stream.
2451 @METHOD :
2452 @GLOBALS :
2453 @CALLS :
2454 @CREATED : February 18, 1997 (Peter Neelin)
2455 @MODIFIED :
2456 ---------------------------------------------------------------------------- */
acr_set_dicom_pres_context_id(Acr_File * afp,int presentation_context_id)2457 public void acr_set_dicom_pres_context_id(Acr_File *afp,
2458 int presentation_context_id)
2459 {
2460 Acr_Dicom_IO *stream_data;
2461
2462 /* Get the structure pointer */
2463 stream_data = get_dicom_io_pointer(afp);
2464 if (stream_data == NULL) return;
2465
2466 /* Set the id */
2467 stream_data->presentation_context_id = presentation_context_id;
2468 }
2469
2470 /* ----------------------------- MNI Header -----------------------------------
2471 @NAME : acr_get_dicom_pres_context_id
2472 @INPUT : afp
2473 @OUTPUT : (none)
2474 @RETURNS : presentation context id
2475 @DESCRIPTION: Gets the presentation context id for an i/o stream.
2476 @METHOD :
2477 @GLOBALS :
2478 @CALLS :
2479 @CREATED : February 18, 1997 (Peter Neelin)
2480 @MODIFIED :
2481 ---------------------------------------------------------------------------- */
acr_get_dicom_pres_context_id(Acr_File * afp)2482 public int acr_get_dicom_pres_context_id(Acr_File *afp)
2483 {
2484 Acr_Dicom_IO *stream_data;
2485
2486 /* Get the structure pointer */
2487 stream_data = get_dicom_io_pointer(afp);
2488 if (stream_data == NULL) return 0;
2489
2490 /* Return the id */
2491 return stream_data->presentation_context_id;
2492 }
2493
2494 /* ----------------------------- MNI Header -----------------------------------
2495 @NAME : acr_set_dicom_client_data
2496 @INPUT : afp
2497 client_data - pointer to client data to store
2498 @OUTPUT : (none)
2499 @RETURNS : (nothing)
2500 @DESCRIPTION: Stores a pointer to client data
2501 @METHOD :
2502 @GLOBALS :
2503 @CALLS :
2504 @CREATED : November 11, 1998 (Peter Neelin)
2505 @MODIFIED :
2506 ---------------------------------------------------------------------------- */
acr_set_dicom_client_data(Acr_File * afp,void * client_data)2507 public void acr_set_dicom_client_data(Acr_File *afp, void *client_data)
2508 {
2509 Acr_Dicom_IO *stream_data;
2510
2511 /* Get the structure pointer */
2512 stream_data = get_dicom_io_pointer(afp);
2513 if (stream_data == NULL) return;
2514
2515 /* Check for an old value */
2516 if (stream_data->client_data != NULL) {
2517 FREE(stream_data->client_data);
2518 }
2519
2520 /* Set the pointer */
2521 stream_data->client_data = client_data;
2522 }
2523
2524 /* ----------------------------- MNI Header -----------------------------------
2525 @NAME : acr_get_dicom_client_data
2526 @INPUT : afp
2527 @OUTPUT : (none)
2528 @RETURNS : pointer to client data
2529 @DESCRIPTION: Gets the pointer to the client data
2530 @METHOD :
2531 @GLOBALS :
2532 @CALLS :
2533 @CREATED : November 11, 1998 (Peter Neelin)
2534 @MODIFIED :
2535 ---------------------------------------------------------------------------- */
acr_get_dicom_client_data(Acr_File * afp)2536 public void *acr_get_dicom_client_data(Acr_File *afp)
2537 {
2538 Acr_Dicom_IO *stream_data;
2539
2540 /* Get the structure pointer */
2541 stream_data = get_dicom_io_pointer(afp);
2542 if (stream_data == NULL) return 0;
2543
2544 /* Return the pointer */
2545 return stream_data->client_data;
2546 }
2547
2548 /* ----------------------------- MNI Header -----------------------------------
2549 @NAME : dicom_reset
2550 @INPUT : afp
2551 @OUTPUT : (none)
2552 @RETURNS : (nothing)
2553 @DESCRIPTION: Resets the dicom (virtual) stream
2554 @METHOD :
2555 @GLOBALS :
2556 @CALLS :
2557 @CREATED : February 18, 1997 (Peter Neelin)
2558 @MODIFIED :
2559 ---------------------------------------------------------------------------- */
dicom_reset(Acr_File * afp)2560 private void dicom_reset(Acr_File *afp)
2561 {
2562 Acr_Dicom_IO *stream_data;
2563
2564 /* Get the structure pointer */
2565 stream_data = get_dicom_io_pointer(afp);
2566 if (stream_data == NULL) return;
2567
2568 /* Do the reset and mark this as the first time */
2569 acr_file_reset(afp);
2570 set_dicom_pdv_watchpoint(afp, ACR_NO_WATCHPOINT);
2571 }
2572
2573 /* ----------------------------- MNI Header -----------------------------------
2574 @NAME : dicom_setup_output
2575 @INPUT : afp
2576 command_length - length of command portion of message
2577 data_length - length of data portion of message
2578 @OUTPUT : (none)
2579 @RETURNS : (nothing)
2580 @DESCRIPTION: Sets up the dicom virtual stream to handle data output
2581 @METHOD :
2582 @GLOBALS :
2583 @CALLS :
2584 @CREATED : February 18, 1997 (Peter Neelin)
2585 @MODIFIED :
2586 ---------------------------------------------------------------------------- */
dicom_setup_output(Acr_File * afp,long command_length,long data_length)2587 private void dicom_setup_output(Acr_File *afp,
2588 long command_length, long data_length)
2589 {
2590 Acr_Dicom_IO *stream_data;
2591
2592 /* Reset the stream */
2593 dicom_reset(afp);
2594
2595 /* Get the structure pointer */
2596 stream_data = get_dicom_io_pointer(afp);
2597 if (stream_data == NULL) return;
2598
2599 /* Set real afp watchpoint to zero to force a PDU header right away */
2600 acr_set_io_watchpoint(stream_data->real_afp, 0);
2601 set_dicom_pdv_watchpoint(afp, 0);
2602
2603 /* Figure out whether we are starting with a command or data, save the
2604 data length and set the watchpoint so that we can figure out when
2605 to change from command to data */
2606 if (command_length > 0) {
2607 stream_data->writing_command = TRUE;
2608 stream_data->data_length = data_length;
2609 acr_set_io_watchpoint(afp, command_length);
2610 }
2611 else {
2612 stream_data->writing_command = FALSE;
2613 stream_data->data_length = data_length;
2614 acr_set_io_watchpoint(afp, data_length);
2615 }
2616
2617 }
2618
2619 /* ----------------------------- MNI Header -----------------------------------
2620 @NAME : dicom_input_routine
2621 @INPUT : io_data
2622 nbytes - number of bytes to read
2623 @OUTPUT : buffer - buffer into which we will read
2624 @RETURNS : Number of bytes read.
2625 @DESCRIPTION: Dicom input routine for reading from a real input stream
2626 into a virtual input stream.
2627 @METHOD : We use three watchpoints. One on the real stream to mark the
2628 end of the PDU. One in stream data structure to mark the end
2629 of the PDV. One is set on the virtual (dicom) stream when we
2630 find the last fragment of a message to mark the end of the
2631 message data.
2632 @GLOBALS :
2633 @CALLS :
2634 @CREATED : February 18, 1997 (Peter Neelin)
2635 @MODIFIED :
2636 ---------------------------------------------------------------------------- */
dicom_input_routine(void * io_data,void * buffer,int nbytes)2637 private int dicom_input_routine(void *io_data, void *buffer, int nbytes)
2638 {
2639 unsigned char header_buffer[DATA_TF_LEN];
2640 Acr_Dicom_IO *stream_data;
2641 long nread;
2642 int pdu_type;
2643 Acr_Long pdu_length, pdv_length;
2644 long pdv_watchpoint;
2645 Acr_Status status;
2646 int presentation_context_id, control_header;
2647 Acr_File *real_afp, *dicom_afp;
2648
2649 /* Get file pointer */
2650 if (io_data == NULL) return 0;
2651 stream_data = (Acr_Dicom_IO *) io_data;
2652 real_afp = stream_data->real_afp;
2653 dicom_afp = stream_data->virtual_afp;
2654
2655 /* Get distance to next watchpoint on real stream */
2656 pdv_watchpoint = get_dicom_pdv_watchpoint(dicom_afp);
2657
2658 /* Check whether we need to read in some more headers */
2659 if (pdv_watchpoint <= 0) {
2660
2661 /* Do we need to read in a PDU header? */
2662 if (acr_get_io_watchpoint(real_afp) <= 0) {
2663 acr_set_io_watchpoint(real_afp,
2664 PDU_HEADER_LEN + sizeof(header_buffer));
2665 status = read_pdu_header(real_afp, &pdu_type, &pdu_length);
2666 if ((status != ACR_OK) || (pdu_type != ACR_PDU_DATA_TF)) {
2667 return -1;
2668 }
2669 acr_set_io_watchpoint(real_afp, pdu_length);
2670 }
2671
2672 /* Read in the PDV header */
2673 status = acr_read_buffer(real_afp, header_buffer,
2674 sizeof(header_buffer), NULL);
2675 if (status != ACR_OK) return -1;
2676 GET_LONG((void *) header_buffer, &pdv_length);
2677 pdv_length -= 2;
2678 presentation_context_id = (int) header_buffer[ACR_SIZEOF_LONG];
2679 control_header = (int) header_buffer[ACR_SIZEOF_LONG+1];
2680
2681 /* Check for a bad PDV length compared to PDU length */
2682 if (pdv_length > acr_get_io_watchpoint(real_afp)) {
2683 return -1;
2684 }
2685
2686 /* Set the watchpoint for this PDV */
2687 set_dicom_pdv_watchpoint(dicom_afp, (long)pdv_length);
2688 pdv_watchpoint = (long)pdv_length;
2689
2690 /* Save the presentation context id */
2691 acr_set_dicom_pres_context_id(dicom_afp, presentation_context_id);
2692
2693 /* Check whether this is the last fragment. If it is, set the
2694 watchpoint. */
2695 if ((control_header & PDV_LAST_FRAGMENT_MASK) &&
2696 ((long)pdv_length == acr_get_io_watchpoint(real_afp))) {
2697 acr_set_io_watchpoint(dicom_afp, (long)pdv_length);
2698 }
2699 }
2700
2701 /* Make sure that we don't read too far */
2702 if (nbytes > pdv_watchpoint) {
2703 nbytes = pdv_watchpoint;
2704 }
2705
2706 /* Read the data into the buffer */
2707 status = acr_read_buffer(real_afp, buffer, (long) nbytes, &nread);
2708
2709 return (int) nread;
2710 }
2711
2712 /* ----------------------------- MNI Header -----------------------------------
2713 @NAME : dicom_output_routine
2714 @INPUT : io_data
2715 nbytes - number of bytes to write
2716 @OUTPUT : buffer - buffer into which we will write
2717 @RETURNS : Number of bytes written.
2718 @DESCRIPTION: Dicom output routine for writing a virtual output stream into
2719 a real output stream.
2720 @METHOD : We use three watchpoints. One one the real stream to mark the
2721 end of the next PDV. One stored in the stream data structure to
2722 indicate the end of the PDU (this is relative to the end of
2723 the PDV watchpoint). One on the virtual stream to mark
2724 the end of the command or of the data.
2725 @METHOD : We use three watchpoints. One on the real stream to mark the
2726 end of the PDU. One in stream data structure to mark the end
2727 of the PDV. One is set on the virtual (dicom) stream to mark
2728 the end of the command or data.
2729 @GLOBALS :
2730 @CALLS :
2731 @CREATED : February 18, 1997 (Peter Neelin)
2732 @MODIFIED :
2733 ---------------------------------------------------------------------------- */
dicom_output_routine(void * io_data,void * buffer,int nbytes)2734 private int dicom_output_routine(void *io_data, void *buffer, int nbytes)
2735 {
2736 unsigned char pdu_buffer[PDU_HEADER_LEN];
2737 unsigned char header_buffer[DATA_TF_LEN];
2738 Acr_Dicom_IO *stream_data;
2739 Acr_File *real_afp, *dicom_afp;
2740 Acr_Long pdu_length, pdv_length;
2741 long message_watchpoint, pdv_watchpoint, data_length, pdu_watchpoint;
2742 long nwritten;
2743 int total_written, bytes_to_write;
2744 Acr_Status status;
2745 int message_control_header;
2746
2747 /* Get file pointer */
2748 if (io_data == NULL) return 0;
2749 stream_data = (Acr_Dicom_IO *) io_data;
2750 real_afp = stream_data->real_afp;
2751 dicom_afp = stream_data->virtual_afp;
2752
2753 /* Get distances to watchpoints */
2754 pdv_watchpoint = get_dicom_pdv_watchpoint(dicom_afp);
2755 message_watchpoint = acr_get_io_watchpoint(dicom_afp);
2756
2757 /* Make sure that we have not gone too far */
2758 if (message_watchpoint < 0) {
2759 return -1;
2760 }
2761
2762 /* Loop until we have written out the whole buffer */
2763 total_written = 0;
2764 while (nbytes > 0) {
2765
2766 /* Check whether we need to write out more headers */
2767 if (pdv_watchpoint <= 0) {
2768
2769 /* If the PDU watchpoint is <= 0, then we need to write out a
2770 PDU header. */
2771 if (acr_get_io_watchpoint(real_afp) <= 0) {
2772
2773 /* Figure out pdu length */
2774 pdu_length = 0;
2775
2776 /* Get data length */
2777 data_length = (stream_data->writing_command ?
2778 stream_data->data_length :
2779 message_watchpoint + nbytes);
2780
2781 /* Add in command length if needed */
2782 if (stream_data->writing_command) {
2783 pdu_length += sizeof(header_buffer) +
2784 message_watchpoint + nbytes;
2785 }
2786
2787 /* Add in data length if needed, making sure that we can
2788 get some data into this pdu */
2789 if ((data_length > 0) && (pdu_length+sizeof(header_buffer) <
2790 stream_data->maximum_length)) {
2791 pdu_length += sizeof(header_buffer) + data_length;
2792 }
2793
2794 /* Check that we haven't exceeded the maximum length */
2795 if (pdu_length > stream_data->maximum_length) {
2796 pdu_length = stream_data->maximum_length;
2797 }
2798
2799 /* Set the PDU watchpoint */
2800 acr_set_io_watchpoint(real_afp, pdu_length + sizeof(pdu_buffer));
2801
2802 /* Write out the buffer */
2803 pdu_buffer[0] = ACR_PDU_DATA_TF;
2804 pdu_buffer[1] = 0;
2805 PUT_LONG(&pdu_length, (Acr_Long*)&pdu_buffer[2]);
2806 status = acr_write_buffer(real_afp, pdu_buffer,
2807 sizeof(pdu_buffer), NULL);
2808 if (status != ACR_OK) return total_written;
2809
2810 } /* If PDU watchpoint <= 0 */
2811
2812 /* Get info for PDV header */
2813 message_control_header = 0;
2814 if (stream_data->writing_command) {
2815 message_control_header |= PDV_COMMAND_PDV_MASK;
2816 }
2817
2818 /* Get length of PDV, making sure that it will fit in the PDU */
2819 pdv_length = message_watchpoint + nbytes;
2820 pdu_watchpoint = acr_get_io_watchpoint(real_afp);
2821 if ((pdv_length + sizeof(header_buffer)) > pdu_watchpoint) {
2822 pdv_length = pdu_watchpoint - sizeof(header_buffer);
2823 }
2824 else {
2825 message_control_header |= PDV_LAST_FRAGMENT_MASK;
2826 }
2827 pdv_length += 2;
2828
2829 /* Write out PDV header */
2830 PUT_LONG(&pdv_length, (Acr_Long*)&header_buffer[0]);
2831 header_buffer[4] =
2832 (unsigned char) stream_data->presentation_context_id;
2833 header_buffer[5] = (unsigned char) message_control_header;
2834 status = acr_write_buffer(real_afp, header_buffer,
2835 sizeof(header_buffer), NULL);
2836 if (status != ACR_OK) return total_written;
2837
2838 /* Set the watchpoint for this PDV */
2839 pdv_length -= 2;
2840 if ((pdv_length <= 0) ||
2841 (pdv_length > acr_get_io_watchpoint(real_afp)))
2842 return total_written;
2843 set_dicom_pdv_watchpoint(dicom_afp, pdv_length);
2844 pdv_watchpoint = pdv_length;
2845
2846 } /* If pdv_watchpoint <= 0 */
2847
2848 /* Write out data */
2849 bytes_to_write = nbytes;
2850 if (nbytes >= pdv_watchpoint) {
2851 bytes_to_write = pdv_watchpoint;
2852 }
2853 status = acr_write_buffer(real_afp, buffer, bytes_to_write, &nwritten);
2854 total_written += nwritten;
2855 if (nwritten < bytes_to_write) return total_written;
2856 nbytes -= bytes_to_write;
2857 buffer = (void *) ((char *) buffer + bytes_to_write);
2858 pdv_watchpoint -= bytes_to_write;
2859
2860 } /* While nbytes > 0 */
2861
2862 /* Check for end of command part */
2863 if (stream_data->writing_command && (message_watchpoint == 0)) {
2864 acr_set_io_watchpoint(dicom_afp, stream_data->data_length);
2865 stream_data->writing_command = FALSE;
2866 }
2867
2868 return total_written;
2869 }
2870
2871 /* ----------------------------- MNI Header -----------------------------------
2872 @NAME : dicom_ismore
2873 @INPUT : io_data
2874 @OUTPUT : (nothing)
2875 @RETURNS : 1 if more data is waiting, 0 if not, and -1 if EOF or error.
2876 @DESCRIPTION: Routine for testing for waiting data on a dicom input stream.
2877 @METHOD :
2878 @GLOBALS :
2879 @CALLS :
2880 @CREATED : May 17, 2000 (Peter Neelin)
2881 @MODIFIED :
2882 ---------------------------------------------------------------------------- */
dicom_ismore(void * io_data)2883 private int dicom_ismore(void *io_data)
2884 {
2885 Acr_Dicom_IO *stream_data;
2886
2887 /* Get file pointer */
2888 if (io_data == NULL) return -1;
2889 stream_data = (Acr_Dicom_IO *) io_data;
2890
2891 /* Call ismore function for real stream */
2892 return acr_file_ismore(stream_data->real_afp);
2893 }
2894