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