1 /* ----------------------------- MNI Header -----------------------------------
2 @NAME       : reply.c
3 @DESCRIPTION: Routines for dealing with dicom messages.
4 @GLOBALS    :
5 @CREATED    : January 28, 1997 (Peter Neelin)
6 @MODIFIED   :
7  * $Log: reply.c,v $
8  * Revision 1.1  2003-08-15 19:52:55  leili
9  * Initial revision
10  *
11  * Revision 1.2  2001/02/26 06:14:39  rhoge
12  * modified to allow target directory to be passed as AE title (only 16 chars)
13  *
14  * Revision 1.1.1.1  2000/11/30 02:13:15  rhoge
15  * imported sources to CVS repository on amoeba
16  *
17  * Revision 6.3  1999/10/29 17:51:57  neelin
18  * Fixed Log keyword
19  *
20  * Revision 6.2  1999/08/05 20:01:16  neelin
21  * Check for broken Siemens software using a list of implementation UIDs.
22  *
23  * Revision 6.1  1998/05/19  19:27:43  neelin
24  * Test for Siemens Vision machine by looking for implementation uid
25  * rather than AE title
26  *
27  * Revision 6.0  1997/09/12  13:24:27  neelin
28  * Release of minc version 0.6
29  *
30  * Revision 5.0  1997/08/21  13:25:26  neelin
31  * Release of minc version 0.5
32  *
33  * Revision 4.1  1997/07/08  23:15:09  neelin
34  * Added support for C_ECHO command.
35  *
36  * Revision 4.0  1997/05/07  20:06:20  neelin
37  * Release of minc version 0.4
38  *
39  * Revision 1.1  1997/03/04  20:56:47  neelin
40  * Initial revision
41  *
42 @COPYRIGHT  :
43               Copyright 1997 Peter Neelin, McConnell Brain Imaging Centre,
44               Montreal Neurological Institute, McGill University.
45               Permission to use, copy, modify, and distribute this
46               software and its documentation for any purpose and without
47               fee is hereby granted, provided that the above copyright
48               notice appear in all copies.  The author and McGill University
49               make no representations about the suitability of this
50               software for any purpose.  It is provided "as is" without
51               express or implied warranty.
52 ---------------------------------------------------------------------------- */
53 
54 #include <dicomserver.h>
55 
56 extern int Do_logging;
57 
58 /* List of implementation UIDs for Siemens Vision scanners with broken
59    handling of transfer syntax. These should be in ascending order of
60    software version, since the UID for version VB33A is used to identify
61    a change in element use. */
62 static char *SPI_Vision_Implementation_UIDs[] = {
63    "2.16.840.1.113669.2.931128",
64    "1.3.12.2.1107.5.1995.1",          /* Version VB33A */
65    /* Added By Leili  */
66    "1.3.12.2.1107.5.2",               /* Version MREASE_VA21A */
67    NULL
68 };
69 /* Index into above array for version VB33A */
70 #define SPI_VISION_VB33A_INDEX 1
71 /* Global to indicate whether we have a pre VB33A version */
72 int SPI_Vision_version_pre33A = TRUE;
73 
74 /* Macros */
75 #define SAVE_SHORT(group, elid, value) \
76    acr_group_add_element(group, \
77       acr_create_element_short(elid, (unsigned short) (value)))
78 
79 /* ----------------------------- MNI Header -----------------------------------
80 @NAME       : uid_equal
81 @INPUT      : uid1
82               uid2
83 @OUTPUT     : (nothing)
84 @RETURNS    : TRUE if uid's are equal, FALSE otherwise
85 @DESCRIPTION: Responds to READYq message
86 @METHOD     :
87 @GLOBALS    :
88 @CALLS      :
89 @CREATED    : February 21, 1997 (Peter Neelin)
90 @MODIFIED   :
91 ---------------------------------------------------------------------------- */
uid_equal(char * uid1,char * uid2)92 private int uid_equal(char *uid1, char *uid2)
93 {
94    int len1, len2, i;
95 
96    len1 = strlen(uid1);
97    len2 = strlen(uid2);
98 
99    /* Skip leading blanks */
100    while (isspace(*uid1)) {uid1++;}
101    while (isspace(*uid2)) {uid2++;}
102 
103    /* Skip trailing blanks */
104    for (i=len1-1; (i >= 0) && isspace(uid1[i]); i++) {}
105    if (isspace(uid1[i+1])) uid1[i+1] = '\0';
106    for (i=len2-1; (i >= 0) && isspace(uid1[i]); i++) {}
107    if (isspace(uid1[i+1])) uid1[i+1] = '\0';
108 
109    /* Compare the strings */
110    return (strcmp(uid1, uid2) == 0);
111 }
112 
113 /* ----------------------------- MNI Header -----------------------------------
114 @NAME       : make_message
115 @INPUT      : group_list
116 @OUTPUT     : (nothing)
117 @RETURNS    : output message.
118 @DESCRIPTION: Convert a group list into a message.
119 @METHOD     :
120 @GLOBALS    :
121 @CALLS      :
122 @CREATED    : November 24, 1993 (Peter Neelin)
123 @MODIFIED   :
124 ---------------------------------------------------------------------------- */
make_message(Acr_Group group_list)125 private Acr_Message make_message(Acr_Group group_list)
126 {
127    Acr_Group next_group, group;
128    Acr_Message output_message;
129 
130    /* Create the output message */
131    output_message = acr_create_message();
132 
133    /* Loop through groups, adding them to the message */
134    group = group_list;
135    while (group != NULL) {
136       next_group = acr_get_group_next(group);
137       acr_set_group_next(group, NULL);
138       acr_message_add_group(output_message, group);
139       group = next_group;
140    }
141 
142    return output_message;
143 
144 }
145 
146 /* ----------------------------- MNI Header -----------------------------------
147 @NAME       : associate_reply
148 @INPUT      : input_message
149 @OUTPUT     : project_name - name to use for project file
150               pres_context_id - presentation context id to use for output. If
151                  this is < 0, then an error occurred.
152               byte_order - byte order to use for messages
153               vr_encoding - VR encoding to use for messages
154               maximum_length - maximum length of output PDU's
155 @RETURNS    : output_message
156 @DESCRIPTION: Responds to an associate request message
157 @METHOD     :
158 @GLOBALS    :
159 @CALLS      :
160 @CREATED    : November 22, 1993 (Peter Neelin)
161 @MODIFIED   :
162 ---------------------------------------------------------------------------- */
associate_reply(Acr_Message input_message,char ** project_name,int * pres_context_id,Acr_byte_order * byte_order,Acr_VR_encoding_type * vr_encoding,long * maximum_length)163 public Acr_Message associate_reply(Acr_Message input_message,
164                                    char **project_name,
165                                    int *pres_context_id,
166                                    Acr_byte_order *byte_order,
167                                    Acr_VR_encoding_type *vr_encoding,
168                                    long *maximum_length)
169 {
170    Acr_Group group, input_group;
171    Acr_Element element, item, subitem, sublist;
172    Acr_Element out_item, out_subitem, out_sublist, out_list;
173    /***********************************/
174    /* Added by Leili */
175    Acr_Element my_element_test;
176    /***********************************/
177    int found_best;
178    int best_pres_context_id, cur_pres_context_id;
179    int best_transfer_syntax_priority;
180    int use_implicit_little_endian;
181    char *best_abstract_syntax, *best_transfer_syntax, *cur_syntax;
182    int impuid;
183 
184    /* Print log message */
185    if (Do_logging >= HIGH_LOGGING) {
186       (void) fprintf(stderr, "\n\nReceived associate request message:\n");
187       acr_dump_message(stderr, input_message);
188    }
189 
190    /* Set default presentation context id to flag error */
191    *pres_context_id = -1;
192 
193    /* Free project_name string if needed */
194    if (*project_name != NULL) FREE(*project_name);
195 
196    /* Get the group list */
197    input_group = acr_get_message_group_list(input_message);
198 
199    /* Get the project name from the DICOM application name */
200    *project_name = strdup(acr_find_string(input_group,
201                                           DCM_PDU_Called_Ap_title, ""));
202 
203    /* Check that the project file is okay. If it is not found, try the host
204       name */
205 
206    /* changed by rhoge so as not to clobber the aetitle in the
207       unlikely event that it is being used to pass a file name (starts
208       with `/' or `~') - this functionality is of limited usefulness, since
209       the AE title can not be longer than 16 characters */
210 
211    if (read_project_file(*project_name, NULL, NULL, NULL, NULL, 0)
212        && !(!strncmp(*project_name,"/",1)||!strncmp(*project_name,"~",1))) {
213      FREE(*project_name);
214      *project_name = NULL;
215      if (read_project_file(*project_name, NULL, NULL, NULL, NULL, 0)) {
216        return associate_reply_reject(input_message,
217 				     ACR_ASSOC_RJ_CALLED_AP_TITLE_UNREC);
218      }
219    }
220 
221    /* Check for Siemens Vision implementation that lies about its
222       transfer syntaxes */
223 
224      /**********************************************/
225      /* The if and else statement is commented out by leili */
226      // if (0) {
227 
228      use_implicit_little_endian = FALSE;
229      for (impuid=0; SPI_Vision_Implementation_UIDs[impuid] != NULL; impuid++) {
230        if (uid_equal(acr_find_string(input_group,
231 				     DCM_PDU_Implementation_class_uid, ""),
232 		     SPI_Vision_Implementation_UIDs[impuid])) {
233          use_implicit_little_endian = TRUE;
234          (void) fprintf(stderr, "just made the implicit little endian true\n ");
235 	   SPI_Vision_version_pre33A = (impuid < SPI_VISION_VB33A_INDEX);
236          break;
237        }
238      }
239      // } else {
240 
241      //use_implicit_little_endian = FALSE;
242 
243      //}
244      /**********************************************/
245 
246 
247    /* Get maximum length */
248    *maximum_length = acr_find_long(input_group, DCM_PDU_Maximum_length, 0L);
249 
250    /* Get presentation context list */
251    best_pres_context_id = -1;
252    best_abstract_syntax = NULL;
253    element =
254       acr_find_group_element(input_group, DCM_PDU_Presentation_context_list);
255    if ((element == NULL) || !acr_element_is_sequence(element)) {
256       (void) fprintf(stderr, "No presentation context list found\n");
257       return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON);
258    }
259 
260    /* Loop over presentation contexts */
261    found_best = FALSE;
262    for (item = (Acr_Element) acr_get_element_data(element);
263         (item != NULL) && acr_element_is_sequence(item) && !found_best;
264         item = acr_get_element_next(item)) {
265 
266       /* Get presentation context info */
267       sublist = (Acr_Element) acr_get_element_data(item);
268 
269       /* Get abstract syntax */
270       subitem = acr_find_element_id(sublist,
271                                     DCM_PDU_Abstract_syntax);
272       if (subitem == NULL) continue;
273       cur_syntax = acr_get_element_string(subitem);
274 
275       /* Check whether this is either MR abstract syntax or we have
276          already found one (we take the first one if we cannot find
277          the one that we want) */
278       if (uid_equal(cur_syntax, FAVORITE_ABSTRACT_SYNTAX))
279          found_best = TRUE;
280       else if (best_abstract_syntax != NULL)
281          continue;
282 
283       /* Save the abstract syntax */
284       best_abstract_syntax = cur_syntax;
285       /* Get presentation context id */
286       subitem = acr_find_element_id(sublist,
287                                     DCM_PDU_Presentation_context_id);
288       if (subitem != NULL)
289          best_pres_context_id = acr_get_element_short(subitem);
290       else {
291          (void) fprintf(stderr,
292                         "No presentation context - internal error\n");
293          return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON);
294       }
295 
296       /* Look for an appropriate transfer syntax */
297       best_transfer_syntax = NULL;
298       best_transfer_syntax_priority = 0;
299       for (subitem = sublist;
300            subitem != NULL;
301            subitem=acr_find_element_id(acr_get_element_next(subitem),
302                                        DCM_PDU_Transfer_syntax)) {
303 
304          /* Check for syntaxes in descending order of preference */
305          cur_syntax = acr_get_element_string(subitem);
306          if (uid_equal(cur_syntax, ACR_EXPLICIT_VR_BIG_END_UID)) {
307             if (best_transfer_syntax_priority < 3) {
308                best_transfer_syntax_priority = 3;
309                best_transfer_syntax = cur_syntax;
310             }
311          }
312          else if (uid_equal(cur_syntax, ACR_EXPLICIT_VR_LITTLE_END_UID)) {
313             if (best_transfer_syntax_priority < 2) {
314                best_transfer_syntax_priority = 2;
315                best_transfer_syntax = cur_syntax;
316             }
317          }
318          else if (uid_equal(cur_syntax, ACR_IMPLICIT_VR_LITTLE_END_UID)) {
319             if (best_transfer_syntax_priority < 1) {
320                best_transfer_syntax_priority = 1;
321                best_transfer_syntax = cur_syntax;
322             }
323          }
324 
325       }         /* Loop over transfer syntaxes */
326 
327    }            /* Loop over presentation contexts */
328 
329 
330    /* Check for machines that lie about their ability to do
331       different transfer syntaxes */
332    if (use_implicit_little_endian) {
333       best_transfer_syntax = ACR_IMPLICIT_VR_LITTLE_END_UID;
334    }
335 
336 
337    /* Check that we found something useful */
338    if ((best_pres_context_id < 0) || (best_abstract_syntax == NULL) ||
339        (best_transfer_syntax == NULL)) {
340       (void) fprintf(stderr,
341                      "Did not find understandable presentation context\n");
342       return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON);
343    }
344 
345    /****************************************************************************/
346    /* Set the transfer syntax information */
347    /* This part was commented out by rick, put it back in the program by leili */
348 
349      if (uid_equal(best_transfer_syntax, ACR_EXPLICIT_VR_BIG_END_UID)) {
350          *byte_order = ACR_BIG_ENDIAN;
351          *vr_encoding = ACR_EXPLICIT_VR;
352       }
353       else if (uid_equal(best_transfer_syntax, ACR_EXPLICIT_VR_LITTLE_END_UID)) {
354 
355       /****************************************/
356       /* this two lines were in rick's version */
357 
358       *byte_order = ACR_LITTLE_ENDIAN;
359       *vr_encoding = ACR_EXPLICIT_VR;
360       /******************************************/
361          }
362          else if (uid_equal(best_transfer_syntax, ACR_IMPLICIT_VR_LITTLE_END_UID)) {
363             *byte_order = ACR_LITTLE_ENDIAN;
364             *vr_encoding = ACR_IMPLICIT_VR;
365          }
366          else {
367             (void) fprintf(stderr,
368                            "Did not understand transfer syntax.\n");
369             return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON);
370          }
371 
372      /*************************************************************************/
373    /* Create the reply */
374    group = acr_create_group(DCM_PDU_GRPID);
375 
376    /* Save the PDU type */
377    SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_ASSOC_AC);
378 
379    /* Save the caller and calling AE titles */
380    acr_group_add_element(group,
381       acr_create_element_string(DCM_PDU_Called_Ap_title,
382          acr_find_string(input_group, DCM_PDU_Called_Ap_title, "")));
383 
384    /****************************************/
385    /* Added by Leili */
386    my_element_test =acr_create_element_string(DCM_PDU_Called_Ap_title,acr_find_string(input_group, DCM_PDU_Called_Ap_title, ""));
387    (void) fprintf(stderr, "This is the called Ap_title: %s \n", my_element_test->data_pointer);
388    /*****************************************/
389 
390    acr_group_add_element(group,
391       acr_create_element_string(DCM_PDU_Calling_Ap_title,
392          acr_find_string(input_group, DCM_PDU_Calling_Ap_title, "")));
393 
394    /****************************************/
395    /* Added by Leili */
396    my_element_test =acr_create_element_string(DCM_PDU_Calling_Ap_title,acr_find_string(input_group, DCM_PDU_Calling_Ap_title, ""));
397    (void) fprintf(stderr, "This is the calling Ap_title: %s \n", my_element_test->data_pointer);
398    /*****************************************/
399 
400    /* Add the application context name */
401    acr_group_add_element(group,
402       acr_create_element_string(DCM_PDU_Application_context,
403          acr_find_string(input_group, DCM_PDU_Application_context,
404                          ACR_APPLICATION_CONTEXT_UID)));
405 
406    /* Loop over presentation contexts */
407    item = (Acr_Element)
408       acr_get_element_data
409          (acr_find_group_element(input_group,
410                                  DCM_PDU_Presentation_context_list));
411    out_list = NULL;
412    for (;(item != NULL); item = acr_get_element_next(item)) {
413 
414       if (!acr_element_is_sequence(item)) continue;
415 
416       /* Get presentation context info */
417       sublist = (Acr_Element) acr_get_element_data(item);
418 
419       /* Save the id */
420       subitem = acr_find_element_id(sublist,
421                                     DCM_PDU_Presentation_context_id);
422       if (subitem == NULL) continue;
423       cur_pres_context_id = acr_get_element_short(subitem);
424 
425       /* Create the presentation context */
426       out_sublist = NULL;
427       out_subitem = acr_create_element_short(DCM_PDU_Presentation_context_id,
428                                              cur_pres_context_id);
429       out_sublist = acr_element_list_add(out_sublist, out_subitem);
430 
431       /* Accept or reject */
432       out_subitem = acr_create_element_short(DCM_PDU_Result,
433          ((cur_pres_context_id == best_pres_context_id) ?
434           ACR_ASSOC_PR_CN_ACCEPT : ACR_ASSOC_PR_CN_REJECT));
435       out_sublist = acr_element_list_add(out_sublist, out_subitem);
436 
437       /* Add the transfer syntax */
438       out_subitem = acr_create_element_string(DCM_PDU_Transfer_syntax,
439                                               best_transfer_syntax);
440       out_sublist = acr_element_list_add(out_sublist, out_subitem);
441 
442       /* Add this context to the list */
443       out_item =
444          acr_create_element_sequence(DCM_PDU_Presentation_context_reply,
445                                      out_sublist);
446       out_list = acr_element_list_add(out_list, out_item);
447    }
448 
449    /* Create the presentation context list element */
450    element =
451       acr_create_element_sequence(DCM_PDU_Presentation_context_reply_list,
452                                   out_list);
453    acr_group_add_element(group, element);
454 
455    /* Add the user information */
456    acr_group_add_element(group,
457                          acr_create_element_long(DCM_PDU_Maximum_length, 0L));
458 
459    /* Set the presentation context id to indicate success */
460    *pres_context_id = best_pres_context_id;
461 
462    return make_message(group);
463 
464 }
465 
466 /* ----------------------------- MNI Header -----------------------------------
467 @NAME       : associate_reply_reject
468 @INPUT      : input_message
469 @OUTPUT     : reason
470 @RETURNS    : output_message
471 @DESCRIPTION: Responds to an associate request message with a rejection
472 @METHOD     :
473 @GLOBALS    :
474 @CALLS      :
475 @CREATED    : February 21, 1997 (Peter Neelin)
476 @MODIFIED   :
477 ---------------------------------------------------------------------------- */
478 /* ARGSUSED */
associate_reply_reject(Acr_Message input_message,int reason)479 public Acr_Message associate_reply_reject(Acr_Message input_message,
480                                           int reason)
481 {
482    Acr_Group group;
483 
484    /* Create the reply */
485    group = acr_create_group(DCM_PDU_GRPID);
486 
487    /* Save the PDU type */
488    SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_ASSOC_RJ);
489 
490    /* Give the result, source and reason */
491    SAVE_SHORT(group, DCM_PDU_Result, ACR_ASSOC_RJ_PERM);
492    SAVE_SHORT(group, DCM_PDU_Source, ACR_ASSOC_RJ_USER);
493    SAVE_SHORT(group, DCM_PDU_Reason, reason);
494 
495    return make_message(group);
496 }
497 
498 /* ----------------------------- MNI Header -----------------------------------
499 @NAME       : release_reply
500 @INPUT      : input_message
501 @OUTPUT     : (nothing)
502 @RETURNS    : output_message
503 @DESCRIPTION: Responds to READYq message
504 @METHOD     :
505 @GLOBALS    :
506 @CALLS      :
507 @CREATED    : November 22, 1993 (Peter Neelin)
508 @MODIFIED   :
509 ---------------------------------------------------------------------------- */
release_reply(Acr_Message input_message)510 public Acr_Message release_reply(Acr_Message input_message)
511 {
512    Acr_Group group;
513 
514    /* Print log message */
515    if (Do_logging >= HIGH_LOGGING) {
516       (void) fprintf(stderr, "\n\nReceived release request message:\n");
517       acr_dump_message(stderr, input_message);
518    }
519 
520    /* Create the reply */
521    group = acr_create_group(DCM_PDU_GRPID);
522 
523    /* Save the PDU type */
524    SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_REL_RP);
525 
526    return make_message(group);
527 }
528 
529 /* ----------------------------- MNI Header -----------------------------------
530 @NAME       : abort_reply
531 @INPUT      : input_message
532 @OUTPUT     : (nothing)
533 @RETURNS    : output_message
534 @DESCRIPTION: Responds to GCENDq message
535 @METHOD     :
536 @GLOBALS    :
537 @CALLS      :
538 @CREATED    : November 22, 1993 (Peter Neelin)
539 @MODIFIED   :
540 ---------------------------------------------------------------------------- */
abort_reply(Acr_Message input_message)541 public Acr_Message abort_reply(Acr_Message input_message)
542 {
543    Acr_Group group;
544 
545    /* Print log message */
546    if (Do_logging >= HIGH_LOGGING) {
547       (void) fprintf(stderr, "\n\nReceived abort message:\n");
548       acr_dump_message(stderr, input_message);
549    }
550 
551    /* Create the reply */
552    group = acr_copy_group_list(acr_get_message_group_list(input_message));
553 
554    return make_message(group);
555 }
556 
557 /* ----------------------------- MNI Header -----------------------------------
558 @NAME       : data_reply
559 @INPUT      : input_message
560               file_prefix
561 @OUTPUT     : new_file_name (must be freed by caller)
562 @RETURNS    : output_message
563 @DESCRIPTION: Responds to SENDq message
564 @METHOD     :
565 @GLOBALS    :
566 @CALLS      :
567 @CREATED    : November 22, 1993 (Peter Neelin)
568 @MODIFIED   :
569 ---------------------------------------------------------------------------- */
data_reply(Acr_Message input_message)570 public Acr_Message data_reply(Acr_Message input_message)
571 {
572    Acr_Group group, input_group;
573    int reply_command;
574 
575 
576    /* Print log message */
577    if (Do_logging >= HIGH_LOGGING) {
578       (void) fprintf(stderr, "\n\nReceived data message:\n");
579       acr_dump_message(stderr, input_message);
580    }
581 
582    /* Get the input group list */
583    input_group = acr_get_message_group_list(input_message);
584 
585 
586    /* Figure out the reply that we need */
587    switch (acr_find_short(input_group, ACR_Command, -1)) {
588    case ACR_C_STORE_RQ:
589       reply_command = ACR_C_STORE_RSP; break;
590    case ACR_C_ECHO_RQ:
591       reply_command = ACR_C_ECHO_RSP; break;
592    default:
593       reply_command = ACR_C_ECHO_RSP; break;
594    }
595 
596    /* Create the reply */
597    group = acr_create_group(ACR_MESSAGE_GID);
598 
599    /* Save appropriate stuff */
600    acr_group_add_element(group,
601       acr_create_element_string(ACR_Affected_SOP_class_UID,
602          acr_find_string(input_group, ACR_Affected_SOP_class_UID, "")));
603    SAVE_SHORT(group, ACR_Command, reply_command);
604    SAVE_SHORT(group, ACR_Message_id_brt,
605               acr_find_short(input_group, ACR_Message_id, 0));
606    SAVE_SHORT(group, ACR_Dataset_type, ACR_NULL_DATASET);
607    SAVE_SHORT(group, ACR_Status, ACR_SUCCESS);
608    if (reply_command == ACR_C_STORE_RSP) {
609       acr_group_add_element(group,
610          acr_create_element_string(ACR_Affected_SOP_instance_UID,
611             acr_find_string(input_group, ACR_Affected_SOP_instance_UID, "")));
612    }
613 
614    return make_message(group);
615 
616 }
617 
618