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(&current_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