LibreOffice Module desktop (master) 1
officeipcthread.cxx
Go to the documentation of this file.
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 <sal/config.h>
21
22#include <config_dbus.h>
23#include <config_features.h>
24#include <config_feature_desktop.h>
25
26#include <app.hxx>
27#include "officeipcthread.hxx"
28#include "cmdlineargs.hxx"
29#include "dispatchwatcher.hxx"
30#include <com/sun/star/frame/TerminationVetoException.hpp>
31#include <salhelper/thread.hxx>
32#include <sal/log.hxx>
34#include <utility>
35#include <vcl/svapp.hxx>
37#include <osl/pipe.hxx>
38#include <rtl/digest.h>
39#include <rtl/ustrbuf.hxx>
40#include <osl/conditn.hxx>
42#include <rtl/strbuf.hxx>
44#include <osl/file.hxx>
45#include <rtl/process.h>
46#include <o3tl/string_view.hxx>
47
48#include <cassert>
49#include <cstdlib>
50#include <memory>
51
52#if ENABLE_DBUS
53#include <dbus/dbus.h>
54#include <sys/socket.h>
55#endif
56
57using namespace desktop;
58using namespace ::com::sun::star::uno;
59using namespace ::com::sun::star::lang;
60using namespace ::com::sun::star::frame;
61
62namespace {
63
64char const ARGUMENT_PREFIX[] = "InternalIPC::Arguments";
65char const SEND_ARGUMENTS[] = "InternalIPC::SendArguments";
66char const PROCESSING_DONE[] = "InternalIPC::ProcessingDone";
67
68// Receives packets from the pipe until a packet ends in a NUL character (that
69// will not be included in the returned string) or it cannot read anything (due
70// to error or closed pipe, in which case an empty string will be returned to
71// signal failure):
72OString readStringFromPipe(osl::StreamPipe const & pipe) {
73 for (OStringBuffer str;;) {
74 char buf[1024];
75 sal_Int32 n = pipe.recv(buf, std::size(buf));
76 if (n <= 0) {
77 SAL_INFO("desktop.app", "read empty string");
78 return "";
79 }
80 bool end = false;
81 if (buf[n - 1] == '\0') {
82 end = true;
83 --n;
84 }
85 str.append(buf, n);
86 //TODO: how does OStringBuffer.append handle overflow?
87 if (end) {
88 auto s = str.makeStringAndClear();
89 SAL_INFO("desktop.app", "read <" << s << ">");
90 return s;
91 }
92 }
93}
94
95}
96
97namespace desktop
98{
99
100namespace {
101
102class Parser: public CommandLineArgs::Supplier {
103public:
104 explicit Parser(OString input): m_input(std::move(input)) {
105 if (!m_input.match(ARGUMENT_PREFIX) ||
106 m_input.getLength() == RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX))
107 {
109 }
110 m_index = RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX);
111 switch (m_input[m_index++]) {
112 case '0':
113 break;
114 case '1':
115 {
116 OUString url;
117 if (!next(&url, false)) {
119 }
120 m_cwdUrl = url;
121 break;
122 }
123 case '2':
124 {
125 OUString path;
126 if (!next(&path, false)) {
128 }
129 OUString url;
130 if (osl::FileBase::getFileURLFromSystemPath(path, url) ==
131 osl::FileBase::E_None)
132 {
133 m_cwdUrl = url;
134 }
135 break;
136 }
137 default:
139 }
140 }
141
142 virtual std::optional< OUString > getCwdUrl() override { return m_cwdUrl; }
143
144 virtual bool next(OUString * argument) override { return next(argument, true); }
145
146private:
147 bool next(OUString * argument, bool prefix) {
148 OSL_ASSERT(argument != nullptr);
149 if (m_index < m_input.getLength()) {
150 if (prefix) {
151 if (m_input[m_index] != ',') {
153 }
154 ++m_index;
155 }
156 OStringBuffer b;
157 while (m_index < m_input.getLength()) {
158 char c = m_input[m_index];
159 if (c == ',') {
160 break;
161 }
162 ++m_index;
163 if (c == '\\') {
164 if (m_index >= m_input.getLength())
166 c = m_input[m_index++];
167 switch (c) {
168 case '0':
169 c = '\0';
170 break;
171 case ',':
172 case '\\':
173 break;
174 default:
176 }
177 }
178 b.append(c);
179 }
180 OString b2(b.makeStringAndClear());
181 if (!rtl_convertStringToUString(
182 &argument->pData, b2.getStr(), b2.getLength(),
183 RTL_TEXTENCODING_UTF8,
184 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
185 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
186 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
187 {
189 }
190 return true;
191 } else {
192 return false;
193 }
194 }
195
196 std::optional< OUString > m_cwdUrl;
197 OString m_input;
198 sal_Int32 m_index;
199};
200
201bool addArgument(OStringBuffer &rArguments, char prefix,
202 const OUString &rArgument)
203{
204 OString utf8;
205 if (!rArgument.convertToString(
206 &utf8, RTL_TEXTENCODING_UTF8,
207 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
208 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
209 {
210 return false;
211 }
212 rArguments.append(prefix);
213 for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
214 char c = utf8[i];
215 switch (c) {
216 case '\0':
217 rArguments.append("\\0");
218 break;
219 case ',':
220 rArguments.append("\\,");
221 break;
222 case '\\':
223 rArguments.append("\\\\");
224 break;
225 default:
226 rArguments.append(c);
227 break;
228 }
229 }
230 return true;
231}
232
233}
234
236
237// Turns a string in aMsg such as file:///home/foo/.libreoffice/3
238// Into a hex string of well known length ff132a86...
239static OUString CreateMD5FromString( const OUString& aMsg )
240{
241 SAL_INFO("desktop.app", "create md5 from '" << aMsg << "'");
242
243 rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
244 if ( handle )
245 {
246 const sal_uInt8* pData = reinterpret_cast<const sal_uInt8*>(aMsg.getStr());
247 sal_uInt32 nSize = aMsg.getLength() * sizeof( sal_Unicode );
248 sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle );
249 std::unique_ptr<sal_uInt8[]> pMD5KeyBuffer(new sal_uInt8[ nMD5KeyLen ]);
250
251 rtl_digest_init( handle, pData, nSize );
252 rtl_digest_update( handle, pData, nSize );
253 rtl_digest_get( handle, pMD5KeyBuffer.get(), nMD5KeyLen );
254 rtl_digest_destroy( handle );
255
256 // Create hex-value string from the MD5 value to keep the string size minimal
257 OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 );
258 for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ )
259 aBuffer.append( static_cast<sal_Int32>(pMD5KeyBuffer[i]), 16 );
260
261 return aBuffer.makeStringAndClear();
262 }
263
264 return OUString();
265}
266
267namespace {
268
269class ProcessEventsClass_Impl
270{
271public:
272 DECL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, void );
273 DECL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, void );
274};
275
276}
277
278IMPL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, pEvent, void )
279{
280 // Application events are processed by the Desktop::HandleAppEvent implementation.
281 Desktop::HandleAppEvent( *static_cast<ApplicationEvent*>(pEvent) );
282 delete static_cast<ApplicationEvent*>(pEvent);
283}
284
285IMPL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, pEvent, void )
286{
287 // Documents requests are processed by the RequestHandler implementation
288 ProcessDocumentsRequest* pDocsRequest = static_cast<ProcessDocumentsRequest*>(pEvent);
289 RequestHandler::ExecuteCmdLineRequests(*pDocsRequest, false);
290 delete pDocsRequest;
291}
292
294{
295 Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, CallEvent ), pEvent );
296}
297
298static void ImplPostProcessDocumentsEvent( std::unique_ptr<ProcessDocumentsRequest> pEvent )
299{
300 Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, ProcessDocumentsEvent ), pEvent.release() );
301}
302
303oslSignalAction SalMainPipeExchangeSignal_impl(SAL_UNUSED_PARAMETER void* /*pData*/, oslSignalInfo* pInfo)
304{
305 if( pInfo->Signal == osl_Signal_Terminate )
307 return osl_Signal_ActCallNextHdl;
308}
309
310
311// The RequestHandlerController implementation is a bookkeeper for all pending requests
312// that were created by the RequestHandler. The requests are waiting to be processed by
313// our framework loadComponentFromURL function (e.g. open/print request).
314// During shutdown the framework is asking RequestHandlerController about pending requests.
315// If there are pending requests framework has to stop the shutdown process. It is waiting
316// for these requests because framework is not able to handle shutdown and open a document
317// concurrently.
318
319
320// XServiceInfo
322{
323 return "com.sun.star.comp.RequestHandlerController";
324}
325
327 OUString const & ServiceName)
328{
330}
331
333{
334 return { };
335}
336
337// XEventListener
338void SAL_CALL RequestHandlerController::disposing( const EventObject& )
339{
340}
341
342// XTerminateListener
343void SAL_CALL RequestHandlerController::queryTermination( const EventObject& )
344{
345 // Desktop ask about pending request through our office ipc pipe. We have to
346 // be sure that no pending request is waiting because framework is not able to
347 // handle shutdown and open a document concurrently.
348
350 throw TerminationVetoException();
352}
353
354void SAL_CALL RequestHandlerController::notifyTermination( const EventObject& )
355{
356}
357
359public:
360 void start(RequestHandler * handler) {
361 m_handler = handler;
362 launch();
363 }
364
365 virtual void close() = 0;
366
367protected:
368 explicit IpcThread(char const * name): Thread(name), m_handler(nullptr) {}
369
370 virtual ~IpcThread() override {}
371
372 bool process(OString const & arguments, bool * waitProcessed);
373
375};
376
378public:
380
381private:
382 explicit PipeIpcThread(osl::Pipe pipe):
383 IpcThread("PipeIPC"), pipe_(std::move(pipe))
384 {}
385
386 virtual ~PipeIpcThread() override {}
387
388 void execute() override;
389
390 void close() override { pipe_.close(); }
391
392 osl::Pipe pipe_;
393};
394
395#if ENABLE_DBUS
396
397namespace {
398
399struct DbusConnectionHolder {
400 explicit DbusConnectionHolder(DBusConnection * theConnection):
401 connection(theConnection)
402 {}
403
404 DbusConnectionHolder(DbusConnectionHolder && other): connection(nullptr)
405 { std::swap(connection, other.connection); }
406
407 ~DbusConnectionHolder() {
408 if (connection != nullptr) {
409 dbus_connection_close(connection);
410 dbus_connection_unref(connection);
411 }
412 }
413
414 DBusConnection * connection;
415};
416
417struct DbusMessageHolder {
418 explicit DbusMessageHolder(DBusMessage * theMessage): message(theMessage) {}
419
420 ~DbusMessageHolder() { clear(); }
421
422 void clear() {
423 if (message != nullptr) {
424 dbus_message_unref(message);
425 }
426 message = nullptr;
427 }
428
429 DBusMessage * message;
430
431private:
432 DbusMessageHolder(DbusMessageHolder const &) = delete;
433 DbusMessageHolder& operator =(DbusMessageHolder const &) = delete;
434};
435
436}
437
438class DbusIpcThread: public IpcThread {
439public:
441
442private:
443 explicit DbusIpcThread(DbusConnectionHolder && connection):
444 IpcThread("DbusIPC"), connection_(std::move(connection))
445 {}
446
447 virtual ~DbusIpcThread() override {}
448
449 void execute() override;
450
451 void close() override;
452
453 DbusConnectionHolder connection_;
454};
455
456RequestHandler::Status DbusIpcThread::enable(rtl::Reference<IpcThread> * thread)
457{
458 assert(thread != nullptr);
459 if (!dbus_threads_init_default()) {
460 SAL_WARN("desktop.app", "dbus_threads_init_default failed");
462 }
463 DBusError e;
464 dbus_error_init(&e);
465 DbusConnectionHolder con(dbus_bus_get_private(DBUS_BUS_SESSION, &e));
466 assert((con.connection == nullptr) == bool(dbus_error_is_set(&e)));
467 if (con.connection == nullptr) {
468 SAL_WARN(
469 "desktop.app",
470 "dbus_bus_get_private failed with: " << e.name << ": "
471 << e.message);
472 dbus_error_free(&e);
474 }
475 for (;;) {
476 int n = dbus_bus_request_name(
477 con.connection, "org.libreoffice.LibreOfficeIpc0",
478 DBUS_NAME_FLAG_DO_NOT_QUEUE, &e);
479 assert((n == -1) == bool(dbus_error_is_set(&e)));
480 switch (n) {
481 case -1:
482 SAL_WARN(
483 "desktop.app",
484 "dbus_bus_request_name failed with: " << e.name << ": "
485 << e.message);
486 dbus_error_free(&e);
488 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
489 *thread = new DbusIpcThread(std::move(con));
491 case DBUS_REQUEST_NAME_REPLY_EXISTS:
492 {
493 OStringBuffer buf(ARGUMENT_PREFIX);
494 OUString arg;
496 && addArgument(buf, '1', arg)))
497 {
498 buf.append('0');
499 }
500 sal_uInt32 narg = rtl_getAppCommandArgCount();
501 for (sal_uInt32 i = 0; i != narg; ++i) {
502 rtl_getAppCommandArg(i, &arg.pData);
503 if (!addArgument(buf, ',', arg)) {
505 }
506 }
507 char const * argstr = buf.getStr();
508 DbusMessageHolder msg(
509 dbus_message_new_method_call(
510 "org.libreoffice.LibreOfficeIpc0",
511 "/org/libreoffice/LibreOfficeIpc0",
512 "org.libreoffice.LibreOfficeIpcIfc0", "Execute"));
513 if (msg.message == nullptr) {
514 SAL_WARN(
515 "desktop.app", "dbus_message_new_method_call failed");
517 }
518 DBusMessageIter it;
519 dbus_message_iter_init_append(msg.message, &it);
520 if (!dbus_message_iter_append_basic(
521 &it, DBUS_TYPE_STRING, &argstr))
522 {
523 SAL_WARN(
524 "desktop.app", "dbus_message_iter_append_basic failed");
526 }
527 DbusMessageHolder repl(
528 dbus_connection_send_with_reply_and_block(
529 con.connection, msg.message, 0x7FFFFFFF, &e));
530 assert(
531 (repl.message == nullptr) == bool(dbus_error_is_set(&e)));
532 if (repl.message == nullptr) {
533 SAL_INFO(
534 "desktop.app",
535 "dbus_connection_send_with_reply_and_block failed"
536 " with: " << e.name << ": " << e.message);
537 dbus_error_free(&e);
538 break;
539 }
541 }
542 case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
543 case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
544 SAL_WARN(
545 "desktop.app",
546 "dbus_bus_request_name failed with unexpected " << +n);
548 default:
549 for (;;) std::abort();
550 }
551 }
552}
553
554void DbusIpcThread::execute()
555{
556 assert(m_handler != nullptr);
557 m_handler->cReady.wait();
558 for (;;) {
559 {
560 osl::MutexGuard g(RequestHandler::GetMutex());
561 if (m_handler->mState == RequestHandler::State::Downing) {
562 break;
563 }
564 }
565 if (!dbus_connection_read_write(connection_.connection, -1)) {
566 break;
567 }
568 for (;;) {
569 DbusMessageHolder msg(
570 dbus_connection_pop_message(connection_.connection));
571 if (msg.message == nullptr) {
572 break;
573 }
574 if (!dbus_message_is_method_call(
575 msg.message, "org.libreoffice.LibreOfficeIpcIfc0",
576 "Execute"))
577 {
578 SAL_INFO("desktop.app", "unknown DBus message ignored");
579 continue;
580 }
581 DBusMessageIter it;
582 if (!dbus_message_iter_init(msg.message, &it)) {
583 SAL_WARN(
584 "desktop.app", "DBus message without argument ignored");
585 continue;
586 }
587 if (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_STRING) {
588 SAL_WARN(
589 "desktop.app",
590 "DBus message with non-string argument ignored");
591 continue;
592 }
593 char const * argstr;
594 dbus_message_iter_get_basic(&it, &argstr);
595 bool waitProcessed = false;
596 {
597 osl::MutexGuard g(RequestHandler::GetMutex());
598 if (!process(argstr, &waitProcessed)) {
599 continue;
600 }
601 }
602 if (waitProcessed) {
603 m_handler->cProcessed.wait();
604 }
605 DbusMessageHolder repl(dbus_message_new_method_return(msg.message));
606 if (repl.message == nullptr) {
607 SAL_WARN(
608 "desktop.app", "dbus_message_new_method_return failed");
609 continue;
610 }
611 dbus_uint32_t serial = 0;
612 if (!dbus_connection_send(
613 connection_.connection, repl.message, &serial)) {
614 SAL_WARN("desktop.app", "dbus_connection_send failed");
615 continue;
616 }
617 dbus_connection_flush(connection_.connection);
618 }
619 }
620}
621
622void DbusIpcThread::close() {
623 assert(connection_.connection != nullptr);
624 // Make dbus_connection_read_write fall out of internal poll call blocking
625 // on POLLIN:
626 int fd;
627 if (!dbus_connection_get_socket(connection_.connection, &fd)) {
628 SAL_WARN("desktop.app", "dbus_connection_get_socket failed");
629 return;
630 }
631 if (shutdown(fd, SHUT_RD) == -1) {
632 auto const e = errno;
633 SAL_WARN("desktop.app", "shutdown failed with errno " << e);
634 }
635}
636
637#endif
638
640{
641 static ::osl::Mutex theRequestHandlerMutex;
642 return theRequestHandlerMutex;
643}
644
646{
647 // We have the order to block all incoming requests. Framework
648 // wants to shutdown and we have to make sure that no loading/printing
649 // requests are executed anymore.
650 ::osl::MutexGuard aGuard( GetMutex() );
651
652 if ( pGlobal.is() )
653 pGlobal->mState = State::Downing;
654}
655
657{
658 // switch between just queueing the requests and executing them
659 ::osl::MutexGuard aGuard( GetMutex() );
660
661 if ( pGlobal.is() )
662 {
663 if (pGlobal->mState != State::Downing) {
665 }
666 ProcessDocumentsRequest aEmptyReq(std::nullopt);
667 // trigger already queued requests
669 }
670}
671
673{
674 // Give info about pending requests
675 ::osl::MutexGuard aGuard( GetMutex() );
676 if ( pGlobal.is() )
677 return ( pGlobal->mnPendingRequests > 0 );
678 else
679 return false;
680}
681
683{
684 // Remove nCount pending requests from our internal counter
685 ::osl::MutexGuard aGuard( GetMutex() );
686 if ( pGlobal.is() )
687 {
688 if ( pGlobal->mnPendingRequests > 0 )
689 pGlobal->mnPendingRequests --;
690 }
691}
692
694{
695 ::osl::MutexGuard aGuard( GetMutex() );
696
697 if( pGlobal.is() )
698 return IPC_STATUS_OK;
699
700#if !HAVE_FEATURE_DESKTOP || HAVE_FEATURE_MACOSX_SANDBOX || defined(EMSCRIPTEN)
701 ipc = false;
702#endif
703
704 if (!ipc) {
706 return IPC_STATUS_OK;
707 }
708
709 enum class Kind { Pipe, Dbus };
710 Kind kind;
711#if ENABLE_DBUS
712 kind = std::getenv("LIBO_FLATPAK") != nullptr ? Kind::Dbus : Kind::Pipe;
713#else
714 kind = Kind::Pipe;
715#endif
717 Status stat = Status(); // silence bogus potentially-uninitialized warnings
718 switch (kind) {
719 case Kind::Pipe:
720 stat = PipeIpcThread::enable(&thread);
721 break;
722 case Kind::Dbus:
723#if ENABLE_DBUS
724 stat = DbusIpcThread::enable(&thread);
725 break;
726#endif
727 default:
728 assert(false);
729 }
730 assert(thread.is() == (stat == IPC_STATUS_OK));
731 if (stat == IPC_STATUS_OK) {
733 pGlobal->mIpcThread = thread;
734 pGlobal->mIpcThread->start(pGlobal.get());
735 }
736 return stat;
737}
738
740{
741 assert(thread != nullptr);
742
743 // The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
744 // this information from a unotools implementation.
745 OUString aUserInstallPath;
747 if (aLocateResult != utl::Bootstrap::PATH_EXISTS
748 && aLocateResult != utl::Bootstrap::PATH_VALID)
749 {
751 }
752
753 // Try to determine if we are the first office or not! This should prevent multiple
754 // access to the user directory !
755 // First we try to create our pipe if this fails we try to connect. We have to do this
756 // in a loop because the other office can crash or shutdown between createPipe
757 // and connectPipe!!
758 auto aUserInstallPathHashCode = CreateMD5FromString(aUserInstallPath);
759
760 // Check result to create a hash code from the user install path
761 if ( aUserInstallPathHashCode.isEmpty() )
762 return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
763
764 osl::Pipe pipe;
765 enum PipeMode
766 {
767 PIPEMODE_DONTKNOW,
768 PIPEMODE_CREATED,
769 PIPEMODE_CONNECTED
770 };
771 PipeMode nPipeMode = PIPEMODE_DONTKNOW;
772
773 OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode );
774 do
775 {
776 osl::Security security;
777
778 // Try to create pipe
779 if ( pipe.create( aPipeIdent, osl_Pipe_CREATE, security ))
780 {
781 // Pipe created
782 nPipeMode = PIPEMODE_CREATED;
783 }
784 else if( pipe.create( aPipeIdent, osl_Pipe_OPEN, security )) // Creation not successful, now we try to connect
785 {
786 osl::StreamPipe aStreamPipe(pipe.getHandle());
787 if (readStringFromPipe(aStreamPipe) == SEND_ARGUMENTS)
788 {
789 // Pipe connected to first office
790 nPipeMode = PIPEMODE_CONNECTED;
791 }
792 else
793 {
794 // Pipe connection failed (other office exited or crashed)
795 TimeValue tval;
796 tval.Seconds = 0;
797 tval.Nanosec = 500000000;
798 salhelper::Thread::wait( tval );
799 }
800 }
801 else
802 {
803 oslPipeError eReason = pipe.getError();
804 if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError))
806
807 // Wait for second office to be ready
808 TimeValue aTimeValue;
809 aTimeValue.Seconds = 0;
810 aTimeValue.Nanosec = 10000000; // 10ms
811 salhelper::Thread::wait( aTimeValue );
812 }
813
814 } while ( nPipeMode == PIPEMODE_DONTKNOW );
815
816 if ( nPipeMode == PIPEMODE_CREATED )
817 {
818 // Seems we are the one and only, so create listening thread
819 *thread = new PipeIpcThread(pipe);
821 }
822 else
823 {
824 // Seems another office is running. Pipe arguments to it and self terminate
825 osl::StreamPipe aStreamPipe(pipe.getHandle());
826
827 OStringBuffer aArguments(ARGUMENT_PREFIX);
828 OUString cwdUrl;
830 addArgument(aArguments, '1', cwdUrl)))
831 {
832 aArguments.append('0');
833 }
834 sal_uInt32 nCount = rtl_getAppCommandArgCount();
835 for( sal_uInt32 i=0; i < nCount; i++ )
836 {
837 rtl_getAppCommandArg( i, &aUserInstallPath.pData );
838 if (!addArgument(aArguments, ',', aUserInstallPath)) {
840 }
841 }
842 aArguments.append('\0');
843 // finally, write the string onto the pipe
844 SAL_INFO("desktop.app", "writing <" << aArguments.getStr() << ">");
845 sal_Int32 n = aStreamPipe.write(
846 aArguments.getStr(), aArguments.getLength());
847 if (n != aArguments.getLength()) {
848 SAL_INFO("desktop.app", "short write: " << n);
850 }
851
852 if (readStringFromPipe(aStreamPipe) != PROCESSING_DONE)
853 {
854 // something went wrong
856 }
857
859 }
860}
861
863{
864 osl::ClearableMutexGuard aMutex( GetMutex() );
865
866 if( !pGlobal.is() )
867 return;
868
870 pGlobal.clear();
871
872 handler->mState = State::Downing;
873 if (handler->mIpcThread.is()) {
874 handler->mIpcThread->close();
875 }
876
877 // release mutex to avoid deadlocks
878 aMutex.clear();
879
880 handler->cReady.set();
881
882 // exit gracefully and join
883 if (handler->mIpcThread.is())
884 {
885 handler->mIpcThread->join();
886 handler->mIpcThread.clear();
887 }
888
889 handler->cReady.reset();
890}
891
893 mState( State::Starting ),
894 mnPendingRequests( 0 )
895{
896}
897
899{
900 assert(!mIpcThread.is());
901}
902
903void RequestHandler::SetReady(bool bIsReady)
904{
905 osl::MutexGuard g(GetMutex());
906 if (pGlobal.is())
907 {
908 if (bIsReady)
909 pGlobal->cReady.set();
910 else
911 pGlobal->cReady.reset();
912 }
913}
914
916{
918 {
919 osl::MutexGuard g(GetMutex());
920 t = pGlobal;
921 }
922 if (t.is())
923 {
924 t->cReady.wait();
925 }
926}
927
928bool IpcThread::process(OString const & arguments, bool * waitProcessed) {
929 assert(waitProcessed != nullptr);
930
931 std::unique_ptr< CommandLineArgs > aCmdLineArgs;
932 try
933 {
934 Parser p(arguments);
935 aCmdLineArgs.reset( new CommandLineArgs( p ) );
936 }
938 {
939 SAL_WARN("desktop.app", "Error in received command line arguments");
940 return false;
941 }
942
943 bool bDocRequestSent = false;
944
945 OUString aUnknown( aCmdLineArgs->GetUnknown() );
946 if (aUnknown.isEmpty() && !aCmdLineArgs->IsHelp() && !aCmdLineArgs->IsVersion())
947 {
948 const CommandLineArgs &rCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
949
950 if ( aCmdLineArgs->IsQuickstart() )
951 {
952 // we have to use application event, because we have to start quickstart service in main thread!!
953 ApplicationEvent* pAppEvent =
955 ImplPostForeignAppEvent( pAppEvent );
956 }
957
958 // handle request for acceptor
959 std::vector< OUString > const & accept = aCmdLineArgs->GetAccept();
960 for (auto const& elem : accept)
961 {
962 ApplicationEvent* pAppEvent = new ApplicationEvent(
964 ImplPostForeignAppEvent( pAppEvent );
965 }
966 // handle acceptor removal
967 std::vector< OUString > const & unaccept = aCmdLineArgs->GetUnaccept();
968 for (auto const& elem : unaccept)
969 {
970 ApplicationEvent* pAppEvent = new ApplicationEvent(
972 ImplPostForeignAppEvent( pAppEvent );
973 }
974
975 std::unique_ptr<ProcessDocumentsRequest> pRequest(new ProcessDocumentsRequest(
976 aCmdLineArgs->getCwdUrl()));
977 m_handler->cProcessed.reset();
978 pRequest->pcProcessed = &m_handler->cProcessed;
979 m_handler->mbSuccess = false;
980 pRequest->mpbSuccess = &m_handler->mbSuccess;
981
982 // Print requests are not dependent on the --invisible cmdline argument as they are
983 // loaded with the "hidden" flag! So they are always checked.
984 pRequest->aPrintList = aCmdLineArgs->GetPrintList();
985 bDocRequestSent |= !pRequest->aPrintList.empty();
986 pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
987 pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
988 bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
989 pRequest->aConversionList = aCmdLineArgs->GetConversionList();
990 pRequest->aConversionParams = aCmdLineArgs->GetConversionParams();
991 pRequest->aConversionOut = aCmdLineArgs->GetConversionOut();
992 pRequest->aImageConversionType = aCmdLineArgs->GetImageConversionType();
993 pRequest->aInFilter = aCmdLineArgs->GetInFilter();
994 pRequest->bTextCat = aCmdLineArgs->IsTextCat();
995 pRequest->bScriptCat = aCmdLineArgs->IsScriptCat();
996 bDocRequestSent |= !pRequest->aConversionList.empty();
997
998 if ( !rCurrentCmdLineArgs.IsInvisible() )
999 {
1000 // Read cmdline args that can open/create documents. As they would open a window
1001 // they are only allowed if the "--invisible" is currently not used!
1002 pRequest->aOpenList = aCmdLineArgs->GetOpenList();
1003 bDocRequestSent |= !pRequest->aOpenList.empty();
1004 pRequest->aViewList = aCmdLineArgs->GetViewList();
1005 bDocRequestSent |= !pRequest->aViewList.empty();
1006 pRequest->aStartList = aCmdLineArgs->GetStartList();
1007 bDocRequestSent |= !pRequest->aStartList.empty();
1008 pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
1009 bDocRequestSent |= !pRequest->aForceOpenList.empty();
1010 pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
1011 bDocRequestSent |= !pRequest->aForceNewList.empty();
1012
1013 // Special command line args to create an empty document for a given module
1014
1015 // #i18338# (lo)
1016 // we only do this if no document was specified on the command line,
1017 // since this would be inconsistent with the behaviour of
1018 // the first process, see OpenClients() (call to OpenDefault()) in app.cxx
1019 if ( aCmdLineArgs->HasModuleParam() && !bDocRequestSent )
1020 {
1021 SvtModuleOptions aOpt;
1023 if ( aCmdLineArgs->IsWriter() )
1025 else if ( aCmdLineArgs->IsCalc() )
1027 else if ( aCmdLineArgs->IsDraw() )
1029 else if ( aCmdLineArgs->IsImpress() )
1031 else if ( aCmdLineArgs->IsBase() )
1033 else if ( aCmdLineArgs->IsMath() )
1035 else if ( aCmdLineArgs->IsGlobal() )
1037 else if ( aCmdLineArgs->IsWeb() )
1039
1040 if ( !pRequest->aOpenList.empty() )
1041 pRequest->aModule = aOpt.GetFactoryName( eFactory );
1042 else
1043 pRequest->aOpenList.push_back( aOpt.GetFactoryEmptyDocumentURL( eFactory ) );
1044 bDocRequestSent = true;
1045 }
1046 }
1047
1048 if ( !aCmdLineArgs->IsQuickstart() ) {
1049 bool bShowHelp = false;
1050 OUStringBuffer aHelpURLBuffer;
1051 if (aCmdLineArgs->IsHelpWriter()) {
1052 bShowHelp = true;
1053 aHelpURLBuffer.append("vnd.sun.star.help://swriter/start");
1054 } else if (aCmdLineArgs->IsHelpCalc()) {
1055 bShowHelp = true;
1056 aHelpURLBuffer.append("vnd.sun.star.help://scalc/start");
1057 } else if (aCmdLineArgs->IsHelpDraw()) {
1058 bShowHelp = true;
1059 aHelpURLBuffer.append("vnd.sun.star.help://sdraw/start");
1060 } else if (aCmdLineArgs->IsHelpImpress()) {
1061 bShowHelp = true;
1062 aHelpURLBuffer.append("vnd.sun.star.help://simpress/start");
1063 } else if (aCmdLineArgs->IsHelpBase()) {
1064 bShowHelp = true;
1065 aHelpURLBuffer.append("vnd.sun.star.help://sdatabase/start");
1066 } else if (aCmdLineArgs->IsHelpBasic()) {
1067 bShowHelp = true;
1068 aHelpURLBuffer.append("vnd.sun.star.help://sbasic/start");
1069 } else if (aCmdLineArgs->IsHelpMath()) {
1070 bShowHelp = true;
1071 aHelpURLBuffer.append("vnd.sun.star.help://smath/start");
1072 }
1073 if (bShowHelp) {
1074 aHelpURLBuffer.append("?Language=");
1075 aHelpURLBuffer.append(utl::ConfigManager::getUILocale());
1076#if defined UNX
1077 aHelpURLBuffer.append("&System=UNX");
1078#elif defined _WIN32
1079 aHelpURLBuffer.append("&System=WIN");
1080#endif
1081 ApplicationEvent* pAppEvent = new ApplicationEvent(
1083 aHelpURLBuffer.makeStringAndClear());
1084 ImplPostForeignAppEvent( pAppEvent );
1085 }
1086 }
1087
1088 if ( bDocRequestSent )
1089 {
1090 // Send requests to dispatch watcher if we have at least one. The receiver
1091 // is responsible to delete the request after processing it.
1092 if ( aCmdLineArgs->HasModuleParam() )
1093 {
1094 SvtModuleOptions aOpt;
1095
1096 // Support command line parameters to start a module (as preselection)
1097 if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
1098 pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
1099 else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
1100 pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
1101 else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
1102 pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
1103 else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
1104 pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
1105 }
1106
1107 ImplPostProcessDocumentsEvent( std::move(pRequest) );
1108 }
1109 else
1110 {
1111 // delete not used request again
1112 pRequest.reset();
1113 }
1114 if (aCmdLineArgs->IsEmpty())
1115 {
1116 // no document was sent, just bring Office to front
1117 ApplicationEvent* pAppEvent =
1119 ImplPostForeignAppEvent( pAppEvent );
1120 }
1121 }
1122 *waitProcessed = bDocRequestSent;
1123 return true;
1124}
1125
1127{
1128 assert(m_handler != nullptr);
1129 do
1130 {
1131 osl::StreamPipe aStreamPipe;
1132 oslPipeError nError = pipe_.accept( aStreamPipe );
1133
1134
1135 if( nError == osl_Pipe_E_None )
1136 {
1137 // if we receive a request while the office is displaying some dialog or error during
1138 // bootstrap, that dialogs event loop might get events that are dispatched by this thread
1139 // we have to wait for cReady to be set by the real main loop.
1140 // only requests that don't dispatch events may be processed before cReady is set.
1141 m_handler->cReady.wait();
1142
1143 // we might have decided to shutdown while we were sleeping
1144 if (!RequestHandler::pGlobal.is()) return;
1145
1146 // only lock the mutex when processing starts, otherwise we deadlock when the office goes
1147 // down during wait
1148 osl::ClearableMutexGuard aGuard( RequestHandler::GetMutex() );
1149
1151 {
1152 break;
1153 }
1154
1155 // notify client we're ready to process its args:
1156 SAL_INFO("desktop.app", "writing <" << SEND_ARGUMENTS << ">");
1157 std::size_t n = aStreamPipe.write(
1158 SEND_ARGUMENTS, std::size(SEND_ARGUMENTS));
1159 // incl. terminating NUL
1160 if (n != std::size(SEND_ARGUMENTS)) {
1161 SAL_WARN("desktop.app", "short write: " << n);
1162 continue;
1163 }
1164
1165 OString aArguments = readStringFromPipe(aStreamPipe);
1166
1167 // Is this a lookup message from another application? if so, ignore
1168 if (aArguments.isEmpty())
1169 continue;
1170
1171 bool waitProcessed = false;
1172 if (!process(aArguments, &waitProcessed)) {
1173 continue;
1174 }
1175
1176 // we don't need the mutex any longer...
1177 aGuard.clear();
1178 bool bSuccess = true;
1179 // wait for processing to finish
1180 if (waitProcessed)
1181 {
1182 m_handler->cProcessed.wait();
1183 bSuccess = m_handler->mbSuccess;
1184 }
1185 if (bSuccess)
1186 {
1187 // processing finished, inform the requesting end:
1188 SAL_INFO("desktop.app", "writing <" << PROCESSING_DONE << ">");
1189 n = aStreamPipe.write(PROCESSING_DONE, std::size(PROCESSING_DONE));
1190 // incl. terminating NUL
1191 if (n != std::size(PROCESSING_DONE))
1192 {
1193 SAL_WARN("desktop.app", "short write: " << n);
1194 continue;
1195 }
1196 }
1197 }
1198 else
1199 {
1200 {
1201 osl::MutexGuard aGuard( RequestHandler::GetMutex() );
1203 {
1204 break;
1205 }
1206 }
1207
1208 SAL_WARN( "desktop.app", "Error on accept: " << static_cast<int>(nError));
1209 TimeValue tval;
1210 tval.Seconds = 1;
1211 tval.Nanosec = 0;
1212 salhelper::Thread::wait( tval );
1213 }
1214 } while( schedule() );
1215}
1216
1218 std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
1219 std::optional< OUString > const & cwdUrl,
1220 std::vector< OUString > const & aRequestList,
1222 const OUString& aParam,
1223 const OUString& aFactory )
1224{
1225 for (auto const& request : aRequestList)
1226 {
1227 rDispatchList.push_back({nType, request, cwdUrl, aParam, aFactory});
1228 }
1229}
1230
1232 std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
1233 std::optional< OUString > const & cwdUrl,
1234 std::vector< OUString > const & rRequestList,
1235 const OUString& rParam,
1236 const OUString& rPrinterName,
1237 const OUString& rFactory,
1238 const OUString& rParamOut,
1239 std::u16string_view rImgOut,
1240 const bool isTextCat,
1241 const bool isScriptCat )
1242{
1244 OUString aParam( rParam );
1245
1246 if( !rParam.isEmpty() )
1247 {
1248 if ( isTextCat )
1250 else
1252 }
1253 else
1254 {
1255 if ( isScriptCat )
1257 else
1258 {
1260 aParam = rPrinterName;
1261 }
1262 }
1263
1264 OUString aPWD;
1265 if (cwdUrl)
1266 {
1267 aPWD = *cwdUrl;
1268 }
1269 else
1270 {
1272 }
1273
1274 if (OUString aOutDir(rParamOut.trim()); !aOutDir.isEmpty())
1275 {
1276 if (osl::FileBase::getAbsoluteFileURL(aPWD, rParamOut, aOutDir) == osl::FileBase::E_None)
1277 osl::FileBase::getSystemPathFromFileURL(aOutDir, aOutDir);
1278 aParam += ";" + aOutDir;
1279 }
1280 else
1281 {
1282 ::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD );
1283 aParam += ";" + aPWD;
1284 }
1285
1286 if( !rImgOut.empty() )
1287 aParam += OUString::Concat("|") + o3tl::trim(rImgOut);
1288
1289 for (auto const& request : rRequestList)
1290 {
1291 rDispatchList.push_back({nType, request, cwdUrl, aParam, rFactory});
1292 }
1293}
1294
1295namespace {
1296
1297struct ConditionSetGuard
1298{
1299 osl::Condition* m_pCondition;
1300 ConditionSetGuard(osl::Condition* pCondition) : m_pCondition(pCondition) {}
1301 ~ConditionSetGuard() { if (m_pCondition) m_pCondition->set(); }
1302};
1303
1304}
1305
1307 ProcessDocumentsRequest& aRequest, bool noTerminate)
1308{
1309 // protect the dispatch list
1310 osl::ClearableMutexGuard aGuard( GetMutex() );
1311
1312 // ensure that Processed flag (if exists) is signaled in any outcome
1313 ConditionSetGuard aSetGuard(aRequest.pcProcessed);
1314
1315 static std::vector<DispatchWatcher::DispatchRequest> aDispatchList;
1316
1317 // Create dispatch list for dispatch watcher
1318 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aInFilter, DispatchWatcher::REQUEST_INFILTER, "", aRequest.aModule );
1319 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, "", aRequest.aModule );
1320 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, "", aRequest.aModule );
1321 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, "", aRequest.aModule );
1322 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, "", aRequest.aModule );
1323 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
1324 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, "", aRequest.aModule );
1325 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, "", aRequest.aModule );
1326 AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut, aRequest.aImageConversionType, aRequest.bTextCat, aRequest.bScriptCat );
1327 bool bShutdown( false );
1328
1329 if ( pGlobal.is() )
1330 {
1331 if( ! pGlobal->AreRequestsEnabled() )
1332 {
1333 // Either starting, or downing - do not process the request, just try to bring Office to front
1334 ApplicationEvent* pAppEvent =
1336 ImplPostForeignAppEvent(pAppEvent);
1337 return bShutdown;
1338 }
1339
1340 pGlobal->mnPendingRequests += aDispatchList.size();
1341 if ( !pGlobal->mpDispatchWatcher.is() )
1342 {
1343 pGlobal->mpDispatchWatcher = new DispatchWatcher;
1344 }
1345 rtl::Reference<DispatchWatcher> dispatchWatcher(
1346 pGlobal->mpDispatchWatcher);
1347
1348 // copy for execute
1349 std::vector<DispatchWatcher::DispatchRequest> aTempList;
1350 aTempList.swap( aDispatchList );
1351
1352 aGuard.clear();
1353
1354 // Execute dispatch requests
1355 bShutdown = dispatchWatcher->executeDispatchRequests( aTempList, noTerminate);
1356 if (aRequest.mpbSuccess)
1357 *aRequest.mpbSuccess = true; // signal that we have actually succeeded
1358 }
1359
1360 return bShutdown;
1361}
1362
1363}
1364
1365/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
::boost::spirit::classic::rule< ScannerT > argument
XPropertyListType t
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
bool IsModuleInstalled(EModule eModule) const
OUString GetFactoryEmptyDocumentURL(EFactory eFactory) const
const OUString & GetFactoryName(EFactory eFactory) const
static void HandleAppEvent(const ApplicationEvent &rAppEvent)
Definition: app.cxx:2259
static CommandLineArgs & GetCommandLineArgs()
Definition: app.cxx:389
IpcThread(char const *name)
virtual ~IpcThread() override
void start(RequestHandler *handler)
RequestHandler * m_handler
virtual void close()=0
bool process(OString const &arguments, bool *waitProcessed)
static RequestHandler::Status enable(rtl::Reference< IpcThread > *thread)
PipeIpcThread(osl::Pipe pipe)
virtual ~PipeIpcThread() override
virtual OUString SAL_CALL getImplementationName() override
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual void SAL_CALL queryTermination(const css::lang::EventObject &aEvent) override
virtual void SAL_CALL notifyTermination(const css::lang::EventObject &aEvent) override
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
static rtl::Reference< RequestHandler > pGlobal
static Status Enable(bool ipc)
rtl::Reference< IpcThread > mIpcThread
static bool AreRequestsPending()
static void SetReady(bool bIsReady)
static bool ExecuteCmdLineRequests(ProcessDocumentsRequest &, bool noTerminate)
::osl::Condition cProcessed
virtual ~RequestHandler() override
Thread(char const *name)
static PathStatus locateUserInstallation(OUString &_rURL)
static bool getProcessWorkingDir(OUString &rUrl)
static OUString getUILocale()
int nCount
bool close
Mutex aMutex
const char * name
Sequence< PropertyValue > aArguments
void * p
sal_Int64 n
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
std::unique_ptr< sal_Int32[]> pData
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
Definition: app.cxx:165
static void ImplPostProcessDocumentsEvent(std::unique_ptr< ProcessDocumentsRequest > pEvent)
static void ImplPostForeignAppEvent(ApplicationEvent *pEvent)
static void AddConversionsToDispatchList(std::vector< DispatchWatcher::DispatchRequest > &rDispatchList, std::optional< OUString > const &cwdUrl, std::vector< OUString > const &rRequestList, const OUString &rParam, const OUString &rPrinterName, const OUString &rFactory, const OUString &rParamOut, std::u16string_view rImgOut, const bool isTextCat, const bool isScriptCat)
static OUString CreateMD5FromString(const OUString &aMsg)
static void AddToDispatchList(std::vector< DispatchWatcher::DispatchRequest > &rDispatchList, std::optional< OUString > const &cwdUrl, std::vector< OUString > const &aRequestList, DispatchWatcher::RequestType nType, const OUString &aParam, const OUString &aFactory)
IMPL_STATIC_LINK(Desktop, ImplInitFilterHdl, ::ConvertData &, rData, bool)
Definition: app.cxx:1727
oslSignalAction SalMainPipeExchangeSignal_impl(SAL_UNUSED_PARAMETER void *, oslSignalInfo *pInfo)
int i
std::u16string_view trim(std::u16string_view str)
end
con
sal_Int32 m_index
std::optional< OUString > m_cwdUrl
OString m_input
osl::Condition * m_pCondition
QPRO_FUNC_TYPE nType
std::vector< OUString > aPrintToList
std::vector< OUString > aPrintList
std::vector< OUString > aConversionList
std::vector< OUString > aViewList
std::vector< OUString > aOpenList
std::vector< OUString > aStartList
std::optional< OUString > aCwdUrl
std::vector< OUString > aInFilter
std::vector< OUString > aForceNewList
std::vector< OUString > aForceOpenList
unsigned char sal_uInt8
unsigned char sal_Bool
sal_uInt16 sal_Unicode
std::unique_ptr< char[]> aBuffer