1 /*=========================================================================
2  *
3  *  Copyright NumFOCUS
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 /*
19 
20 This file contains the implementation for the classes for the AE Actions,
21 Association Establishment Related Actions (Table 9-6 of ps 3.8-2009).
22 
23 Since each class is essentially a placeholder for a function pointer, I'm breaking with having
24 each class have its own file for the sake of brevity of the number of files.
25 
26 */
27 
28 #include "gdcmULActionAE.h"
29 #include "gdcmARTIMTimer.h"
30 #include "gdcmAAssociateRQPDU.h"
31 #include "gdcmAAssociateACPDU.h"
32 #include "gdcmAAssociateRJPDU.h"
33 
34 #include <socket++/echo.h>//for setting up the local socket
35 
36 namespace gdcm
37 {
38 namespace network
39 {
40 
41 //Issue TRANSPORT CONNECT request primitive to local transport service.
PerformAction(Subject *,ULEvent &,ULConnection & inConnection,bool & outWaitingForEvent,EEventID & outRaisedEvent)42 EStateID ULActionAE1::PerformAction(Subject *, ULEvent& , ULConnection& inConnection,
43         bool& outWaitingForEvent, EEventID& outRaisedEvent){
44 
45   //opening a local socket
46   outWaitingForEvent = false;
47   if (!inConnection.InitializeConnection())
48     {
49     outRaisedEvent = eEventDoesNotExist;
50     return eSta1Idle;
51     }
52   else
53     {
54     outRaisedEvent = eTransportConnConfirmLocal;
55     }
56   return eSta4LocalAssocDone;
57 }
58 
59 //Send A-ASSOCIATE-RQ-PDU
PerformAction(Subject *,ULEvent &,ULConnection & inConnection,bool & outWaitingForEvent,EEventID & outRaisedEvent)60 EStateID ULActionAE2::PerformAction(Subject *, ULEvent& , ULConnection& inConnection,
61   bool& outWaitingForEvent, EEventID& outRaisedEvent)
62 {
63   AAssociateRQPDU thePDU;
64 
65   thePDU.SetCallingAETitle( inConnection.GetConnectionInfo().GetCallingAETitle() );
66   thePDU.SetCalledAETitle( inConnection.GetConnectionInfo().GetCalledAETitle() );
67 
68   //the presentation context is now defined when the connection is first
69   //desired to be established. The connection proposes these different
70   //presentation contexts. ideally, we could refine it further to a particular
71   //presentation context, but if the server supports many and we support many,
72   //then an arbitrary decision can be made.
73   std::vector<PresentationContextRQ> const & thePCS =
74     inConnection.GetPresentationContexts();
75 
76   std::vector<PresentationContextRQ>::const_iterator itor;
77   for (itor = thePCS.begin(); itor < thePCS.end(); itor++)
78     {
79     thePDU.AddPresentationContext(*itor);
80     }
81 
82   thePDU.Write(*inConnection.GetProtocol());
83   inConnection.GetProtocol()->flush();
84 
85   outWaitingForEvent = true;
86   outRaisedEvent = eEventDoesNotExist;
87 
88   return eSta5WaitRemoteAssoc;
89 }
90 
91 //Issue A-ASSOCIATE confirmation (accept) primitive
92 // NOTE: A-ASSOCIATE is NOT A-ASSOCIATE-AC
93 // PS 3.7 / Annex D for A-ASSOCIATE definition
PerformAction(Subject *,ULEvent & inEvent,ULConnection & inConnection,bool & outWaitingForEvent,EEventID & outRaisedEvent)94 EStateID ULActionAE3::PerformAction(Subject *, ULEvent& inEvent, ULConnection& inConnection,
95         bool& outWaitingForEvent, EEventID& outRaisedEvent){
96 
97 
98   // Mark please check this junk:
99   assert(!inEvent.GetPDUs().empty());
100   AAssociateACPDU* acpdu;
101     acpdu = dynamic_cast<AAssociateACPDU*>(inEvent.GetPDUs()[0]);
102   assert( acpdu );
103   uint32_t maxpdu = acpdu->GetUserInformation().GetMaximumLengthSub().GetMaximumLength();
104   inConnection.SetMaxPDUSize(maxpdu);
105 
106   // once again duplicate AAssociateACPDU vs ULConnection
107   for( unsigned int index = 0; index < acpdu->GetNumberOfPresentationContextAC(); index++ ){
108     PresentationContextAC const &pc = acpdu->GetPresentationContextAC(index);
109     inConnection.AddAcceptedPresentationContext(pc);
110   }
111 
112   outWaitingForEvent = false;
113   outRaisedEvent = eEventDoesNotExist;//no event is raised,
114   //wait for the user to try to send some data.
115   return eSta6TransferReady;
116 }
117 
118 //Issue A-ASSOCIATE confirmation (reject) primitive and close transport connection
PerformAction(Subject *,ULEvent &,ULConnection &,bool & outWaitingForEvent,EEventID & outRaisedEvent)119 EStateID ULActionAE4::PerformAction(Subject *, ULEvent& , ULConnection& ,
120         bool& outWaitingForEvent, EEventID& outRaisedEvent){
121 
122   outWaitingForEvent = false;
123   outRaisedEvent = eASSOCIATE_RJPDUreceived;
124   return eSta1Idle;
125 }
126 
127 //Issue Transport connection response primitive, start ARTIM timer
PerformAction(Subject *,ULEvent &,ULConnection & inConnection,bool & outWaitingForEvent,EEventID & outRaisedEvent)128 EStateID ULActionAE5::PerformAction(Subject *, ULEvent& , ULConnection& inConnection,
129         bool& outWaitingForEvent, EEventID& outRaisedEvent){
130 
131   //issue response primitive; have to set that up
132   inConnection.GetTimer().Start();
133 
134   outWaitingForEvent = false;
135   outRaisedEvent = eTransportConnConfirmLocal;
136   return eSta2Open;
137 }
138 
139 //Stop ARTIM timer and if A-ASSOCIATE-RQ acceptable by service-provider:
140 //- issue A-ASSOCIATE indication primitive
141 //Next state: eSta3WaitLocalAssoc
142 //otherwise:
143 //- issue A-ASSOCIATE-RJ-PDU and start ARTIM timer
144 //Next state: eSta13AwaitingClose
PerformAction(Subject *,ULEvent & inEvent,ULConnection & inConnection,bool & outWaitingForEvent,EEventID & outRaisedEvent)145 EStateID ULActionAE6::PerformAction(Subject *, ULEvent& inEvent, ULConnection& inConnection,
146         bool& outWaitingForEvent, EEventID& outRaisedEvent){
147 
148  // we are in a C-MOVE
149 
150   inConnection.GetTimer().Stop();
151 
152   //have to determine 'acceptability'
153   //this is more server side than client, so it's a bit empty now
154   //we have one server type, a store scp started on a cmove
155   //so, it's defined as acceptable.
156   bool acceptable = true;//for now, always accept
157   if (inEvent.GetPDUs().empty()){
158     acceptable = false; //can't accept an empty set of pdus.
159     //also, requrie little endian, not sure how to set that, but it should be here.
160   }
161   AAssociateRQPDU* rqpdu;
162   if (acceptable){
163     rqpdu = dynamic_cast<AAssociateRQPDU*>(inEvent.GetPDUs()[0]);
164     if (rqpdu == nullptr){
165       acceptable = false;
166     }
167   }
168   if (acceptable){
169     outWaitingForEvent = false;//not waiting, now want to get the
170     //sending of data underway.  Have to get info now
171     outRaisedEvent = eAASSOCIATEresponseAccept;
172 
173     TransferSyntaxSub ts1;
174     ts1.SetNameFromUID( UIDs::ImplicitVRLittleEndianDefaultTransferSyntaxforDICOM );
175 
176     AAssociateACPDU acpdu;
177 
178     assert( rqpdu->GetNumberOfPresentationContext() );
179     for( unsigned int index = 0; index < rqpdu->GetNumberOfPresentationContext(); index++ )
180       {
181       // FIXME / HARDCODED We only ever accept Little Endian
182       // FIXME we should check :
183       // rqpdu.GetAbstractSyntax() contains LittleEndian
184       PresentationContextAC pcac1;
185       PresentationContextRQ const &pc = rqpdu->GetPresentationContext(index);
186       //add the presentation context back into the connection,
187       //so later functions will know what's allowed on this connection
188       // BOGUS (MM):
189       //inConnection.AddAcceptedPresentationContext(pc);
190 
191       const uint8_t id = pc.GetPresentationContextID();
192 
193       std::vector<TransferSyntaxSub> const & tsSet = pc.GetTransferSyntaxes();
194       std::vector<TransferSyntaxSub>::const_iterator tsitor;
195       // PS 3.8 Table 9-18 PRESENTATION CONTEXT ITEM FIELDS
196       uint8_t result = 4; // transfer-syntaxes-not-supported (provider rejection)
197       for (tsitor = tsSet.begin(); tsitor < tsSet.end(); tsitor++)
198         {
199         //gdcmDebugMacro( "Checking: [" << tsitor->GetName() << "] vs [" << ts1.GetName() << "]" << std::endl );
200         if (strcmp(tsitor->GetName(), ts1.GetName()) == 0 )
201           {
202           result = 0; // 0 - acceptance
203           inConnection.SetCStoreTransferSyntax( ts1 );
204           pcac1.SetTransferSyntax( ts1 );
205           }
206         }
207       if( result )
208         {
209         gdcmWarningMacro( "Could not find Implicit or Explicit Little Endian in Response. Giving another try" );
210         // Okay little endian implicit was not found, this happen sometimes, for eg with DVTk, let's be nice and accept also Explicit
211         TransferSyntaxSub ts2;
212         ts2.SetNameFromUID( UIDs::ExplicitVRLittleEndian );
213         for (tsitor = tsSet.begin(); tsitor < tsSet.end(); tsitor++)
214           {
215           //gdcmDebugMacro( "Checking: [" << tsitor->GetName() << "] vs [" << ts1.GetName() << "]" << std::endl );
216           if (strcmp(tsitor->GetName(), ts2.GetName()) == 0 )
217             {
218             result = 0; // 0 - acceptance
219             inConnection.SetCStoreTransferSyntax( ts2 );
220             pcac1.SetTransferSyntax( ts2 );
221             }
222           }
223         }
224       if( result )
225         {
226         gdcmErrorMacro( "Could not find Implicit or Explicit Little Endian in Response. Giving up" );
227         }
228       pcac1.SetPresentationContextID( id );
229       pcac1.SetReason( result );
230       acpdu.AddPresentationContextAC( pcac1 );
231     }
232     assert( acpdu.GetNumberOfPresentationContextAC() );
233 
234     // Init AE-Titles:
235     acpdu.InitFromRQ( *rqpdu );
236 
237     acpdu.Write( *inConnection.GetProtocol() );
238     inConnection.GetProtocol()->flush();
239 
240     return eSta3WaitLocalAssoc;
241   } else {
242 
243     outWaitingForEvent = false;
244     outRaisedEvent = eAASSOCIATEresponseReject;
245     AAssociateRJPDU thePDU;
246     thePDU.Write(*inConnection.GetProtocol());
247     inConnection.GetProtocol()->flush();
248     inConnection.GetTimer().Stop();
249     return eSta13AwaitingClose;
250   }
251 
252 }
253 
254 //Send A-ASSOCIATE-AC PDU
PerformAction(Subject *,ULEvent &,ULConnection &,bool & outWaitingForEvent,EEventID & outRaisedEvent)255 EStateID ULActionAE7::PerformAction(Subject *, ULEvent& , ULConnection& ,
256         bool& outWaitingForEvent, EEventID& outRaisedEvent)
257 {
258   outWaitingForEvent = true;
259   outRaisedEvent = eEventDoesNotExist;
260   return eSta6TransferReady;
261 }
262 
263 //Send A-ASSOCIATE-RJ PDU and start ARTIM timer
PerformAction(Subject *,ULEvent &,ULConnection & inConnection,bool & outWaitingForEvent,EEventID & outRaisedEvent)264 EStateID ULActionAE8::PerformAction(Subject *, ULEvent& , ULConnection& inConnection,
265         bool& outWaitingForEvent, EEventID& outRaisedEvent)
266 {
267   AAssociateRJPDU thePDU;
268   thePDU.Write(*inConnection.GetProtocol());
269   inConnection.GetTimer().Start();
270   outWaitingForEvent = false;
271   outRaisedEvent = eAASSOCIATEresponseReject;
272 
273   return eSta13AwaitingClose;
274 }
275 
276 } // end namespace network
277 } // end namespace gdcm
278