1 /* ----------------------------- MNI Header -----------------------------------
2 @NAME       : message.c
3 @DESCRIPTION: Routines for doing acr_nema message operations.
4 @METHOD     :
5 @GLOBALS    :
6 @CREATED    : November 16, 1993 (Peter Neelin)
7 @MODIFIED   :
8  * $Log: message.c,v $
9  * Revision 6.8  2008-08-12 05:00:22  rotor
10  *  * large number of changes from Claude (64 bit and updates)
11  *
12  * Revision 6.7  2004/10/29 13:08:42  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.6  2002/12/08 21:43:08  neelin
19  * Fixed excessive memory freeing on error when reading message (seen in linux)
20  *
21  * Revision 6.5  2002/11/13 03:00:27  neelin
22  * Fixed an unterminated comment
23  *
24  * Revision 6.4  1999/10/29 17:51:53  neelin
25  * Fixed Log keyword
26  *
27  * Revision 6.3  1998/03/10 17:06:29  neelin
28  * Added code to acr_input_message so that if we reach the watchpoint and
29  * more message is expected, we keep on reading.
30  *
31  * Revision 6.2  1998/03/09  19:30:23  neelin
32  * Fixed bug in acr_input_message where the last group added to the input
33  * message was being deleted followed by the message itself when a
34  * message length error occurred. When an input error occurs the message
35  * should not be deleted.
36  *
37  * Revision 6.1  1998/02/18  20:27:13  neelin
38  * Minor bug fix.
39  *
40  * Revision 6.0  1997/09/12  13:23:59  neelin
41  * Release of minc version 0.6
42  *
43  * Revision 5.0  1997/08/21  13:25:00  neelin
44  * Release of minc version 0.5
45  *
46  * Revision 4.1  1997/06/13  21:27:16  neelin
47  * Made use of message length to figure out how much to read in -
48  * previously was not checking it, so if watchpoint was not set, we would
49  * read indefinitely.
50  *
51  * Revision 4.0  1997/05/07  20:01:23  neelin
52  * Release of minc version 0.4
53  *
54  * Revision 3.1  1997/04/21  20:21:09  neelin
55  * Updated the library to handle dicom messages.
56  *
57  * Revision 3.0  1995/05/15  19:32:12  neelin
58  * Release of minc version 0.3
59  *
60  * Revision 2.0  1994/09/28  10:36:17  neelin
61  * Release of minc version 0.2
62  *
63  * Revision 1.6  94/09/28  10:35:49  neelin
64  * Pre-release
65  *
66  * Revision 1.5  94/05/18  08:48:12  neelin
67  * Changed some ACR_OTHER_ERROR's to ACR_ABNORMAL_END_OF_OUTPUT.
68  *
69  * Revision 1.4  94/04/07  10:05:06  neelin
70  * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs
71  * to that or ACR_OTHER_ERROR.
72  * Added #ifdef lint to DEFINE_ELEMENT.
73  *
74  * Revision 1.3  93/11/24  11:25:59  neelin
75  * Added dump_message.
76  *
77  * Revision 1.2  93/11/22  13:12:09  neelin
78  * Changed to use new Acr_Element_Id stuff
79  *
80  * Revision 1.1  93/11/19  12:49:09  neelin
81  * Initial revision
82  *
83 @COPYRIGHT  :
84               Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre,
85               Montreal Neurological Institute, McGill University.
86               Permission to use, copy, modify, and distribute this
87               software and its documentation for any purpose and without
88               fee is hereby granted, provided that the above copyright
89               notice appear in all copies.  The author and McGill University
90               make no representations about the suitability of this
91               software for any purpose.  It is provided "as is" without
92               express or implied warranty.
93 ---------------------------------------------------------------------------- */
94 
95 #include <stdlib.h>
96 #include <stdio.h>
97 #include <limits.h>
98 #include <acr_nema.h>
99 
100 /* Message length group and element id */
101 #define ACR_GID_MESSLEN 0
102 #define ACR_EID_MESSLEN 1
103 
104 DEFINE_ELEMENT(static, ACR_Message_length,
105                ACR_GID_MESSLEN, ACR_EID_MESSLEN, UL);
106 
107 /* Private functions */
108 private void update_message_length_element(Acr_Message message,
109                                            Acr_VR_encoding_type vr_encoding);
110 
111 /* ----------------------------- MNI Header -----------------------------------
112 @NAME       : acr_create_message
113 @INPUT      : (none)
114 @OUTPUT     : (none)
115 @RETURNS    : Pointer to message structure
116 @DESCRIPTION: Creates an acr-nema message structure.
117 @METHOD     : No groups are created here. Message length is checked when
118               groups are added.
119 @GLOBALS    :
120 @CALLS      :
121 @CREATED    : November 16, 1993 (Peter Neelin)
122 @MODIFIED   :
123 ---------------------------------------------------------------------------- */
acr_create_message(void)124 public Acr_Message acr_create_message(void)
125 {
126    Acr_Message message;
127 
128    /* Allocate the message */
129    message = MALLOC(sizeof(*message));
130 
131    /* Assign fields */
132    acr_message_reset(message);
133 
134    return message;
135 }
136 
137 /* ----------------------------- MNI Header -----------------------------------
138 @NAME       : acr_delete_message
139 @INPUT      : message
140 @OUTPUT     : (none)
141 @RETURNS    : (nothing)
142 @DESCRIPTION: Deletes an acr-nema message structure (freeing the group list)
143 @METHOD     :
144 @GLOBALS    :
145 @CALLS      :
146 @CREATED    : November 16, 1993 (Peter Neelin)
147 @MODIFIED   :
148 ---------------------------------------------------------------------------- */
acr_delete_message(Acr_Message message)149 public void acr_delete_message(Acr_Message message)
150 {
151    if (message->list_head != NULL) {
152       acr_delete_group_list(message->list_head);
153    }
154 
155    FREE(message);
156 
157    return;
158 }
159 
160 /* ----------------------------- MNI Header -----------------------------------
161 @NAME       : acr_message_reset
162 @INPUT      : message
163 @OUTPUT     : (none)
164 @RETURNS    : (nothing)
165 @DESCRIPTION: Resets the message to empty without freeing any existing
166               group list. Thus the group list can be used elsewhere
167               without being deleted when the message is deleted.
168 @METHOD     :
169 @GLOBALS    :
170 @CALLS      :
171 @CREATED    : February 18, 1997 (Peter Neelin)
172 @MODIFIED   :
173 ---------------------------------------------------------------------------- */
acr_message_reset(Acr_Message message)174 public void acr_message_reset(Acr_Message message)
175 {
176    message->ngroups = 0;
177    message->implicit_total_length = 0;
178    message->explicit_total_length = 0;
179    message->message_implicit_offset = 0;
180    message->message_explicit_offset = 0;
181    message->message_length_element = NULL;
182    message->list_head = NULL;
183    message->list_tail = NULL;
184 }
185 
186 /* ----------------------------- MNI Header -----------------------------------
187 @NAME       : acr_message_add_group
188 @INPUT      : message
189               group
190 @OUTPUT     : (none)
191 @RETURNS    : (nothing)
192 @DESCRIPTION: Add a group to an acr-nema message
193 @METHOD     : If this is the first group, then look for a message length
194               element. If none is found, then report an error.
195 @GLOBALS    :
196 @CALLS      :
197 @CREATED    : November 16, 1993 (Peter Neelin)
198 @MODIFIED   : February 7, 1997 (P.N.)
199 ---------------------------------------------------------------------------- */
acr_message_add_group(Acr_Message message,Acr_Group group)200 public void acr_message_add_group(Acr_Message message, Acr_Group group)
201 {
202    Acr_Element length_element;
203    int element_id;
204    long message_implicit_offset, message_explicit_offset;
205 
206    /* If first message, then check for message length element */
207    if (message->ngroups == 0) {
208       length_element = NULL;
209       message_implicit_offset = 0;
210       message_explicit_offset = 0;
211       if (acr_get_group_group(group) == ACR_GID_MESSLEN) {
212          length_element = acr_get_group_element_list(group);
213          if (length_element != NULL) {
214             element_id = acr_get_element_element(length_element);
215             message_implicit_offset =
216                acr_get_element_total_length(length_element, ACR_IMPLICIT_VR);
217             message_explicit_offset =
218                acr_get_element_total_length(length_element, ACR_EXPLICIT_VR);
219          }
220          else {
221             element_id = 0;
222             message_implicit_offset = 0;
223             message_explicit_offset = 0;
224          }
225          while ((element_id != ACR_EID_MESSLEN) &&
226                 (length_element != NULL)) {
227             length_element = acr_get_element_next(length_element);
228             if (length_element != NULL) {
229                element_id = acr_get_element_element(length_element);
230                message_implicit_offset +=
231                   acr_get_element_total_length(length_element,
232                                                ACR_IMPLICIT_VR);
233                message_explicit_offset +=
234                   acr_get_element_total_length(length_element,
235                                                ACR_EXPLICIT_VR);
236             }
237          }
238       }
239 
240       /* Check for length element found but not of correct length */
241       if ((length_element != NULL) &&
242           (acr_get_element_length(length_element) != ACR_SIZEOF_LONG)) {
243          (void) fprintf(stderr,
244     "ACR error: First message group contains length element of wrong size\n");
245          exit(EXIT_FAILURE);
246       }
247 
248       /* Set up the message length info */
249       message->message_implicit_offset = message_implicit_offset;
250       message->message_explicit_offset = message_explicit_offset;
251       message->message_length_element = length_element;
252 
253    }
254 
255    /* Add group (check for empty list) */
256    if (message->ngroups == 0) {
257       message->list_head = group;
258       message->list_tail = group;
259    }
260    else {
261       acr_set_group_next(message->list_tail, group);
262       message->list_tail = group;
263       acr_set_group_next(group, NULL);
264    }
265    message->ngroups++;
266    message->implicit_total_length +=
267       acr_get_group_total_length(group, ACR_IMPLICIT_VR);
268    message->explicit_total_length +=
269       acr_get_group_total_length(group, ACR_EXPLICIT_VR);
270 
271    /* Update the length element */
272    if (message->message_length_element != NULL) {
273       update_message_length_element(message,
274          acr_get_element_vr_encoding(message->message_length_element));
275    }
276 
277    return;
278 }
279 
280 /* ----------------------------- MNI Header -----------------------------------
281 @NAME       : update_message_length_element
282 @INPUT      : message
283               vr_encoding - ACR_IMPLICIT_VR or ACR_EXPLICIT_VR
284 @OUTPUT     : (none)
285 @RETURNS    : (nothing)
286 @DESCRIPTION: Update the length element of the message according to the VR type
287 @METHOD     :
288 @GLOBALS    :
289 @CALLS      :
290 @CREATED    : February 14, 1997 (Peter Neelin)
291 @MODIFIED   :
292 ---------------------------------------------------------------------------- */
update_message_length_element(Acr_Message message,Acr_VR_encoding_type vr_encoding)293 private void update_message_length_element(Acr_Message message,
294                                            Acr_VR_encoding_type vr_encoding)
295 {
296    Acr_Long message_length;
297    Acr_Element length_element;
298    void *message_length_data;
299 
300    /* Get the element */
301    length_element = message->message_length_element;
302    if (length_element == NULL) return;
303 
304    /* Calculate the appropriate length */
305    if (vr_encoding == ACR_IMPLICIT_VR) {
306       message_length = message->implicit_total_length -
307          message->message_implicit_offset;
308    }
309    else {
310       message_length = message->explicit_total_length -
311          message->message_explicit_offset;
312    }
313 
314    /* Update the element */
315    message_length_data = acr_get_element_data(length_element);
316    acr_put_long(acr_get_element_byte_order(length_element),
317                 1, &message_length, message_length_data);
318 
319 }
320 
321 /* ----------------------------- MNI Header -----------------------------------
322 @NAME       : acr_message_add_group_list
323 @INPUT      : message
324               group_list
325 @OUTPUT     : (none)
326 @RETURNS    : (nothing)
327 @DESCRIPTION: Add a group list to an acr-nema message
328 @METHOD     :
329 @GLOBALS    :
330 @CALLS      :
331 @CREATED    : February 12, 1997 (Peter Neelin)
332 @MODIFIED   :
333 ---------------------------------------------------------------------------- */
acr_message_add_group_list(Acr_Message message,Acr_Group group_list)334 public void acr_message_add_group_list(Acr_Message message,
335                                        Acr_Group group_list)
336 {
337    Acr_Group next_group, group;
338 
339    /* Loop through groups, adding them to the message */
340    group = group_list;
341    while (group != NULL) {
342       next_group = acr_get_group_next(group);
343       acr_set_group_next(group, NULL);
344       acr_message_add_group(message, group);
345       group = next_group;
346    }
347 
348 }
349 
350 /* ----------------------------- MNI Header -----------------------------------
351 @NAME       : acr_get_message_group_list
352 @INPUT      : message
353 @OUTPUT     : (none)
354 @RETURNS    : group list
355 @DESCRIPTION: Get group list for message
356 @METHOD     :
357 @GLOBALS    :
358 @CALLS      :
359 @CREATED    : November 16, 1993 (Peter Neelin)
360 @MODIFIED   :
361 ---------------------------------------------------------------------------- */
acr_get_message_group_list(Acr_Message message)362 public Acr_Group acr_get_message_group_list(Acr_Message message)
363 {
364    return message->list_head;
365 }
366 
367 /* ----------------------------- MNI Header -----------------------------------
368 @NAME       : acr_get_message_total_length
369 @INPUT      : message
370               vr_encoding - ACR_EXPLICIT_VR or ACR_IMPLICIT_VR
371 @OUTPUT     : (none)
372 @RETURNS    : total length of message
373 @DESCRIPTION: Get total length of message depending on type of VR encoding
374 @METHOD     :
375 @GLOBALS    :
376 @CALLS      :
377 @CREATED    : November 16, 1993 (Peter Neelin)
378 @MODIFIED   :
379 ---------------------------------------------------------------------------- */
acr_get_message_total_length(Acr_Message message,Acr_VR_encoding_type vr_encoding)380 public long acr_get_message_total_length(Acr_Message message,
381                                          Acr_VR_encoding_type vr_encoding)
382 {
383    if (vr_encoding == ACR_IMPLICIT_VR)
384       return message->implicit_total_length;
385    else
386       return message->explicit_total_length;
387 }
388 
389 /* ----------------------------- MNI Header -----------------------------------
390 @NAME       : acr_get_message_ngroups
391 @INPUT      : message
392 @OUTPUT     : (none)
393 @RETURNS    : number of groups in message
394 @DESCRIPTION: Get number of groups in message
395 @METHOD     :
396 @GLOBALS    :
397 @CALLS      :
398 @CREATED    : November 16, 1993 (Peter Neelin)
399 @MODIFIED   :
400 ---------------------------------------------------------------------------- */
acr_get_message_ngroups(Acr_Message message)401 public int acr_get_message_ngroups(Acr_Message message)
402 {
403    return message->ngroups;
404 }
405 
406 /* ----------------------------- MNI Header -----------------------------------
407 @NAME       : acr_input_message
408 @INPUT      : afp - acr file pointer
409 @OUTPUT     : message
410 @RETURNS    : status
411 @DESCRIPTION: Read in an acr-nema message. The amount of input to read is
412               determined either by setting a watchpoint or by having messages
413               that contain their own length. One cannot rely on finding an
414               EOF (as for a file) since messages usually come over a
415               connection.
416 @METHOD     :
417 @GLOBALS    :
418 @CALLS      :
419 @CREATED    : November 16, 1993 (Peter Neelin)
420 @MODIFIED   : February 7, 1997 (P.N.)
421 ---------------------------------------------------------------------------- */
acr_input_message(Acr_File * afp,Acr_Message * message)422 public Acr_Status acr_input_message(Acr_File *afp, Acr_Message *message)
423 {
424    Acr_Status status;
425    Acr_Group group;
426    Acr_Element length_element;
427    Acr_Long lvalue;
428    long message_length;
429    long group_length;
430    long watchpoint;
431    int get_more_groups;
432 
433    /* Initialize the message pointer */
434    *message = NULL;
435 
436    /* Read in the first group */
437    status = acr_input_group(afp, &group);
438 
439    /* Check status */
440    if (status != ACR_OK) {
441       if (group != NULL) acr_delete_group(group);
442       return status;
443    }
444 
445    /* Check whether it contains the message length. If there is no
446       watchpoint, then it must contain the length, otherwise we don't
447       know when to stop reading (we would if the input was from a file, but
448       messages don't usually come from a file) */
449    length_element =
450       acr_find_group_element(group, ACR_Message_length);
451    if ((acr_get_io_watchpoint(afp) == ACR_NO_WATCHPOINT) &&
452        ((length_element == NULL) ||
453         (acr_get_element_length(length_element) != ACR_SIZEOF_LONG))) {
454       acr_delete_group(group);
455       status = ACR_PROTOCOL_ERROR;
456       return status;
457    }
458 
459    /* Get the message length from the element */
460    if (length_element != NULL) {
461       acr_get_long(acr_get_element_byte_order(length_element), (long) 1,
462                    acr_get_element_data(length_element), &lvalue);
463       message_length = (long)lvalue;
464    }
465 
466    /* Create the message and add the group (this will modify the message
467       length element value to include only the first group) */
468    *message = acr_create_message();
469    acr_message_add_group(*message, group);
470 
471    /* Correct message_length for the length of the first group. Note
472       that adding the group had the side effect of changing the
473       message length to include only the first group */
474    if (length_element != NULL) {
475       acr_get_long(acr_get_element_byte_order(length_element), (long) 1,
476                    acr_get_element_data(length_element), &lvalue);
477       group_length = (long)lvalue;
478       message_length -= group_length;
479    }
480 
481    /* Loop through elements, adding them to the list */
482    get_more_groups = ((length_element != NULL) ? (message_length > 0) : TRUE);
483    while (get_more_groups) {
484 
485       /* Check for a watchpoint. If we have reached it, but the message
486          length indicates that there is more to come, then move it along
487          so that we can keep reading. */
488       watchpoint = acr_get_io_watchpoint(afp);
489       if ((watchpoint == 0) &&
490           (length_element != NULL) && (message_length > 0)) {
491          acr_set_io_watchpoint(afp, LONG_MAX-1);
492       }
493       else if (watchpoint <= 0) {
494          get_more_groups = FALSE;
495          break;
496       }
497 
498       /* If we reach the end of a fragment and PDU, but we need more data */
499 
500       /* Read in the next group and check for an error */
501       status = acr_input_group(afp, &group);
502 
503       /* Add the group to the message */
504       if (group != NULL) {
505          acr_message_add_group(*message, group);
506       }
507 
508       /* Check the status */
509       if (status != ACR_OK) {
510          if (status == ACR_END_OF_INPUT) status = ACR_ABNORMAL_END_OF_INPUT;
511          return status;
512       }
513 
514       /* Keep track of remaining bytes to read, if necessary */
515       if (length_element != NULL) {
516          message_length -=
517             acr_get_group_total_length(group, acr_get_vr_encoding(afp));
518          get_more_groups = (message_length > 0);
519       }
520    }
521 
522    /* Check that we got a full message */
523    if ((length_element != NULL) && (message_length != 0)) {
524       status = ACR_PROTOCOL_ERROR;
525       return status;
526    }
527 
528    return status;
529 }
530 
531 /* ----------------------------- MNI Header -----------------------------------
532 @NAME       : acr_output_message
533 @INPUT      : afp - acr file pointer
534               message
535 @OUTPUT     : (none)
536 @RETURNS    : status
537 @DESCRIPTION: Write out an acr-nema message
538 @METHOD     :
539 @GLOBALS    :
540 @CALLS      :
541 @CREATED    : November 16, 1993 (Peter Neelin)
542 @MODIFIED   :
543 ---------------------------------------------------------------------------- */
acr_output_message(Acr_File * afp,Acr_Message message)544 public Acr_Status acr_output_message(Acr_File *afp, Acr_Message message)
545 {
546    long igroup, ngroups;
547    Acr_Group cur, next;
548    Acr_Status status;
549 
550    /* Update the length element */
551    update_message_length_element(message, acr_get_vr_encoding(afp));
552 
553    /* Loop through the groups of the message, writing them out */
554    ngroups = acr_get_message_ngroups(message);
555    next = acr_get_message_group_list(message);
556    for (igroup=0; igroup < ngroups && next != NULL; igroup++) {
557       cur = next;
558       next = cur->next;
559       status = acr_output_group(afp, cur);
560       if (status != ACR_OK) {
561          return status;
562       }
563    }
564 
565    /* Flush the buffer */
566    if (acr_file_flush(afp) == EOF) {
567       status = ACR_ABNORMAL_END_OF_OUTPUT;
568       return status;
569    }
570 
571    /* Check for a bogus message (the true number of groups is different from
572       ngroups) */
573    if ((igroup < ngroups) || (next != NULL)) {
574       status = ACR_OTHER_ERROR;
575       return status;
576    }
577 
578    return status;
579 
580 }
581 
582 /* ----------------------------- MNI Header -----------------------------------
583 @NAME       : acr_dump_message
584 @INPUT      : file_pointer - where output should go
585               group_list
586 @OUTPUT     : (none)
587 @RETURNS    : (nothing)
588 @DESCRIPTION: Dump information from an acr-nema message
589 @METHOD     :
590 @GLOBALS    :
591 @CALLS      :
592 @CREATED    : November 24, 1993 (Peter Neelin)
593 @MODIFIED   :
594 ---------------------------------------------------------------------------- */
acr_dump_message(FILE * file_pointer,Acr_Message message)595 public void acr_dump_message(FILE *file_pointer, Acr_Message message)
596 {
597 
598    acr_dump_group_list(file_pointer, acr_get_message_group_list(message));
599 
600    return;
601 }
602