1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_automation.hxx"
26 #include <stdio.h>
27 #if OSL_DEBUG_LEVEL > 1
28 #define DEBUGPRINTF(x) { printf(x); fflush( stdout ); }
29 #else
30 #define DEBUGPRINTF(x)
31 #endif
32 #include <tools/debug.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vos/socket.hxx>
35 #include <tools/stream.hxx>
36 #include <vcl/timer.hxx>
37 #include <tools/fsys.hxx>
38 
39 #include <automation/communi.hxx>
40 
41 
42 /*	Um den Destruktor protected zu machen wurde unten das delete entfernt.
43 	Die Methode wird ohnehin hucht benutzt.
44 //				delete *((AE*)pData+n);
45 */
46 
47 #undef  SV_IMPL_PTRARR_SORT
48 #define SV_IMPL_PTRARR_SORT( nm,AE )\
49 _SV_IMPL_SORTAR_ALG( nm,AE )\
50 	void nm::DeleteAndDestroy( sal_uInt16 nP, sal_uInt16 nL ) { \
51 		if( nL ) {\
52 			DBG_ASSERT( nP < nA && nP + nL <= nA, "ERR_VAR_DEL" );\
53 			for( sal_uInt16 n=nP; n < nP + nL; n++ ) \
54 				DBG_ERROR("Das Element der Liste wurde nicht gel�scht"); \
55 			SvPtrarr::Remove( nP, nL ); \
56 		} \
57 	} \
58 _SV_SEEK_PTR( nm, AE )
59 
60 
61 
62 
63 SV_IMPL_PTRARR_SORT( CommunicationLinkList, CommunicationLink* );
64 
65 vos::OMutex *pMPostUserEvent=NULL;		// Notwendig, da nicht threadfest
66 
67 CommunicationLinkViaSocket::CommunicationLinkViaSocket( CommunicationManager *pMan, vos::OStreamSocket *pSocket )
68 : SimpleCommunicationLinkViaSocket( pMan, pSocket )
69 , nConnectionClosedEventId( 0 )
70 , nDataReceivedEventId( 0 )
71 , bShutdownStarted( sal_False )
72 , bDestroying( sal_False )
73 {
74     SetPutDataReceivedHdl(LINK( this, CommunicationLinkViaSocket, PutDataReceivedHdl ));
75     if ( !pMPostUserEvent )
76         pMPostUserEvent = new vos::OMutex;
77     // this is necassary to prevent the running thread from sending the close event
78     // before the open event has been sent.
79    	StartCallback();
80 
81 	create();
82 }
83 
84 CommunicationLinkViaSocket::~CommunicationLinkViaSocket()
85 {
86     bDestroying = sal_True;
87 	StopCommunication();
88 	while ( nConnectionClosedEventId || nDataReceivedEventId )
89 		GetpApp()->Yield();
90 	{
91 		vos::OGuard aGuard( aMConnectionClosed );
92 		if ( nConnectionClosedEventId )
93 		{
94 			GetpApp()->RemoveUserEvent( nConnectionClosedEventId );
95 			nConnectionClosedEventId = 0;
96 			INFO_MSG( CByteString("Event gel�scht"),
97 				CByteString( "ConnectionClosedEvent aus Queue gel�scht"),
98 				CM_MISC, NULL );
99 		}
100 	}
101 	{
102 		vos::OGuard aGuard( aMDataReceived );
103 		if ( nDataReceivedEventId )
104 		{
105 			GetpApp()->RemoveUserEvent( nDataReceivedEventId );
106 			nDataReceivedEventId = 0;
107 			delete GetServiceData();
108 			INFO_MSG( CByteString("Event gel�scht"),
109 				CByteString( "DataReceivedEvent aus Queue gel�scht"),
110 				CM_MISC, NULL );
111 		}
112 	}
113 }
114 
115 sal_Bool CommunicationLinkViaSocket::ShutdownCommunication()
116 {
117 	if ( isRunning() )
118 	{
119 
120 		terminate();
121 		if ( GetStreamSocket() )
122 			GetStreamSocket()->shutdown();
123 
124 		if ( GetStreamSocket() )	// Mal wieder nach oben verschoben, da sonst nicht vom Read runtergesprungen wird.
125 			GetStreamSocket()->close();
126 
127 		resume();	// So da� das run auch die Schleife verlassen kann
128 
129 		join();
130 
131         vos::OStreamSocket *pTempSocket = GetStreamSocket();
132         SetStreamSocket( NULL );
133         delete pTempSocket;
134 
135 //		ConnectionClosed();		Wird am Ende des Thread gerufen
136 
137 	}
138 	else
139 	{
140 		join();
141 	}
142 
143 	return sal_True;
144 }
145 
146 sal_Bool CommunicationLinkViaSocket::StopCommunication()
147 {
148     if ( !bShutdownStarted )
149     {
150         return SimpleCommunicationLinkViaSocket::StopCommunication();
151     }
152     else
153     {
154         WaitForShutdown();
155         return sal_True;
156     }
157 }
158 
159 
160 IMPL_LINK( CommunicationLinkViaSocket, ShutdownLink, void*, EMPTYARG )
161 {
162 	if ( !IsCommunicationError() )
163     	ShutdownCommunication();
164     return 0;
165 }
166 
167 
168 void CommunicationLinkViaSocket::WaitForShutdown()
169 {
170     if ( !bShutdownStarted )
171     {
172 	    aShutdownTimer.SetTimeout( 30000 );		// Should be 30 Seconds
173 	    aShutdownTimer.SetTimeoutHdl( LINK( this, CommunicationLinkViaSocket, ShutdownLink ) );
174 	    aShutdownTimer.Start();
175         bShutdownStarted = sal_True;
176     }
177     if ( bDestroying )
178     {
179 	    while ( pMyManager && aShutdownTimer.IsActive() )
180 	    {
181 		    if ( IsCommunicationError() )
182 			    return;
183 		    GetpApp()->Yield();
184 	    }
185 	    ShutdownCommunication();
186     }
187 }
188 
189 sal_Bool CommunicationLinkViaSocket::IsCommunicationError()
190 {
191 	return !isRunning() || SimpleCommunicationLinkViaSocket::IsCommunicationError();
192 }
193 
194 void CommunicationLinkViaSocket::run()
195 {
196 	sal_Bool bWasError = sal_False;
197     while ( schedule() && !bWasError && GetStreamSocket() )
198 	{
199 		if ( bWasError |= !DoReceiveDataStream() )
200 			continue;
201 
202 		TimeValue sNochEins = {0, 1000000};
203 		while ( schedule() && bIsInsideCallback )	// solange der letzte Callback nicht beendet ist
204 			sleep( sNochEins );
205 		SetNewPacketAsCurrent();
206 		StartCallback();
207 		{
208 			vos::OGuard aGuard( aMDataReceived );
209             vos::OGuard aGuard2( *pMPostUserEvent );
210             mlPutDataReceived.Call(this);
211 		}
212 	}
213 	TimeValue sNochEins = {0, 1000000};
214 	while ( schedule() && bIsInsideCallback )	// solange der letzte Callback nicht beendet ist
215 		sleep( sNochEins );
216 
217     StartCallback();
218 	{
219 		vos::OGuard aGuard( aMConnectionClosed );
220         vos::OGuard aGuard2( *pMPostUserEvent );
221 		nConnectionClosedEventId = GetpApp()->PostUserEvent( LINK( this, CommunicationLinkViaSocket, ConnectionClosed ) );
222 	}
223 }
224 
225 sal_Bool CommunicationLinkViaSocket::DoTransferDataStream( SvStream *pDataStream, CMProtocol nProtocol )
226 {
227 	if ( !isRunning() )
228 		return sal_False;
229 
230 	return SimpleCommunicationLinkViaSocket::DoTransferDataStream( pDataStream, nProtocol );
231 }
232 
233 /// Dies ist ein virtueller Link!!!
234 long CommunicationLinkViaSocket::ConnectionClosed( void* EMPTYARG )
235 {
236 	{
237 		vos::OGuard aGuard( aMConnectionClosed );
238 		nConnectionClosedEventId = 0;	// Achtung!! alles andere mu� oben gemacht werden.
239 	}
240 	ShutdownCommunication();
241 	return CommunicationLink::ConnectionClosed( );
242 }
243 
244 /// Dies ist ein virtueller Link!!!
245 long CommunicationLinkViaSocket::DataReceived( void* EMPTYARG )
246 {
247 	{
248 		vos::OGuard aGuard( aMDataReceived );
249 		nDataReceivedEventId = 0;	// Achtung!! alles andere mu� oben gemacht werden.
250 	}
251 	return CommunicationLink::DataReceived( );
252 }
253 
254 IMPL_LINK( CommunicationLinkViaSocket, PutDataReceivedHdl, CommunicationLinkViaSocket*, EMPTYARG )
255 {
256     nDataReceivedEventId = GetpApp()->PostUserEvent( LINK( this, CommunicationLink, DataReceived ) );
257     return 0;
258 }
259 
260 
261 
262 MultiCommunicationManager::MultiCommunicationManager( sal_Bool bUseMultiChannel )
263 : CommunicationManager( bUseMultiChannel )
264 , bGracefullShutdown( sal_True )
265 {
266 	ActiveLinks = new CommunicationLinkList;
267 	InactiveLinks = new CommunicationLinkList;
268 }
269 
270 MultiCommunicationManager::~MultiCommunicationManager()
271 {
272 	StopCommunication();
273 
274     if ( bGracefullShutdown )   // first try to collect all callbacks for closing channels
275     {
276         Timer aTimeout;
277         aTimeout.SetTimeout( 40000 );
278         aTimeout.Start();
279         sal_uInt16 nLinkCount = 0;
280         sal_uInt16 nNewLinkCount = 0;
281         while ( aTimeout.IsActive() )
282         {
283             GetpApp()->Yield();
284             nNewLinkCount = GetCommunicationLinkCount();
285             if ( nNewLinkCount == 0 )
286                 aTimeout.Stop();
287             if ( nNewLinkCount != nLinkCount )
288             {
289                 aTimeout.Start();
290                 nLinkCount = nNewLinkCount;
291             }
292         }
293     }
294 
295 	// Alles weghauen, was nicht rechtzeitig auf die B�ume gekommen ist
296 	// Was bei StopCommunication �brig geblieben ist, da es sich asynchron austragen wollte
297 	sal_uInt16 i = ActiveLinks->Count();
298 	while ( i-- )
299 	{
300 		CommunicationLinkRef rTempLink = ActiveLinks->GetObject( i );
301 		ActiveLinks->Remove( i );
302 		rTempLink->InvalidateManager();
303 		rTempLink->ReleaseReference();
304 	}
305 	delete ActiveLinks;
306 
307 	/// Die Links zwischen ConnectionClosed und Destruktor.
308 	/// Hier NICHT gerefcounted, da sie sich sonst im Kreis festhaten w�rden,
309 	/// da die Links sich erst in ihrem Destruktor austragen
310 	i = InactiveLinks->Count();
311 	while ( i-- )
312 	{
313 		CommunicationLinkRef rTempLink = InactiveLinks->GetObject( i );
314 		InactiveLinks->Remove( i );
315 		rTempLink->InvalidateManager();
316 	}
317 	delete InactiveLinks;
318 }
319 
320 sal_Bool MultiCommunicationManager::StopCommunication()
321 {
322 	// Alle Verbindungen abbrechen
323 	// ConnectionClosed entfernt die Links aus der Liste. Je nach Implementation syncron
324 	// oder asyncron. Daher Von oben nach unten Abr�umen, so da� sich nichts verschiebt.
325 	sal_uInt16 i = ActiveLinks->Count();
326 	int nFail = 0;
327 	while ( i )
328 	{
329 		if ( !ActiveLinks->GetObject(i-1)->StopCommunication() )
330             nFail++;    // Hochz�hlen, da Verbindung sich nicht (sofort) beenden l�sst.
331 		i--;
332 	}
333 
334 	return nFail == 0;
335 }
336 
337 sal_Bool MultiCommunicationManager::IsLinkValid( CommunicationLink* pCL )
338 {
339 	if ( ActiveLinks->Seek_Entry( pCL ) )
340 		return sal_True;
341 	else
342 		return sal_False;
343 }
344 
345 sal_uInt16 MultiCommunicationManager::GetCommunicationLinkCount()
346 {
347 	return ActiveLinks->Count();
348 }
349 
350 CommunicationLinkRef MultiCommunicationManager::GetCommunicationLink( sal_uInt16 nNr )
351 {
352 	return ActiveLinks->GetObject( nNr );
353 }
354 
355 void MultiCommunicationManager::CallConnectionOpened( CommunicationLink* pCL )
356 {
357 	CommunicationLinkRef rHold(pCL);	// H�lt den Zeiger bis zum Ende des calls
358 	ActiveLinks->C40_PTR_INSERT(CommunicationLink, pCL);
359 	rHold->AddRef();
360 
361 	CommunicationManager::CallConnectionOpened( pCL );
362 }
363 
364 void MultiCommunicationManager::CallConnectionClosed( CommunicationLink* pCL )
365 {
366 	CommunicationLinkRef rHold(pCL);	// H�lt denm Zeiger bis zum Ende des calls
367 
368 	CommunicationManager::CallConnectionClosed( pCL );
369 
370 	sal_uInt16 nPos;
371 	if ( ActiveLinks->Seek_Entry( pCL, &nPos ) )
372 	{
373 		InactiveLinks->C40_PTR_INSERT(CommunicationLink, pCL);	// Ohne Reference
374 		ActiveLinks->Remove( nPos );
375 	}
376 	pCL->ReleaseReference();
377 
378 	bIsCommunicationRunning = ActiveLinks->Count() > 0;
379 //	delete pCL;
380 #if OSL_DEBUG_LEVEL > 1
381         rHold->bFlag = sal_True;
382 #endif
383 }
384 
385 void MultiCommunicationManager::DestroyingLink( CommunicationLink *pCL )
386 {
387 	sal_uInt16 nPos;
388 	if ( InactiveLinks->Seek_Entry( pCL, &nPos ) )
389 		InactiveLinks->Remove( nPos );
390 	pCL->InvalidateManager();
391 }
392 
393 
394 
395 CommunicationManagerClient::CommunicationManagerClient( sal_Bool bUseMultiChannel )
396 : MultiCommunicationManager( bUseMultiChannel )
397 {
398 	ByteString aApplication("Something inside ");
399 	aApplication.Append( ByteString( DirEntry( Application::GetAppFileName() ).GetName(), gsl_getSystemTextEncoding() ) );
400     SetApplication( aApplication );
401 }
402 
403 
404 
405 CommunicationManagerServerViaSocket::CommunicationManagerServerViaSocket( sal_uLong nPort, sal_uInt16 nMaxCon, sal_Bool bUseMultiChannel )
406 : CommunicationManagerServer( bUseMultiChannel )
407 , nPortToListen( nPort )
408 , nMaxConnections( nMaxCon )
409 , pAcceptThread( NULL )
410 {
411 }
412 
413 CommunicationManagerServerViaSocket::~CommunicationManagerServerViaSocket()
414 {
415 	StopCommunication();
416 }
417 
418 sal_Bool CommunicationManagerServerViaSocket::StartCommunication()
419 {
420 	if ( !pAcceptThread )
421 		pAcceptThread = new CommunicationManagerServerAcceptThread( this, nPortToListen, nMaxConnections );
422 	return sal_True;
423 }
424 
425 
426 sal_Bool CommunicationManagerServerViaSocket::StopCommunication()
427 {
428 	// Erst den Acceptor anhalten
429 	delete pAcceptThread;
430 	pAcceptThread = NULL;
431 
432 	// Dann alle Verbindungen kappen
433 	return CommunicationManagerServer::StopCommunication();
434 }
435 
436 
437 void CommunicationManagerServerViaSocket::AddConnection( CommunicationLink *pNewConnection )
438 {
439 	CallConnectionOpened( pNewConnection );
440 }
441 
442 
443 CommunicationManagerServerAcceptThread::CommunicationManagerServerAcceptThread( CommunicationManagerServerViaSocket* pServer, sal_uLong nPort, sal_uInt16 nMaxCon )
444 : pMyServer( pServer )
445 , pAcceptorSocket( NULL )
446 , nPortToListen( nPort )
447 , nMaxConnections( nMaxCon )
448 , nAddConnectionEventId( 0 )
449 , xmNewConnection( NULL )
450 {
451     if ( !pMPostUserEvent )
452         pMPostUserEvent = new vos::OMutex;
453 	create();
454 }
455 
456 
457 CommunicationManagerServerAcceptThread::~CommunicationManagerServerAcceptThread()
458 {
459 #ifndef aUNX		// Weil das Accept nicht abgebrochen werden kann, so terminiert wenigstens das Prog
460 	// #62855# pl: gilt auch bei anderen Unixen
461 	// die richtige Loesung waere natuerlich, etwas auf die pipe zu schreiben,
462 	// was der thread als Abbruchbedingung erkennt
463 	// oder wenigstens ein kill anstatt join
464 	terminate();
465 	if ( pAcceptorSocket )
466 		pAcceptorSocket->close();	// Dann das Accept unterbrechen
467 
468 	join();		// Warten bis fertig
469 
470 	if ( pAcceptorSocket )
471 	{
472 		delete pAcceptorSocket;
473 		pAcceptorSocket = NULL;
474 	}
475 #else
476 	DEBUGPRINTF ("Destructor CommunicationManagerServerAcceptThread �bersprungen!!!! (wegen Solaris BUG)\n");
477 #endif
478 	{
479 		vos::OGuard aGuard( aMAddConnection );
480 		if ( nAddConnectionEventId )
481 		{
482 			GetpApp()->RemoveUserEvent( nAddConnectionEventId );
483 			nAddConnectionEventId = 0;
484 			CommunicationLinkRef xNewConnection = GetNewConnection();
485 			INFO_MSG( CByteString("Event gel�scht"),
486 				CByteString( "AddConnectionEvent aus Queue gel�scht"),
487 				CM_MISC, xNewConnection );
488 			xNewConnection->InvalidateManager();
489 			xNewConnection.Clear();	// sollte das Objekt hier l�schen
490 		}
491 	}
492 }
493 
494 void CommunicationManagerServerAcceptThread::run()
495 {
496 	if ( !nPortToListen )
497 		return;
498 
499 	pAcceptorSocket = new vos::OAcceptorSocket();
500 	vos::OInetSocketAddr Addr;
501 	Addr.setPort( nPortToListen );
502 	pAcceptorSocket->setReuseAddr( 1 );
503 	if ( !pAcceptorSocket->bind( Addr ) )
504 	{
505 		return;
506 	}
507 	if ( !pAcceptorSocket->listen( nMaxConnections ) )
508 	{
509 		return;
510 	}
511 
512 
513 	vos::OStreamSocket *pStreamSocket = NULL;
514 
515 	while ( schedule() )
516 	{
517 		pStreamSocket = new vos::OStreamSocket;
518 		switch ( pAcceptorSocket->acceptConnection( *pStreamSocket ) )
519 		{
520 		case vos::ISocketTypes::TResult_Ok:
521 			{
522 				pStreamSocket->setTcpNoDelay( 1 );
523 
524 				TimeValue sNochEins = {0, 100};
525 				while ( schedule() && xmNewConnection.Is() )	// Solange die letzte Connection nicht abgeholt wurde warten wir
526 					sleep( sNochEins );
527 				xmNewConnection = new CommunicationLinkViaSocket( pMyServer, pStreamSocket );
528 				xmNewConnection->StartCallback();
529 				{
530 					vos::OGuard aGuard( aMAddConnection );
531                     vos::OGuard aGuard2( *pMPostUserEvent );
532 					nAddConnectionEventId = GetpApp()->PostUserEvent( LINK( this, CommunicationManagerServerAcceptThread, AddConnection ) );
533 				}
534 			}
535 			break;
536 		case vos::ISocketTypes::TResult_TimedOut:
537 			delete pStreamSocket;
538 			pStreamSocket = NULL;
539 			break;
540 		case vos::ISocketTypes::TResult_Error:
541 			delete pStreamSocket;
542 			pStreamSocket = NULL;
543 			break;
544 
545 		case vos::ISocketTypes::TResult_Interrupted:
546 		case vos::ISocketTypes::TResult_InProgress:
547 			break;  // -Wall not handled...
548 		}
549 	}
550 }
551 
552 
553 IMPL_LINK( CommunicationManagerServerAcceptThread, AddConnection, void*, EMPTYARG )
554 {
555 	{
556 		vos::OGuard aGuard( aMAddConnection );
557 		nAddConnectionEventId = 0;
558 	}
559 	pMyServer->AddConnection( xmNewConnection );
560 	xmNewConnection.Clear();
561 	return 1;
562 }
563 
564 
565 #define GETSET(aVar, KeyName, Dafault)                 \
566 	aVar = aConf.ReadKey(KeyName,"No Entry");          \
567 	if ( aVar == "No Entry" )                          \
568 	{                                                  \
569 		aVar = Dafault;                                \
570 		aConf.WriteKey(KeyName, aVar);                 \
571 	}
572 
573 
574 CommunicationManagerClientViaSocket::CommunicationManagerClientViaSocket( ByteString aHost, sal_uLong nPort, sal_Bool bUseMultiChannel )
575 : CommunicationManagerClient( bUseMultiChannel )
576 , aHostToTalk( aHost )
577 , nPortToTalk( nPort )
578 {
579 }
580 
581 CommunicationManagerClientViaSocket::CommunicationManagerClientViaSocket( sal_Bool bUseMultiChannel )
582 : CommunicationManagerClient( bUseMultiChannel )
583 , aHostToTalk( "" )
584 , nPortToTalk( 0 )
585 {
586 }
587 
588 CommunicationManagerClientViaSocket::~CommunicationManagerClientViaSocket()
589 {
590 }
591 
592 
593