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