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