Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include "app.hxx"
22 : #include "officeipcthread.hxx"
23 : #include "cmdlineargs.hxx"
24 : #include "dispatchwatcher.hxx"
25 : #include <memory>
26 : #include <stdio.h>
27 : #include <osl/process.h>
28 : #include <unotools/bootstrap.hxx>
29 : #include <vcl/svapp.hxx>
30 : #include <vcl/help.hxx>
31 : #include <unotools/configmgr.hxx>
32 : #include <osl/thread.hxx>
33 : #include <rtl/digest.h>
34 : #include <rtl/ustrbuf.hxx>
35 : #include <rtl/instance.hxx>
36 : #include <osl/conditn.hxx>
37 : #include <unotools/moduleoptions.hxx>
38 : #include <rtl/bootstrap.hxx>
39 : #include <rtl/strbuf.hxx>
40 : #include <comphelper/processfactory.hxx>
41 : #include <osl/file.hxx>
42 : #include <rtl/process.h>
43 : #include "tools/getprocessworkingdir.hxx"
44 :
45 : using namespace desktop;
46 : using namespace ::com::sun::star::uno;
47 : using namespace ::com::sun::star::lang;
48 : using namespace ::com::sun::star::frame;
49 :
50 : using ::rtl::OString;
51 : using ::rtl::OUString;
52 : using ::rtl::OUStringBuffer;
53 :
54 : const char *OfficeIPCThread::sc_aShowSequence = "-tofront";
55 : const int OfficeIPCThread::sc_nShSeqLength = 5;
56 : const char *OfficeIPCThread::sc_aConfirmationSequence = "InternalIPC::ProcessingDone";
57 : const int OfficeIPCThread::sc_nCSeqLength = 27;
58 : const char *OfficeIPCThread::sc_aSendArgumentsSequence = "InternalIPC::SendArguments";
59 : const int OfficeIPCThread::sc_nCSASeqLength = 26;
60 :
61 : namespace { static char const ARGUMENT_PREFIX[] = "InternalIPC::Arguments"; }
62 :
63 : // Type of pipe we use
64 : enum PipeMode
65 : {
66 : PIPEMODE_DONTKNOW,
67 : PIPEMODE_CREATED,
68 : PIPEMODE_CONNECTED
69 : };
70 :
71 : namespace desktop
72 : {
73 :
74 : namespace {
75 :
76 : class Parser: public CommandLineArgs::Supplier {
77 : public:
78 0 : explicit Parser(rtl::OString const & input): m_input(input) {
79 0 : if (!m_input.match(ARGUMENT_PREFIX) ||
80 0 : m_input.getLength() == RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX))
81 : {
82 0 : throw CommandLineArgs::Supplier::Exception();
83 : }
84 0 : m_index = RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX);
85 0 : switch (m_input[m_index++]) {
86 : case '0':
87 0 : break;
88 : case '1':
89 : {
90 0 : rtl::OUString url;
91 0 : if (!next(&url, false)) {
92 0 : throw CommandLineArgs::Supplier::Exception();
93 : }
94 0 : m_cwdUrl.reset(url);
95 0 : break;
96 : }
97 : case '2':
98 : {
99 0 : rtl::OUString path;
100 0 : if (!next(&path, false)) {
101 0 : throw CommandLineArgs::Supplier::Exception();
102 : }
103 0 : rtl::OUString url;
104 0 : if (osl::FileBase::getFileURLFromSystemPath(path, url) ==
105 : osl::FileBase::E_None)
106 : {
107 0 : m_cwdUrl.reset(url);
108 : }
109 0 : break;
110 : }
111 : default:
112 0 : throw CommandLineArgs::Supplier::Exception();
113 : }
114 0 : }
115 :
116 0 : virtual ~Parser() {}
117 :
118 0 : virtual boost::optional< rtl::OUString > getCwdUrl() { return m_cwdUrl; }
119 :
120 0 : virtual bool next(rtl::OUString * argument) { return next(argument, true); }
121 :
122 : private:
123 0 : virtual bool next(rtl::OUString * argument, bool prefix) {
124 : OSL_ASSERT(argument != NULL);
125 0 : if (m_index < m_input.getLength()) {
126 0 : if (prefix) {
127 0 : if (m_input[m_index] != ',') {
128 0 : throw CommandLineArgs::Supplier::Exception();
129 : }
130 0 : ++m_index;
131 : }
132 0 : rtl::OStringBuffer b;
133 0 : while (m_index < m_input.getLength()) {
134 0 : char c = m_input[m_index];
135 0 : if (c == ',') {
136 0 : break;
137 : }
138 0 : ++m_index;
139 0 : if (c == '\\') {
140 0 : if (m_index < m_input.getLength()) {
141 0 : c = m_input[m_index++];
142 0 : switch (c) {
143 : case '0':
144 0 : c = '\0';
145 0 : break;
146 : case ',':
147 : case '\\':
148 0 : break;
149 : default:
150 0 : throw CommandLineArgs::Supplier::Exception();
151 : }
152 : } else {
153 0 : throw CommandLineArgs::Supplier::Exception();
154 : }
155 : }
156 0 : b.append(c);
157 : }
158 0 : rtl::OString b2(b.makeStringAndClear());
159 0 : if (!rtl_convertStringToUString(
160 : &argument->pData, b2.getStr(), b2.getLength(),
161 : RTL_TEXTENCODING_UTF8,
162 : (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
163 : RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
164 0 : RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
165 : {
166 0 : throw CommandLineArgs::Supplier::Exception();
167 : }
168 0 : return true;
169 : } else {
170 0 : return false;
171 : }
172 : }
173 :
174 : boost::optional< rtl::OUString > m_cwdUrl;
175 : rtl::OString m_input;
176 : sal_Int32 m_index;
177 : };
178 :
179 0 : bool addArgument(rtl::OStringBuffer &rArguments, char prefix,
180 : const rtl::OUString &rArgument)
181 : {
182 0 : rtl::OString utf8;
183 0 : if (!rArgument.convertToString(
184 : &utf8, RTL_TEXTENCODING_UTF8,
185 : (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
186 0 : RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
187 : {
188 0 : return false;
189 : }
190 0 : rArguments.append(prefix);
191 0 : for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
192 0 : char c = utf8[i];
193 0 : switch (c) {
194 : case '\0':
195 0 : rArguments.append("\\0");
196 0 : break;
197 : case ',':
198 0 : rArguments.append("\\,");
199 0 : break;
200 : case '\\':
201 0 : rArguments.append("\\\\");
202 0 : break;
203 : default:
204 0 : rArguments.append(c);
205 0 : break;
206 : }
207 : }
208 0 : return true;
209 : }
210 :
211 : }
212 :
213 0 : rtl::Reference< OfficeIPCThread > OfficeIPCThread::pGlobalOfficeIPCThread;
214 : namespace { struct Security : public rtl::Static<osl::Security, Security> {}; }
215 :
216 : // Turns a string in aMsg such as file:///home/foo/.libreoffice/3
217 : // Into a hex string of well known length ff132a86...
218 0 : String CreateMD5FromString( const OUString& aMsg )
219 : {
220 : #if (OSL_DEBUG_LEVEL > 2)
221 : fprintf( stderr, "create md5 from '%s'\n",
222 : rtl::OUStringToOString (aMsg, RTL_TEXTENCODING_UTF8).getStr() );
223 : #endif
224 :
225 0 : rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
226 0 : if ( handle )
227 : {
228 0 : const sal_uInt8* pData = (const sal_uInt8*)aMsg.getStr();
229 0 : sal_uInt32 nSize = ( aMsg.getLength() * sizeof( sal_Unicode ));
230 0 : sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle );
231 0 : sal_uInt8* pMD5KeyBuffer = new sal_uInt8[ nMD5KeyLen ];
232 :
233 0 : rtl_digest_init( handle, pData, nSize );
234 0 : rtl_digest_update( handle, pData, nSize );
235 0 : rtl_digest_get( handle, pMD5KeyBuffer, nMD5KeyLen );
236 0 : rtl_digest_destroy( handle );
237 :
238 : // Create hex-value string from the MD5 value to keep the string size minimal
239 0 : OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 );
240 0 : for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ )
241 0 : aBuffer.append( (sal_Int32)pMD5KeyBuffer[i], 16 );
242 :
243 0 : delete [] pMD5KeyBuffer;
244 0 : return aBuffer.makeStringAndClear();
245 : }
246 :
247 0 : return String();
248 : }
249 :
250 : class ProcessEventsClass_Impl
251 : {
252 : public:
253 : DECL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void* pEvent );
254 : DECL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void* pEvent );
255 : };
256 :
257 0 : IMPL_STATIC_LINK_NOINSTANCE( ProcessEventsClass_Impl, CallEvent, void*, pEvent )
258 : {
259 : // Application events are processed by the Desktop::HandleAppEvent implementation.
260 0 : Desktop::HandleAppEvent( *((ApplicationEvent*)pEvent) );
261 0 : delete (ApplicationEvent*)pEvent;
262 0 : return 0;
263 : }
264 :
265 0 : IMPL_STATIC_LINK_NOINSTANCE( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, pEvent )
266 : {
267 : // Documents requests are processed by the OfficeIPCThread implementation
268 0 : ProcessDocumentsRequest* pDocsRequest = (ProcessDocumentsRequest*)pEvent;
269 :
270 0 : if ( pDocsRequest )
271 : {
272 0 : OfficeIPCThread::ExecuteCmdLineRequests( *pDocsRequest );
273 0 : delete pDocsRequest;
274 : }
275 0 : return 0;
276 : }
277 :
278 0 : void ImplPostForeignAppEvent( ApplicationEvent* pEvent )
279 : {
280 0 : Application::PostUserEvent( STATIC_LINK( NULL, ProcessEventsClass_Impl, CallEvent ), pEvent );
281 0 : }
282 :
283 0 : void ImplPostProcessDocumentsEvent( ProcessDocumentsRequest* pEvent )
284 : {
285 0 : Application::PostUserEvent( STATIC_LINK( NULL, ProcessEventsClass_Impl, ProcessDocumentsEvent ), pEvent );
286 0 : }
287 :
288 0 : oslSignalAction SAL_CALL SalMainPipeExchangeSignal_impl(void* /*pData*/, oslSignalInfo* pInfo)
289 : {
290 0 : if( pInfo->Signal == osl_Signal_Terminate )
291 0 : OfficeIPCThread::DisableOfficeIPCThread(false);
292 0 : return osl_Signal_ActCallNextHdl;
293 : }
294 :
295 : // ----------------------------------------------------------------------------
296 :
297 : // The OfficeIPCThreadController implementation is a bookkeeper for all pending requests
298 : // that were created by the OfficeIPCThread. The requests are waiting to be processed by
299 : // our framework loadComponentFromURL function (e.g. open/print request).
300 : // During shutdown the framework is asking OfficeIPCThreadController about pending requests.
301 : // If there are pending requests framework has to stop the shutdown process. It is waiting
302 : // for these requests because framework is not able to handle shutdown and open a document
303 : // concurrently.
304 :
305 :
306 : // XServiceInfo
307 0 : OUString SAL_CALL OfficeIPCThreadController::getImplementationName()
308 : throw ( RuntimeException )
309 : {
310 0 : return OUString( "com.sun.star.comp.OfficeIPCThreadController" );
311 : }
312 :
313 0 : sal_Bool SAL_CALL OfficeIPCThreadController::supportsService( const OUString& )
314 : throw ( RuntimeException )
315 : {
316 0 : return sal_False;
317 : }
318 :
319 0 : Sequence< OUString > SAL_CALL OfficeIPCThreadController::getSupportedServiceNames()
320 : throw ( RuntimeException )
321 : {
322 0 : Sequence< OUString > aSeq( 0 );
323 0 : return aSeq;
324 : }
325 :
326 : // XEventListener
327 0 : void SAL_CALL OfficeIPCThreadController::disposing( const EventObject& )
328 : throw( RuntimeException )
329 : {
330 0 : }
331 :
332 : // XTerminateListener
333 0 : void SAL_CALL OfficeIPCThreadController::queryTermination( const EventObject& )
334 : throw( TerminationVetoException, RuntimeException )
335 : {
336 : // Desktop ask about pending request through our office ipc pipe. We have to
337 : // be sure that no pending request is waiting because framework is not able to
338 : // handle shutdown and open a document concurrently.
339 :
340 0 : if ( OfficeIPCThread::AreRequestsPending() )
341 0 : throw TerminationVetoException();
342 : else
343 0 : OfficeIPCThread::SetDowning();
344 0 : }
345 :
346 0 : void SAL_CALL OfficeIPCThreadController::notifyTermination( const EventObject& )
347 : throw( RuntimeException )
348 : {
349 0 : }
350 :
351 : namespace
352 : {
353 : class theOfficeIPCThreadMutex
354 : : public rtl::Static<osl::Mutex, theOfficeIPCThreadMutex> {};
355 : }
356 :
357 0 : ::osl::Mutex& OfficeIPCThread::GetMutex()
358 : {
359 0 : return theOfficeIPCThreadMutex::get();
360 : }
361 :
362 0 : void OfficeIPCThread::SetDowning()
363 : {
364 : // We have the order to block all incoming requests. Framework
365 : // wants to shutdown and we have to make sure that no loading/printing
366 : // requests are executed anymore.
367 0 : ::osl::MutexGuard aGuard( GetMutex() );
368 :
369 0 : if ( pGlobalOfficeIPCThread.is() )
370 0 : pGlobalOfficeIPCThread->mbDowning = true;
371 0 : }
372 :
373 : static bool s_bInEnableRequests = false;
374 :
375 0 : void OfficeIPCThread::EnableRequests( bool i_bEnable )
376 : {
377 : // switch between just queueing the requests and executing them
378 0 : ::osl::MutexGuard aGuard( GetMutex() );
379 :
380 0 : if ( pGlobalOfficeIPCThread.is() )
381 : {
382 0 : s_bInEnableRequests = true;
383 0 : pGlobalOfficeIPCThread->mbRequestsEnabled = i_bEnable;
384 0 : if( i_bEnable )
385 : {
386 : // hit the compiler over the head
387 0 : ProcessDocumentsRequest aEmptyReq = ProcessDocumentsRequest( boost::optional< rtl::OUString >() );
388 : // trigger already queued requests
389 0 : OfficeIPCThread::ExecuteCmdLineRequests( aEmptyReq );
390 : }
391 0 : s_bInEnableRequests = false;
392 0 : }
393 0 : }
394 :
395 0 : sal_Bool OfficeIPCThread::AreRequestsPending()
396 : {
397 : // Give info about pending requests
398 0 : ::osl::MutexGuard aGuard( GetMutex() );
399 0 : if ( pGlobalOfficeIPCThread.is() )
400 0 : return ( pGlobalOfficeIPCThread->mnPendingRequests > 0 );
401 : else
402 0 : return sal_False;
403 : }
404 :
405 0 : void OfficeIPCThread::RequestsCompleted( int nCount )
406 : {
407 : // Remove nCount pending requests from our internal counter
408 0 : ::osl::MutexGuard aGuard( GetMutex() );
409 0 : if ( pGlobalOfficeIPCThread.is() )
410 : {
411 0 : if ( pGlobalOfficeIPCThread->mnPendingRequests > 0 )
412 0 : pGlobalOfficeIPCThread->mnPendingRequests -= nCount;
413 0 : }
414 0 : }
415 :
416 0 : OfficeIPCThread::Status OfficeIPCThread::EnableOfficeIPCThread()
417 : {
418 0 : ::osl::MutexGuard aGuard( GetMutex() );
419 :
420 0 : if( pGlobalOfficeIPCThread.is() )
421 0 : return IPC_STATUS_OK;
422 :
423 0 : ::rtl::OUString aUserInstallPath;
424 0 : ::rtl::OUString aDummy;
425 :
426 0 : rtl::Reference< OfficeIPCThread > pThread(new OfficeIPCThread);
427 :
428 : // The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
429 : // this information from a unotools implementation.
430 0 : ::utl::Bootstrap::PathStatus aLocateResult = ::utl::Bootstrap::locateUserInstallation( aUserInstallPath );
431 0 : if ( aLocateResult == ::utl::Bootstrap::PATH_EXISTS || aLocateResult == ::utl::Bootstrap::PATH_VALID)
432 0 : aDummy = aUserInstallPath;
433 : else
434 : {
435 0 : return IPC_STATUS_BOOTSTRAP_ERROR;
436 : }
437 :
438 : // Try to determine if we are the first office or not! This should prevent multiple
439 : // access to the user directory !
440 : // First we try to create our pipe if this fails we try to connect. We have to do this
441 : // in a loop because the the other office can crash or shutdown between createPipe
442 : // and connectPipe!!
443 :
444 0 : OUString aIniName;
445 :
446 0 : osl_getExecutableFile( &aIniName.pData );
447 :
448 0 : sal_uInt32 lastIndex = aIniName.lastIndexOf('/');
449 0 : if ( lastIndex > 0 )
450 : {
451 0 : aIniName = aIniName.copy( 0, lastIndex+1 );
452 0 : aIniName += OUString( "perftune" );
453 : #if defined(WNT)
454 : aIniName += OUString( ".ini" );
455 : #else
456 0 : aIniName += OUString( "rc" );
457 : #endif
458 : }
459 :
460 0 : ::rtl::Bootstrap aPerfTuneIniFile( aIniName );
461 :
462 0 : OUString aDefault( "0" );
463 0 : OUString aPreloadData;
464 :
465 0 : aPerfTuneIniFile.getFrom( OUString( "FastPipeCommunication" ), aPreloadData, aDefault );
466 :
467 :
468 0 : OUString aUserInstallPathHashCode;
469 :
470 0 : if ( aPreloadData == "1" )
471 : {
472 : sal_Char szBuffer[32];
473 0 : sprintf( szBuffer, "%d", SUPD );
474 0 : aUserInstallPathHashCode = OUString( szBuffer, strlen(szBuffer), osl_getThreadTextEncoding() );
475 : }
476 : else
477 0 : aUserInstallPathHashCode = CreateMD5FromString( aDummy );
478 :
479 :
480 : // Check result to create a hash code from the user install path
481 0 : if ( aUserInstallPathHashCode.isEmpty() )
482 0 : return IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
483 :
484 0 : OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode );
485 :
486 0 : PipeMode nPipeMode = PIPEMODE_DONTKNOW;
487 0 : do
488 : {
489 0 : osl::Security &rSecurity = Security::get();
490 : // Try to create pipe
491 0 : if ( pThread->maPipe.create( aPipeIdent.getStr(), osl_Pipe_CREATE, rSecurity ))
492 : {
493 : // Pipe created
494 0 : nPipeMode = PIPEMODE_CREATED;
495 : }
496 0 : else if( pThread->maPipe.create( aPipeIdent.getStr(), osl_Pipe_OPEN, rSecurity )) // Creation not successfull, now we try to connect
497 : {
498 0 : osl::StreamPipe aStreamPipe(pThread->maPipe.getHandle());
499 : char pReceiveBuffer[sc_nCSASeqLength + 1];
500 0 : int nResult = 0;
501 0 : int nBytes = 0;
502 0 : int nBufSz = sc_nCSASeqLength + 1;
503 : // read byte per byte
504 0 : while ((nResult=aStreamPipe.recv( pReceiveBuffer+nBytes, nBufSz-nBytes))>0) {
505 0 : nBytes += nResult;
506 0 : if (pReceiveBuffer[nBytes-1]=='\0') {
507 0 : break;
508 : }
509 : }
510 0 : if (rtl::OString(sc_aSendArgumentsSequence).equals(pReceiveBuffer))
511 : {
512 : // Pipe connected to first office
513 0 : nPipeMode = PIPEMODE_CONNECTED;
514 : }
515 : else
516 : {
517 : // Pipe connection failed (other office exited or crashed)
518 : TimeValue tval;
519 0 : tval.Seconds = 0;
520 0 : tval.Nanosec = 500000000;
521 0 : salhelper::Thread::wait( tval );
522 0 : }
523 : }
524 : else
525 : {
526 0 : oslPipeError eReason = pThread->maPipe.getError();
527 0 : if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError))
528 0 : return IPC_STATUS_BOOTSTRAP_ERROR;
529 :
530 : // Wait for second office to be ready
531 : TimeValue aTimeValue;
532 0 : aTimeValue.Seconds = 0;
533 0 : aTimeValue.Nanosec = 10000000; // 10ms
534 0 : salhelper::Thread::wait( aTimeValue );
535 : }
536 :
537 : } while ( nPipeMode == PIPEMODE_DONTKNOW );
538 :
539 0 : if ( nPipeMode == PIPEMODE_CREATED )
540 : {
541 : // Seems we are the one and only, so start listening thread
542 0 : pGlobalOfficeIPCThread = pThread;
543 0 : pThread->launch();
544 : }
545 : else
546 : {
547 : // Seems another office is running. Pipe arguments to it and self terminate
548 0 : osl::StreamPipe aStreamPipe(pThread->maPipe.getHandle());
549 :
550 : rtl::OStringBuffer aArguments(RTL_CONSTASCII_STRINGPARAM(
551 0 : ARGUMENT_PREFIX));
552 0 : rtl::OUString cwdUrl;
553 0 : if (!(tools::getProcessWorkingDir(cwdUrl) &&
554 0 : addArgument(aArguments, '1', cwdUrl)))
555 : {
556 0 : aArguments.append('0');
557 : }
558 0 : sal_uInt32 nCount = rtl_getAppCommandArgCount();
559 0 : for( sal_uInt32 i=0; i < nCount; i++ )
560 : {
561 0 : rtl_getAppCommandArg( i, &aDummy.pData );
562 0 : if (!addArgument(aArguments, ',', aDummy)) {
563 0 : return IPC_STATUS_BOOTSTRAP_ERROR;
564 : }
565 : }
566 : // finally, write the string onto the pipe
567 0 : aStreamPipe.write(aArguments.getStr(), aArguments.getLength());
568 0 : aStreamPipe.write("\0", 1);
569 :
570 0 : rtl::OString aToken(sc_aConfirmationSequence);
571 0 : char *pReceiveBuffer = new char[aToken.getLength()+1];
572 0 : sal_Int32 n = aStreamPipe.read(pReceiveBuffer, aToken.getLength());
573 0 : pReceiveBuffer[n]='\0';
574 :
575 0 : bool bIsConfirmationSequence = aToken.equals(pReceiveBuffer);
576 0 : delete[] pReceiveBuffer;
577 :
578 0 : if (!bIsConfirmationSequence)
579 : {
580 : // something went wrong
581 0 : return IPC_STATUS_BOOTSTRAP_ERROR;
582 : }
583 :
584 0 : return IPC_STATUS_2ND_OFFICE;
585 : }
586 :
587 0 : return IPC_STATUS_OK;
588 : }
589 :
590 0 : void OfficeIPCThread::DisableOfficeIPCThread(bool join)
591 : {
592 0 : osl::ClearableMutexGuard aMutex( GetMutex() );
593 :
594 0 : if( pGlobalOfficeIPCThread.is() )
595 : {
596 : rtl::Reference< OfficeIPCThread > pOfficeIPCThread(
597 0 : pGlobalOfficeIPCThread);
598 0 : pGlobalOfficeIPCThread.clear();
599 :
600 0 : pOfficeIPCThread->mbDowning = true;
601 0 : pOfficeIPCThread->maPipe.close();
602 :
603 : // release mutex to avoid deadlocks
604 0 : aMutex.clear();
605 :
606 0 : OfficeIPCThread::SetReady(pOfficeIPCThread);
607 :
608 : // exit gracefully and join
609 0 : if (join)
610 : {
611 0 : pOfficeIPCThread->join();
612 0 : }
613 0 : }
614 0 : }
615 :
616 0 : OfficeIPCThread::OfficeIPCThread() :
617 : Thread( "OfficeIPCThread" ),
618 : mbDowning( false ),
619 : mbRequestsEnabled( false ),
620 : mnPendingRequests( 0 ),
621 0 : mpDispatchWatcher( 0 )
622 : {
623 0 : }
624 :
625 0 : OfficeIPCThread::~OfficeIPCThread()
626 : {
627 0 : ::osl::ClearableMutexGuard aGuard( GetMutex() );
628 :
629 0 : if ( mpDispatchWatcher )
630 0 : mpDispatchWatcher->release();
631 0 : maPipe.close();
632 0 : pGlobalOfficeIPCThread.clear();
633 0 : }
634 :
635 0 : void OfficeIPCThread::SetReady(
636 : rtl::Reference< OfficeIPCThread > const & pThread)
637 : {
638 : rtl::Reference< OfficeIPCThread > const & t(
639 0 : pThread.is() ? pThread : pGlobalOfficeIPCThread);
640 0 : if (t.is())
641 : {
642 0 : t->cReady.set();
643 : }
644 0 : }
645 :
646 0 : void OfficeIPCThread::execute()
647 : {
648 0 : do
649 : {
650 0 : osl::StreamPipe aStreamPipe;
651 0 : oslPipeError nError = maPipe.accept( aStreamPipe );
652 :
653 :
654 0 : if( nError == osl_Pipe_E_None )
655 : {
656 : // if we receive a request while the office is displaying some dialog or error during
657 : // bootstrap, that dialogs event loop might get events that are dispatched by this thread
658 : // we have to wait for cReady to be set by the real main loop.
659 : // only reqests that dont dispatch events may be processed before cReady is set.
660 0 : cReady.wait();
661 :
662 : // we might have decided to shutdown while we were sleeping
663 0 : if (!pGlobalOfficeIPCThread.is()) return;
664 :
665 : // only lock the mutex when processing starts, othewise we deadlock when the office goes
666 : // down during wait
667 0 : osl::ClearableMutexGuard aGuard( GetMutex() );
668 :
669 0 : if ( mbDowning )
670 : {
671 : break;
672 : }
673 :
674 : // notify client we're ready to process its args
675 0 : int nBytes = 0;
676 : int nResult;
677 0 : while (
678 0 : (nResult = aStreamPipe.send(sc_aSendArgumentsSequence+nBytes, sc_nCSASeqLength-nBytes))>0 &&
679 : ((nBytes += nResult) < sc_nCSASeqLength) ) ;
680 0 : aStreamPipe.write("\0", 1);
681 :
682 : // test byte by byte
683 0 : const int nBufSz = 2048;
684 : char pBuf[nBufSz];
685 0 : nBytes = 0;
686 0 : rtl::OStringBuffer aBuf;
687 : // read into pBuf until '\0' is read or read-error
688 0 : while ((nResult=aStreamPipe.recv( pBuf+nBytes, nBufSz-nBytes))>0) {
689 0 : nBytes += nResult;
690 0 : if (pBuf[nBytes-1]=='\0') {
691 0 : aBuf.append(pBuf);
692 0 : break;
693 : }
694 : }
695 : // don't close pipe ...
696 :
697 0 : rtl::OString aArguments = aBuf.makeStringAndClear();
698 :
699 : // Is this a lookup message from another application? if so, ignore
700 0 : if (aArguments.isEmpty())
701 0 : continue;
702 :
703 0 : std::auto_ptr< CommandLineArgs > aCmdLineArgs;
704 : try
705 : {
706 0 : Parser p(aArguments);
707 0 : aCmdLineArgs.reset( new CommandLineArgs( p ) );
708 : }
709 0 : catch ( const CommandLineArgs::Supplier::Exception & )
710 : {
711 : #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
712 : fprintf( stderr, "Error in received command line arguments\n" );
713 : #endif
714 0 : continue;
715 : }
716 :
717 0 : sal_Bool bDocRequestSent = sal_False;
718 :
719 0 : OUString aUnknown( aCmdLineArgs->GetUnknown() );
720 0 : if ( !aUnknown.isEmpty() || aCmdLineArgs->IsHelp() )
721 : {
722 : ApplicationEvent* pAppEvent =
723 0 : new ApplicationEvent(ApplicationEvent::TYPE_HELP, aUnknown);
724 0 : ImplPostForeignAppEvent( pAppEvent );
725 : }
726 0 : else if ( aCmdLineArgs->IsVersion() )
727 : {
728 : ApplicationEvent* pAppEvent =
729 0 : new ApplicationEvent(ApplicationEvent::TYPE_VERSION);
730 0 : ImplPostForeignAppEvent( pAppEvent );
731 : }
732 : else
733 : {
734 0 : const CommandLineArgs &rCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
735 :
736 0 : if ( aCmdLineArgs->IsQuickstart() )
737 : {
738 : // we have to use application event, because we have to start quickstart service in main thread!!
739 : ApplicationEvent* pAppEvent =
740 0 : new ApplicationEvent(ApplicationEvent::TYPE_QUICKSTART);
741 0 : ImplPostForeignAppEvent( pAppEvent );
742 : }
743 :
744 : // handle request for acceptor
745 : std::vector< rtl::OUString > const & accept = aCmdLineArgs->
746 0 : GetAccept();
747 0 : for (std::vector< rtl::OUString >::const_iterator i(accept.begin());
748 0 : i != accept.end(); ++i)
749 : {
750 : ApplicationEvent* pAppEvent = new ApplicationEvent(
751 0 : ApplicationEvent::TYPE_ACCEPT, *i);
752 0 : ImplPostForeignAppEvent( pAppEvent );
753 : }
754 : // handle acceptor removal
755 : std::vector< rtl::OUString > const & unaccept = aCmdLineArgs->
756 0 : GetUnaccept();
757 0 : for (std::vector< rtl::OUString >::const_iterator i(
758 0 : unaccept.begin());
759 0 : i != unaccept.end(); ++i)
760 : {
761 : ApplicationEvent* pAppEvent = new ApplicationEvent(
762 0 : ApplicationEvent::TYPE_UNACCEPT, *i);
763 0 : ImplPostForeignAppEvent( pAppEvent );
764 : }
765 :
766 : ProcessDocumentsRequest* pRequest = new ProcessDocumentsRequest(
767 0 : aCmdLineArgs->getCwdUrl());
768 0 : cProcessed.reset();
769 0 : pRequest->pcProcessed = &cProcessed;
770 :
771 : // Print requests are not dependent on the --invisible cmdline argument as they are
772 : // loaded with the "hidden" flag! So they are always checked.
773 0 : pRequest->aPrintList = aCmdLineArgs->GetPrintList();
774 0 : bDocRequestSent |= !pRequest->aPrintList.empty();
775 0 : pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
776 0 : pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
777 0 : bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
778 :
779 0 : if ( !rCurrentCmdLineArgs.IsInvisible() )
780 : {
781 : // Read cmdline args that can open/create documents. As they would open a window
782 : // they are only allowed if the "--invisible" is currently not used!
783 0 : pRequest->aOpenList = aCmdLineArgs->GetOpenList();
784 0 : bDocRequestSent |= !pRequest->aOpenList.empty();
785 0 : pRequest->aViewList = aCmdLineArgs->GetViewList();
786 0 : bDocRequestSent |= !pRequest->aViewList.empty();
787 0 : pRequest->aStartList = aCmdLineArgs->GetStartList();
788 0 : bDocRequestSent |= !pRequest->aStartList.empty();
789 0 : pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
790 0 : bDocRequestSent |= !pRequest->aForceOpenList.empty();
791 0 : pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
792 0 : bDocRequestSent |= !pRequest->aForceNewList.empty();
793 :
794 : // Special command line args to create an empty document for a given module
795 :
796 : // #i18338# (lo)
797 : // we only do this if no document was specified on the command line,
798 : // since this would be inconsistent with the the behaviour of
799 : // the first process, see OpenClients() (call to OpenDefault()) in app.cxx
800 0 : if ( aCmdLineArgs->HasModuleParam() && (!bDocRequestSent) )
801 : {
802 0 : SvtModuleOptions aOpt;
803 0 : SvtModuleOptions::EFactory eFactory = SvtModuleOptions::E_WRITER;
804 0 : if ( aCmdLineArgs->IsWriter() )
805 0 : eFactory = SvtModuleOptions::E_WRITER;
806 0 : else if ( aCmdLineArgs->IsCalc() )
807 0 : eFactory = SvtModuleOptions::E_CALC;
808 0 : else if ( aCmdLineArgs->IsDraw() )
809 0 : eFactory = SvtModuleOptions::E_DRAW;
810 0 : else if ( aCmdLineArgs->IsImpress() )
811 0 : eFactory = SvtModuleOptions::E_IMPRESS;
812 0 : else if ( aCmdLineArgs->IsBase() )
813 0 : eFactory = SvtModuleOptions::E_DATABASE;
814 0 : else if ( aCmdLineArgs->IsMath() )
815 0 : eFactory = SvtModuleOptions::E_MATH;
816 0 : else if ( aCmdLineArgs->IsGlobal() )
817 0 : eFactory = SvtModuleOptions::E_WRITERGLOBAL;
818 0 : else if ( aCmdLineArgs->IsWeb() )
819 0 : eFactory = SvtModuleOptions::E_WRITERWEB;
820 :
821 0 : if ( !pRequest->aOpenList.empty() )
822 0 : pRequest->aModule = aOpt.GetFactoryName( eFactory );
823 : else
824 0 : pRequest->aOpenList.push_back( aOpt.GetFactoryEmptyDocumentURL( eFactory ) );
825 0 : bDocRequestSent = sal_True;
826 : }
827 : }
828 :
829 0 : if ( !aCmdLineArgs->IsQuickstart() ) {
830 0 : sal_Bool bShowHelp = sal_False;
831 0 : rtl::OUStringBuffer aHelpURLBuffer;
832 0 : if (aCmdLineArgs->IsHelpWriter()) {
833 0 : bShowHelp = sal_True;
834 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://swriter/start");
835 0 : } else if (aCmdLineArgs->IsHelpCalc()) {
836 0 : bShowHelp = sal_True;
837 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://scalc/start");
838 0 : } else if (aCmdLineArgs->IsHelpDraw()) {
839 0 : bShowHelp = sal_True;
840 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://sdraw/start");
841 0 : } else if (aCmdLineArgs->IsHelpImpress()) {
842 0 : bShowHelp = sal_True;
843 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://simpress/start");
844 0 : } else if (aCmdLineArgs->IsHelpBase()) {
845 0 : bShowHelp = sal_True;
846 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://sdatabase/start");
847 0 : } else if (aCmdLineArgs->IsHelpBasic()) {
848 0 : bShowHelp = sal_True;
849 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://sbasic/start");
850 0 : } else if (aCmdLineArgs->IsHelpMath()) {
851 0 : bShowHelp = sal_True;
852 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://smath/start");
853 : }
854 0 : if (bShowHelp) {
855 0 : aHelpURLBuffer.appendAscii("?Language=");
856 0 : aHelpURLBuffer.append(utl::ConfigManager::getLocale());
857 : #if defined UNX
858 0 : aHelpURLBuffer.appendAscii("&System=UNX");
859 : #elif defined WNT
860 : aHelpURLBuffer.appendAscii("&System=WIN");
861 : #endif
862 : ApplicationEvent* pAppEvent = new ApplicationEvent(
863 : ApplicationEvent::TYPE_OPENHELPURL,
864 0 : aHelpURLBuffer.makeStringAndClear());
865 0 : ImplPostForeignAppEvent( pAppEvent );
866 0 : }
867 : }
868 :
869 0 : if ( bDocRequestSent )
870 : {
871 : // Send requests to dispatch watcher if we have at least one. The receiver
872 : // is responsible to delete the request after processing it.
873 0 : if ( aCmdLineArgs->HasModuleParam() )
874 : {
875 0 : SvtModuleOptions aOpt;
876 :
877 : // Support command line parameters to start a module (as preselection)
878 0 : if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SWRITER ) )
879 0 : pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::E_WRITER );
880 0 : else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SCALC ) )
881 0 : pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::E_CALC );
882 0 : else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SIMPRESS ) )
883 0 : pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::E_IMPRESS );
884 0 : else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SDRAW ) )
885 0 : pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::E_DRAW );
886 : }
887 :
888 0 : ImplPostProcessDocumentsEvent( pRequest );
889 : }
890 : else
891 : {
892 : // delete not used request again
893 0 : delete pRequest;
894 0 : pRequest = NULL;
895 : }
896 0 : if (aArguments.equalsL(sc_aShowSequence, sc_nShSeqLength) ||
897 0 : aCmdLineArgs->IsEmpty())
898 : {
899 : // no document was sent, just bring Office to front
900 : ApplicationEvent* pAppEvent =
901 0 : new ApplicationEvent(ApplicationEvent::TYPE_APPEAR);
902 0 : ImplPostForeignAppEvent( pAppEvent );
903 : }
904 : }
905 :
906 : // we don't need the mutex any longer...
907 0 : aGuard.clear();
908 : // wait for processing to finish
909 0 : if (bDocRequestSent)
910 0 : cProcessed.wait();
911 : // processing finished, inform the requesting end
912 0 : nBytes = 0;
913 0 : while (
914 0 : (nResult = aStreamPipe.send(sc_aConfirmationSequence+nBytes, sc_nCSeqLength-nBytes))>0 &&
915 0 : ((nBytes += nResult) < sc_nCSeqLength) ) ;
916 : }
917 : else
918 : {
919 : {
920 0 : osl::MutexGuard aGuard( GetMutex() );
921 0 : if ( mbDowning )
922 : {
923 : break;
924 0 : }
925 : }
926 :
927 : #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
928 : fprintf( stderr, "Error on accept: %d\n", (int)nError );
929 : #endif
930 : TimeValue tval;
931 0 : tval.Seconds = 1;
932 0 : tval.Nanosec = 0;
933 0 : salhelper::Thread::wait( tval );
934 0 : }
935 0 : } while( schedule() );
936 : }
937 :
938 0 : static void AddToDispatchList(
939 : DispatchWatcher::DispatchList& rDispatchList,
940 : boost::optional< rtl::OUString > const & cwdUrl,
941 : std::vector< rtl::OUString > const & aRequestList,
942 : DispatchWatcher::RequestType nType,
943 : const OUString& aParam,
944 : const OUString& aFactory )
945 : {
946 0 : for (std::vector< rtl::OUString >::const_iterator i(aRequestList.begin());
947 0 : i != aRequestList.end(); ++i)
948 : {
949 : rDispatchList.push_back(
950 0 : DispatchWatcher::DispatchRequest( nType, *i, cwdUrl, aParam, aFactory ));
951 : }
952 0 : }
953 :
954 0 : static void AddConversionsToDispatchList(
955 : DispatchWatcher::DispatchList& rDispatchList,
956 : boost::optional< rtl::OUString > const & cwdUrl,
957 : std::vector< rtl::OUString > const & rRequestList,
958 : const OUString& rParam,
959 : const OUString& rPrinterName,
960 : const OUString& rFactory,
961 : const OUString& rParamOut )
962 : {
963 : DispatchWatcher::RequestType nType;
964 0 : OUString aParam( rParam );
965 :
966 0 : if( !rParam.isEmpty() )
967 : {
968 0 : nType = DispatchWatcher::REQUEST_CONVERSION;
969 0 : aParam = rParam;
970 : }
971 : else
972 : {
973 0 : nType = DispatchWatcher::REQUEST_BATCHPRINT;
974 0 : aParam = rPrinterName;
975 : }
976 :
977 0 : OUString aOutDir( rParamOut.trim() );
978 0 : ::rtl::OUString aPWD;
979 0 : ::tools::getProcessWorkingDir( aPWD );
980 :
981 0 : if( !::osl::FileBase::getAbsoluteFileURL( aPWD, rParamOut, aOutDir ) )
982 0 : ::osl::FileBase::getSystemPathFromFileURL( aOutDir, aOutDir );
983 :
984 0 : if( !rParamOut.trim().isEmpty() )
985 : {
986 0 : aParam += ::rtl::OUString(";");
987 0 : aParam += aOutDir;
988 : }
989 : else
990 : {
991 0 : ::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD );
992 0 : aParam += ::rtl::OUString(";" ) + aPWD;
993 : }
994 :
995 0 : for (std::vector< rtl::OUString >::const_iterator i(rRequestList.begin());
996 0 : i != rRequestList.end(); ++i)
997 : {
998 : rDispatchList.push_back(
999 0 : DispatchWatcher::DispatchRequest( nType, *i, cwdUrl, aParam, rFactory ));
1000 0 : }
1001 0 : }
1002 :
1003 :
1004 0 : sal_Bool OfficeIPCThread::ExecuteCmdLineRequests( ProcessDocumentsRequest& aRequest )
1005 : {
1006 : // protect the dispatch list
1007 0 : osl::ClearableMutexGuard aGuard( GetMutex() );
1008 :
1009 0 : static DispatchWatcher::DispatchList aDispatchList;
1010 :
1011 0 : rtl::OUString aEmpty;
1012 : // Create dispatch list for dispatch watcher
1013 0 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aInFilter, DispatchWatcher::REQUEST_INFILTER, aEmpty, aRequest.aModule );
1014 0 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, aEmpty, aRequest.aModule );
1015 0 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, aEmpty, aRequest.aModule );
1016 0 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, aEmpty, aRequest.aModule );
1017 0 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, aEmpty, aRequest.aModule );
1018 0 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
1019 0 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, aEmpty, aRequest.aModule );
1020 0 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, aEmpty, aRequest.aModule );
1021 0 : AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut );
1022 0 : sal_Bool bShutdown( sal_False );
1023 :
1024 0 : if ( pGlobalOfficeIPCThread.is() )
1025 : {
1026 0 : if( ! pGlobalOfficeIPCThread->AreRequestsEnabled() )
1027 0 : return bShutdown;
1028 :
1029 0 : pGlobalOfficeIPCThread->mnPendingRequests += aDispatchList.size();
1030 0 : if ( !pGlobalOfficeIPCThread->mpDispatchWatcher )
1031 : {
1032 0 : pGlobalOfficeIPCThread->mpDispatchWatcher = DispatchWatcher::GetDispatchWatcher();
1033 0 : pGlobalOfficeIPCThread->mpDispatchWatcher->acquire();
1034 : }
1035 :
1036 : // copy for execute
1037 0 : DispatchWatcher::DispatchList aTempList( aDispatchList );
1038 0 : aDispatchList.clear();
1039 :
1040 0 : aGuard.clear();
1041 :
1042 : // Execute dispatch requests
1043 0 : bShutdown = pGlobalOfficeIPCThread->mpDispatchWatcher->executeDispatchRequests( aTempList, s_bInEnableRequests );
1044 :
1045 : // set processed flag
1046 0 : if (aRequest.pcProcessed != NULL)
1047 0 : aRequest.pcProcessed->set();
1048 : }
1049 :
1050 0 : return bShutdown;
1051 : }
1052 :
1053 0 : }
1054 :
1055 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|